Основы
Позволяет получить четкие определения, прежде чем изучать актуальные вопросы.
Предположим, что наш корпус содержит 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) Как я могу предоставить свой собственный корпус и указать его в качестве параметра?
Это показано в приведенных выше примерах.
- Преобразование текстовых документов в документы textacy с использованием textacy.Doc api
- Использование текстовых документов. Использование 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 и т. Д.).