Как вычислить сходство между двумя текстовыми документами? - PullRequest
170 голосов
/ 17 января 2012

Я смотрю на работу над проектом НЛП на любом языке программирования (хотя Python будет моим предпочтением).

Я хочу взять два документа и определить, насколько они похожи.

Ответы [ 8 ]

250 голосов
/ 17 января 2012

Обычный способ сделать это - преобразовать документы в векторы tf-idf, а затем вычислить косинусное сходство между ними.Любой учебник по информационному поиску (IR) освещает это.Смотрите esp. Введение в информационный поиск , который является бесплатным и доступен онлайн.

Tf-idf (и аналогичные преобразования текста) реализованы в пакетах Python Gensim и scikit-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

или, если документы представляют собой простые строки,

>>> vect = TfidfVectorizer(min_df=1)
>>> tfidf = vect.fit_transform(["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"])
>>> (tfidf * tfidf.T).A
array([[ 1.        ,  0.25082859,  0.39482963,  0.        ],
       [ 0.25082859,  1.        ,  0.22057609,  0.        ],
       [ 0.39482963,  0.22057609,  1.        ,  0.26264139],
       [ 0.        ,  0.        ,  0.26264139,  1.        ]])

, хотя у Gensim может быть больше вариантов для такого рода задач.

См. Также этот вопрос .

[Отказ от ответственности: я участвовал в реализации scikit-learn tf-idf.]

80 голосов
/ 10 июня 2014

Идентичен @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')
33 голосов
/ 22 мая 2017

Это старый вопрос, но я обнаружил, что это легко сделать с помощью 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
17 голосов
/ 17 января 2012

Обычно в качестве меры сходства документов используется косинусное сходство между двумя документами. В Java вы можете использовать Lucene (если ваша коллекция довольно большая) или LingPipe , чтобы сделать это. Основной концепцией будет подсчет терминов в каждом документе и вычисление точечного произведения векторов терминов. Библиотеки обеспечивают несколько улучшений по сравнению с этим общим подходом, например, использование обратных частот документа и вычисление векторов tf-idf. Если вы хотите что-то сделать с помощью copmlex, LingPipe также предоставляет методы для вычисления сходства LSA между документами, что дает лучшие результаты, чем косинусное сходство. Для Python вы можете использовать NLTK .

4 голосов
/ 01 июля 2012

Вот небольшое приложение, с которого можно начать ...

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)
2 голосов
/ 17 апреля 2019

Если вы ищете что-то очень точное, вам нужно использовать какой-то лучший инструмент, чем tf-idf. Универсальный кодировщик предложений является одним из наиболее точных, чтобы найти сходство между любыми двумя частями текста. Google предоставил предварительно обученные модели, которые вы можете использовать для своего собственного приложения без необходимости тренироваться с нуля. Для начала вам нужно установить tenorflow и tenorflow-hub:

    pip install tensorflow
    pip install tensorflow_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()

результат будет: the similarity matrix between pairs of texts

Как вы можете видеть, наибольшее сходство происходит между текстами с самими собой, а затем с их близкими по смыслу текстами.

ВАЖНО : при первом запуске кода будет работать медленно, потому что ему нужно загрузить модель. если вы хотите запретить повторную загрузку модели и использовать локальную модель, вам необходимо создать папку для кэша и добавить ее в переменную среды, а затем после первого запуска использовать этот путь:

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

2 голосов
/ 01 февраля 2018

Если вас больше интересует измерение семантического сходства двух фрагментов текста, я предлагаю взглянуть на этот проект gitlab . Вы можете запустить его в качестве сервера, также есть встроенная модель, которую вы можете легко использовать для измерения сходства двух фрагментов текста; даже если он в основном обучен измерению сходства двух предложений, вы все равно можете использовать его в своем случае. Он написан на языке Java, но вы можете запустить его как службу RESTful.

Другая опция также DKPro Similarity , которая представляет собой библиотеку с различным алгоритмом для измерения сходства текстов. Тем не менее, это также написано на Java.

2 голосов
/ 12 февраля 2013

Возможно, вы захотите попробовать этот онлайн-сервис для сходства документов косинуса 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...