2017-08-29 12 views
0

J'essaie de calculer des scores de similarité de cosinus entre toutes les combinaisons possibles de documents texte d'un corpus. J'utilise la fonction cosine_similarity de scikit-learn pour ce faire. Comme mon corpus est énorme (30 millions de documents), le nombre de combinaisons possibles entre les documents du corpus est trop important pour être stocké en tant que dataframe. Donc, je voudrais filtrer les scores de similarité en utilisant un seuil, au moment où ils sont créés, avant de les stocker dans une base de données pour une utilisation future. Pendant que je fais cela, je veux aussi assigner les identifiants correspondants de chacun de ces documents aux noms d'index et de colonnes de la base de données. Ainsi, pour une valeur de données dans la trame de données, chaque valeur doit avoir des noms d'index (ligne) et de colonne qui sont les ID de document pour lesquels la valeur est un score de similarité cosinus.Filtrage des scores de similarité de cosinus dans un cadre de données pandas

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

Cette pièce de code fonctionne bien sans la partie filtrante. IDs est une variable de liste dont tous les ID de document sont triés en fonction de la matrice tfidf.

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

Cette modification permet le filtrage mais les scores de similarité sont transformées en valeurs booléennes (Vrai/Faux). Comment puis-je conserver les scores de similarité cosinus ici à la place des valeurs booléennes Vrai/Faux.

+0

cosine_similarity va afficher une matrice carrée et en ce qu 'il peut être possible que dans une seule colonne une valeur est> 0,65 et d'autres sont moins. Alors, dans ce cas, comment voulez-vous que la colonne apparaisse dans la base de données? –

+0

@VivekKumar Bonne question. Je souhaite que toutes les valeurs de l'ensemble de données soient empilées les unes sur les autres. c'est-à-dire que chaque rangée de la trame de données devrait avoir un seul score de similarité et les ID de document correspondants. 'similarity_values ​​= similarity_values.stack(). reset_index(). rename (colonnes = {'level_0': 'ID1', 'level_1': 'ID2', 0: 'Score'})' – Minu

Répondre

0

3E7 x 3E7 est une taille de matrice ridicule. La seule façon d'obtenir ce résultat sur un ordinateur portatif ou de bureau est d'utiliser des générateurs pour réduire l'empreinte mémoire et subdiviser le problème en pensant à l'efficacité.

La fonction suivante utilise une fabrique de générateurs et utilise une double boucle for sur un produit cartésien de blocs. Nous précalculons les normes pour chaque tfidf dans le corpus.

Ceci n'est pas conçu pour être la solution la plus rapide pour effectuer cette même tâche avec des données plus petites. Ceci est destiné à accomplir cette tâche dans la mémoire d'une seule machine modeste.

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 

Avec que nous extrayons les similitudes cosinus en les calculant nous sur chaque sous-bloc et de suivre les similitudes étaient supérieurs à notre seuil.

Enfin, nous passons les coordonnées de similarité suffisante avec les similitudes avec un constructeur de matrice clairsemée et affecter le résultat au nom m. Si vous avez besoin de la représentation matricielle, utilisez 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)) 

Notez que je mets à zéro les diagonales. Sinon, si nous utilisons un seuil de -1, cela produirait exactement la même chose que cosine_similarity de sklearn.metrics.pairwise.

Validation de même-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

Pourriez-vous s'il vous plaît expliquer en détail ce que exactement votre fonction est en train de faire, et ce que chaque variable signifie? – Minu

+0

Je suis désolé, non. Cela a pris beaucoup de temps à mettre ensemble. Je n'ai tout simplement pas le temps de passer en revue et de tout expliquer en détail. Vous remarquerez que personne d'autre n'a pris le temps. C'est parce que c'est un énorme investissement de temps. Je vais vous offrir ceci, je suis en train de subdiviser la matrice en morceaux et de ne sauvegarder que les emplacements et les valeurs qui dépassent un seuil. Au-delà de cela, prenez comme une tâche d'apprendre ce que chaque partie du code fait. En outre, il pourrait vous être utile de comprendre que nous consacrons notre temps à répondre aux questions. Nous ne voulons pas perdre notre temps. Tu devrais essayer de ne pas le faire non plus. – piRSquared