Làm thế nào để tính toán sự giống nhau giữa hai tài liệu văn bản?


207

Tôi đang xem xét làm việc trong một dự án NLP, bằng bất kỳ ngôn ngữ lập trình nào (mặc dù Python sẽ là sở thích của tôi).

Tôi muốn lấy hai tài liệu và xác định chúng giống nhau như thế nào.


1
Câu hỏi tương tự ở đây stackoverflow.com/questions/101569/

Câu trả lời:


292

Cách phổ biến để làm điều này là chuyển đổi các tài liệu thành các vectơ TF-IDF và sau đó tính toán độ tương tự cosin giữa chúng. Bất kỳ sách giáo khoa về truy xuất thông tin (IR) bao gồm điều này. Xem đặc biệt. Giới thiệu về Truy xuất thông tin , miễn phí và có sẵn trực tuyến.

Tính toán điểm tương đồng

TF-IDF (và các phép biến đổi văn bản tương tự) được triển khai trong các gói Python Gensimscikit-learn . Trong gói thứ hai, tính tương tự cosine dễ như

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

hoặc, nếu các tài liệu là các chuỗi đơn giản,

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

mặc dù Gensim có thể có nhiều lựa chọn hơn cho loại nhiệm vụ này.

Xem thêm câu hỏi này .

[Tuyên bố miễn trừ trách nhiệm: Tôi đã tham gia vào quá trình triển khai TF-IDF của scikit-learn.]

Giải thích kết quả

pairwise_similarityNhìn từ trên xuống, là một ma trận thưa thớt Scipy có hình vuông, với số lượng hàng và cột bằng với số lượng tài liệu trong kho văn bản.

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

Bạn có thể chuyển đổi mảng thưa sang mảng NumPy thông qua .toarray()hoặc .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.        ]])

Giả sử chúng ta muốn tìm tài liệu tương tự như tài liệu cuối cùng, "Các tài liệu tìm hiểu scikit là Orange và Blue". Tài liệu này có chỉ số 4 trong corpus. Bạn có thể tìm thấy chỉ mục của tài liệu tương tự nhất bằng cách lấy argmax của hàng đó, nhưng trước tiên bạn sẽ cần che dấu 1, đại diện cho sự giống nhau của từng tài liệu với chính nó . Bạn có thể thực hiện cái sau thông qua np.fill_diagonal(), và cái trước thông qua 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'

Lưu ý: mục đích của việc sử dụng ma trận thưa thớt là để tiết kiệm (một lượng không gian đáng kể) cho một kho từ vựng lớn. Thay vì chuyển đổi sang mảng NumPy, bạn có thể làm:

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

1
@larsmans Bạn có thể giải thích mảng một chút nếu có thể, làm thế nào tôi nên đọc mảng này. Hai cột đầu tiên có giống nhau giữa hai câu đầu tiên không?
add-semi-colons

1
@ Null-Giả thuyết: tại vị trí (i, j), bạn tìm thấy điểm tương đồng giữa tài liệu i và tài liệu j. Vì vậy, tại vị trí (0,2) là giá trị tương tự giữa tài liệu đầu tiên và tài liệu thứ ba (sử dụng lập chỉ mục dựa trên số 0), là giá trị tương tự mà bạn tìm thấy ở (2,0), vì độ tương tự cosine là giao hoán.
Fred Foo

1
Nếu tôi tính trung bình tất cả các giá trị bên ngoài đường chéo của 1, thì đó có phải là một cách hợp lý để có được một điểm số giống nhau của bốn tài liệu với nhau không? Nếu không, có cách nào tốt hơn để xác định sự tương đồng tổng thể giữa nhiều tài liệu không?
dùng495752

2
@ user301752: bạn có thể lấy giá trị trung bình theo phần tử của các vectơ tf-idf (như k-mean sẽ làm) với X.mean(axis=0), sau đó tính khoảng cách trung bình / tối đa / trung bình (∗) từ trung bình đó. (∗) Chọn bất cứ thứ gì bạn thích.
Fred Foo

1
@cantly: Tôi đã cập nhật mã ví dụ lên API scikit-learn hiện tại; bạn có thể muốn thử mã mới.
Fred Foo

87

Đồng nhất với @larsman, nhưng với một số tiền xử lý

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, câu trả lời thực sự tốt và rõ ràng! Tôi có hai nghi ngờ: I) [0,1] mà bạn kết hợp sau tfidf * tfidf.T) và II) Tần suất tài liệu nghịch đảo được hình thành từ tất cả các bài viết hoặc chỉ hai (xem xét rằng bạn có nhiều hơn 2) ?
economist_Ayahuasca

2
@AndresAzqueta [0,1] là các vị trí trong ma trận cho sự giống nhau vì hai đầu vào văn bản sẽ tạo ra ma trận đối xứng 2x2.
Philip Bergstrom

1
@Renaud, Cảm ơn bạn đã hoàn thành mã của bạn. Đối với những người gặp phải lỗi khi yêu cầu nltk.doad (), bạn có thể dễ dàng thực hiện nltk.doad ('punkt'). Bạn không cần phải tải xuống mọi thứ.
1man

@Renaud Tôi không nhận được một vấn đề cơ bản hơn. Những chuỗi văn bản nên fit, và những transformgì?
John Strood

@JohnStrood Tôi không hiểu câu hỏi của bạn, xin lỗi bạn có thể cải tổ không?
Đổi mới

45

Đó là một câu hỏi cũ, nhưng tôi thấy điều này có thể được thực hiện dễ dàng với Spacy . Sau khi tài liệu được đọc, một api đơn giản similaritycó thể được sử dụng để tìm sự tương tự cosin giữa các vectơ tài liệu.

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
Tôi tự hỏi tại sao sự giống nhau giữa doc1 và doc2 là 0.999999954642 chứ không phải 1.0
JordanBelf

4
Các số dấu phẩy động @JordanBelf đi lang thang một chút trong hầu hết các ngôn ngữ - vì chúng không thể có độ chính xác không giới hạn trong các biểu diễn kỹ thuật số. ví dụ: các phép toán dấu phẩy động trên hoặc tạo ra các số vô tỷ luôn có các lỗi làm tròn nhỏ, sau đó nhân lên. Đó là nhược điểm của một đại diện linh hoạt như vậy về quy mô.
scipilot

2
Hàm khoảng cách mà phương thức tương tự sử dụng trong trường hợp này là gì?
ikel

Nếu bạn gặp sự cố khi tìm "en", hãy cài đặt pip sau đây cài đặt spacy && python -m spacy tải xuống en
Cyber ​​từ


17

Nói chung, độ tương tự cosin giữa hai tài liệu được sử dụng như một thước đo tương tự của tài liệu. Trong Java, bạn có thể sử dụng Lucene (nếu bộ sưu tập của bạn khá lớn) hoặc LingPipe để làm điều này. Khái niệm cơ bản sẽ là đếm các thuật ngữ trong mỗi tài liệu và tính sản phẩm chấm của các vectơ hạn. Các thư viện cung cấp một số cải tiến so với phương pháp chung này, ví dụ như sử dụng tần số tài liệu nghịch đảo và tính toán các vectơ tf-idf. Nếu bạn đang tìm cách làm một cái gì đó copmlex, LingPipe cũng cung cấp các phương pháp để tính toán độ tương tự LSA giữa các tài liệu mang lại kết quả tốt hơn so với độ tương tự cosine. Đối với Python, bạn có thể sử dụng NLTK .


4
Lưu ý rằng không có "sự tương tự LSA". LSA là một phương pháp để giảm tính chiều của không gian vectơ (để tăng tốc mọi thứ hoặc mô hình hóa các chủ đề hơn là các thuật ngữ). Các số liệu tương tự giống nhau được sử dụng với BOW và tf-idf có thể được sử dụng với LSA (độ tương tự cosine, độ tương tự euclide, BM25, Lỗi).
Witiko

16

Nếu bạn đang tìm kiếm một cái gì đó rất chính xác, bạn cần sử dụng một số công cụ tốt hơn tf-idf. Bộ mã hóa câu phổ là một trong những bộ chính xác nhất để tìm ra sự tương đồng giữa bất kỳ hai đoạn văn bản nào. Google cung cấp các mô hình đã được sàng lọc mà bạn có thể sử dụng cho ứng dụng của riêng mình mà không cần phải đào tạo từ đầu bất cứ điều gì. Đầu tiên, bạn phải cài đặt tenorflow và tenorflow-hub:

    pip install tensorflow
    pip install tensorflow_hub

Mã dưới đây cho phép bạn chuyển đổi bất kỳ văn bản nào thành biểu diễn vectơ có độ dài cố định và sau đó bạn có thể sử dụng sản phẩm dấu chấm để tìm ra sự tương đồng giữa chúng

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)

và mã cho âm mưu:

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

kết quả sẽ là: ma trận tương tự giữa các cặp văn bản

như bạn có thể thấy sự tương đồng nhất là giữa các văn bản với chính chúng và sau đó với các văn bản gần gũi của chúng có ý nghĩa.

QUAN TRỌNG : lần đầu tiên bạn chạy mã, nó sẽ chậm vì cần tải xuống mô hình. nếu bạn muốn ngăn nó tải lại mô hình và sử dụng mô hình cục bộ, bạn phải tạo một thư mục cho bộ đệm và thêm nó vào biến môi trường và sau lần chạy đầu tiên, hãy sử dụng đường dẫ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)

Thêm thông tin: https://tfhub.dev/google/universal-sentence-encoder/2


hi cảm ơn vì ví dụ này khuyến khích tôi dùng thử TF - đối tượng "np" nên đến từ đâu?
Nhà môi giới thực phẩm mở

1
CẬP NHẬT ok, tôi đã cài đặt numpy, matplotlib và cả hệ thống ràng buộc TK Python cho cốt truyện và nó hoạt động !!
Nhà môi giới thực phẩm mở

1
Chỉ trong trường hợp (xin lỗi vì thiếu ngắt dòng): nhập tenorflow khi tf nhập tenorflow_hub làm trung tâm nhập matplotlib.pyplot khi nhập numpy như np
dinnouti

5

Đây là một ứng dụng nhỏ để giúp bạn bắt đầu ...

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
difflib rất chậm nếu bạn đi làm với số lượng lớn tài liệu.
Phyo Arkar Lwin

2

Bạn có thể muốn dùng thử dịch vụ trực tuyến này để có sự tương tự về tài liệu cosine 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

Api có sử dụng Bộ so khớp tuần tự khác biệt không? Nếu có, thì một chức năng đơn giản trong python sẽ thực hiện công việc ____________________________________ từ difflib SequenceMatcher nhập khẩu def isStringSimilar (a, b): Tỷ lệ = SequenceMatcher (Không, a, b) .ratio () trả lại tỷ lệ ______________________________
Rudresh Ajgaonkar

2

Nếu bạn quan tâm hơn đến việc đo lường sự giống nhau về ngữ nghĩa của hai đoạn văn bản, tôi khuyên bạn nên xem dự án gitlab này . Bạn có thể chạy nó như một máy chủ, cũng có một mô hình được xây dựng sẵn mà bạn có thể dễ dàng sử dụng để đo lường sự giống nhau của hai đoạn văn bản; mặc dù phần lớn được đào tạo để đo lường sự giống nhau của hai câu, bạn vẫn có thể sử dụng nó trong trường hợp của mình. Nó được viết bằng java nhưng bạn có thể chạy nó như một dịch vụ RESTful.

Một tùy chọn khác cũng là DKPro Tương tự , một thư viện với nhiều thuật toán khác nhau để đo lường sự giống nhau của các văn bản. Tuy nhiên, nó cũng được viết bằng java.

mã ví dụ:

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

Để tìm sự tương tự câu với rất ít tập dữ liệu và để có độ chính xác cao, bạn có thể sử dụng gói python bên dưới đang sử dụng các mô hình BERT được đào tạo trước,

pip install similar-sentences

Tôi chỉ thử điều đó, nhưng nó cho sự tương đồng của mỗi câu với một câu chính, nhưng có cách nào để tạo tất cả dữ liệu đào tạo câu.txt thành một lớp và nhận được điểm về mức độ tin cậy của nó đối với tất cả các bài kiểm tra ?
Đạo sư Teja

1
có bạn có thể, hãy thử .batch_predict (batchfile, NumberOfPrediction) mà sẽ cung cấp sản lượng như Results.xls với Cột [ 'Câu', 'Đề xuất', 'Điểm']
Shankar Ganesh Jayaraman

1

Đối với sự tương tự cú pháp Có thể có 3 cách dễ dàng để phát hiện sự tương tự.

  • Word2Vec
  • Găng tay
  • Tfidf hoặc bộ đếm

Đối với sự giống nhau về ngữ nghĩa Người ta có thể sử dụng Nhúng BERT và thử một chiến lược gộp từ khác để nhúng tài liệu và sau đó áp dụng độ tương tự cosine cho việc nhúng tài liệu.

Một phương pháp nâng cao có thể sử dụng BERT SCORE để có được sự tương đồng. CỬA HÀNG BERTNG

Liên kết tài liệu nghiên cứu: https://arxiv.org/abs/1904.09675

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.