三种词袋模型CountVectorizerTFIDFHashVectorizer
三种词袋模型CountVectorizerTFIDFHashVectorizer

三种词袋模型CountVectorizerTFIDFHashVectorizer

1. CountVectorizer

CountVectorizer类会将文本中的词语转换为词频矩阵。 例如矩阵中包含一个元素a[i][j],它表示j词在i类文本下的词频。它通过fit_transform函数计算各个词语出现的次数,通过get_feature_names()可获取词袋中所有文本的关键字,通过toarray()可看到词频矩阵的结果。

from sklearn.feature_extraction.text import CountVectorizer
#语料
corpus = [
    'This is the first document.',
    'This is the this second second document.',
    'And the third one.',
    'Is this the first document?'
]
#将文本中的词转换成词频矩阵
vectorizer = CountVectorizer()
print(vectorizer)
#计算某个词出现的次数
X = vectorizer.fit_transform(corpus)
print(type(X),X)
#获取词袋中所有文本关键词
word = vectorizer.get_feature_names()
print(word)
#查看词频结果
print(X.toarray())

结果:

<class 'scipy.sparse._csr.csr_matrix'>   (np.int32(0), np.int32(8)) 1
  (np.int32(0), np.int32(3))    1
  (np.int32(0), np.int32(6))    1
  (np.int32(0), np.int32(2))    1
  (np.int32(0), np.int32(1))    1
  (np.int32(1), np.int32(8))    2
  (np.int32(1), np.int32(3))    1
  (np.int32(1), np.int32(6))    1
  (np.int32(1), np.int32(1))    1
  (np.int32(1), np.int32(5))    2
  (np.int32(2), np.int32(6))    1
  (np.int32(2), np.int32(0))    1
  (np.int32(2), np.int32(7))    1
  (np.int32(2), np.int32(4))    1
  (np.int32(3), np.int32(8))    1
  (np.int32(3), np.int32(3))    1
  (np.int32(3), np.int32(6))    1
  (np.int32(3), np.int32(2))    1
  (np.int32(3), np.int32(1))    1
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']
[[0 1 1 1 0 0 1 0 1]
 [0 1 0 1 0 2 1 0 2]
 [1 0 0 0 1 0 1 1 0]
 [0 1 1 1 0 0 1 0 1]]

我们看下词的分布:

and document first is one second the third this
0 1 1 1 0 0 1 0 1
0 1 0 1 0 2 1 0 2
1 0 0 0 1 0 1 1 0
0 1 1 1 0 0 1 0 1

再看下原段落,可以验证以下分布是否正确(a[i][j],它表示j词在i类文本下的词频):

1:    'This is the first document.',
2:    'This is the this second second document.',
3:    'And the third one.',
4:    'Is this the first document?'

关于结果 X:

X = vectorizer.fit_transform(corpus):

image-20250611113043782

文档词频矩阵(稠密形式)

文档 and document first is one second the third this 非零值数量
1 0 1 1 1 0 0 1 0 1 5个 (1+1+1+1+1)
2 0 1 0 1 0 2 1 0 2 6个 (1+1+2+1+2)
3 1 0 0 0 1 0 1 1 0 4个 (1+1+1+1)
4 0 1 1 1 0 0 1 0 1 4个 (1+1+1+1+1)
总计 - - - - - - - - - 19个非零值

矩阵维度是4(文档数)×9(词汇量)=36,但实际非零元素只有19个(见X.data数组长度),因此X.size=19反映的是所有文档中实际出现的词汇计数总和

关于词汇表顺序:

vectorizer.get_feature_names() 返回的词汇列表按 字母升序 排列(从 a 到 z),这是 CountVectorizer 的默认行为。
在用户代码的语料中,词汇顺序为:
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']

  • 验证and(a开头)排最前,this(t开头)排最后。

📌 顺序调整方法

若需自定义顺序,可通过以下方式实现:

# 按词汇首次出现的顺序排序(非字母序)
vectorizer = CountVectorizer(vocabulary=sorted(set(" ".join(corpus).split())))

注意:默认字母序可确保结果可复现,不受语料输入顺序影响

2. TF-IDF

TF-IDF(term frequency-inverse document frequency)是文本加权方法,采用统计思想,即文本出现的次数和整个语料中文档频率来计算字词的重要度,是一种用于评估词在文档中重要性的统计方法,其核心思想是:词的重要性随其在文档中的出现频率正比增加,但随其在语料库中的普遍性反比下降

什么是TF-IDF

TF-IDF是一种常用的文本处理技术,用以评估一个词对于一篇文章或语料库中一篇文章的重要性。TF代表词频(Term Frequency),IDF代表逆文档频率(Inverse Document Frequency)。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。

TF-IDF的使用场景

TF-IDF常被用于文本分类、信息检索、关键词提取等领域。在文本分类中,可以根据TF-IDF值来计算文本与某个类别的相关程度;在信息检索中,可以根据用户输入的关键词的TF-IDF值来排序搜索结果;在关键词提取中,可以根据TF-IDF值来确定文本中的关键词。

TF-IDF原理

TF(全称TermFrequency)指的是某个词在文本中出现的频率。如果一个词在文本中出现的次数越多,那么它的TF值就越高。例如,在一篇文章中,词语“apple”出现了5次,而总词数为1000个,那么它的TF值为0.005。

这其中还有一个漏洞,就是 ”的“ ”是“ ”啊“ 等类似的词在文章中出现的此时是非常多的,但是这些大多都是没有意义词,对于判断文章的关键词几乎没有什么用处,我们称这些词为”停用词“,也就是说,在度量相关性的时候不应该考虑这些词的频率。

IDF(全称InverseDocumentFrequency)指的是一个词在文本集合中的重要程度。如果一个词在整个文本集合中出现的文档数越少,那么它的IDF值就越高,说明这个词在文本中的重要程度越高。例如,在一个由1000篇文章组成的文本集合中,词语“apple”只出现在10篇文章中,那么它的IDF值为log(1000/10) = 2。

TF-IDF 值就是将TF和IDF相乘得到的结果。它反映了一个词在文本中的重要性。如果一个词在文本中出现的次数越多,同时在整个文本集合中出现的文档数越少,那么它的TF-IDF值就越高,说明这个词在文本中的重要程度越高。

TF-IDF的计算公式为

优点:过滤一些常见但是无关紧要的字词。

image-20250611114013531

TF(Term Frequency)表示某个关键词在整篇文章中出现的频率。(某个词在文章中出现的总次数/文章的总词数);

image-20250611114025223

IDF(Inverse Document Frequency)表示计算倒文本频率。文本频率是指某个关键词在整个语料所有文章中出现的次数。倒文档频率又称为逆文档频率,它是文档频率的倒数,主要用于降低所有文档中一些常见却对文档影响不大的词语的作用。

Transformer 官方文档

默认:

image-20250611114040635

这里, N为总的文档数,N(x)为包含这个词x的文档数。

教科书标准的idf定义:

image-20250611114153410

其中, N代表语料库的文档总数; N(x)代表包含该词x的文档数.

Tfidf 实现,一般是先通过countVectorizer, 然后再通过 tfidfTransformer, 转换成 tfidf 向量; 也有现成的 TfidfVectorizer API。

语句:

TfidfTransformer(norm='l2', use_idf=True, smooth_idf=True, sublinear_tf=False)

示例:

from sklearn.feature_extraction.text import TfidfVectorizer, TfidfTransformer, CountVectorizer
import numpy as np
#语料
cc = [
      'aa bb.',
      'aa cc.'
]
# method 1
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(cc)
print('feature',vectorizer.get_feature_names())
print(X.toarray())

结果:

feature ['aa', 'bb', 'cc']
[[0.57973867 0.81480247 0.        ]
 [0.57973867 0.         0.81480247]]

值得注意的是,默认语料会把单个字符当作停止单词(stop_words)进行过滤,如果需要保留单个字符组成的单词,可以修改分词方式:token_pattern='(?u)\\b\\w+\\b'

此外,上面还可实现如下:

# method 2
vectorizer=CountVectorizer()#token_pattern='(?u)\\b\\w+\\b'
transformer = TfidfTransformer()
cntTf = vectorizer.fit_transform(cc)
print('feature', vectorizer.get_feature_names())
print(cntTf)
cnt_array = cntTf.toarray()
X = transformer.fit_transform(cntTf)
print(X.toarray())

结果:

feature ['aa', 'bb', 'cc']
# 第一个数字表示第几个文档;第二个数字表示第几个feature,结果表示相应的词频。
  (0, 1)        1
  (0, 0)        1
  (1, 2)        1
  (1, 0)        1
[[0.57973867 0.81480247 0.        ]
 [0.57973867 0.         0.81480247]]

为了更加明白TfidfTransformer的操作,进行简单分解实现该功能:

# method 3
vectorizer=CountVectorizer()
cntTf = vectorizer.fit_transform(cc)
tf = cnt_array/np.sum(cnt_array, axis = 1, keepdims = True)
print('tf',tf)
idf = np.log((1+len(cnt_array))/(1+np.sum(cnt_array,axis = 0))) + 1
print('idf', idf)
t = tf*idf
print('tfidf',t)
print('norm tfidf', t/np.sqrt(np.sum(t**2, axis = 1, keepdims=True)))

结果:

tf [[0.5 0.5 0. ]
 [0.5 0.  0.5]]
idf [1.         1.40546511 1.40546511]
tfidf [[0.5        0.70273255 0.        ]
 [0.5        0.         0.70273255]]
norm tfidf [[0.57973867 0.81480247 0.        ]
 [0.57973867 0.         0.81480247]]

也就是说,TfidfTransformer 默认会对获得的向量除以2范数进行归一化。

image-20250611114340337

TF-IDF的信息论依据

一个查询(Query)中的每个关键词(Key Word)w的权重应该反映这个词查询来讲提供了多少信息。一个简单方法是用每个词的信息量作为他的权重。

img

但是,如果两个词出现的频率 TF相同, 一个是某篇特定文章中的常见词,而另外一个词时分散在多篇文章中,显然第一个词有更高的分辨率,权重应该更大。

img

3. HashingVectorizer

语法

HashingVectorizer(alternate_sign=True, analyzer='word', binary=False,
         decode_error='strict', dtype=<class 'numpy.float64'>,
         encoding='utf-8', input='content', lowercase=True,
         n_features=1048576, ngram_range=(1, 1), non_negative=False,
         norm='l2', preprocessor=None, stop_words=None, strip_accents=None,
         token_pattern='(?u)\\b\\w\\w+\\b', tokenizer=None)

特点

普通的CountVectorizer存在但词库很大时,占用大内存,因此,使用hash技巧,并用稀疏矩阵存储编译后的矩阵,能很好解决这个问题。

实现的伪代码为:

 function hashing_vectorizer(features : array of string, N : integer):
     x := new vector[N]
     for f in features:
         h := hash(f)
         x[h mod N] += 1
     return x

这里伪代码没有考虑到hash冲突的情况,实际实现会更加复杂。

from sklearn.feature_extraction.text import HashingVectorizer
corpus = [
     'This is the first document.',
     'This document is the second document.',
     'And this is the third one.',
     'Is this the first document?',
 ]
vectorizer = HashingVectorizer(n_features=2**4)
X = vectorizer.fit_transform(corpus)
print(X.toarray())
print(X.shape)

结果:

[[-0.57735027  0.          0.          0.          0.          0.
   0.          0.         -0.57735027  0.          0.          0.
   0.          0.57735027  0.          0.        ]
 [-0.81649658  0.          0.          0.          0.          0.
   0.          0.          0.          0.          0.          0.40824829
   0.          0.40824829  0.          0.        ]
 [ 0.          0.          0.          0.         -0.70710678  0.70710678
   0.          0.          0.          0.          0.          0.
   0.          0.          0.          0.        ]
 [-0.57735027  0.          0.          0.          0.          0.
   0.          0.         -0.57735027  0.          0.          0.
   0.          0.57735027  0.          0.        ]]
(4, 16)

4. 总结

总的而言,这三种都是词袋模型的方法,其中,由于tfidfvectorizer这种方法可以降低高频信息量少词语的干扰,应用得更多。


reference:

  1. (推荐)sklearn tfidf
  2. TF-IDF blog
  3. 刘建平 博客
  4. (推荐) Sklearn官网 Feature extraction;
  5. 学习sklearn之文本特征提取;
  6. wiki, feature hashing;
  7. 数学之美 吴军

发表回复

您的电子邮箱地址不会被公开。