İki metin belgesi arasındaki benzerlik nasıl hesaplanır?


207

Herhangi bir programlama dilinde (Python benim tercihim olsa da) bir NLP projesi üzerinde çalışıyorum.

İki belge almak ve bunların ne kadar benzer olduklarını belirlemek istiyorum.


1
Burada benzer soru stackoverflow.com/questions/101569/… cadı bazı güzel cevaplar

Yanıtlar:


292

Bunu yapmanın yaygın yolu, belgeleri TF-IDF vektörlerine dönüştürmek ve daha sonra bunlar arasındaki kosinüs benzerliğini hesaplamaktır. Bilgi alma (IR) ile ilgili herhangi bir ders kitabı bunu kapsar. Bkz. Ücretsiz ve çevrimiçi olarak sunulan Bilgi Erişimine Giriş .

İkili Benzerliklerin Hesaplanması

TF-IDF (ve benzeri metin dönüşümleri) Python paketleri Gensim ve scikit- learn'da uygulanır . İkinci pakette, kosinüs benzerliklerini hesaplamak kadar kolaydır

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

veya belgeler düz dizelerse,

>>> 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 

Gensim'in bu tür görevler için daha fazla seçeneği olabilir.

Ayrıca bu soruya bakın .

[Feragatname: scikit-learn TF-IDF uygulamasına katıldım.]

Sonuçları Yorumlama

Yukarıdan, kare şeklindeki pairwise_similaritybir Scipy seyrek matrisi , satır ve sütunların sayısı korpustaki belge sayısına eşittir.

>>> pairwise_similarity                                                                                                                                                                                                                                      
<5x5 sparse matrix of type '<class 'numpy.float64'>'
    with 17 stored elements in Compressed Sparse Row format>

Seyrek diziyi bir NumPy dizisine .toarray()veya yoluyla dönüştürebilirsiniz .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.        ]])

Diyelim ki son belgeye en çok benzeyen belgeyi bulmak istiyoruz, "Scikit-öğren dokümanlar Turuncu ve Mavi". Bu belgede 4 inçlik dizin var corpus. Bu satırın argmax'ını alarak en benzer belgenin dizinini bulabilirsiniz , ancak önce her belgenin kendisine benzerliğini temsil eden 1'leri maskelemeniz gerekir . Sen ikinci through yapabilirsiniz np.fill_diagonal()ve eski through 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'

Not: seyrek bir matris kullanmanın amacı, büyük bir ceset ve kelime haznesi için (önemli miktarda alan) tasarruf etmektir. NumPy dizisine dönüştürmek yerine şunları yapabilirsiniz:

>>> n, _ = pairwise_similarity.shape                                                                                                                                                                                                                         
>>> pairwise_similarity[np.arange(n), np.arange(n)] = -1.0
>>> pairwise_similarity[input_idx].argmax()                                                                                                                                                                                                                  
3

1
@larsmans Mümkünse diziyi biraz açıklayabilir misiniz, bu diziyi nasıl okumalıyım. İlk iki sütun İlk iki cümle arasındaki benzerlik midir?
add-semi-colons

1
@ Boş-Hipotez: (i, j) konumunda, belge i ve belge j arasındaki benzerlik puanını bulursunuz. Yani, (0,2) konumunda birinci belge ile üçüncü (sıfır tabanlı indeksleme kullanarak) arasındaki benzerlik değeri vardır (sıfır tabanlı indeksleme kullanarak), bu değer (2,0) 'da bulduğunuz değerle aynıdır, çünkü kosinüs benzerliği değişebilirdir.
Fred Foo

1
1'lerin köşegen dışındaki tüm değerleri ortalamam gerekirse, bu dört belgenin birbirine ne kadar benzediğine dair tek bir puan elde etmenin sağlam bir yolu olabilir mi? Değilse, birden fazla belge arasındaki genel benzerliği belirlemenin daha iyi bir yolu var mı?
user301752

2
@ user301752: tf-idf vektörlerinin (k-araçlarının yaptığı gibi) element bakımından ortalamasını alabilir X.mean(axis=0), sonra bu ortalamadan ortalama / maksimum / medyan (∗) Öklid mesafesini hesaplayabilirsiniz. (∗) Hangisinin fantezi olduğunu seç.
Fred Foo

1
@curious: Örnek kodu geçerli scikit-learn API'sine güncelledim; yeni kodu denemek isteyebilirsiniz.
Fred Foo

87

@Larsman ile aynı, ancak bazı ön işlemlerle

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')

@ Renaud, gerçekten iyi ve açık bir cevap! İki şüphem var: I) tfidf * tfidf.T) ve II'den sonra dahil ettiğiniz [0,1] nedir? ?
Economist_Ayahuasca

2
@AndresAzqueta [0,1], iki metin girişi 2x2 simetrik bir matris oluşturacağından benzerlik için matristeki konumlardır.
Philip Bergström

1
@Renaud, Kodunuzun tamamı için teşekkür ederiz. Nltk.download () öğesini soran hatayla karşılaşanlar için nltk.download ('punkt') öğesini kolayca yapabilirsiniz. Her şeyi indirmenize gerek yok.
1man

@Renaud Daha temel bir problemim yok. Hangi metin dizeleri fitve hangileri transform?
John Strood

@JohnStrood Sorunuzu anlamadım, üzgünüm yeniden formüle edebilir misiniz?
Renaud

45

Bu eski bir soru, ama bunun Spacy ile kolayca yapılabileceğini buldum . Belge okunduktan sonra, similaritybelge vektörleri arasındaki kosinüs benzerliğini bulmak için basit bir api kullanılabilir.

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

2
Acaba doc1 ve doc2 arasındaki benzerliğin neden 1.0 değil
0.999999954642 olduğunu

4
@JordanKendi kayan nokta sayıları çoğu dilde biraz dolaşıyor - dijital gösterimlerde sınırsız hassasiyete sahip olamadıklarından. örneğin irrasyonel sayılar üzerinde kayan nokta işlemleri ya da irrasyonel sayıların üretilmesi her zaman küçük yuvarlama hatalarına neden olur ve bu sayı çoğalır. Ölçek açısından böylesine esnek bir temsilin dezavantajı.
scipilot

2
bu durumda benzerlik yönteminin kullandığı mesafe fonksiyonu nedir?
ikel

"En" bulma sorunlarınız varsa aşağıdaki pip install spacy & & python -m spacy indir tr çalıştırın
Cybernetic


17

Genellikle iki belge arasındaki kosinüs benzerliği, belgelerin benzerlik ölçüsü olarak kullanılır. Java'da bunu yapmak için Lucene (koleksiyonunuz oldukça büyükse) veya LingPipe kullanabilirsiniz. Temel kavram, her bir belgedeki terimleri saymak ve terim vektörlerinin nokta çarpımını hesaplamak olacaktır. Kütüphaneler, bu genel yaklaşım üzerinde, örneğin ters belge frekanslarının kullanılması ve tf-idf vektörlerinin hesaplanması üzerinde çeşitli iyileştirmeler sağlar. Bir şey copmlex yapmak istiyorsanız, LingPipe ayrıca kosinüs benzerliğinden daha iyi sonuçlar veren belgeler arasındaki LSA benzerliğini hesaplamak için yöntemler sağlar. Python için NLTK kullanabilirsiniz .


4
"LSA benzerliği" olmadığını unutmayın. LSA, bir vektör uzayının boyutsallığını azaltmak için bir yöntemdir (şeyleri hızlandırmak veya terimler yerine konuları modellemek için). BOW ve tf-idf ile kullanılan benzerlik metrikleri LSA (kosinüs benzerliği, öklid benzerliği, BM25,…) ile kullanılabilir.
Witiko

16

Çok doğru bir şey arıyorsanız, tf-idf'den daha iyi bir araç kullanmanız gerekir. Evrensel cümle kodlayıcı , herhangi iki metin parçası arasındaki benzerliği bulmak için en doğru olanlardan biridir. Google, herhangi bir şeyi sıfırdan eğitmeye gerek kalmadan kendi uygulamanız için kullanabileceğiniz önceden hazırlanmış modeller sunmuştur. İlk olarak, tensorflow ve tensorflow-hub'ı kurmalısınız:

    pip install tensorflow
    pip install tensorflow_hub

Aşağıdaki kod, herhangi bir metni sabit uzunluklu vektör temsiline dönüştürmenizi sağlar ve daha sonra aralarındaki benzerliği bulmak için nokta ürününü kullanabilirsiniz.

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)

ve çizim kodu:

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()

sonuç şöyle olur: metin çiftleri arasındaki benzerlik matrisi

Gördüğünüz gibi, en çok benzerlik kendileri ve sonra yakın metinleri arasındaki metinler arasındadır.

ÖNEMLİ : kodu ilk kez çalıştırdığınızda, modeli indirmesi gerektiğinden yavaş olacaktır. modeli tekrar indirmesini ve yerel modeli kullanmasını önlemek istiyorsanız, önbellek için bir klasör oluşturmanız ve ortam değişkenine eklemeniz gerekir ve ardından ilk çalıştırdıktan sonra bu yolu kullanın:

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)

Daha fazla bilgi: https://tfhub.dev/google/universal-sentence-encoder/2


merhaba bu örnek için teşekkürler beni TF denemeye teşvik - "np" nesnesi nereden gelmeli?
Açık Gıda Komisyoncusu

1
UPD tamam, arsa için numpy, matplotlib ve ayrıca sistem TK Python bağlama yükledim ve çalışıyor!
Open Food Broker

1
Her ihtimale karşı (üzgün satır sonları eksikliği): np olarak plt ithalat Numpy olarak göbek ithalat matplotlib.pyplot olarak tf ithalat tensorflow_hub olarak ithalat tensorflow
dinnouti

5

İşte başlamanız için küçük bir uygulama ...

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)

4
Çok sayıda dokümanla çalışacaksanız difflib çok yavaştır.
Phyo Arkar Lwin

2

Kosinüs belgesi benzerliği için bu çevrimiçi hizmeti denemek isteyebilirsiniz http://www.scurtu.it/documentSlikeity.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

Sıralı Eşleştirici Api Kullanıyor mu? Oranı = SequenceMatcher (Hiçbiri a, b) .ratio () dönüş oranı ______________________________: evet, o zaman python Basit fonksiyon isStringSimilar (a, b) def difflib ithalat SequenceMatcher işi ____________________________________ yapsaydı
Rudresh Ajgaonkar

2

İki metnin anlamsal benzerliğini ölçmekle ilgileniyorsanız , bu gitlab projesine göz atmanızı öneririm . Bir sunucu olarak çalıştırabilirsiniz, ayrıca iki metnin benzerliğini ölçmek için kolayca kullanabileceğiniz önceden oluşturulmuş bir model vardır; çoğunlukla iki cümlenin benzerliğini ölçmek için eğitilmiş olsa da, yine de sizin durumunuzda kullanabilirsiniz.Java ile yazılmıştır, ancak RESTful hizmeti olarak çalıştırabilirsiniz.

Başka bir seçenek de metinlerin benzerliğini ölçmek için çeşitli algoritmaya sahip bir kütüphane olan DKPro Similarity'dir . Ancak, java ile de yazılmıştır.

kod örneği:

// 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);

2

Çok daha az veri kümesiyle cümle benzerliği bulmak ve yüksek doğruluk elde etmek için önceden eğitilmiş BERT modellerini kullanan python paketini kullanabilirsiniz,

pip install similar-sentences

Sadece denedim, ama her cümlenin tek bir ana cümleyle benzerliğini veriyor, ancak tüm cümle.txt eğitim verilerini bir sınıf olarak oluşturmanın ve tüm örneklerle ne kadar güven duyduğuna dair puan almanın herhangi bir yolu var ?
Guru Teja

1
evet, yapabilirsiniz .batch_predict'i (BatchFile, NumberOfPrediction), Results.xls ile Sütunlar ['Cümle', 'Öneri', 'Puan'] olarak vermeyi deneyin
Shankar Ganesh Jayaraman

1

Sözdizimsel Benzerlik İçin Benzerliği tespit etmenin 3 kolay yolu olabilir.

  • Word2Vec
  • Eldiven
  • TFIDF veya Countvectorizer

Anlamsal Benzerlik için BERT Gömme kullanılabilir ve belge gömme almak için farklı bir kelime havuzu oluşturma stratejileri denemek ve sonra belge gömme kosinüs benzerliği uygulamak olabilir.

Gelişmiş bir metodoloji, benzerlik elde etmek için BERT SCORE'u kullanabilir. BERT PUANI

Araştırma Makalesi Bağlantısı: https://arxiv.org/abs/1904.09675

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.