Я дивлюся на роботу над проектом NLP, будь-якою мовою програмування (хоча Python буде моїм уподобанням).
Я хочу взяти два документи і визначити, наскільки вони схожі.
Я дивлюся на роботу над проектом NLP, будь-якою мовою програмування (хоча Python буде моїм уподобанням).
Я хочу взяти два документи і визначити, наскільки вони схожі.
Відповіді:
Загальний спосіб зробити це - перетворення документів у вектори TF-IDF, а потім обчислення косинусної подібності між ними. Будь-який підручник з пошуку інформації (ІЧ) охоплює це. Див. 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
або, якщо документи є простими рядками,
>>> 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 може бути більше варіантів для такого роду завдань.
Дивіться також це питання .
[Відмова від відповідальності: Я брав участь у впровадженні TF-IDF у науковому курсі.]
Зверху pairwise_similarity
- розріджена матриця Scipy, що має квадратну форму, кількість рядків і стовпців дорівнює кількості документів у корпусі.
>>> 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 надав попередньо перевірені моделі, які ви можете використовувати для власного застосування без необхідності тренуватися з нуля. Спочатку потрібно встановити 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
Ось невеличкий додаток для початку роботи ...
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/documentS Схожіity.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
Якщо вас більше цікавить вимірювання смислової подібності двох фрагментів тексту, пропоную поглянути на цей проект із гітлабу . Ви можете запустити його як сервер, також є заздалегідь побудована модель, яку ви можете легко використати для вимірювання подібності двох фрагментів тексту; незважаючи на те, що він здебільшого підготовлений для вимірювання схожості двох речень, ви все одно можете використовувати його у вашому випадку. Це написано на Java, але ви можете запустити його як службу 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);
Щоб знайти подібність пропозицій з дуже меншим набором даних та отримати високу точність, ви можете використовувати нижче пакет python, який використовує попередньо навчені моделі BERT,
pip install similar-sentences
Для синтаксичної подібності Існує 3 простих способи виявлення подібності.
Для семантичної подібності можна використовувати BERT Embedding та спробувати іншу стратегію об’єднання слів, щоб отримати вбудовуваний документ, а потім застосувати схожість косинуса при вкладанні документа.
Розширена методологія може використовувати BERT SCORE для отримання подібності.
Дослідницьке посилання: https://arxiv.org/abs/1904.09675