Рассчитать TD-IDF для одного слова в текстовой
Я пытаюсь использовать Textacy, чтобы вычислить оценку TF-IDF для одного слова в стандартном корпусе, но немного неясно, какой результат я получаю.
Я ожидал единственного числа, которое представляло частоту слова в корпусе. Так почему я получаю список (?) Из 7 результатов?
"обвинитель" на самом деле французское слово, поэтому ожидал результата 0 от английского корпуса.
word = 'acculer'
vectorizer = textacy.Vectorizer(tf_type='linear', apply_idf=True, idf_type='smooth')
tf_idf = vectorizer.fit_transform(word)
logger.info("tf_idf:")
logger.info(tfidf)
Выход
tf_idf:
(0, 0) 2.386294361119891
(1, 1) 1.9808292530117262
(2, 1) 1.9808292530117262
(3, 5) 2.386294361119891
(4, 3) 2.386294361119891
(5, 2) 2.386294361119891
(6, 4) 2.386294361119891
Вторая часть вопроса заключается в том, как я могу предоставить свой собственный корпус для функции TF-IDF в Textacy, esp. один на другом языке?
РЕДАКТИРОВАТЬ
Как упомянуто @Vishal, я зарегистрировал выход, используя эту строку:
logger.info(vectorizer.vocabulary_terms)
Кажется предоставленное слово acculer
был разделен на персонажей.
{'a': 0, 'c': 1, 'u': 5, 'l': 3, 'e': 2, 'r': 4}
(1) Как я могу получить TF-IDF для этого слова против корпуса, а не каждого персонажа?
(2) Как я могу предоставить свой собственный корпус и указать на него как на парам?
(3) Можно ли использовать TF-IDF на уровне предложений? то есть: какова относительная частота терминов этого предложения по отношению к корпусу.
2 ответа
основы
Давайте разберемся с определениями, прежде чем изучать актуальные вопросы.
Предположим, что наш корпус содержит 3 документа (d1, d2 и d3 соответственно):
corpus = ["this is a red apple", "this is a green apple", "this is a cat"]
Термин частота (тс)
TF (слова) определяется как количество раз, когда слово появляется в документе.
tf(word, document) = count(word, document) # Number of times word appears in the document
TF определяется для слова на уровне документа.
tf('a',d1) = 1 tf('a',d2) = 1 tf('a',d3) = 1
tf('apple',d1) = 1 tf('apple',d2) = 1 tf('apple',d3) = 0
tf('cat',d1) = 0 tf('cat',d2) = 0 tf('cat',d3) = 1
tf('green',d1) = 0 tf('green',d2) = 1 tf('green',d3) = 0
tf('is',d1) = 1 tf('is',d2) = 1 tf('is',d3) = 1
tf('red',d1) = 1 tf('red',d2) = 0 tf('red',d3) = 0
tf('this',d1) = 1 tf('this',d2) = 1 tf('this',d3) = 1
При использовании необработанных подсчетов проблема заключается в том, что tf
значения слов в более длинных документах имеют более высокие значения по сравнению с более короткими документами. Эта проблема может быть решена путем нормализации необработанных значений счетчика путем деления на длину документа (количество слов в соответствующем документе). Это называется l1
нормализация. Документ d1
теперь может быть представлен tf vector
со всем tf
значения всех слов в словаре корпуса. Существует еще один тип нормализации, называемый l2
что делает l2
Норма вектора tf документа равна 1.
tf(word, document, normalize='l1') = count(word, document)/|document|
tf(word, document, normalize='l2') = count(word, document)/l2_norm(document)
|d1| = 5, |d2| = 5, |d3| = 4
l2_norm(d1) = 0.447, l2_norm(d2) = 0.447, l2_norm(d3) = 0.5,
Код: тф
corpus = ["this is a red apple", "this is a green apple", "this is a cat"]
# Convert docs to textacy format
textacy_docs = [textacy.Doc(doc) for doc in corpus]
for norm in [None, 'l1', 'l2']:
# tokenize the documents
tokenized_docs = [
doc.to_terms_list(ngrams=1, named_entities=True, as_strings=True, filter_stops=False, normalize='lower')
for doc in textacy_docs]
# Fit the tf matrix
vectorizer = textacy.Vectorizer(apply_idf=False, norm=norm)
doc_term_matrix = vectorizer.fit_transform(tokenized_docs)
print ("\nVocabulary: ", vectorizer.vocabulary_terms)
print ("TF with {0} normalize".format(norm))
print (doc_term_matrix.toarray())
Выход:
Vocabulary: {'this': 6, 'is': 4, 'a': 0, 'red': 5, 'apple': 1, 'green': 3, 'cat': 2}
TF with None normalize
[[1 1 0 0 1 1 1]
[1 1 0 1 1 0 1]
[1 0 1 0 1 0 1]]
Vocabulary: {'this': 6, 'is': 4, 'a': 0, 'red': 5, 'apple': 1, 'green': 3, 'cat': 2}
TF with l1 normalize
[[0.2 0.2 0. 0. 0.2 0.2 0.2 ]
[0.2 0.2 0. 0.2 0.2 0. 0.2 ]
[0.25 0. 0.25 0. 0.25 0. 0.25]]
Vocabulary: {'this': 6, 'is': 4, 'a': 0, 'red': 5, 'apple': 1, 'green': 3, 'cat': 2}
TF with l2 normalize
[[0.4472136 0.4472136 0. 0. 0.4472136 0.4472136 0.4472136]
[0.4472136 0.4472136 0. 0.4472136 0.4472136 0. 0.4472136]
[0.5 0. 0.5 0. 0.5 0. 0.5 ]]
Строки в tf
матрица соответствует документам (следовательно, 3 строки для нашего корпуса), а столбцы соответствуют каждому слову в словаре (индекс слова, показанный в словаре словаря)
Частота обратных документов (IDF)
Некоторые слова передают меньше информации, чем другие. Например, такие слова, как, a, an, this, являются очень распространенными словами и передают очень мало информации. IDF является мерой важности слова. Мы считаем слово, встречающееся во многих документах, менее информативным по сравнению со словами, встречающимися в нескольких документах.
idf(word, corpus) = log(|corpus| / No:of documents containing word) + 1 # standard idf
Для нашего корпуса интуитивно idf(apple, corpus) < idf(cat,corpus)
idf('apple', corpus) = log(3/2) + 1 = 1.405
idf('cat', corpus) = log(3/1) + 1 = 2.098
idf('this', corpus) = log(3/3) + 1 = 1.0
Код: IDF
textacy_docs = [textacy.Doc(doc) for doc in corpus]
tokenized_docs = [
doc.to_terms_list(ngrams=1, named_entities=True, as_strings=True, filter_stops=False, normalize='lower')
for doc in textacy_docs]
vectorizer = textacy.Vectorizer(apply_idf=False, norm=None)
doc_term_matrix = vectorizer.fit_transform(tokenized_docs)
print ("\nVocabulary: ", vectorizer.vocabulary_terms)
print ("standard idf: ")
print (textacy.vsm.matrix_utils.get_inverse_doc_freqs(doc_term_matrix, type_='standard'))
Выход:
Vocabulary: {'this': 6, 'is': 4, 'a': 0, 'red': 5, 'apple': 1, 'green': 3, 'cat': 2}
standard idf:
[1. 1.405 2.098 2.098 1. 2.098 1.]
Термин Частота - Обратная частота документа (tf-idf): tf-idf является мерой того, насколько важно слово в документе в корпусе. tf слова, взвешенного по его идентификаторам, дает нам меру tf-idf слова.
tf-idf(word, document, corpus) = tf(word, docuemnt) * idf(word, corpus)
tf-idf('apple', 'd1', corpus) = tf('apple', 'd1') * idf('apple', corpus) = 1 * 1.405 = 1.405
tf-idf('cat', 'd3', corpus) = tf('cat', 'd3') * idf('cat', corpus) = 1 * 2.098 = 2.098
Код: TF-IDF
textacy_docs = [textacy.Doc(doc) for doc in corpus]
tokenized_docs = [
doc.to_terms_list(ngrams=1, named_entities=True, as_strings=True, filter_stops=False, normalize='lower')
for doc in textacy_docs]
print ("\nVocabulary: ", vectorizer.vocabulary_terms)
print ("tf-idf: ")
vectorizer = textacy.Vectorizer(apply_idf=True, norm=None, idf_type='standard')
doc_term_matrix = vectorizer.fit_transform(tokenized_docs)
print (doc_term_matrix.toarray())
Выход:
Vocabulary: {'this': 6, 'is': 4, 'a': 0, 'red': 5, 'apple': 1, 'green': 3, 'cat': 2}
tf-idf:
[[1. 1.405 0. 0. 1. 2.098 1. ]
[1. 1.405 0. 2.098 1. 0. 1. ]
[1. 0. 2.098 0. 1. 0. 1. ]]
Теперь перейдем к вопросам:
(1) Как я могу получить TF-IDF для этого слова против корпуса, а не каждого персонажа?
Как видно выше, нет tf-idf
определяется независимо, tf-idf
слова по отношению к документу в корпусе.
(2) Как я могу предоставить свой собственный корпус и указать на него как на парам?
Это показано в приведенных выше образцах.
- Преобразуйте текстовые документы в документы textacy с помощью textacy.Doc api
- Tokenzie textacy.Doc использует метод to_terms_list. (С помощью этого метода вы можете добавить в словарь униграмму, биграмму или триграмму, отфильтровать стоп-слова, норализовать текст и т. Д.)
- Используйте textacy.Vectorizer для создания матрицы терминов из размеченных документов. Возвращенная матрица терминов
tf (raw counts): apply_idf=False, norm=None
tf (l1 normalized): apply_idf=False, norm='l1'
tf (l2 normalized): apply_idf=False, norm='l2'
tf-idf (standard): apply_idf=True, idf_type='standard'
(3) Можно ли использовать TF-IDF на уровне предложений? то есть: какова относительная частота терминов этого предложения по отношению к корпусу.
Да, вы можете, если и только если вы рассматриваете каждое предложение как отдельный документ. В таком случае tf-idf
вектор (полная строка) соответствующего документа может рассматриваться как векторное представление документа (в вашем случае это одно предложение).
В случае нашего корпуса (который содержит одно предложение на документ), векторное представление d1 и d2 должно быть близким по сравнению с векторами d1 и d3. Давайте проверим сходство косинусов и посмотрим:
cosine_similarity(doc_term_matrix)
Выход
array([[1. , 0.53044716, 0.35999211],
[0.53044716, 1. , 0.35999211],
[0.35999211, 0.35999211, 1. ]])
Как вы можете видеть, cosine_simility(d1,d2) = 0.53 и cosine_sdentifity(d1,d3) = 0.35, так что действительно d1 и d2 более похожи, чем d1 и d3 (1 точно одинаковы, а 0 не похожи - ортогональные векторы).
Как только вы тренируете свой Vectorizer
Вы можете закрепить обученный объект на диске для дальнейшего использования.
Заключение
tf
слова на уровне документа, idf
слова на уровне корпуса и tf-idf
слова в документе по отношению к корпусу. Они хорошо подходят для векторного представления документа (или предложения, когда документ состоит из одного предложения). Если вы заинтересованы в векторном представлении слов, изучите встраивание слов, например (word2vec, fasttext, glove и т. Д.).
Вы можете получить TF-IDF за слово против корпуса.
docs = ['this is me','this was not that you thought', 'lets test them'] ## create a list of documents
from sklearn.feature_extraction.text import TfidfVectorizer
vec = TfidfVectorizer()
vec.fit(docs) ##fit your documents
print(vec.vocabulary_) #print vocabulary, don't run for 2.5 million documents
Вывод: содержит idf для каждого слова и ему присваивается уникальный индекс в выводе
{u'me': 2, u'them': 6, u'that': 5, u'this': 7, u'is': 0, u'thought': 8, u'not': 3, u'lets': 1, u'test': 4, u'you': 10, u'was': 9}
print(vec.idf_)
Вывод: выводит значение idf для каждого словарного слова
[ 1.69314718 1.69314718 1.69314718 1.69314718 1.69314718 1.69314718 1.69314718 1.28768207 1.69314718 1.69314718 1.69314718]
Теперь, в соответствии с вашим вопросом, допустим, вы хотите найти tf-idf для некоторого слова, тогда вы можете получить его как:
word = 'thought' #example
index = vec.vocabulary_[word]
>8
print(vec.idf_[index]) #prints idf value
>1.6931471805599454
Ссылка: 1. подготовить текст
Сейчас проделываем ту же самую операцию с textacy
import spacy
nlp = spacy.load('en') ## install it by python -m spacy download en (run as administrator)
doc_strings = [
'this is me','this was not that you thought', 'lets test them'
]
docs = [nlp(string.lower()) for string in doc_strings]
corpus = textacy.Corpus(nlp,docs =docs)
vectorizer = textacy.Vectorizer(tf_type='linear', apply_idf=True, idf_type='smooth')
doc_term_matrix = vectorizer.fit_transform((doc.to_terms_list(ngrams=1, normalize='lower',as_strings=True,filter_stops=False) for doc in corpus))
print(vectorizer.terms_list)
print(doc_term_matrix.toarray())
Выход
['is', 'lets', 'me', 'not', 'test', 'that', 'them', 'this', 'thought','was', 'you']
[[1.69314718 0. 1.69314718 0. 0. 0.
0. 1.28768207 0. 0. 0. ]
[0. 0. 0. 1.69314718 0. 1.69314718
0. 1.28768207 1.69314718 1.69314718 1.69314718]
[0. 1.69314718 0. 0. 1.69314718 0.
1.69314718 0. 0. 0. 0. ]]
Ссылка: ссылка