Ответ 1
Обычный способ сделать это - преобразовать документы в векторы 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
или, если документы представляют собой простые строки,
>>> 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