本节摘要:SOURCE 3.1 详解 BoW 与 TF-IDF——统计时代最稳的基线。本节给出公式、sklearn 代码与中文示例,并说明高维稀疏向量的利弊。
构建词汇表 V,文档表示为 |V| 维向量,值为词频或 0/1。
SOURCE 示例:
缺点:丢失语序(「我不满意」≈「满意我不」);高维稀疏;OOV 无法处理。
from sklearn.feature_extraction.text import CountVectorizer corpus = ["我 喜欢 NLP", "NLP 很 有趣"] vec = CountVectorizer(token_pattern=r"(?u)\b\w+\b") X = vec.fit_transform(corpus) print(vec.get_feature_names_out())
稀有词权重更高,更能区分文档。
from sklearn.feature_extraction.text import TfidfVectorizer tfidf = TfidfVectorizer() X = tfidf.fit_transform(corpus)

| 条件 | 建议 |
|---|---|
| 数据 <5k | TF-IDF + 线性模型 |
| 需可解释 | 查看高权重词 |
| 资源受限 | 无 GPU 推理 |
⚠️ 常见坑:未做 min_df/max_df 过滤——超高维矩阵拖垮训练。
💡 关键直觉:BoW/TF-IDF 是「演化史」的可靠起点,不是过时技术。
前面给了 TF-IDF 公式与 sklearn 代码,这里把「稀疏性」讲透。假设语料有 10 万篇文档、词汇表 20 万词,BoW 矩阵就是 10 万乘 20 万,但每篇文档平均只出现几百个词,非零率低于 0.5%。scikit-learn 用稀疏矩阵存储,内存友好;但如果你误用转稠密矩阵的方法,内存立刻爆炸。所以生产代码里要习惯用形状与非零数这些属性,而不是直接打印矩阵。
min_df 与 max_df 的语义要分清:min_df=2 表示至少在 2 篇文档出现,过滤只在单篇出现的罕见词(通常是噪声);max_df=0.9 表示在超过 90% 文档出现的词被过滤(通常是停用词级别的常见词)。对中文,建议同时设 min_df 与 max_df,并配合 ngram_range 让「不」加「喜欢」这类组合成为特征。
TF-IDF 的可解释性来自「高权重词」:一篇被分为「差评」的样本,可以打印权重最高的若干特征词,人工一眼看出模型依据。这正是小数据场景仍选它的核心理由——可解释、可审计、低延迟。它的天花板在于忽略语序与上下文,所以理解它的边界,也就理解了后面的嵌入模型为什么出现。
还有一个实操点:TF-IDF 要在「训练集内部」拟合词汇表,再用同一份向量器去变换验证集与测试集,绝不能把所有数据合在一起拟合——那会造成信息泄漏,验证分数虚高。这一点和「测试集只能碰一次」是同一条纪律的两个侧面。第一次做特征工程的人最容易在这里翻车,把特征拟合法则写成注释挂在代码头部,能省很多返工。
对中文情感任务,一个高频操作是构建「情感词典扩展特征」:把 TF-IDF 稀疏向量与规则特征(情感词数量、否定词数量、感叹号个数、程度副词)拼接,往往能显著提升小数据上的表现。这类特征工程在 BERT 时代依然有价值,因为它提供的是模型之外的先验知识,尤其在标注有限时相当于免费信号。
最后,做超参搜索时,注意 TfidfVectorizer 在中文语料上第一次拟合较慢,可以把预处理结果缓存为稀疏矩阵文件,超参搜索复用同一份矩阵,避免每次都重新分词。
from sklearn.feature_extraction.text import TfidfVectorizer tfidf = TfidfVectorizer( token_pattern=r"(?u)\b\w+\b", ngram_range=(1, 2), min_df=2, max_df=0.9, sublinear_tf=True, # 用 1+log(tf) 压制高频词 ) X = tfidf.fit_transform(corpus) print("矩阵形状:", X.shape, "非零元素:", X.nnz)
| 参数 | 作用 | 中文建议 |
|---|---|---|
| ngram_range | 组合特征 | (1,2) |
| min_df | 滤罕见词 | 2~3 |
| max_df | 滤常见词 | 0.85~0.95 |
| sublinear_tf | 平滑词频 | True |
中文语料用 TF-IDF 有三个高频翻车点,值得单独列出。
| 翻车点 | 现象 | 对策 |
|---|---|---|
| 标点未处理 | 逗号、句号成特征 | 分词前清洗标点 |
| 数字未归一 | 型号、日期炸裂维度 | 归一为占位符 |
| 未设词性过滤 | 虚词稀释信息 | 按任务保留词性 |
实践中「产品型号」这类数据如果原样保留,会出现上万个低频特征;把它们归一化为一个占位符,既降维度又保语义。另外,TF-IDF 的 idf 对长文档有偏置,长文本可在子句级先切分再做聚合。最后一点:中文情感任务建议在特征层补「情感词与否定词计数」,这类规则特征与 TF-IDF 拼接后的提升,往往比换模型更明显。
当 TF-IDF 矩阵过大时,除了 min_df 与 max_df 过滤,还可以用特征选择(卡方检验、互信息)挑选与标签相关性最高的前 N 个特征。卡方检验计算每个词与类别的统计量,互信息衡量词对类别的信息增益;两者都依赖标签,因此只能在训练集上拟合,再应用到验证与测试,与 TF-IDF 的拟合纪律完全一致。中文场景下特征选择还能压缩词表到几千维,配合线性模型往往能得到与全量特征接近甚至更好的分数,同时训练和推理都快很多。