Я следил за учебником, который был доступен в частях 1 и 2 . К сожалению, у автора не было времени на последний раздел, в котором использовалось косинусное сходство, чтобы на самом деле найти расстояние между двумя документами. Я следил за примерами в статье с помощью следующей ссылки из stackoverflow , включая код, упомянутый в приведенной выше ссылке (просто чтобы облегчить жизнь)
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from nltk.corpus import stopwords
import numpy as np
import numpy.linalg as LA
train_set = ["The sky is blue.", "The sun is bright."] # Documents
test_set = ["The sun in the sky is bright."] # Query
stopWords = stopwords.words('english')
vectorizer = CountVectorizer(stop_words = stopWords)
#print vectorizer
transformer = TfidfTransformer()
#print transformer
trainVectorizerArray = vectorizer.fit_transform(train_set).toarray()
testVectorizerArray = vectorizer.transform(test_set).toarray()
print 'Fit Vectorizer to train set', trainVectorizerArray
print 'Transform Vectorizer to test set', testVectorizerArray
transformer.fit(trainVectorizerArray)
print
print transformer.transform(trainVectorizerArray).toarray()
transformer.fit(testVectorizerArray)
print
tfidf = transformer.transform(testVectorizerArray)
print tfidf.todense()
в результате приведенного выше кода у меня есть следующая матрица
Fit Vectorizer to train set [[1 0 1 0]
[0 1 0 1]]
Transform Vectorizer to test set [[0 1 1 1]]
[[ 0.70710678 0. 0.70710678 0. ]
[ 0. 0.70710678 0. 0.70710678]]
[[ 0. 0.57735027 0.57735027 0.57735027]]
Я не уверен, как использовать этот вывод для вычисления косинусного сходства, я знаю, как реализовать косинусное сходство по отношению к двум векторам одинаковой длины, но здесь я не уверен, как идентифицировать два вектора.
python
machine-learning
nltk
information-retrieval
tf-idf
добавить точку с запятой
источник
источник
Ответы:
Во-первых, если вы хотите извлечь функции подсчета и применить нормализацию TF-IDF и евклидову нормализацию по строкам, вы можете сделать это за одну операцию с помощью
TfidfVectorizer
:>>> from sklearn.feature_extraction.text import TfidfVectorizer >>> from sklearn.datasets import fetch_20newsgroups >>> twenty = fetch_20newsgroups() >>> tfidf = TfidfVectorizer().fit_transform(twenty.data) >>> tfidf <11314x130088 sparse matrix of type '<type 'numpy.float64'>' with 1787553 stored elements in Compressed Sparse Row format>
Теперь, чтобы найти косинусные расстояния одного документа (например, первого в наборе данных) и всех остальных, вам просто нужно вычислить скалярные произведения первого вектора со всеми остальными, поскольку векторы tfidf уже нормализованы по строкам.
Как объяснил Крис Кларк в комментариях, и здесь косинусное сходство не учитывает величину векторов. Нормализованные по строкам имеют величину 1, поэтому линейного ядра достаточно для вычисления значений подобия.
API-интерфейс scipy sparse matrix немного странный (не такой гибкий, как плотные N-мерные массивы numpy). Чтобы получить первый вектор, вам нужно разрезать матрицу по строкам, чтобы получить подматрицу с одной строкой:
>>> tfidf[0:1] <1x130088 sparse matrix of type '<type 'numpy.float64'>' with 89 stored elements in Compressed Sparse Row format>
scikit-learn уже предоставляет попарные метрики (также известные как ядра на языке машинного обучения), которые работают как для плотных, так и для разреженных представлений векторных коллекций. В этом случае нам нужен точечный продукт, который также известен как линейное ядро:
>>> from sklearn.metrics.pairwise import linear_kernel >>> cosine_similarities = linear_kernel(tfidf[0:1], tfidf).flatten() >>> cosine_similarities array([ 1. , 0.04405952, 0.11016969, ..., 0.04433602, 0.04457106, 0.03293218])
Следовательно, чтобы найти 5 лучших связанных документов, мы можем использовать
argsort
и некоторую отрицательную нарезку массива (большинство связанных документов имеют самые высокие значения косинусного сходства, следовательно, в конце отсортированного массива индексов):>>> related_docs_indices = cosine_similarities.argsort()[:-5:-1] >>> related_docs_indices array([ 0, 958, 10576, 3277]) >>> cosine_similarities[related_docs_indices] array([ 1. , 0.54967926, 0.32902194, 0.2825788 ])
Первым результатом является проверка работоспособности: мы находим документ запроса как наиболее похожий документ с показателем сходства косинуса 1, который имеет следующий текст:
>>> print twenty.data[0] From: lerxst@wam.umd.edu (where's my thing) Subject: WHAT car is this!? Nntp-Posting-Host: rac3.wam.umd.edu Organization: University of Maryland, College Park Lines: 15 I was wondering if anyone out there could enlighten me on this car I saw the other day. It was a 2-door sports car, looked to be from the late 60s/ early 70s. It was called a Bricklin. The doors were really small. In addition, the front bumper was separate from the rest of the body. This is all I know. If anyone can tellme a model name, engine specs, years of production, where this car is made, history, or whatever info you have on this funky looking car, please e-mail. Thanks, - IL ---- brought to you by your neighborhood Lerxst ----
Второй наиболее похожий документ - это ответ, в котором цитируется исходное сообщение, поэтому в нем много общих слов:
>>> print twenty.data[958] From: rseymour@reed.edu (Robert Seymour) Subject: Re: WHAT car is this!? Article-I.D.: reed.1993Apr21.032905.29286 Reply-To: rseymour@reed.edu Organization: Reed College, Portland, OR Lines: 26 In article <1993Apr20.174246.14375@wam.umd.edu> lerxst@wam.umd.edu (where's my thing) writes: > > I was wondering if anyone out there could enlighten me on this car I saw > the other day. It was a 2-door sports car, looked to be from the late 60s/ > early 70s. It was called a Bricklin. The doors were really small. In addition, > the front bumper was separate from the rest of the body. This is > all I know. If anyone can tellme a model name, engine specs, years > of production, where this car is made, history, or whatever info you > have on this funky looking car, please e-mail. Bricklins were manufactured in the 70s with engines from Ford. They are rather odd looking with the encased front bumper. There aren't a lot of them around, but Hemmings (Motor News) ususally has ten or so listed. Basically, they are a performance Ford with new styling slapped on top. > ---- brought to you by your neighborhood Lerxst ---- Rush fan? -- Robert Seymour rseymour@reed.edu Physics and Philosophy, Reed College (NeXTmail accepted) Artificial Life Project Reed College Reed Solar Energy Project (SolTrain) Portland, OR
источник
cosine_similarities = linear_kernel(tfidf, tfidf)
:?С помощью комментария @ excray мне удалось выяснить ответ. Что нам нужно сделать, так это написать простой цикл for для перебора двух массивов, представляющих данные поезда и тестовые данные.
Сначала реализуйте простую лямбда-функцию для хранения формулы для вычисления косинуса:
cosine_function = lambda a, b : round(np.inner(a, b)/(LA.norm(a)*LA.norm(b)), 3)
А затем просто напишите простой цикл for для итерации по вектору to, логика для каждого «Для каждого вектора в trainVectorizerArray вы должны найти косинусное сходство с вектором в testVectorizerArray».
from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extraction.text import TfidfTransformer from nltk.corpus import stopwords import numpy as np import numpy.linalg as LA train_set = ["The sky is blue.", "The sun is bright."] #Documents test_set = ["The sun in the sky is bright."] #Query stopWords = stopwords.words('english') vectorizer = CountVectorizer(stop_words = stopWords) #print vectorizer transformer = TfidfTransformer() #print transformer trainVectorizerArray = vectorizer.fit_transform(train_set).toarray() testVectorizerArray = vectorizer.transform(test_set).toarray() print 'Fit Vectorizer to train set', trainVectorizerArray print 'Transform Vectorizer to test set', testVectorizerArray cx = lambda a, b : round(np.inner(a, b)/(LA.norm(a)*LA.norm(b)), 3) for vector in trainVectorizerArray: print vector for testV in testVectorizerArray: print testV cosine = cx(vector, testV) print cosine transformer.fit(trainVectorizerArray) print print transformer.transform(trainVectorizerArray).toarray() transformer.fit(testVectorizerArray) print tfidf = transformer.transform(testVectorizerArray) print tfidf.todense()
Вот результат:
Fit Vectorizer to train set [[1 0 1 0] [0 1 0 1]] Transform Vectorizer to test set [[0 1 1 1]] [1 0 1 0] [0 1 1 1] 0.408 [0 1 0 1] [0 1 1 1] 0.816 [[ 0.70710678 0. 0.70710678 0. ] [ 0. 0.70710678 0. 0.70710678]] [[ 0. 0.57735027 0.57735027 0.57735027]]
источник
transformer.fit
операций иtfidf.todense()
? Вы получили значения подобия из цикла, а затем продолжили выполнение tfidf? Где используется ваше вычисленное значение косинуса? Ваш пример сбивает с толку.0.408
и0.816
, что это за значения?Я знаю, что это старый пост. но я попробовал пакет http://scikit-learn.sourceforge.net/stable/ . вот мой код, чтобы найти сходство косинуса. Вопрос заключался в том, как вы рассчитаете косинусное сходство с этим пакетом, и вот мой код для этого
from sklearn.feature_extraction.text import CountVectorizer from sklearn.metrics.pairwise import cosine_similarity from sklearn.feature_extraction.text import TfidfVectorizer f = open("/root/Myfolder/scoringDocuments/doc1") doc1 = str.decode(f.read(), "UTF-8", "ignore") f = open("/root/Myfolder/scoringDocuments/doc2") doc2 = str.decode(f.read(), "UTF-8", "ignore") f = open("/root/Myfolder/scoringDocuments/doc3") doc3 = str.decode(f.read(), "UTF-8", "ignore") train_set = ["president of India",doc1, doc2, doc3] tfidf_vectorizer = TfidfVectorizer() tfidf_matrix_train = tfidf_vectorizer.fit_transform(train_set) #finds the tfidf score with normalization print "cosine scores ==> ",cosine_similarity(tfidf_matrix_train[0:1], tfidf_matrix_train) #here the first element of tfidf_matrix_train is matched with other three elements
Предположим, что запрос - это первый элемент train_set, а doc1, doc2 и doc3 - это документы, которые я хочу ранжировать с помощью косинусного сходства. тогда я могу использовать этот код.
Также были очень полезны уроки, представленные в вопросе. Вот все части для него часть-I , часть-II , часть-III
вывод будет следующим:
[[ 1. 0.07102631 0.02731343 0.06348799]]
здесь 1 означает, что запрос совпадает с самим собой, а три других - это оценки за сопоставление запроса с соответствующими документами.
источник
ValueError: Incompatible dimension for X and Y matrices: X.shape[1] == 1664 while Y.shape[1] == 2
Позвольте представить вам еще один урок, написанный мной. Он отвечает на ваш вопрос, но также дает объяснение, почему мы делаем некоторые из вещей. Я тоже постарался сделать его кратким.
Итак, у вас есть
list_of_documents
один массив строк, а другойdocument
- просто строка. Вам нужно найти такой документ изlist_of_documents
наиболее похожих наdocument
.Объединим их вместе:
documents = list_of_documents + [document]
Начнем с зависимостей. Становится понятно, почему мы используем каждую из них.
from nltk.corpus import stopwords import string from nltk.tokenize import wordpunct_tokenize as tokenize from nltk.stem.porter import PorterStemmer from sklearn.feature_extraction.text import TfidfVectorizer from scipy.spatial.distance import cosine
Один из подходов, который можно использовать, - это метод «набора слов» , когда мы обрабатываем каждое слово в документе независимо от других и просто складываем их все вместе в большой мешок. С одной точки зрения, он теряет много информации (например, как связаны слова), но с другой точки зрения он упрощает модель.
В английском и любом другом человеческом языке есть много «бесполезных» слов, таких как «a», «the», «in», которые настолько распространены, что не имеют большого значения. Они называются стоп-словами, и их рекомендуется удалить. Еще одна вещь, которую можно заметить, - это то, что такие слова, как «анализировать», «анализатор», «анализ» действительно похожи. У них общий корень, и все они могут быть преобразованы в одно слово. Этот процесс называется стеммингом, и существуют разные стеммеры, которые различаются по скорости, агрессивности и так далее. Таким образом, мы преобразуем каждый из документов в список основных слов без стоп-слов. Также мы отбрасываем все знаки препинания.
porter = PorterStemmer() stop_words = set(stopwords.words('english')) modified_arr = [[porter.stem(i.lower()) for i in tokenize(d.translate(None, string.punctuation)) if i.lower() not in stop_words] for d in documents]
Так чем же нам поможет этот набор слов? Представьте себе , у нас есть 3 сумки:
[a, b, c]
,[a, c, a]
и[b, c, d]
. Мы можем преобразовать их в векторы в базисе[a, b, c, d]
. Таким образом , мы в конечном итоге с векторами:[1, 1, 1, 0]
,[2, 0, 1, 0]
и[0, 1, 1, 1]
. То же самое и с нашими документами (только векторы будут длиннее). Теперь мы видим, что мы удалили много слов и вырезали другие, чтобы уменьшить размерность векторов. Здесь просто любопытное наблюдение. В более длинных документах будет намного больше положительных элементов, чем в более коротких, поэтому хорошо нормализовать вектор. Это называется термином частота TF, люди также использовали дополнительную информацию о том, как часто это слово используется в других документах - обратная частота документа IDF. Вместе у нас есть метрика TF-IDF, у которой есть несколько разновидностей. Этого можно добиться с помощью одной строчки в sklearn :-)modified_doc = [' '.join(i) for i in modified_arr] # this is only to convert our list of lists to list of strings that vectorizer uses. tf_idf = TfidfVectorizer().fit_transform(modified_doc)
На самом деле векторизатор позволяет делать много вещей, таких как удаление стоп-слов и строчные буквы. Я сделал их на отдельном этапе только потому, что sklearn не имеет неанглийских стоп-слов, а nltk есть.
Итак, мы рассчитали все векторы. Последний шаг - найти, какой из них больше всего похож на последний. Есть разные способы добиться этого, один из них - евклидово расстояние, которое не так велико по причине, обсуждаемой здесь . Другой подход - косинусное подобие . Мы перебираем все документы и вычисляем косинусное сходство между документом и последним:
l = len(documents) - 1 for i in xrange(l): minimum = (1, None) minimum = min((cosine(tf_idf[i].todense(), tf_idf[l + 1].todense()), i), minimum) print minimum
Теперь минимум будет иметь информацию о лучшем документе и его оценке.
источник
Это должно вам помочь.
from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity tfidf_vectorizer = TfidfVectorizer() tfidf_matrix = tfidf_vectorizer.fit_transform(train_set) print tfidf_matrix cosine = cosine_similarity(tfidf_matrix[length-1], tfidf_matrix) print cosine
и вывод будет:
[[ 0.34949812 0.81649658 1. ]]
источник
Вот функция, которая сравнивает ваши тестовые данные с обучающими данными, с преобразователем Tf-Idf, снабженным обучающими данными. Преимущество состоит в том, что вы можете быстро выполнить поворот или группировку, чтобы найти n ближайших элементов, и что вычисления выполняются по матрице.
def create_tokenizer_score(new_series, train_series, tokenizer): """ return the tf idf score of each possible pairs of documents Args: new_series (pd.Series): new data (To compare against train data) train_series (pd.Series): train data (To fit the tf-idf transformer) Returns: pd.DataFrame """ train_tfidf = tokenizer.fit_transform(train_series) new_tfidf = tokenizer.transform(new_series) X = pd.DataFrame(cosine_similarity(new_tfidf, train_tfidf), columns=train_series.index) X['ix_new'] = new_series.index score = pd.melt( X, id_vars='ix_new', var_name='ix_train', value_name='score' ) return score train_set = pd.Series(["The sky is blue.", "The sun is bright."]) test_set = pd.Series(["The sun in the sky is bright."]) tokenizer = TfidfVectorizer() # initiate here your own tokenizer (TfidfVectorizer, CountVectorizer, with stopwords...) score = create_tokenizer_score(train_series=train_set, new_series=test_set, tokenizer=tokenizer) score ix_new ix_train score 0 0 0 0.617034 1 0 1 0.862012
источник