두 텍스트 문서 사이의 유사성을 계산하는 방법은 무엇입니까?


207

모든 프로그래밍 언어로 NLP 프로젝트를 작업하고 있습니다 (Python이 선호됩니다).

두 개의 문서를 가져 와서 얼마나 비슷한 지 결정하고 싶습니다.


1
비슷한 질문이 여기에 stackoverflow.com/questions/101569/… 좋은 답변을 마녀

답변:


292

이를 수행하는 일반적인 방법은 문서를 TF-IDF 벡터로 변환 한 다음 이들 간의 코사인 유사성을 계산하는 것입니다. 정보 검색 (IR)에 관한 모든 교과서가 이에 해당됩니다. esp를 참조하십시오. 정보 검색 소개 는 무료이며 온라인으로 제공됩니다.

쌍별 유사성 계산

TF-IDF (및 유사한 텍스트 변환)는 Python 패키지 Gensimscikit-learn 에서 구현됩니다 . 후자의 패키지에서 코사인 유사성을 계산하는 것은

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

또는 문서가 일반 문자열 인 경우

>>> 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은 이런 종류의 작업에 더 많은 옵션을 제공 할 수 있습니다.

이 질문 도 참조하십시오 .

[면책 조항 : 나는 scikit-learn TF-IDF 구현에 관여했습니다.]

결과 해석

위에서, pairwise_similarityScipy 희소 행렬 은 정사각형이며 행과 열의 수는 말뭉치의 문서 수와 같습니다.

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

당신은 NumPy와 배열을 통해 스파 스 배열을 변환 할 수 있습니다 .toarray()또는 .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.        ]])

"scikit-learn 문서는 주황색과 파란색입니다"라는 최종 문서와 가장 유사한 문서를 찾고 싶다고 가정하겠습니다. 이 문서의 색인은 4입니다 corpus. 해당 행의 argmax 를 사용하여 가장 유사한 문서의 색인을 찾을 수 있지만 먼저 각 문서의 유사성을 나타내는 1을 마스킹해야합니다 . 당신은 후자를 통해 할 수있는 np.fill_diagonal(), 그리고 전을 통해를 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'

참고 : 희소 행렬을 사용하는 목적은 큰 말뭉치와 어휘를 위해 공간을 상당히 절약하는 것입니다. NumPy 배열로 변환하는 대신 다음을 수행 할 수 있습니다.

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

1
@larsmans 가능한 경우 배열을 조금 설명해 주시겠습니까?이 배열을 어떻게 읽어야합니까? 처음 두 열은 처음 두 문장 사이에 유사합니까?
추가 세미 콜론

1
@ 널-가설 : 위치 (i, j)에서 문서 i와 문서 j 사이의 유사성 점수를 찾습니다. 따라서 (0,2) 위치는 첫 번째 문서와 세 번째 문서 (0부터 시작하는 인덱싱 사용)의 유사성 값입니다. 코사인 유사성은 계산적이므로 (2,0)에서 찾은 것과 같은 값입니다.
Fred Foo

1
내가 1의 대각선을 벗어난 모든 값의 평균을 계산한다면, 네 문서가 얼마나 비슷한 지에 대한 단일 점수를 얻는 것이 좋은 방법일까요? 그렇지 않은 경우 여러 문서 간의 전반적인 유사성을 결정하는 더 좋은 방법이 있습니까?
user301752

2
@ user301752 :를 사용하여 tf-idf 벡터의 요소 별 평균 (k-means처럼)을 취한 X.mean(axis=0)다음 해당 평균으로부터 평균 / 최대 / 중간 값 (*) 유클리드 거리를 계산할 수 있습니다. (*) 당신의 공상 중 하나를 선택하십시오.
Fred Foo

1
@ 호기심 : 예제 코드를 현재 scikit-learn API로 업데이트했습니다. 새 코드를 사용해 볼 수 있습니다.
Fred Foo

87

@larsman과 동일하지만 일부 전처리

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, 정말 좋고 명확한 답변! 나는 두 가지 의심을 가지고있다 : I) tfidf * tfidf.T 이후에 [0,1]은 무엇인가?와 II) 역 문서 빈도는 모든 기사 또는 두 개 (2 개 이상인 것으로 간주)에서 형성된다. ?
Economist_Ayahuasca

2
@AndresAzqueta [0,1]은 두 개의 텍스트 입력이 2x2 대칭 행렬을 생성하므로 유사성을위한 행렬의 위치입니다.
Philip Bergström

1
@Renaud, 완전한 코드 감사합니다. nltk.download ()를 묻는 오류가 발생한 사람들은 쉽게 nltk.download ( 'punkt')를 수행 할 수 있습니다. 모든 것을 다운로드 할 필요는 없습니다.
1man

@ Renaud 더 근본적인 문제는 없습니다. 텍스트가해야 어떤 문자열 fit, 어느 transform?
John Strood

@JohnStrood 귀하의 질문을 이해하지 못합니다. 죄송합니다.
Renaud

45

그것은 오래된 질문이지만 Spacy로 쉽게 수행 할 수 있음을 알았습니다 . 문서가 읽 히면 간단한 API similarity를 사용하여 문서 벡터 사이의 코사인 유사성을 찾을 수 있습니다.

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
doc1과 doc2의 유사성이 왜
1.09999가

4
@JordanBelf 부동 소수점 숫자는 대부분의 언어에서 조금씩 돌아 다닙니다. 디지털 표현에서 무제한 정밀도를 가질 수 없기 때문입니다. 예를 들어, 비이성적 인 숫자에 대한 부동 소수점 연산은 항상 약간의 반올림 오차가 발생하여 증가합니다. 규모 측면에서 이러한 유연한 표현의 단점입니다.
scipilot

2
이 경우 유사성 방법으로 사용하는 거리 함수는 무엇입니까?
ikel

"en"을 찾는 데 문제가있는 경우 다음 pip install spacy && python -m spacy download ko
Cybernetic


17

일반적으로 두 문서 간의 코사인 유사성이 문서의 유사성 측정으로 사용됩니다. Java에서는 Lucene (컬렉션이 꽤 큰 경우) 또는 LingPipe 를 사용하여이를 수행 할 수 있습니다. 기본 개념은 모든 문서에서 항을 세고 항 벡터의 내적을 계산하는 것입니다. 라이브러리는이 일반적인 접근 방식에 비해 여러 가지 개선 된 기능을 제공합니다. copmlex를 수행하려는 경우 LingPipe는 코사인 유사성보다 더 나은 결과를 제공하는 문서 간의 LSA 유사성을 계산하는 방법도 제공합니다. Python의 경우 NLTK 를 사용할 수 있습니다 .


4
"LSA 유사성"은 없습니다. LSA는 벡터 공간의 차원을 줄이는 방법입니다 (용어가 아닌 속도를 높이거나 주제를 모델링). BOW 및 tf-idf에 사용되는 동일한 유사성 메트릭을 LSA (코사인 유사성, 유클리드 유사성, BM25 등)와 함께 사용할 수 있습니다.
Witiko

16

매우 정확한 것을 찾고 있다면 tf-idf보다 더 나은 도구를 사용해야합니다. 범용 문장 인코더 는 두 텍스트 사이의 유사성을 찾는 가장 정확한 것 중 하나입니다. Google은 처음부터 학습 할 필요없이 자체 애플리케이션에 사용할 수있는 사전 훈련 된 모델을 제공했습니다. 먼저 tensorflow 및 tensorflow-hub를 설치해야합니다.

    pip install tensorflow
    pip install tensorflow_hub

아래 코드를 사용하면 텍스트를 고정 길이 벡터 표현으로 변환 한 다음 내적을 사용하여 유사성을 찾을 수 있습니다

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)

그리고 음모를 꾸미는 코드 :

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

결과는 다음과 같습니다. 텍스트 쌍 사이의 유사성 행렬

보시다시피 가장 유사한 점은 텍스트 자체와 텍스트 사이의 의미가 유사하다는 것입니다.

중요 : 코드를 처음 실행하면 모델을 다운로드해야하므로 속도가 느려집니다. 모델을 다시 다운로드하지 못하게하고 로컬 모델을 사용하지 않으려면 캐시 폴더를 작성하고 환경 변수에 추가 한 다음 처음 실행 한 후 해당 경로를 사용해야합니다.

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)

자세한 정보 : https://tfhub.dev/google/universal-sentence-encoder/2


안녕하세요.이 예제를 통해 TF를 시험해 보라고 권장합니다. "np"개체는 어디에서 왔습니까?
오픈 푸드 브로커

1
UPD ok, 나는 numpy, matplotlib 및 플롯에 대한 시스템 TK Python 바인딩을 설치했으며 작동합니다!
오픈 푸드 브로커

1
그냥 경우 (죄송 줄 바꿈의 부족) : NP로 PLT 가져 오기 NumPy와 같은 허브 가져 오기 matplotlib.pyplot로 TF 가져 오기 tensorflow_hub 등의 수입 tensorflow
dinnouti

5

시작하는 데 도움이되는 작은 앱이 있습니다.

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가 매우 느립니다.
Phyo Arkar Lwin

2

코사인 문서 유사성 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입니까? 그렇다면 파이썬의 간단한 함수는 difflib import에서 ____________________________________ 작업을 수행합니다. SequenceMatcher def isStringSimilar (a, b) : ratio = SequenceMatcher (None, a, b) .ratio () 반환 비율 ______________________________
Rudresh Ajgaonkar

2

두 조각의 텍스트의 의미 적 유사성을 측정하는 데 더 관심 이 있다면이 gitlab 프로젝트를 살펴보십시오 . 서버로 실행할 수 있으며, 사전 구축 된 모델도 있으며,이를 사용하여 두 개의 텍스트의 유사성을 쉽게 측정 할 수 있습니다. 비록 두 문장의 유사성을 측정하기 위해 대부분 훈련을 받았지만, 당신의 경우에도 여전히 사용할 수 있습니다. 그것은 자바로 작성되었지만 RESTful 서비스로 실행할 수 있습니다.

또 다른 옵션 은 텍스트의 유사성을 측정하는 다양한 알고리즘을 갖춘 라이브러리 인 DKPro 유사성입니다. 그러나 Java로도 작성되었습니다.

코드 예 :

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

데이터 세트가 매우 적고 문장의 유사성을 찾고 정확도를 높이려면 사전 훈련 된 BERT 모델을 사용하는 Python 패키지 아래를 사용할 수 있습니다.

pip install similar-sentences

방금 시도했지만 하나의 주요 문장과 각 문장의 유사성을 제공하지만 모든 sentence.txt 교육 데이터를 하나의 클래스로 작성하고 모든 예제와 일치하는 정도에 대한 점수를 얻을 수있는 방법이 있습니까? ?
전문가 테자

1
그렇습니다. .batch_predict (BatchFile, NumberOfPrediction)를 사용하면 결과가 열 [ 'Sentence', 'Suggestion', 'Score'] 인 Results.xls로 제공됩니다.
Shankar Ganesh Jayaraman

1

구문 유사성 유사성을 감지하는 쉬운 3 가지 방법이 있습니다.

  • Word2Vec
  • 장갑
  • Tfidf 또는 카운트 벡터 라이저

시맨틱 유사성 BERT 임베딩을 사용하고 다른 단어 풀링 전략을 시도하여 문서 임베딩을 얻은 다음 문서 임베딩에 코사인 유사성을 적용 할 수 있습니다.

고급 방법론은 BERT SCORE를 사용하여 유사성을 얻을 수 있습니다. 버트 스코어

연구 논문 링크 : https://arxiv.org/abs/1904.09675

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.