2017-08-29 3 views
0

Ich versuche Cosinus-Ähnlichkeitswerte zwischen allen möglichen Kombinationen von Textdokumenten aus einem Korpus zu berechnen. Ich verwende scikit-learn's cosine_similarity Funktion, um dies zu tun. Da mein Korpus riesig ist (30 Millionen Dokumente), ist die Anzahl möglicher Kombinationen zwischen den Dokumenten im Korpus einfach zu hoch, um sie als Datenrahmen zu speichern. Daher möchte ich die Ähnlichkeitswerte unter Verwendung eines Schwellenwerts filtern, während sie erstellt werden, bevor sie in einem Datenrahmen für die zukünftige Verwendung gespeichert werden. Während ich das tue, möchte ich auch die entsprechenden IDs jedes dieser Dokumente den Index- und Spaltennamen des Datenrahmens zuweisen. Für einen Datenwert im Datenframe sollte jeder Wert Index- (Zeilen-) und Spaltennamen haben, die die Dokumenten-IDs sind, für die der Wert ein Kosinus-Ähnlichkeits-Score ist.Filtern von Kosinus-Ähnlichkeitswerten in einen Pandas-Datenrahmen

similarity_values = pd.DataFrame(cosine_similarity(tfidf_matrix), index = IDs, columns= IDs) 

Dieses Stück Code funktioniert gut ohne den Filterteil. IDs ist eine Listenvariable, bei der alle Dokument-IDs entsprechend der tfidf-Matrix sortiert sind.

Diese Änderung hilft beim Filtern, aber die Ähnlichkeitswerte werden in boolesche (Wahr/Falsch) -Werte umgewandelt. Wie kann ich anstelle der booleschen True/False-Werte die tatsächlichen Cosinus-Ähnlichkeitswerte beibehalten?

+0

cosine_similarity ausgibt eine quadratische Matrix ist und daß es möglich sein kann, dass in einer einzigen Spalte ein Wert> 0,65 und andere weniger. Also, in diesem Fall, wie willst du die Spalte im Dataframe erscheinen? –

+0

@VivekKumar Gute Frage. Ich möchte, dass der Datenrahmen alle Werte übereinander gestapelt hat. d.h. jede Zeile in dem Datenrahmen sollte nur einen Ähnlichkeitswert und die entsprechenden Dokument-IDs aufweisen. 'similarity_values ​​= similarity_values.stack(). reset_index(). umbenennen (columns = {'level_0': 'ID1', 'level_1': 'ID2', 0: 'Score'})' – Minu

Antwort

0

3E7 x 3E7 ist eine lächerliche Matrixgröße. Der einzige Weg, dies auf einem niedrigen Laptop/Desktop zu erreichen, ist die Verwendung von Generatoren, um den Speicherbedarf zu reduzieren und das Problem mit einigen Gedanken in Richtung Effizienz zu unterteilen.

Die folgende Funktion verwendet eine Generator Factory zu und verwendet eine doppelte for Schleife über ein kartesisches Produkt von Chunks. Wir berechnen die Normen für jede tfidf im Korpus vorberechnen.

Dies ist nicht dafür gedacht, die schnellste Lösung für die gleiche Aufgabe mit kleineren Daten zu sein. Dies soll diese Aufgabe im Gedächtnis einer einzigen bescheidenen Maschine erfüllen.

from scipy.sparse import coo_matrix 
import numpy as np 

def f(t, c, p=-1, v=False): 
    n = (t ** 2).sum(1) ** .5 
    g = lambda: ((x, t[x:x+c]) for x in range(0, t.shape[0], c)) 
    h = lambda a, b, i, j: a.dot(b.T)/n[i:i+c, None]/n[j:j+c] 
    d = lambda s: (s * (1 - np.eye(s.shape[0]))) 

    for i, a in g(): 
     for j, b in g(): 
      s = h(a, b, i, j) 
      if i == j: 
       s = d(s) 
      i_, j_ = np.where(s > p) 
      if v: 
       print('\r', 'i = {:0000000d}; j = {:0000000d}'.format(i, j), end='') 
      yield s[i_, j_], i_ + i, j_ + j 

Damit extrahieren wir die Cosinus Ähnlichkeiten von ihnen sich über jeden Unter chunk Berechnung und verfolgen, wo die Ähnlichkeiten als unsere Schwelle größer waren.

Schließlich übergeben wir die Koordinaten ausreichender Ähnlichkeit mit den Ähnlichkeiten zu einem dünn besetzten Matrixkonstruktor und weisen das Ergebnis dem Namen m zu. Wenn Sie die Matrixdarstellung benötigen, verwenden Sie m.toarray().

values, *ij = zip(*f(tfidf_matrix, 5000, .8, v=True)) 

values = np.concatenate(values) 
ij = list(map(np.concatenate, ij)) 

m = coo_matrix((values, ij)) 

Beachten Sie, dass ich die Diagonalen zero. Andernfalls, wenn wir einen Schwellenwert von -1 verwenden, würde dies genau dasselbe wie cosine_similarity von sklearn.metrics.pairwise erzeugen.

Validation von gleich Ness

from sklearn.metrics.pairwise import cosine_similarity 

tfidf_matrix = np.random.randint(10, size=(1000, 100)) 
s = cosine_similarity(tfidf_matrix) 

values, *ij = zip(*f(tfidf_matrix, 5000, -1, v=True)) 

values = np.concatenate(values) 
ij = list(map(np.concatenate, ij)) 

m = coo_matrix((values, ij)) 

# This should be equal the 1000. The number of 1's in the diagonal. 
(s - m.toarray()).sum() 

1000.0 
+0

Könnten Sie bitte im Detail erklären was genau das macht deine Funktion und was bedeutet jede Variable? – Minu

+0

Es tut mir leid, nein. Das hat lange gedauert. Ich habe einfach keine Zeit, alles im Detail zu erklären. Du wirst bemerken, dass sich niemand sonst die Zeit genommen hat. Das ist, weil es eine riesige Investition der Zeit ist. Ich werde dir das anbieten, ich unterteile die Matrix in Blöcke und speichere nur die Orte und Werte, die einen Grenzwert überschreiten. Darüber hinaus ist es eine Aufgabe, zu lernen, was jeder Teil des Codes tut. Außerdem könnte es Ihnen gut tun zu verstehen, dass wir unsere Zeit freiwillig zur Beantwortung von Fragen zur Verfügung stellen. Wir wollen unsere Zeit nicht verschwenden. Sie sollten es auch nicht versuchen. – piRSquared