Sto cercando di lavorare su un progetto NLP, in qualsiasi linguaggio di programmazione (anche se Python sarà la mia preferenza).
Voglio prendere due documenti e determinare quanto sono simili.
Sto cercando di lavorare su un progetto NLP, in qualsiasi linguaggio di programmazione (anche se Python sarà la mia preferenza).
Voglio prendere due documenti e determinare quanto sono simili.
Risposte:
Il modo comune per farlo è trasformare i documenti in vettori TF-IDF e quindi calcolare la somiglianza del coseno tra loro. Qualsiasi libro di testo sul recupero delle informazioni (IR) copre questo. Vedi esp. Introduzione al recupero delle informazioni , che è gratuito e disponibile online.
TF-IDF (e trasformazioni di testo simili) sono implementati nei pacchetti Python Gensim e scikit-learn . In quest'ultimo pacchetto, calcolare le somiglianze del coseno è facile come
from sklearn.feature_extraction.text import TfidfVectorizer
documents = [open(f) for f in text_files]
tfidf = TfidfVectorizer().fit_transform(documents)
# no need to normalize, since Vectorizer will return normalized tf-idf
pairwise_similarity = tfidf * tfidf.T
o, se i documenti sono semplici stringhe,
>>> corpus = ["I'd like an apple",
... "An apple a day keeps the doctor away",
... "Never compare an apple to an orange",
... "I prefer scikit-learn to Orange",
... "The scikit-learn docs are Orange and Blue"]
>>> vect = TfidfVectorizer(min_df=1, stop_words="english")
>>> tfidf = vect.fit_transform(corpus)
>>> pairwise_similarity = tfidf * tfidf.T
sebbene Gensim possa avere più opzioni per questo tipo di attività.
Vedi anche questa domanda .
[Dichiarazione di non responsabilità: sono stato coinvolto nell'implementazione TF-IDF di scikit-learn.]
Dall'alto, pairwise_similarity
è una matrice sparsa di Scipy di forma quadrata, con il numero di righe e colonne uguale al numero di documenti nel corpus.
>>> pairwise_similarity
<5x5 sparse matrix of type '<class 'numpy.float64'>'
with 17 stored elements in Compressed Sparse Row format>
È possibile convertire l'array sparse in un array NumPy tramite .toarray()
o .A
:
>>> pairwise_similarity.toarray()
array([[1. , 0.17668795, 0.27056873, 0. , 0. ],
[0.17668795, 1. , 0.15439436, 0. , 0. ],
[0.27056873, 0.15439436, 1. , 0.19635649, 0.16815247],
[0. , 0. , 0.19635649, 1. , 0.54499756],
[0. , 0. , 0.16815247, 0.54499756, 1. ]])
Diciamo che vogliamo trovare il documento più simile al documento finale, "I documenti di scikit-learn sono Orange e Blue". Questo documento ha un indice di 4 pollici corpus
. Puoi trovare l'indice del documento più simile prendendo l'argmax di quella riga, ma prima dovrai mascherare gli 1, che rappresentano la somiglianza di ciascun documento con se stesso . Puoi fare il secondo np.fill_diagonal()
e il primo attraverso np.nanargmax()
:
>>> import numpy as np
>>> arr = pairwise_similarity.toarray()
>>> np.fill_diagonal(arr, np.nan)
>>> input_doc = "The scikit-learn docs are Orange and Blue"
>>> input_idx = corpus.index(input_doc)
>>> input_idx
4
>>> result_idx = np.nanargmax(arr[input_idx])
>>> corpus[result_idx]
'I prefer scikit-learn to Orange'
Nota: lo scopo dell'utilizzo di una matrice sparsa è di risparmiare (una notevole quantità di spazio) per un corpus e un vocabolario di grandi dimensioni. Invece di convertire in un array NumPy, potresti fare:
>>> n, _ = pairwise_similarity.shape
>>> pairwise_similarity[np.arange(n), np.arange(n)] = -1.0
>>> pairwise_similarity[input_idx].argmax()
3
X.mean(axis=0)
medie ) , quindi calcolare la distanza euclidea media / massima / mediana (∗) da quella media. (∗) Scegli quello che ti piace.
Identico a @larsman, ma con qualche pre-elaborazione
import nltk, string
from sklearn.feature_extraction.text import TfidfVectorizer
nltk.download('punkt') # if necessary...
stemmer = nltk.stem.porter.PorterStemmer()
remove_punctuation_map = dict((ord(char), None) for char in string.punctuation)
def stem_tokens(tokens):
return [stemmer.stem(item) for item in tokens]
'''remove punctuation, lowercase, stem'''
def normalize(text):
return stem_tokens(nltk.word_tokenize(text.lower().translate(remove_punctuation_map)))
vectorizer = TfidfVectorizer(tokenizer=normalize, stop_words='english')
def cosine_sim(text1, text2):
tfidf = vectorizer.fit_transform([text1, text2])
return ((tfidf * tfidf.T).A)[0,1]
print cosine_sim('a little bird', 'a little bird')
print cosine_sim('a little bird', 'a little bird chirps')
print cosine_sim('a little bird', 'a big dog barks')
fit
e quali transform
?
È una vecchia domanda, ma ho scoperto che questo può essere fatto facilmente con Spacy . Una volta letto il documento, è similarity
possibile utilizzare una semplice API per trovare la somiglianza del coseno tra i vettori del documento.
import spacy
nlp = spacy.load('en')
doc1 = nlp(u'Hello hi there!')
doc2 = nlp(u'Hello hi there!')
doc3 = nlp(u'Hey whatsup?')
print doc1.similarity(doc2) # 0.999999954642
print doc2.similarity(doc3) # 0.699032527716
print doc1.similarity(doc3) # 0.699032527716
Generalmente una somiglianza del coseno tra due documenti viene utilizzata come misura di somiglianza dei documenti. In Java, puoi usare Lucene (se la tua collezione è piuttosto grande) o LingPipe per farlo. Il concetto di base sarebbe quello di contare i termini in ogni documento e calcolare il punto prodotto dei termini vettori. Le biblioteche offrono numerosi miglioramenti rispetto a questo approccio generale, ad esempio utilizzando frequenze inverse di documenti e calcolando vettori tf-idf. Se stai cercando di fare qualcosa di copmlex, LingPipe fornisce anche metodi per calcolare la somiglianza LSA tra i documenti che fornisce risultati migliori rispetto alla somiglianza del coseno. Per Python, puoi usare NLTK .
Se stai cercando qualcosa di molto preciso, devi usare uno strumento migliore di tf-idf. Il codificatore di frasi universale è uno dei più precisi per trovare la somiglianza tra due parti di testo. Google ha fornito modelli predefiniti che è possibile utilizzare per la propria applicazione senza la necessità di allenarsi da zero. Innanzitutto, devi installare tensorflow e tensorflow-hub:
pip install tensorflow
pip install tensorflow_hub
Il codice seguente consente di convertire qualsiasi testo in una rappresentazione vettoriale a lunghezza fissa e quindi è possibile utilizzare il prodotto punto per scoprire la somiglianza tra loro
import tensorflow_hub as hub
module_url = "https://tfhub.dev/google/universal-sentence-encoder/1?tf-hub-format=compressed"
# Import the Universal Sentence Encoder's TF Hub module
embed = hub.Module(module_url)
# sample text
messages = [
# Smartphones
"My phone is not good.",
"Your cellphone looks great.",
# Weather
"Will it snow tomorrow?",
"Recently a lot of hurricanes have hit the US",
# Food and health
"An apple a day, keeps the doctors away",
"Eating strawberries is healthy",
]
similarity_input_placeholder = tf.placeholder(tf.string, shape=(None))
similarity_message_encodings = embed(similarity_input_placeholder)
with tf.Session() as session:
session.run(tf.global_variables_initializer())
session.run(tf.tables_initializer())
message_embeddings_ = session.run(similarity_message_encodings, feed_dict={similarity_input_placeholder: messages})
corr = np.inner(message_embeddings_, message_embeddings_)
print(corr)
heatmap(messages, messages, corr)
e il codice per la stampa:
def heatmap(x_labels, y_labels, values):
fig, ax = plt.subplots()
im = ax.imshow(values)
# We want to show all ticks...
ax.set_xticks(np.arange(len(x_labels)))
ax.set_yticks(np.arange(len(y_labels)))
# ... and label them with the respective list entries
ax.set_xticklabels(x_labels)
ax.set_yticklabels(y_labels)
# Rotate the tick labels and set their alignment.
plt.setp(ax.get_xticklabels(), rotation=45, ha="right", fontsize=10,
rotation_mode="anchor")
# Loop over data dimensions and create text annotations.
for i in range(len(y_labels)):
for j in range(len(x_labels)):
text = ax.text(j, i, "%.2f"%values[i, j],
ha="center", va="center", color="w",
fontsize=6)
fig.tight_layout()
plt.show()
come vedi la maggior somiglianza è tra i testi con se stessi e poi con i loro testi vicini nel significato.
IMPORTANTE : la prima volta che si esegue il codice sarà lento perché deve scaricare il modello. se si desidera impedire che scarichi nuovamente il modello e utilizzare il modello locale, è necessario creare una cartella per la cache e aggiungerla alla variabile di ambiente, quindi dopo la prima esecuzione utilizzare quel percorso:
tf_hub_cache_dir = "universal_encoder_cached/"
os.environ["TFHUB_CACHE_DIR"] = tf_hub_cache_dir
# pointing to the folder inside cache dir, it will be unique on your system
module_url = tf_hub_cache_dir+"/d8fbeb5c580e50f975ef73e80bebba9654228449/"
embed = hub.Module(module_url)
Ulteriori informazioni: https://tfhub.dev/google/universal-sentence-encoder/2
Ecco una piccola app per iniziare ...
import difflib as dl
a = file('file').read()
b = file('file1').read()
sim = dl.get_close_matches
s = 0
wa = a.split()
wb = b.split()
for i in wa:
if sim(i, wb):
s += 1
n = float(s) / float(len(wa))
print '%d%% similarity' % int(n * 100)
Potresti provare questo servizio online per la somiglianza dei documenti del coseno http://www.scurtu.it/documentSimilarity.html
import urllib,urllib2
import json
API_URL="http://www.scurtu.it/apis/documentSimilarity"
inputDict={}
inputDict['doc1']='Document with some text'
inputDict['doc2']='Other document with some text'
params = urllib.urlencode(inputDict)
f = urllib2.urlopen(API_URL, params)
response= f.read()
responseObject=json.loads(response)
print responseObject
Se sei più interessato a misurare la somiglianza semantica di due pezzi di testo, ti suggerisco di dare un'occhiata a questo progetto gitlab . Puoi eseguirlo come un server, c'è anche un modello pre-costruito che puoi usare facilmente per misurare la somiglianza di due pezzi di testo; anche se è principalmente addestrato per misurare la somiglianza di due frasi, puoi comunque usarlo nel tuo caso. È scritto in Java ma puoi eseguirlo come servizio RESTful.
Un'altra opzione è anche la somiglianza DKPro che è una libreria con vari algoritmi per misurare la somiglianza dei testi. Tuttavia, è anche scritto in Java.
esempio di codice:
// this similarity measure is defined in the dkpro.similarity.algorithms.lexical-asl package
// you need to add that to your .pom to make that example work
// there are some examples that should work out of the box in dkpro.similarity.example-gpl
TextSimilarityMeasure measure = new WordNGramJaccardMeasure(3); // Use word trigrams
String[] tokens1 = "This is a short example text .".split(" ");
String[] tokens2 = "A short example text could look like that .".split(" ");
double score = measure.getSimilarity(tokens1, tokens2);
System.out.println("Similarity: " + score);
Per trovare la somiglianza delle frasi con un set di dati molto minore e per ottenere un'elevata precisione, puoi utilizzare il pacchetto Python di seguito che utilizza modelli BERT pre-addestrati,
pip install similar-sentences
Per la somiglianza sintattica Esistono 3 semplici modi per rilevare la somiglianza.
Per la somiglianza semantica Si può usare l'incorporamento BERT e provare diverse strategie di raggruppamento di parole per ottenere l'incorporamento dei documenti e quindi applicare la somiglianza del coseno sull'incorporamento dei documenti.
Una metodologia avanzata può utilizzare BERT SCORE per ottenere la somiglianza.
Link al documento di ricerca: https://arxiv.org/abs/1904.09675