2016-10-16 2 views
2

Ich versuche, eine Klassifizierungsübung für E-Mail-Dokumente (Strings mit Wörtern) durchzuführen.Wie man die Entfernungsfunktion in Python verbessert

I definiert die Abstandsfunktion wie folgend:

def distance(wordset1, wordset2): 

if len(wordset1) < len(wordset2): 
    return len(wordset2) - len(wordset1) 
elif len(wordset1) > len(wordset2): 
    return len(wordset1) - len(wordset2) 
elif len(wordset1) == len(wordset2): 
    return 0  

jedoch die Genauigkeit am Ende ziemlich niedrig ist (0,8). Ich schätze das liegt an der nicht so genauen Distanzfunktion. Wie kann ich die Funktion verbessern? Oder was sind andere Möglichkeiten, um die "Entfernung" zwischen E-Mail-Dokumenten zu berechnen?

+1

Vielleicht kann die Länge der Kreuzung beider Wortsätze eine bessere Abstandsfunktion sein. –

+2

Diese Funktion könnte eine Zeile sein. 'math.abs (len1 - len2)' –

Antwort

1

Sie haben den Typ wordset1 und wordset2 nicht erwähnt. Ich nehme an, sie sind beide strings.

Sie haben Ihre Entfernung als Wortzählung definiert und eine schlechte Bewertung erhalten. Es ist offensichtlich, dass die Textlänge kein gutes Unähnlichkeitsmaß ist: zwei E-Mails unterschiedlicher Größe können über dasselbe sprechen, während zwei E-Mails gleicher Größe über ganz andere Dinge sprechen.

So, wie oben vorgeschlagen, können Sie versuchen, und stattdessen nach ähnlichen Worten überprüfen:

import numpy as np 

def distance(wordset1, wordset2): 
    wordset1 = set(wordset1.split()) 
    wordset2 = set(wordset2.split()) 

    common_words = wordset1 & wordset2 
    if common_words: 
     return 1/len(common_words) 
    else: 
     # They don't share any word. They are infinitely different. 
     return np.inf 

Das Problem dabei ist, dass zwei große E-Mails sind eher Worte als zwei kleine teilen und diese Metrik würde diejenigen bevorzugen, die sie "ähnlicher machen" als die Kleinen. Wie lösen wir das? Naja, wir normalisieren die Metrik irgendwie:

Das scheint cool, aber ignoriert völlig die FREQUENZ der Wörter. Um dies zu berücksichtigen, können wir das Modell Bag-of-words verwenden. Das heißt, erstellen Sie eine Liste aller möglichen Wörter und Histogramm ihre Erscheinung in jedem Dokument. Lassen Sie uns CountVectorizer Implementierung von verwenden Scikit-Learn unsere Aufgabe eaiser zu machen:

from sklearn.feature_extraction.text import CountVectorizer 

def distance(wordset1, wordset2): 
    model = CountVectorizer() 
    X = model.fit_transform([wordset1, wordset2]).toarray() 

    # uses Euclidean distance between bags. 
    return np.linalg.norm(X[0] - X[1]) 

Aber jetzt zwei Paare von E-Mails betrachten. Die E-Mails in dem ersten Paar bestehen aus perfekt geschriebenem Englisch, das mit "kleinen" Wörtern gefüllt ist (z. B. a, an, and, that), die notwendig sind, damit es grammatikalisch korrekt ist. Die E-Mails im zweiten Paar sind anders: nur die Keywords enthalten, ist es extrem trocken. Sie sehen, die Chancen stehen gut, dass das erste Paar ähnlicher ist als das zweite. Das passiert, weil wir derzeit alle Wörter gleich schreiben, während wir die BEDEUTENDEN Wörter in jedem Text priorisieren sollten. Um das zu tun, verwenden wir term frequency–inverse document frequency. Luckly, gibt es eine sehr ähnliche Implementierung in Scikit-Learn:

from sklearn.feature_extraction.text import TfidfVectorizer 

def distance(wordset1, wordset2): 
    model = TfidfVectorizer() 
    X = model.fit_transform([wordset1, wordset2]).toarray() 

    similarity_matrix = X.dot(X.T) 
    # The dissimilarity between samples wordset1 and wordset2. 
    return 1-similarity_matrix[0, 1] 

mehr dazu lesen Sie in diesem question. Auch duplizieren?

Sie sollten jetzt eine ziemlich gute Genauigkeit haben. Versuch es.Wenn es immer noch nicht so gut ist, wie du willst, dann müssen wir tiefer gehen ... (verstanden? Weil ... Deep-Learning). Die erste Sache ist, dass wir entweder einen Datensatz zum Trainieren benötigen oder ein bereits trainiertes Modell. Dies ist erforderlich, weil Netzwerke viele Parameter haben, die angepasst werden müssen, um nützliche Transformationen bereitzustellen.

Was bis jetzt fehlt, ist VERSTÄNDNIS. Wir histogrammierten die Wörter und streiften sie von jedem Kontext oder jeder Bedeutung. Stattdessen sollten wir sie dort belassen, wo sie sind, und Blockmuster erkennen. Wie kann das gemacht werden?

  1. Betten Sie die Wörter in Zahlen ein, die sich mit den verschiedenen Wortgrößen befassen.
  2. Fügen Sie jede Nummer (Wort eingebettet) Sequenz auf eine einzige Länge.
  3. Verwenden Sie konvolutionelle Netzwerke, um wichtige Funktionen aus Sequenzen zu extrahieren.
  4. Verwenden Sie vollständig verbundene Netzwerke, um die extrahierten Features in einen Bereich zu projizieren, der die Entfernung zwischen ähnlichen E-Mails minimiert und den Abstand zwischen nichtähnlichen E-Mails maximiert.

Lassen Sie uns Keras einfach unser Leben verwenden. Es sollte wie folgt aussehen:

# ... imports and params definitions 

model = Sequential([ 
    Embedding(max_features, 
       embedding_dims, 
       input_length=maxlen, 
       dropout=0.2), 
    Convolution1D(nb_filter=nb_filter, 
        filter_length=filter_length, 
        border_mode='valid', 
        activation='relu', 
        subsample_length=1), 
    MaxPooling1D(pool_length=model.output_shape[1]), 
    Flatten(), 
    Dense(256, activation='relu'), 
]) 

# ... train or load model weights. 

def distance(wordset1, wordset2): 
    global model 
    # X = ... # Embed both emails. 
    X = sequence.pad_sequences(X, maxlen=maxlen) 
    y = model.predict(X) 
    # Euclidean distance between emails. 
    return np.linalg.norm(y[0]-y[1]) 

Es gibt ein praktisches Beispiel für Satzverarbeitung, die Sie Keras github repo überprüfen können. Auch jemand löst genau dieses Problem mit einem siamesischen wiederkehrenden Netzwerk in diesem stackoverflow question.

Nun, ich hoffe, das gibt Ihnen eine Richtung. :-)

+0

Vielen Dank! Wenn ich versuche, dies zu implementieren, erhalte ich den Fehler: – trika

+0

Entschuldigung dafür! Ich habe es in dem Beispiel behoben. Beschreibung des Fehlers: '.fit_transform (...)' Methode gibt eine dünn besetzte Matrix zurück. Die Punktoperation kann dann zu einem "ValueError: Dimensionskonflikt" führen. Um es zu beheben, habe ich es in '.fit_transform (...). Toarray()' geändert, was eine dichte Matrix zurückgibt. :-) – ldavid

2

Ein gängiges Maß für die Ähnlichkeit in dieser Situation ist die Jaccard similarity. Es reicht von 0 bis 1, wobei 0 vollständige Unähnlichkeit anzeigt und 1 bedeutet, dass die beiden Dokumente identisch sind. Sie ist definiert als

wordSet1 = set(wordSet1) 
wordSet2 = set(wordSet2) 
sim = len(wordSet1.intersection(wordSet2))/len(wordSet1.union(wordSet2)) 

Im Wesentlichen ist es das Verhältnis des Schnittpunktes der Sätze von Worten zu dem Verhältnis der Vereinigung der Mengen von Worten. Dies hilft bei der Kontrolle von E-Mails, die unterschiedliche Größen haben und dennoch ein gutes Maß an Ähnlichkeit bieten.

+0

Aber dann wird die Entfernung nicht größer sein, je unterschiedlicher die Dokumente sind. Das ist hilfreich beim Mappen der Dokumente. Oder ist das egal? – trika

+0

@trika Es kommt darauf an, was "E-Mail-Entfernung" zu Ihnen ist. Wenn das Größenunterschied bedeutet, dann unterdrückt diese Lösung das. In der Regel interessieren wir uns jedoch für semantische Unterschiede (d. H. "Sprechen diese beiden E-Mails über dasselbe?"). In diesen Fällen spielt die Größe keine Rolle. – ldavid

+1

Kannst du 2 Sets teilen? Gibt es einen Tippfehler? 'TypeError: nicht unterstützte Operandentypen für /: 'set' und 'set'' –

Verwandte Themen