Je cherche à travailler sur un projet NLP, dans n'importe quel langage de programmation (bien que Python sera ma préférence).
Je veux prendre deux documents et déterminer leur similitude.
Je cherche à travailler sur un projet NLP, dans n'importe quel langage de programmation (bien que Python sera ma préférence).
Je veux prendre deux documents et déterminer leur similitude.
Réponses:
La manière courante de procéder consiste à transformer les documents en vecteurs TF-IDF, puis à calculer la similitude en cosinus entre eux. Tout manuel sur la recherche d'informations (IR) couvre cela. Voir esp.Introduction à la recherche d'informations , qui est gratuite et disponible en ligne.
TF-IDF (et des transformations de texte similaires) sont implémentés dans les packages Python Gensim et scikit-learn . Dans ce dernier package, le calcul des similitudes cosinus est aussi simple que
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
ou, si les documents sont des chaînes simples,
>>> 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
mais Gensim peut avoir plus d'options pour ce genre de tâche.
Voir aussi cette question .
[Avertissement: J'ai participé à la mise en œuvre de TF-IDF scikit-learn.]
De dessus, pairwise_similarity
est une matrice clairsemée Scipy qui est de forme carrée, avec le nombre de lignes et de colonnes égal au nombre de documents dans le corpus.
>>> pairwise_similarity
<5x5 sparse matrix of type '<class 'numpy.float64'>'
with 17 stored elements in Compressed Sparse Row format>
Vous pouvez convertir le tableau fragmenté en tableau NumPy via .toarray()
ou .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. ]])
Disons que nous voulons trouver le document le plus similaire au document final, "Les documents scikit-learn sont Orange et Bleu". Ce document a un index de 4 po corpus
. Vous pouvez trouver l'index du document le plus similaire en prenant l'argmax de cette ligne, mais vous devrez d'abord masquer les 1, qui représentent la similitude de chaque document avec lui-même . Vous pouvez faire ce dernier à travers np.fill_diagonal()
, et le premier à travers 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'
Remarque: le but de l'utilisation d'une matrice clairsemée est d'économiser (une quantité substantielle d'espace) pour un grand corpus et vocabulaire. Au lieu de convertir en tableau NumPy, vous pouvez faire:
>>> n, _ = pairwise_similarity.shape
>>> pairwise_similarity[np.arange(n), np.arange(n)] = -1.0
>>> pairwise_similarity[input_idx].argmax()
3
X.mean(axis=0)
, puis calculer la distance euclidienne moyenne / maximale / médiane (∗) à partir de cette moyenne. (∗) Choisissez celui qui vous convient.
Identique à @larsman, mais avec un certain prétraitement
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
et lesquelles transform
?
C'est une vieille question, mais j'ai trouvé que cela peut être fait facilement avec Spacy . Une fois le document lu, une simple api similarity
peut être utilisée pour trouver la similitude cosinus entre les vecteurs de document.
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
Généralement, une similitude en cosinus entre deux documents est utilisée comme mesure de similitude des documents. En Java, vous pouvez utiliser Lucene (si votre collection est assez grande) ou LingPipe pour ce faire. Le concept de base serait de compter les termes dans chaque document et de calculer le produit scalaire des termes vecteurs. Les bibliothèques fournissent plusieurs améliorations par rapport à cette approche générale, par exemple en utilisant des fréquences de document inverses et en calculant des vecteurs tf-idf. Si vous cherchez à faire quelque chose de copmlex, LingPipe fournit également des méthodes pour calculer la similitude LSA entre les documents, ce qui donne de meilleurs résultats que la similitude cosinus. Pour Python, vous pouvez utiliser NLTK .
Si vous cherchez quelque chose de très précis, vous devez utiliser un meilleur outil que tf-idf. L'encodeur de phrases universel est l'un des plus précis pour trouver la similitude entre deux morceaux de texte. Google a fourni des modèles pré-formés que vous pouvez utiliser pour votre propre application sans avoir à vous entraîner à partir de rien. Tout d'abord, vous devez installer tensorflow et tensorflow-hub:
pip install tensorflow
pip install tensorflow_hub
Le code ci-dessous vous permet de convertir n'importe quel texte en une représentation vectorielle de longueur fixe, puis vous pouvez utiliser le produit scalaire pour découvrir la similitude entre eux
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)
et le code pour tracer:
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()
comme vous pouvez le voir, la plus grande similitude est entre les textes avec eux-mêmes puis avec leurs textes proches en termes de sens.
IMPORTANT : la première fois que vous exécutez le code, il sera lent car il doit télécharger le modèle. si vous voulez l'empêcher de télécharger à nouveau le modèle et d'utiliser le modèle local, vous devez créer un dossier pour le cache et l'ajouter à la variable d'environnement, puis après la première exécution, utilisez ce chemin:
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)
Plus d'informations: https://tfhub.dev/google/universal-sentence-encoder/2
Voici une petite application pour vous aider à démarrer ...
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)
Vous voudrez peut-être essayer ce service en ligne pour la similitude des documents cosinus 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
Si vous êtes plus intéressé à mesurer la similitude sémantique de deux morceaux de texte, je vous suggère de jeter un œil à ce projet gitlab . Vous pouvez l'exécuter en tant que serveur, il existe également un modèle prédéfini que vous pouvez utiliser facilement pour mesurer la similitude de deux morceaux de texte; même s'il est principalement formé pour mesurer la similitude de deux phrases, vous pouvez toujours l'utiliser dans votre cas.Il est écrit en java mais vous pouvez l'exécuter en tant que service RESTful.
Une autre option est également DKPro Similarity qui est une bibliothèque avec divers algorithmes pour mesurer la similitude des textes. Cependant, il est également écrit en java.
exemple de code:
// 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);
Pour trouver la similitude de phrase avec un ensemble de données très réduit et pour obtenir une grande précision, vous pouvez utiliser le package python ci-dessous qui utilise des modèles BERT pré-formés,
pip install similar-sentences
Pour la similitude syntaxique Il peut y avoir 3 façons simples de détecter la similitude.
Pour la similitude sémantique, on peut utiliser l'incorporation BERT et essayer différentes stratégies de regroupement de mots pour obtenir l'incorporation de documents, puis appliquer la similitude en cosinus à l'incorporation de documents.
Une méthodologie avancée peut utiliser BERT SCORE pour obtenir la similitude.
Lien vers le document de recherche: https://arxiv.org/abs/1904.09675