Patrzę na pracę nad projektem NLP w dowolnym języku programowania (choć wolę Python).
Chcę wziąć dwa dokumenty i ustalić ich podobieństwo.
Patrzę na pracę nad projektem NLP w dowolnym języku programowania (choć wolę Python).
Chcę wziąć dwa dokumenty i ustalić ich podobieństwo.
Odpowiedzi:
Częstym sposobem na to jest przekształcenie dokumentów w wektory TF-IDF, a następnie obliczenie podobieństwa cosinus między nimi. Każdy podręcznik dotyczący wyszukiwania informacji (IR) to obejmuje. Zobacz esp. Wprowadzenie do wyszukiwania informacji , które jest bezpłatne i dostępne online.
TF-IDF (i podobne transformacje tekstu) są zaimplementowane w pakietach Python Gensim i scikit-learn . W drugim pakiecie obliczanie podobieństw cosinusa jest równie łatwe jak
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
lub, jeśli dokumenty są zwykłymi ciągami,
>>> 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
chociaż Gensim może mieć więcej opcji dla tego rodzaju zadań.
Zobacz także to pytanie .
[Oświadczenie: Byłem zaangażowany w implementację TF-IDF scikit-learn.]
Z góry pairwise_similarity
jest rzadka macierz Scipy , która ma kwadratowy kształt, a liczba wierszy i kolumn jest równa liczbie dokumentów w korpusie.
>>> pairwise_similarity
<5x5 sparse matrix of type '<class 'numpy.float64'>'
with 17 stored elements in Compressed Sparse Row format>
Możesz przekonwertować rzadką tablicę na tablicę NumPy za pomocą .toarray()
lub .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. ]])
Powiedzmy, że chcemy znaleźć dokument najbardziej podobny do dokumentu końcowego: „Dokumenty scikit-learn są pomarańczowe i niebieskie”. Ten dokument ma indeks 4 cali corpus
. Możesz znaleźć indeks najbardziej podobnego dokumentu, biorąc argmax tego wiersza, ale najpierw musisz zamaskować jedynki, które reprezentują podobieństwo każdego dokumentu do siebie . Możesz zrobić to drugie np.fill_diagonal()
, a pierwsze przez 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'
Uwaga: celem użycia rzadkiej macierzy jest zaoszczędzenie (znaczna ilość miejsca) na dużym korpusie i słownictwie. Zamiast konwertować na tablicę NumPy, możesz:
>>> n, _ = pairwise_similarity.shape
>>> pairwise_similarity[np.arange(n), np.arange(n)] = -1.0
>>> pairwise_similarity[input_idx].argmax()
3
X.mean(axis=0)
, a następnie obliczyć średnią / maksymalną / medianę (∗) odległość euklidesową od tej średniej. (∗) Wybierz, co ci się podoba.
Identyczny z @larsman, ale z pewnym wstępnym przetwarzaniem
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
, a które transform
?
To stare pytanie, ale stwierdziłem, że można to łatwo zrobić za pomocą Spacy . Po odczytaniu dokumentu można użyć prostego interfejsu API, similarity
aby znaleźć podobieństwo cosinus między wektorami dokumentów.
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
Zasadniczo podobieństwo cosinus między dwoma dokumentami jest stosowane jako miara podobieństwa dokumentów. W Javie możesz do tego użyć Lucene (jeśli twoja kolekcja jest dość duża) lub LingPipe . Podstawową koncepcją byłoby policzenie terminów w każdym dokumencie i obliczanie iloczynu kropek wektorów terminów. Biblioteki zapewniają kilka ulepszeń w stosunku do tego ogólnego podejścia, np. Wykorzystują odwrotne częstotliwości dokumentów i obliczają wektory tf-idf. Jeśli chcesz zrobić coś copmlex, LingPipe zapewnia również metody obliczania podobieństwa LSA między dokumentami, co daje lepsze wyniki niż podobieństwo cosinus. W przypadku Pythona możesz użyć NLTK .
Jeśli szukasz czegoś bardzo dokładnego, musisz użyć lepszego narzędzia niż tf-idf. Uniwersalny koder zdań jest jednym z najdokładniejszych w celu znalezienia podobieństwa między dowolnymi dwoma fragmentami tekstu. Google dostarczyło wstępnie przeszkolone modele, których można używać do własnej aplikacji bez potrzeby trenowania od zera. Najpierw musisz zainstalować tensorflow i tensorflow-hub:
pip install tensorflow
pip install tensorflow_hub
Poniższy kod pozwala przekonwertować dowolny tekst na reprezentację wektora o stałej długości, a następnie można użyć iloczynu kropkowego, aby znaleźć podobieństwo między nimi
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)
oraz kod do kreślenia:
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()
jak widać, największe podobieństwo występuje między tekstami ze sobą, a następnie z ich bliskimi tekstami w znaczeniu.
WAŻNE : przy pierwszym uruchomieniu kod będzie działał powoli, ponieważ musi pobrać model. jeśli chcesz zapobiec ponownemu pobieraniu modelu i korzystaniu z modelu lokalnego, musisz utworzyć folder pamięci podręcznej i dodać go do zmiennej środowiskowej, a następnie po pierwszym uruchomieniu użyć tej ścieżki:
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)
Więcej informacji: https://tfhub.dev/google/universal-sentence-encoder/2
Oto mała aplikacja na początek ...
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)
Możesz wypróbować tę usługę online pod kątem podobieństwa dokumentów 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
Jeśli jesteś bardziej zainteresowany mierzeniem semantycznego podobieństwa dwóch fragmentów tekstu, proponuję rzucić okiem na ten projekt gitlab . Możesz uruchomić go jako serwer, istnieje również wstępnie zbudowany model, którego można łatwo użyć do pomiaru podobieństwa dwóch fragmentów tekstu; nawet jeśli jest on głównie przeszkolony do pomiaru podobieństwa dwóch zdań, nadal możesz go używać w twoim przypadku. Jest napisany w Javie, ale możesz go uruchomić jako usługę RESTful.
Inną opcją jest również podobieństwo DKPro, które jest biblioteką z różnymi algorytmami do pomiaru podobieństwa tekstów. Jednak jest również napisane w języku Java.
przykład kodu:
// 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);
Aby znaleźć podobieństwo zdań z bardzo mniejszym zestawem danych i uzyskać wysoką dokładność, możesz użyć poniższego pakietu python, który używa wstępnie przeszkolonych modeli BERT,
pip install similar-sentences
Podobieństwo składniowe Istnieją 3 proste sposoby wykrywania podobieństwa.
W przypadku podobieństwa semantycznego Można użyć osadzania BERT i wypróbować inne strategie łączenia słów, aby uzyskać osadzanie dokumentów, a następnie zastosować podobieństwo cosinus do osadzania dokumentów.
Zaawansowana metodologia może wykorzystywać BERT SCORE, aby uzyskać podobieństwo.
Link do artykułu naukowego: https://arxiv.org/abs/1904.09675