为什么sklearn的tf-idf向量化器会给停用词最高的分数?

6

我使用nltk库中Brown语料库的每个类别,使用sklearn实现了Tf-idf。总共有15个类别,对于每个类别,最高分数都分配给一个停用词。

默认参数是use_idf=True,所以我在使用idf。该语料库足够大,可以正确计算分数。那么,为什么停用词会被分配高值呢?

import nltk, sklearn, numpy
import pandas as pd
from nltk.corpus import brown, stopwords
from sklearn.feature_extraction.text import TfidfVectorizer

nltk.download('brown')
nltk.download('stopwords')

corpus = []
for c in brown.categories():
  doc = ' '.join(brown.words(categories=c))
  corpus.append(doc)

thisvectorizer = TfidfVectorizer()
X = thisvectorizer.fit_transform(corpus)
tfidf_matrix = X.toarray()
features = thisvectorizer.get_feature_names_out()

for array in tfidf_matrix:
  tfidf_per_doc = list(zip(features, array))
  tfidf_per_doc.sort(key=lambda x: x[1], reverse=True)
  print(tfidf_per_doc[:3])

结果是:
[('the', 0.6893251240111703), ('and', 0.31175508121108203), ('he', 0.24393467757919754)]
[('the', 0.6907757197452503), ('of', 0.4103688069243256), ('and', 0.28727742797362427)]
[('the', 0.7263025975051108), ('of', 0.3656242079748301), ('to', 0.291070574384772)]
[('the', 0.6754696081456901), ('and', 0.31548027033056486), ('to', 0.2688347676067454)]
[('the', 0.6814989142114783), ('of', 0.45275950370682505), ('and', 0.2884682701141856)]
[('the', 0.695577697455948), ('of', 0.35341130124782577), ('and', 0.31967658612871513)]
[('the', 0.6319718467602307), ('and', 0.3252073024670836), ('of', 0.31905971640910474)]
[('the', 0.7201346766200954), ('of', 0.4283480504712354), ('and', 0.2462470090388333)]
[('the', 0.7145625245362096), ('of', 0.3795569321959571), ('and', 0.2911711705971684)]
[('the', 0.6452744438258314), ('to', 0.2965331457609836), ('and', 0.29378534827130653)]
[('the', 0.7507413874270662), ('of', 0.3364825248186412), ('and', 0.25753131787795447)]
[('the', 0.6883038024694869), ('of', 0.41770049303087814), ('and', 0.2675503490244296)]
[('the', 0.6952456562438267), ('of', 0.39285038765440655), ('and', 0.34045082029960866)]
[('the', 0.5816391566950566), ('and', 0.3731049841274644), ('to', 0.2960718382909285)]
[('the', 0.6514884130485116), ('of', 0.29645876610367955), ('to', 0.2766347756651356)]

每个单词都是停用词。每个类别的前大约15个单词都是停用词。
如果我使用带有nltk内置停用词的参数stop_words,那么这些值或多或少都是合适的。但是这对我来说没有意义——Tf-idf默认应该将它们降级,不是吗?我在哪里犯了一个愚蠢的错误吗?
my_stop_words = stopwords.words('english')
thisvectorizer = TfidfVectorizer(stop_words=my_stop_words)

[('said', 0.27925480211869536), ('would', 0.18907877226786665), ('man', 0.18520023334955144)]
[('one', 0.2904582969159082), ('would', 0.1989714323107254), ('new', 0.1394799739062623)]
[('would', 0.2225121466087311), ('one', 0.21533433542780428), ('new', 0.1603044497073654)]
[('would', 0.3015860042740072), ('said', 0.20105733618267146), ('one', 0.19691182409643082)]
[('state', 0.20994145654158766), ('year', 0.16516637619246616), ('fiscal', 0.1627693480477495)]
[('one', 0.27315617167196987), ('new', 0.1339515841852929), ('time', 0.12957408143413954)]
[('said', 0.25253824925464713), ('barco', 0.2297681382507305), ('one', 0.22671047376269457)]
[('af', 0.53260466412674), ('one', 0.2029977500545255), ('may', 0.12401317094240104)]
[('one', 0.29617565661385375), ('time', 0.15556701155475144), ('would', 0.14135656338388475)]
[('said', 0.22644107030344426), ('would', 0.2097909916046616), ('one', 0.1986909391388065)]
[('said', 0.2724277852935244), ('mrs', 0.19471476451838934), ('would', 0.1650670817295739)]
[('god', 0.2540052570261857), ('one', 0.18304020379411245), ('church', 0.17784155752544287)]
[('one', 0.2402151822472666), ('mr', 0.1854602509997279), ('new', 0.16073221753309752)]
[('said', 0.32053197885047946), ('would', 0.23918851593978377), ('could', 0.18980141345828996)]
[('helva', 0.34147320176374735), ('ekstrohm', 0.27116989551827), ('would', 0.2609130084842849)]

如果您使用 my_stop_words = list(stopwords.words('english')) 会发生什么? - rickhg12hs
2个回答

3

停用词被赋予较大的值,因为您的语料库和tfidf计算存在问题。

矩阵X的形状是(15, 42396),这意味着您只有15个文档,而这些文档包含了42396个不同的单词。

错误在于您将给定类别的所有单词合并为一个文档,而不是在此代码段中使用所有定义的文档。

for c in brown.categories():
  doc = ' '.join(brown.words(categories=c))
  corpus.append(doc)

您可以修改您的代码以:

for c in brown.categories():
    doc = [" ".join(x) for x in brown.sents(categories=c)]
    corpus.extend(doc)

每个文档都会创建一个条目。因此,您的X矩阵将具有形状(57340, 42396)

这非常重要,因为停用词将出现在大多数文档中,这将使它们的TFIDF值非常低。

您可以使用以下代码段查看最重要的25个单词:

import numpy as np
feature_names = thisvectorizer.get_feature_names_out()
sorted_nzs = np.argsort(X.data)[:-(25):-1]
feature_names[X.indices[sorted_nzs]]

输出:

 array(['customer', 'asked', 'properties', 'itch', 'locked', 'achieving',
        'jack', 'guess', 'criticality', 'me', 'sir', 'beckworth', 'visa',
        'will', 'casey', 'athletics', 'norms', 'yeah', 'eh', 'oh', 'af',
        'currency', 'example', 'movies'], dtype=object)

1
谢谢!但最初有15个文档,停用词(如“the”)肯定在这15个文档中出现了 - 那么它们为什么会有高值呢? - khrystyna_s
我故意只在语料库中放了15个文档 - 我想比较布朗语料库每个类别中最重要的单词。 - khrystyna_s
1
矩阵的形状为(2351,36092),但我仍然遇到了这个问题。停用词被分配了最高分数。 - Hashan Mahesh

1
The corpus is big enough...” 实际上,在这种情况下,足够大的是语料库中每个文档/文本的大小。然而,语料库的大小只有15个文档(因此,idf中的N为15)。如果打印 brown.categories(),您会看到Brown语料库包含15个类别,这些类别被用作您的文档。拥有一个小的语料库意味着在语料库中一些术语(如停用词)将在文档中具有相同的分布,因此,它们将通过idf以相同的方式受到惩罚。例如,如果单词“customer”仅像“and”一样在语料库中出现(即,两者出现在相同数量的文档中),则它们的idf值将相同;但是,由于通常具有更高的词项频率tf,停用词(如上面的“and”)将获得比诸如“customer”的单词更高的tf-idf分数;后者也可能出现在每个文档中(作为示例),但是其词项频率较低。
然而,语料库中的文档数量只是问题的一部分。事实上,Tf-idf已知会降低这些频繁出现的术语的重要性,同时突出显示在一个文档中频繁而在所有其他文档中罕见的术语。第二个可能的原因是sklearn的TfidfVectorizer(因此也包括TfidfTransformer)如何计算tf-idf分数。根据文档,tf-idf公式默认情况下计算为idf(t) = log [ (1 + n) / (1 + df(t)) ] + 1(还带有余弦归一化),这与标准公式不同,即idf(t) = log [ n / df(t) ]。因此,简言之,在使用tf-idf时应使用足够数量的文档样本。另外,值得尝试使用标准公式来计算tf-idf,并查看它们的效果。我最近在一个非常类似的问题上发布了一篇扩展答案,表明随着语料库大小(即文档数量)的增加,会消除更多的停用词(或语料库中常见的词)。请查看这里

矩阵的形状为(2351,36092),但我仍然遇到了这个问题。停用词被分配了最高分数。 - Hashan Mahesh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接