7.2.特征提取#

2025-12-04 10:57:40

我们用以下计数为例。第一个词元出现了100%的时间,因此不太有趣。另外两个特征只出现了不到50%的时间,因此可能更能代表文档的内容

>>> counts = [[3, 0, 1],

... [2, 0, 0],

... [3, 0, 0],

... [4, 0, 0],

... [3, 2, 0],

... [3, 0, 2]]

...

>>> tfidf = transformer.fit_transform(counts)

>>> tfidf

with 9 stored elements and shape (6, 3)>

>>> tfidf.toarray()

array([[0.81940995, 0. , 0.57320793],

[1. , 0. , 0. ],

[1. , 0. , 0. ],

[1. , 0. , 0. ],

[0.47330339, 0.88089948, 0. ],

[0.58149261, 0. , 0.81355169]])

每行都被归一化为单位欧几里得范数

\(v_{norm} = \frac{v}{||v||_2} = \frac{v}{\sqrt{v{_1}^2 + v{_2}^2 + \dots + v{_n}^2}}\)

例如,我们可以计算counts数组中第一个文档中第一个词元的tf-idf,如下所示

\(n = 6\)

\(\text{df}(t)_{\text{term1}} = 6\)

\(\text{idf}(t)_{\text{term1}} = \log \frac{n}{\text{df}(t)} + 1 = \log(1)+1 = 1\)

\(\text{tf-idf}_{\text{term1}} = \text{tf} \times \text{idf} = 3 \times 1 = 3\)

现在,如果我们在文档中对剩余的2个词元重复此计算,我们得到

\(\text{tf-idf}_{\text{term2}} = 0 \times (\log(6/1)+1) = 0\)

\(\text{tf-idf}_{\text{term3}} = 1 \times (\log(6/2)+1) \approx 2.0986\)

以及原始tf-idf向量

\(\text{tf-idf}_{\text{raw}} = [3, 0, 2.0986].\)

然后,应用欧几里得(L2)范数,我们得到文档1的以下tf-idf值

\(\frac{[3, 0, 2.0986]}{\sqrt{\big(3^2 + 0^2 + 2.0986^2\big)}} = [ 0.819, 0, 0.573].\)

此外,默认参数smooth_idf=True会在分子和分母中添加“1”,就好像额外看到一个文档,其中每个词元在集合中恰好出现一次,这可以防止零除法

\(\text{idf}(t) = \log{\frac{1 + n}{1+\text{df}(t)}} + 1\)

使用此修改,文档1中第三个词元的tf-idf变为1.8473

\(\text{tf-idf}_{\text{term3}} = 1 \times \log(7/3)+1 \approx 1.8473\)

L2-标准化后的tf-idf变为

\(\frac{[3, 0, 1.8473]}{\sqrt{\big(3^2 + 0^2 + 1.8473^2\big)}} = [0.8515, 0, 0.5243]\):

>>> transformer = TfidfTransformer()

>>> transformer.fit_transform(counts).toarray()

array([[0.85151335, 0. , 0.52433293],

[1. , 0. , 0. ],

[1. , 0. , 0. ],

[1. , 0. , 0. ],

[0.55422893, 0.83236428, 0. ],

[0.63035731, 0. , 0.77630514]])

通过fit方法调用计算的每个特征的权重存储在模型属性中

>>> transformer.idf_

array([1., 2.25, 1.84])

由于tf-idf经常用于文本特征,因此还有一个名为TfidfVectorizer的类,它将CountVectorizer和TfidfTransformer的所有选项组合到一个模型中

>>> from sklearn.feature_extraction.text import TfidfVectorizer

>>> vectorizer = TfidfVectorizer()

>>> vectorizer.fit_transform(corpus)

with 19 stored elements and shape (4, 9)>

虽然tf-idf标准化通常非常有用,但在某些情况下,二进制出现标记可能提供更好的特征。这可以通过使用CountVectorizer的binary参数来实现。特别是,某些估计器(如伯努利朴素贝叶斯)明确建模离散布尔随机变量。此外,非常短的文本可能具有嘈杂的tf-idf值,而二进制出现信息则更稳定。

通常,调整特征提取参数的最佳方法是使用交叉验证网格搜索,例如通过将特征提取器与分类器进行流水线操作

文本特征提取和评估的示例流水线