Рассчитать TD-IDF для одного слова в текстовой - PullRequest
1 голос
/ 19 апреля 2019

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

EDIT

Как уже упоминалось @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 голосов
/ 23 апреля 2019

Основы

Позволяет получить четкие определения, прежде чем изучать актуальные вопросы.

Предположим, что наш корпус содержит 3 документа (d1, d2 и d3 соответственно):

corpus = ["this is a red apple", "this is a green apple", "this is a cat"]

Term Frequency (tf)

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, 

Код: tf

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

Term Frequency - Inverse Frequency документа (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) Как я могу предоставить свой собственный корпус и указать его в качестве параметра?

Это показано в приведенных выше примерах.

  1. Преобразование текстовых документов в документы textacy с использованием textacy.Doc api
  2. Использование текстовых документов. Использование toTterms_list.(Используя этот метод, вы можете использовать добавление в словарь униграмм, биграмм или триграмм, отфильтровывать стоп-слова, норализовать текст и т. Д.)Возвращаемая матрица терминов:
    • 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_shoity (d1, d3) = 0,35, так и естьd1 и d2 более похожи, чем d1 и d3 (1 точно совпадает, а 0 не похож - ортогональные векторы).

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

Заключение

tf слова находится на уровне документа, idf слова находится на уровне корпуса, а tf-idf слова находится в документе относительно корпуса.Они хорошо подходят для векторного представления документа (или предложения, когда документ состоит из одного предложения).Если вы заинтересованы в векторном представлении слов, то изучите встраивание слов, например (word2vec, fasttext, glove и т. Д.).

1 голос
/ 22 апреля 2019

Вы можете получить 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. подготовить текст

Теперь выполняем ту же операцию с текстом

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

Ссылка: ссылка

...