在 Textacy 中计算单个单词的 TD-IDF

ard*_*igh 5 python nlp machine-learning spacy textacy

我正在尝试使用Textacy计算标准语料库中单个单词的 TF-IDF 分数,但我对收到的结果有点不清楚。

我期待一个代表词在语料库中的频率的浮点数。那么为什么我会收到 7 个结果的列表 (?)?

“acculer”实际上是一个法语单词,因此期望从英语语料库中得到 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)
Run Code Online (Sandbox Code Playgroud)

输出

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
Run Code Online (Sandbox Code Playgroud)

问题的第二部分是如何将我自己的语料库提供给 Textacy 中的 TF-IDF 函数,尤其是。一种不同的语言?

编辑

正如@Vishal 所提到的,我已经使用这一行记录了输出:

logger.info(vectorizer.vocabulary_terms)
Run Code Online (Sandbox Code Playgroud)

似乎提供的单词acculer已被拆分为字符。

{'a': 0, 'c': 1, 'u': 5, 'l': 3, 'e': 2, 'r': 4}
Run Code Online (Sandbox Code Playgroud)

(1)如何针对语料库获取这个词的TF-IDF,而不是每个字符?

(2) 如何提供自己的语料库并将其作为参数指向?

(3)TF-IDF可以在句子层面使用吗?即:这句话的术语相对于语料库的相对频率是多少。

muj*_*iga 13

基本面

在研究实际问题之前,让我们先弄清楚定义。

假设我们的语料库包含 3 个文档(分别为 d1、d2 和 d3):

corpus = ["this is a red apple", "this is a green apple", "this is a cat"]
Run Code Online (Sandbox Code Playgroud)

词频 (tf)

tf (of a word) 被定义为一个词在文档中出现的次数。

tf(word, document) = count(word, document) # Number of times word appears in the document
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

使用原始计数存在一个问题,即tf与较短文档相比,较长文档中的单词值具有较高的值。这个问题可以通过将原始计数值除以文档长度(相应文档中的单词数)进行归一化来解决。这称为l1规范化。文档d1现在可以tf vectortf语料库词汇表中所有单词的所有值来表示。还有另一种标准化称为l2使l2文档的 tf 向量的范数等于 1。

tf(word, document, normalize='l1') = count(word, document)/|document|
tf(word, document, normalize='l2') = count(word, document)/l2_norm(document)
Run Code Online (Sandbox Code Playgroud)
|d1| = 5, |d2| = 5, |d3| = 4
l2_norm(d1) = 0.447, l2_norm(d2) = 0.447, l2_norm(d3) = 0.5, 
Run Code Online (Sandbox Code Playgroud)

代码: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())
Run Code Online (Sandbox Code Playgroud)

输出:

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      ]]
Run Code Online (Sandbox Code Playgroud)

tf矩阵中的行对应于文档(因此我们的语料库有 3 行),列对应于词汇表中的每个单词(词汇表中显示的单词的索引)

逆向文档频率 (idf)

有些词传达的信息比其他词少。例如像 the、a、an、this 这样的词,它们是非常常见的词,它们传达的信息非常少。idf 是衡量单词重要性的指标。我们认为与出现在少数文档中的单词相比,出现在许多文档中的单词的信息量较少。

idf(word, corpus) = log(|corpus| / No:of documents containing word) + 1  # standard idf
Run Code Online (Sandbox Code Playgroud)

直观地为我们的语料库 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
Run Code Online (Sandbox Code Playgroud)

代码: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'))
Run Code Online (Sandbox Code Playgroud)

输出:

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.]
Run Code Online (Sandbox Code Playgroud)

词频-逆文档频率(tf-idf):tf-idf是衡量一个词在语料库中的文档中的重要性的度量。用其 id 加权的词的 tf 为我们提供了该词的 tf-idf 度量。

tf-idf(word, document, corpus) = tf(word, docuemnt) * idf(word, corpus)
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

代码: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())
Run Code Online (Sandbox Code Playgroud)

输出:

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.        ]]
Run Code Online (Sandbox Code Playgroud)

现在来回答问题:

(1)如何针对语料库获取这个词的TF-IDF,而不是每个字符?

如上所见,没有tf-idf独立的定义,tf-idf一个词是相对于语料库中的一个文档而言的。

(2) 如何提供自己的语料库并将其作为参数指向?

它显示在上面的示例中。

  1. 使用 textacy.Doc api 将文本文档转换为 textacy Docs
  2. Tokenzie the textacy.Doc's using to_terms_list 方法。(使用此方法,您可以使用将 unigram、bigram 或 trigram 添加到词汇表中,过滤掉停用词和规范化文本等)
  3. 使用 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 和 d3 相比,d1 和 d2 的向量表示应该接近。让我们检查余弦相似度,看看:

cosine_similarity(doc_term_matrix)
Run Code Online (Sandbox Code Playgroud)

输出

array([[1.        ,     0.53044716,     0.35999211],
       [0.53044716,     1.        ,     0.35999211],
       [0.35999211,     0.35999211,     1.        ]])
Run Code Online (Sandbox Code Playgroud)

如您所见,cosine_similarity(d1,d2) = 0.53 和 cosine_similarity(d1,d3) = 0.35,因此确实 d1 和 d2 比 d1 和 d3 更相似(1 完全相似,0 不相似 - 正交向量)。

训练后,您Vectorizer可以将训练过的对象腌制到磁盘以备后用。

结论

tf一个词在文档级别,idf一个词在语料库级别tf-idf,一个词在相对于语料库的文档。它们非常适合文档(或当文档由单个句子组成时的句子)的向量表示。如果您对单词的向量表示感兴趣,请探索单词嵌入(如 word2vec、fasttext、glove 等)。