2013-03-02 13 views
49

Da Python: tf-idf-cosine: to find document similarity, è possibile calcolare la somiglianza del documento utilizzando tf-idf coseno. Senza importare librerie esterne, ci sono modi per calcolare la similarità del coseno tra 2 stringhe?Calcolare la somiglianza del coseno data stringhe di 2 frasi

s1 = "This is a foo bar sentence ." 
s2 = "This sentence is similar to a foo bar sentence ." 
s3 = "What is this string ? Totally not related to the other two lines ." 

cosine_sim(s1, s2) # Should give high cosine similarity 
cosine_sim(s1, s3) # Shouldn't give high cosine similarity value 
cosine_sim(s2, s3) # Shouldn't give high cosine similarity value 
+1

Non ho la risposta, ma qualcosa come word2vec (https://code.google.com/p/word2vec/) sarebbe probabilmente un buon inizio se si desidera ottenere risultati significativi. –

risposta

107

Una semplice implementazione puro Python sarebbe:

import re, math 
from collections import Counter 

WORD = re.compile(r'\w+') 

def get_cosine(vec1, vec2): 
    intersection = set(vec1.keys()) & set(vec2.keys()) 
    numerator = sum([vec1[x] * vec2[x] for x in intersection]) 

    sum1 = sum([vec1[x]**2 for x in vec1.keys()]) 
    sum2 = sum([vec2[x]**2 for x in vec2.keys()]) 
    denominator = math.sqrt(sum1) * math.sqrt(sum2) 

    if not denominator: 
     return 0.0 
    else: 
     return float(numerator)/denominator 

def text_to_vector(text): 
    words = WORD.findall(text) 
    return Counter(words) 

text1 = 'This is a foo bar sentence .' 
text2 = 'This sentence is similar to a foo bar sentence .' 

vector1 = text_to_vector(text1) 
vector2 = text_to_vector(text2) 

cosine = get_cosine(vector1, vector2) 

print 'Cosine:', cosine 

stampe:

Cosine: 0.861640436855 

La formula coseno utilizzato qui è descritta here.

Questo non include la ponderazione delle parole di tf-idf, ma per utilizzare tf-idf, è necessario disporre di un corpus abbastanza grande da cui valutare i pesi di tfidf.

È possibile anche sviluppare ulteriormente, utilizzando un modo più sofisticato per estrarre parole da un pezzo di testo, arginare o lemmatizzare esso, ecc

+1

Che ne dite di "nutrire i felini sui topi" e "i roditori sono spesso mangiati dai gatti"? Il codice restituisce erroneamente 0. – mbatchkarov

+48

Sicuramente, una domanda SO non è il posto per risolvere definitivamente il problema della modellazione della somiglianza semantica delle frasi. La domanda riguarda la misurazione della somiglianza (superficiale) tra due bit di testo, questo è ciò che fa il codice. – vpekar

+7

Il codice restituisce 0, correttamente, perché misura la somiglianza di superficie di due testi, non misura il significato in quanto tale. – vpekar

43

La risposta breve è "no, non è possibile farlo in un modo di principio che funziona anche lontanamente bene". È un problema irrisolto nella ricerca sull'elaborazione del linguaggio naturale ed è anche il tema del mio dottorato. Cercherò di riassumere molto brevemente dove siamo e puntare a un paio di pubblicazioni:

significato delle parole

Il presupposto più importante qui è che è possibile ottenere un vettore che rappresenta ogni parola nella frase in questione. Questo vettore viene solitamente scelto per catturare i contesti in cui la parola può apparire. Ad esempio, se consideriamo solo i tre contesti "mangia", "rosso" e "soffice", la parola "gatto" potrebbe essere rappresentata come [98, 1 , 87], perché se dovessi leggere un pezzo di testo molto lungo (qualche miliardo di parole non è raro per gli standard odierni), la parola "gatto" apparirebbe molto spesso nel contesto di "birichino" e "mangia" , ma non così spesso nel contesto del "rosso". Allo stesso modo, "cane" potrebbe essere rappresentato come [87,2,34] e "ombrello" potrebbe essere [1,13,0]. Immaginando questi vettori come punti nello spazio 3D, "gatto" è chiaramente più vicino a "cane" che a "ombrello", quindi "gatto" significa anche qualcosa di più simile a "cane" che a un "ombrello".

Questa linea di lavoro è stata studiata fin dai primi anni '90 (ad esempio this di Greffenstette) e ha prodotto risultati sorprendentemente buoni. Ad esempio, ecco un paio di voci casuali in un thesaurus ho costruito di recente per avere il mio computer leggono wikipedia:

theory -> analysis, concept, approach, idea, method 
voice -> vocal, tone, sound, melody, singing 
james -> william, john, thomas, robert, george, charles 

Questi elenchi di parole simili sono stati ottenuti del tutto priva di umana intervention- si alimenta testo in e torni a pochi ore dopo.

Il problema con frasi

Si potrebbe chiedere perché non stiamo facendo la stessa cosa per le frasi più lunghe, come ad esempio "volpi zenzero amano la frutta". È perché non abbiamo abbastanza testo. Per fare in modo che in modo affidabile stabilisca quale X è simile a, abbiamo bisogno di vedere molti esempi di X usati nel contesto. Quando X è una singola parola come "voce", questo non è troppo difficile. Tuttavia, quando X si allunga, le possibilità di trovare le occorrenze naturali di X diventano esponenzialmente più lente. Per fare un confronto, Google ha circa 1B di pagine contenenti la parola "volpe" e non una singola pagina che contiene "zenzero volpe amore frutto", nonostante sia una frase inglese perfettamente valida e tutti noi capiamo cosa significa.

Composizione

Per affrontare il problema della scarsità di dati, si desidera eseguire la composizione, cioè di prendere vettori per le parole, che sono facili da ottenere da un testo vero e proprio, e di mettere il insieme in un modo che cattura il loro significato. La cattiva notizia è che nessuno è stato in grado di farlo bene finora.

Il modo più semplice e più ovvio è di aggiungere o moltiplicare i singoli vettori di parole. Questo porta a effetti collaterali indesiderati che "i cani inseguono i cani" e "i cani inseguono i gatti" significherebbe lo stesso per il vostro sistema. Inoltre, se stai moltiplicando, devi essere molto attento o ogni frase finirà per essere rappresentata da [0,0,0, ..., 0], che sconfigge il punto.

Ulteriore lettura

Non voglio discutere i metodi più sofisticati per la composizione che sono stati proposti finora. Ti suggerisco di leggere lo "Vector space models of word meaning and phrase meaning: a survey" di Katrin Erk. Questo è un ottimo sondaggio di alto livello per iniziare. Sfortunatamente, non è disponibile gratuitamente sul sito Web dell'editore, invia direttamente l'autore all'autore per ottenerne una copia. In quel documento troverai riferimenti a molti altri metodi concreti. I più comprensibili sono da Mitchel and Lapata (2008) e Baroni and Zamparelli (2010).


Edit dopo commento di @vpekar: La linea di fondo di questa risposta è sottolineare il fatto che, mentre metodi naive esistono (es addizione, moltiplicazione, somiglianza superficiale, ecc), questi sono fondamentalmente imperfetto e in generale non ci si deve aspettare grandi prestazioni da loro.

+0

Per curiosità, quale approccio hai usato per costruire il thesaurus? – JesseBuesking

+2

È un thesaurus distributivo, creato con [Byblo] (https://github.com/MLCL/Byblo).In questa particolare istanziazione ciascun token ha come caratteristiche gli altri token che si presentano in una finestra di 5 parole attorno ad esso in tutta la Wikipedia, e la somiglianza è calcolata in base a queste caratteristiche. Abbiamo costruito altri thesauri dove le caratteristiche sono le altre parole con cui la parola target ha relazioni grammaticali. Questo in genere funziona meglio, ma richiede almeno un'analisi parziale del corpus, che richiede molto tempo. – mbatchkarov

1

Grazie @vpekar per l'implementazione. Ha aiutato molto. Ho appena scoperto che manca il peso tf-idf mentre si calcola la somiglianza del coseno. Il contatore (parola) restituisce un dizionario che contiene l'elenco di parole insieme al loro verificarsi.

cos (q, d) = sim (q, d) = (q · d)/(| q || d |) = (somma (qi, di)/(sqrt (somma (qi2))) * (sqrt (sum (vi2))) dove i = 1 a v)

  • qi è il peso tf-idf del termine i nella query.
  • di è il tf-idf
  • peso del termine i nel documento. | Q | e | d | sono le lunghezze di q e d.
  • Questa è la somiglianza del coseno di qe d. . . . . . oppure, in modo equivalente, il coseno dell'angolo tra qe d.

Non esitate a vedere il mio codice here. Ma prima dovrai scaricare il pacchetto anaconda. Verrà automaticamente impostato il tuo percorso python in Windows. Aggiungi questo interprete python in Eclipse.