Я смотрю на работу над проектом НЛП, на любом языке программирования (хотя Python будет моим предпочтением).
Я хочу взять два документа и определить, насколько они похожи.
Я смотрю на работу над проектом НЛП, на любом языке программирования (хотя Python будет моим предпочтением).
Я хочу взять два документа и определить, насколько они похожи.
Ответы:
Обычный способ сделать это - преобразовать документы в векторы TF-IDF и затем вычислить косинусное сходство между ними. Любой учебник по поиску информации (IR) охватывает это. Смотрите особенно Введение в поиск информации , которая бесплатна и доступна онлайн.
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
или, если документы представляют собой простые строки,
>>> 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
хотя у Генсима может быть больше вариантов для такого рода задач.
Смотрите также этот вопрос .
[Отказ от ответственности: я принимал участие в реализации TF-IDF scikit-learn.]
Сверху pairwise_similarity
- скудная матрица Сципи, имеющая квадратную форму, с числом строк и столбцов, равным количеству документов в корпусе.
>>> 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
X.mean(axis=0)
, а затем вычислить среднее / максимальное / медианное (∗) евклидово расстояние от этого среднего. (*) Выбери, что у тебя есть.
Идентичен @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')
fit
, а какие transform
?
Это старый вопрос, но я обнаружил, что это легко сделать с помощью Spacy . Как только документ прочитан, similarity
можно использовать простой API , чтобы найти косинусное сходство между векторами документа.
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
Обычно косинусное сходство между двумя документами используется как мера сходства документов. В Java вы можете использовать Lucene (если ваша коллекция довольно большая) или LingPipe для этого. Основная концепция заключается в подсчете терминов в каждом документе и вычислении точечного произведения векторов терминов. Библиотеки предоставляют несколько улучшений по сравнению с этим общим подходом, например, использование обратных частот документа и вычисление векторов tf-idf. Если вы хотите что-то сделать с помощью copmlex, LingPipe также предоставляет методы для вычисления сходства LSA между документами, что дает лучшие результаты, чем косинусное сходство. Для Python вы можете использовать NLTK .
Если вы ищете что-то очень точное, вам нужно использовать какой-то лучший инструмент, чем tf-idf. Универсальный кодировщик предложений является одним из наиболее точных, чтобы найти сходство между любыми двумя частями текста. Google предоставил предварительно обученные модели, которые вы можете использовать для своего собственного приложения без необходимости тренироваться с нуля. Во-первых, вам нужно установить тензор потока и тензор потока-хаб:
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.
Вот небольшое приложение, чтобы вы начали ...
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)
Возможно, вы захотите попробовать этот онлайн-сервис для сходства документов косинуса http://www.scurtu.it/documentSdentifity.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
Если вас больше интересует измерение семантического сходства двух фрагментов текста, я предлагаю взглянуть на этот проект gitlab . Вы можете запустить его в качестве сервера, также есть встроенная модель, которую вы можете легко использовать для измерения сходства двух фрагментов текста; хотя он в основном обучен измерению сходства двух предложений, вы все равно можете использовать его в своем случае. Он написан на Java, но вы можете запустить его как службу RESTful.
Другим вариантом также является DKPro Similarity - библиотека с различным алгоритмом измерения сходства текстов. Тем не менее, это также написано на 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);
Чтобы найти сходство предложений с очень меньшим набором данных и получить высокую точность, вы можете использовать ниже пакет python, который использует предварительно обученные модели BERT,
pip install similar-sentences
Для синтаксического сходства может быть 3 простых способа обнаружения сходства.
Для семантического сходства Можно использовать BERT Embedding и попробовать разные стратегии объединения слов, чтобы получить вложение документа, а затем применить косинусное сходство при встраивании документа.
Продвинутая методология может использовать BERT SCORE для получения сходства.
Ссылка на исследовательскую статью: https://arxiv.org/abs/1904.09675