上一课我们讲了 TF-IDF。为了把 TF-IDF 理解得更扎实,这一课先回到更基础的表示方法:词袋模型。
词袋模型听起来有点抽象,其实它的想法很朴素:不管词在句子里的顺序,先统计每个词出现了多少次。
什么是词袋模型
词袋模型的英文是 Bag of Words。它把一段文本看成一个装满词的袋子,只关心袋子里有哪些词、每个词出现了几次。
例如两句话:
text
我 喜欢 机器学习
机器学习 很 有用
如果词表是 [我, 喜欢, 机器学习, 很, 有用],那么:
| 文档 | 词袋向量 |
|---|---|
| 我 喜欢 机器学习 | [1, 1, 1, 0, 0] |
| 机器学习 很 有用 | [0, 0, 1, 1, 1] |
每个文档变成等长向量,长度等于词表大小,每个位置的数字就是对应词在这段文本里的出现次数。这就把文本变成了数字。
词袋模型丢掉了什么
词袋模型有一个明显缺点:它会丢掉顺序。
text
我 不 喜欢 这个 功能
我 喜欢 这个 功能 不
如果只统计词频,两句话包含的词差不多,但真实语义可能完全不同。所以词袋模型不是完美的语言理解方法,它更像一个简单、稳定、容易解释的文本特征表。
入门阶段先掌握它很有价值,因为后面的 TF-IDF、文本分类 baseline、模型评估都会建立在这个基础上。
CountVectorizer 做了什么
在 sklearn 里,CountVectorizer 就是用来做词袋特征的工具。它主要做两件事:
- 从训练文本里建立词表;
- 把每条文本转换成词频向量。
python
from sklearn.feature_extraction.text import CountVectorizer
# 假设文本已经分好词、用空格隔开(中文项目里这一步由 jieba 完成)
texts = [
"我 喜欢 机器学习",
"机器学习 很 有用",
"这个 功能 不好用",
]
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(texts)
print(vectorizer.get_feature_names_out()) # 词表
print(X.toarray()) # 词频矩阵:每行一个文档,每列一个词
词频矩阵里,每一行是一个文档,每一列是一个词,值是这个词在该文档里的出现次数。
一个要注意的细节:CountVectorizer 默认的 token_pattern 会过滤掉单个字符。中文里"我""很"这种单字词默认会被丢弃,需要保留单字时可以设 token_pattern=r"(?u)\b\w+\b",或者在分词阶段就控制好词长。
fit_transform 和 transform 的区别
这个区别很重要:
python
X_train_vec = vectorizer.fit_transform(X_train_text)
X_test_vec = vectorizer.transform(X_test_text)
训练集用 fit_transform,因为要从训练集里学习词表。测试集只用 transform,因为测试集不能参与词表学习,否则就会发生数据泄漏。
这和前面讲过的标准化、Pipeline 是同一个原则:训练阶段学到的规则,测试阶段只能拿来用,不能重新学。
词表大小会影响模型
如果训练文本很多,词表可能非常大。词表越大,矩阵列数越多,模型训练也会更重。常用参数有:
| 参数 | 作用 | 建议 |
|---|---|---|
max_features |
最多保留多少个词 | 数据量大时必设,否则特征爆炸 |
min_df |
过滤出现太少的词 | 中小数据集设 2,过滤只出现 1 次的词 |
max_df |
过滤出现太多的词 | 0.7 ~ 0.9 之间,太常见的词没区分力 |
ngram_range |
加入连续词组合 | (1, 2) 同时取单词和双词组合 |
例如:
python
vectorizer = CountVectorizer(
max_features=5000,
min_df=2,
max_df=0.9,
ngram_range=(1, 2),
)
这表示最多保留 5000 个特征,过滤太少见和太常见的词,同时加入单词和连续两个词的组合。
ngram 是什么
ngram_range=(1, 2) 表示同时考虑 unigram 和 bigram。比如文本:
text
机器 学习 很 有用
unigram 是:
text
机器 / 学习 / 很 / 有用
bigram 是:
text
机器 学习 / 学习 很 / 很 有用
加入 bigram 后,模型有机会捕捉"机器 学习"这种连续搭配,而不是只看到"机器"和"学习"两个分散的词。
词袋和 TF-IDF 的关系
词袋模型输出的是词频计数,缺点也由此而来:
- 顺序丢失,"我不喜欢你"和"你不喜欢我"的向量完全一样;
- 高频词霸权,一个词出现 100 次未必比另一个词说一次更重要;
- 维度容易爆炸,10 万篇文档乘 2 万词汇就是 2 亿维矩阵(稀疏矩阵能处理,但内存压力大)。
TF-IDF 可以看成在词频基础上进一步加权:当前文本里重要、全局又有区分度的词权重更高。
text
CountVectorizer:数一数每个词出现几次
TfidfVectorizer:在词频基础上考虑全局区分度
两者都能用于文本分类。CountVectorizer 更直观,TF-IDF 往往在很多文本分类任务里效果更好。
这一课先记住
词袋模型是文本特征工程的第一块积木。它会把文本变成"样本数 × 词表大小"的矩阵,每一列代表一个词或词组,每个值代表它在文本里的出现次数。max_features、min_df、max_df 是控制维度的三件套,丢失词序和高频词霸权是它的主要弱点,下一步交给 TF-IDF 修正。
下一课我们把 TF-IDF 和逻辑回归接起来,训练一个真正可运行的中文文本分类 baseline。
在线阅读
原文地址:https://bestsdz.xyz/posts/countvectorizer-bag-of-words/