Подтвердить что ты не робот

Python: tf-idf-cosine: найти сходство документа

Я следовал учебному пособию, которое было доступно в части 1 и части 2. К сожалению, у автора не было времени на последний раздел, в котором использовалось косинусное сходство, чтобы найти расстояние между двумя документами. Я следовал за примерами в статье с помощью следующей ссылки от qaru.site/info/99138/..., включил код, упомянутый в приведенной выше ссылке (просто, чтобы облегчить жизнь)

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]]

Я не уверен, как использовать этот выход для вычисления косинусного сходства, я знаю, как реализовать косинусное сходство в отношении двух векторов одинаковой длины, но здесь я не уверен, как идентифицировать эти два вектора.

4b9b3361

Ответ 1

С помощью справки @excray мне удается понять ответ: нам нужно написать простой цикл for для итерации по двум массивам, которые представляют данные поезда и тестовые данные.

Сначала реализуем простую лямбда-функцию для вычисления формулы для вычисления косинуса:

cosine_function = lambda a, b : round(np.inner(a, b)/(LA.norm(a)*LA.norm(b)), 3)

И тогда просто напишите простой цикл для итерации по вектору, логика для каждого "Для каждого вектора в 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]]

Ответ 2

Прежде всего, если вы хотите извлечь функции подсчета и применить нормализацию 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 уже являются строковыми, нормализуется. 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 уже предоставляет парные метрики (ядра a.k.a. на языке машинного обучения), которые работают как для плотных, так и для разреженных представлений векторных коллекций. В этом случае нам нужен точечный продукт, который также известен как линейное ядро:

>>> 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[email protected] (where 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: [email protected] (Robert Seymour)
Subject: Re: WHAT car is this!?
Article-I.D.: reed.1993Apr21.032905.29286
Reply-To: [email protected]
Organization: Reed College, Portland, OR
Lines: 26

In article <[email protected]> [email protected] (where 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              [email protected]
Physics and Philosophy, Reed College    (NeXTmail accepted)
Artificial Life Project         Reed College
Reed Solar Energy Project (SolTrain)    Portland, OR

Ответ 3

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

Ответ 4

Позвольте мне дать вам другой урок, написанный мной. Он отвечает на ваш вопрос, но также дает объяснение, почему мы занимаемся некоторыми вещами. Я также попытался сделать его кратким.

Итак, у вас есть 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

Одним из подходов, которые могут быть использованы, является подход bag-of-words, где мы обрабатываем каждое слово в документе независимо от других и просто бросаем их вместе в большой сумке. С одной точки зрения, он теряет много информации (например, как связаны слова), но с другой точки зрения делает модель простой.

На английском и любом другом человеческом языке существует множество "бесполезных" слов, таких как "а", "the", "in", которые настолько распространены, что они не имеют большого значения. Они называются останавливать слова, и это хорошая идея удалить их. Другое дело, что можно заметить, что слова "анализ", "анализатор", "анализ" действительно похожи. У них есть общий корень, и все они могут быть преобразованы в одно слово. Этот процесс называется stemming и существуют разные стеблемеры, которые отличаются скоростью, агрессивностью и т.д. Поэтому мы преобразуем каждый из документов в список стеблей слов без слов остановки. Также мы отбрасываем все знаки препинания.

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)

Фактически vectorizer позволяет делать много вещей, например, удаление стоп-слов и нижнего индекса. Я сделал их в отдельном шаге только потому, что 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

Теперь минимум будет содержать информацию о лучшем документе и его оценке.

Ответ 5

Это поможет вам.

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.        ]]

Ответ 6

Вот функция, которая сравнивает ваши тестовые данные с данными обучения, с трансформатором 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