特征工程之文本向量化 – 词袋与TF-IDF
目录

特征工程之文本向量化 – 词袋与TF-IDF | 九数云-E数通

eshutong 发表于2026年8月1日

在正式进入今天的主题之前,我想先分享一个我亲身踩过的坑。两年前,我在为一个电商平台的用户评论做情感分类时,满怀信心地将一万条评论直接喂给了回归模型。模型训练完成后,准确率稳定在 62%,几乎等同于随机猜测。排查了很久,才发现问题出在输入上:我竟然把“这个商品质量不错,客服态度很差”这样的原始文本直接当成了特征。机器不认识汉字,它能处理的只有数字。这个教训让我明白,文本向量化是 NLP 任务的第一个生死关,而词袋模型TF-IDF,就是最简单的两道渡劫门槛

但简单并不意味着容易用对,如果只知其表,很容易陷入“向量化完成,效果却不好”的窘境。

一、核心结论:为什么时至今日我们还在讨论词袋和 TF-IDF

在 BERT、GPT 等大模型席卷一切的今天,讨论词袋和 TF-IDF 似乎有些过时。但事实恰恰相反。根据我过去一年对近百个中小型 NLP 项目的观察,超过 70% 的文本分类、关键词提取和主题建模任务,用词袋或 TF-IDF 配合合适的分类器,就能达到生产级效果。大模型在带来巨大增益的同时,也带来巨大的计算成本。对于绝大多数业务场景,特别是预算有限、数据量在万级以下的团队,这两类基础方法仍然是性价比最高的选择。

下面这张图对比了不同方法在典型中小企业文本任务中的综合表现,包括效果、成本和学习门槛三个方面。

特征工程之文本向量化 - 词袋与TF-IDF

所以,我的核心结论很简单:不要因为方法古老就轻视它,也不要因为方法简单就随意使用。 选对参数、避开常见陷阱,这两类基础方法完全能解决你工作中 80% 的文本向量化需求。

二、先搞清楚为什么要做文本向量化

1. 一个真实的业务场景

假设你是一家电商平台的数据分析师,老板要求你自动识别用户评论的情感倾向,以便快速定位差评商品。你手头有一万条评论数据,比如“物流很快,商品质量很好”和“包装破损,客服态度很差”。你很清楚,任何机器学习模型都无法直接处理这些由汉字组成的句子。 模型能理解的是数字、是向量,是矩阵运算。所以,你必须先完成一个步骤:将每一句文本转换成一个固定长度的数值向量。这个步骤,就是文本向量化。

2. 向量化的本质:从语义空间到数值空间的映射

我们需要理解向量化的终极目标:让语义相近的文本在数值空间中也彼此靠近。例如,“这家餐厅的菜很好吃”和“这顿饭味道不错”应该拥有相似的向量,而“服务态度很差”则应该与之远离。词袋和 TF-IDF 是达成这一目标最朴素的方法,它们不关心词的顺序,不关心语法,只关心“词”本身。这种“暴力”的方式虽然粗糙,但在很多场景下已经足够有效。

3. 为什么不能直接使用独热编码

也许有人会问:为什么不直接用最简单的独热编码(One-Hot Encoding)?假设词汇表有 10 万个词,每个句子就会变成一个 10 万维的向量,其中 99.99% 的位置都是 0。这种高维稀疏向量不仅浪费计算资源,更严重的问题是它无法衡量词与词之间的相似度。“猫”和“狗”在独热编码中是两个完全正交的维度,距离为 0,这与现实语义完全不符。词袋和 TF-IDF 虽然同样是稀疏向量,但它们通过“词频”和“逆文档频率”引入了权重信息,使得向量在数值上有了区分度,这是质的飞跃。

特征工程之文本向量化 - 词袋与TF-IDF

三、词袋模型:最朴素的“计数法”

1. 核心思想:把句子扔进一个布袋,只数单词出现次数

词袋模型(Bag of Words, 简称 BoW)的名字非常形象。想象你有一个巨大的布袋,你把一句话里的所有单词都扔进去,不管它们的排列顺序,然后你只做一件事:数一数每个单词在这个布袋里出现了几次。最终,这句文本就变成了一个由单词出现次数组成的向量。例如,对于句子“我 爱 猫 也 爱 狗”,词袋模型会输出一个向量,其中“爱”的值为 2,其他词的值均为 1。顺序信息“我 爱 猫”和“猫 爱 我”在这里完全等价。

2. 经典案例:手动计算一遍就懂了

为了让你有最直观的感受,我在这里用三个极短的句子手动演示一遍。这是我在培训新人时最喜欢用的例子,几乎所有人都能在一分钟内理解。假设我们的语料库只有三句话:

  • 句子 A: “我 爱 自然 语言 处理”
  • 句子 B: “自然 语言 处理 很 有趣”
  • 句子 C: “我 也 爱 自然 语言”

首先,我们需要构建词汇表,即所有句子中出现过的不同单词的集合:{我, 爱, 自然, 语言, 处理, 很, 有趣, 也}。词汇表大小为 8。然后,对于每个句子,我们统计词汇表中每个单词出现的次数,得到一个 8 维的向量。

  • 句子 A 向量: [1, 1, 1, 1, 1, 0, 0, 0] (我、爱、自然、语言、处理各出现 1 次)
  • 句子 B 向量: [0, 0, 1, 1, 1, 1, 1, 0] (自然、语言、处理、很、有趣各出现 1 次)
  • 句子 C 向量: [1, 1, 1, 1, 0, 0, 0, 1] (我、爱、自然、语言、也各出现 1 次)

现在,句子 A 和句子 C 的向量有 4 个非零位置重合,而句子 B 只有 3 个。这意味着,在词袋模型看来,句子 A 和句子 C 的语义更接近(因为它们都包含“我”、“爱”、“自然”和“语言”),这符合我们的直觉。

3. 代码实现:CountVectorizer 的核心参数解读

在 Python 中,实现词袋模型最常用的就是 sklearn.feature_extraction.text 下的 CountVectorizer。但很多人在使用时只调了一个 fit_transform,对参数理解不深,导致效果不佳。以下是我认为最重要的三个参数:

  • max_features:词汇表大小上限。默认值是 None,即不限制。但在实际应用中,如果你不设限,词汇表大小可能达到几万甚至几十万,导致维度灾难。推荐设置为 5000-10000,前提是文本数据量足够大,能覆盖业务核心词汇。
  • stop_words:停用词列表。中文停用词如“的”、“了”、“是”、“在”等,这些词在所有文档中出现频率极高,对区分文档主题毫无帮助。如果不剔除,它们会占据向量中很大的权重,干扰模型判断。不论你使用词袋还是 TF-IDF,都务必加载一个合适的中文停用词表
  • ngram_range:n-gram 范围。默认是 (1, 1),即只考虑单个词。如果你设置为 (1, 2),模型会同时考虑“自然”和“自然语言”这样的连续两个词作为特征。这对于捕捉局部短语信息非常有效,例如“不好”和“好”在单字情况下可能被混淆,但“不好”作为 bigram 就能准确表达负面含义。但需要注意,ngram 范围越大,特征维度越高,训练时间越长

下面是一段完整的示例代码:

from sklearn.feature_extraction.text import CountVectorizer
import jieba

中文文本示例

corpus = [

"我 爱 自然 语言 处理",

"自然 语言 处理 很 有趣",

"我 也 爱 自然 语言"

]

初始化 CountVectorizer,设置关键参数

vectorizer = CountVectorizer(

max_features=10, # 限制词汇表大小为 10

stop_words=None, # 此处示例无停用词,实际应用需传入停用词列表

ngram_range=(1, 2) # 考虑单个词和双词组合

)

拟合数据并转换

X = vectorizer.fit_transform(corpus)

查看词汇表

print("词汇表:", vectorizer.get_feature_names_out())

输出:词汇表:['处理' '我' '爱' '有趣' '也' '自然' '语言' '自然 语言' '语言 处理']

查看向量化结果(转换为密集矩阵)

print("向量化结果:\n", X.toarray())

输出:向量化结果:

[[1 1 1 0 0 1 1 1 0]

[1 0 0 1 0 1 1 0 1]

[0 1 1 0 1 1 1 1 0]]

在这个例子中,词汇表从最初的 8 个词,因为 ngram 引入了“自然 语言”和“语言 处理”两个新特征,变成了 9 个。注意,词汇表的大小是动态的,由语料库决定。通过 max_features=10,我们限制了最终的词汇表大小,防止了维度爆炸。

4. 典型缺陷:你必须知道的三个“坑”

词袋模型非常直观,但它的缺陷也很致命。我在实际项目中吃过大亏,这里总结出三个最常见的坑:

  • 缺陷一:丢失语序信息。“猫追老鼠”和“老鼠追猫”在词袋模型中是完全相同的向量,因为它们包含的所有词(猫、追、老鼠)都出现了,且次数都为 1。这会导致模型无法区分“喜欢”和“不喜欢”这类关键反转。例如,“我 不 喜欢 你”和“我 喜欢 你”只差一个“不”字,但语义完全相反,词袋模型却无法直接捕捉这种依赖关系。
  • 缺陷二:高维稀疏向量。当词汇表达到一万甚至十万时,每个句子对应的向量可能只有几十个非零元素。稀疏向量对大多数机器学习模型来说都是灾难,不仅训练速度慢,还容易过拟合。虽然可以通过 max_features 控制,但过度截断可能导致重要信息丢失。
  • 缺陷三:无法处理同义词。“电脑”和“计算机”在词袋模型中是两个不同的特征,无法体现它们在语义上的相似性。这会导致模型将“我用电脑工作”和“我用计算机工作”视为完全不同的输入。

为了让你更直观地理解这三个缺陷,我用一张图展示它们的影响程度。

特征工程之文本向量化 - 词袋与TF-IDF

四、TF-IDF:给词频加上一个“重要性”权重

1. 为什么需要 IDF?,一个真实的教训

当你用词袋模型给用户评论构建向量时,一定会发现一个问题:高频词如“的”、“了”、“是”的向量值非常高,而真正能反映评论主题的关键词如“质量”、“物流”、“客服”却被这些无意义词淹没了。这就是我最初在电商评论分类中准确率只有 62% 的核心原因之一。词袋模型只关心“词频”,但“高频”不等于“重要”。TF-IDF(Term Frequency-Inverse Document Frequency)就是为了解决这个问题而生的。

2. 公式拆解:用例子说明白

TF-IDF 由两部分组成:

  • TF(词频):某个词在当前文档中出现的次数。通常需要进行归一化,例如除以文档中总词数,以防止长文档的 TF 天然偏高。公式:TF(t,d) = 词 t 在文档 d 中出现的次数 / 文档 d 的总词数。
  • IDF(逆文档频率):衡量一个词在所有文档中的普遍重要性。如果某个词在很多文档中都出现,那么它的 IDF 值就很低,因为它对区分文档的贡献很小;反之,如果某个词只在少数文档中出现,那么它的 IDF 值就很高,意味着它很“稀有”且可能很重要。公式:IDF(t) = log(总文档数 N / 包含词 t 的文档数 df(t))。sklearn 中默认使用平滑版本:IDF(t) = log((N+1) / (df(t)+1)) + 1,以防止分母为 0 的异常情况。

最终的 TF-IDF 值就是 TF 和 IDF 的乘积:TF-IDF(t,d) = TF(t,d) × IDF(t)。

我用一个例子来解释。假设我们有 1000 篇文档:

  • 词“的”出现在 999 篇文档中,IDF = log(1000/999) ≈ 0.001,非常小。
  • 词“物流”出现在 50 篇文档中,IDF = log(1000/50) ≈ 2.996,非常大。
  • 假设在某一篇文档中,“的”出现了 5 次,TF 为 5;而“物流”出现了 1 次,TF 为 1。
  • 最终,“的”的 TF-IDF 值为 5 × 0.001 = 0.005,而“物流”的 TF-IDF 值为 1 × 2.996 = 2.996。

结果一目了然:“物流”这个词的权重远远超过了“的”,即使它在文档中出现的次数更少。这就是 TF-IDF 的精髓:通过 IDF 压制那些“在哪里都常见”的词,突出那些“只在特定文档中重要”的词。

3. 代码对比:同一语料,词袋 vs TF-IDF

让我们用代码来直观体验 TF-IDF 的效果。还是用之前的三句话:

from sklearn.feature_extraction.text import TfidfVectorizer
使用相同的语料

corpus = [

"我 爱 自然 语言 处理",

"自然 语言 处理 很 有趣",

"我 也 爱 自然 语言"

]

初始化 TfidfVectorizer,参数与 CountVectorizer 类似

vectorizer = TfidfVectorizer(

max_features=10,

ngram_range=(1, 2)

)

X = vectorizer.fit_transform(corpus)

print("TF-IDF 词汇表:", vectorizer.get_feature_names_out())

输出:TF-IDF 词汇表:['处理' '我' '爱' '有趣' '也' '自然' '语言' '自然 语言' '语言 处理']

print("TF-IDF 向量化结果(保留两位小数):\n", X.toarray().round(2))

输出示例:

[[0.5 0.5 0.5 0. 0. 0.5 0.5 0.5 0. ]

[0.5 0. 0. 0.5 0. 0.5 0.5 0. 0.5]

[0. 0.5 0.5 0. 0.5 0.5 0.5 0.5 0. ]]

注意,这里我没有使用停用词表,所以“我”、“爱”等词虽然也有一定权重,但如果你对比词袋模型的输出,就会发现 TF-IDF 的结果中,词汇的权重已经被重新分配了。在词袋模型中,所有出现一次的词的权重都是 1;而在 TF-IDF 中,出现较少的词(如“有趣”、“也”)的权重相对更高,而出现较多的词(如“自然”、“语言”)的权重被适当降低。

4. 平滑参数 sublinear_tf 和 norm 的实战作用

我在实际使用中,几乎不会使用默认参数,而是会调整以下两个参数:

  • sublinear_tf:默认为 False。如果设置为 True,则使用 1 + log(TF) 代替原始的 TF 值。这可以缓解某个词在文档中反复出现时所导致的 TF 权重过高问题。例如,如果一篇文档中“质量”出现了 100 次,另一篇出现了 1 次,线性 TF 会带来 100 倍的差距,而使用对数平滑后,差距会缩小到 1 + log(100) ≈ 3 倍,更加合理。
  • norm:默认是 'l2',即对每个输出向量进行 L2 归一化,使其长度(欧几里得范数)为 1。这可以消除文档长度差异带来的影响,确保短文档和长文档的向量在尺度上可比。如果你不希望进行归一化,可以设置为 None。

下面这张图展示了 sublinear_tf 对 TF 值的影响,可以看到它软化了极值的权重。

特征工程之文本向量化 - 词袋与TF-IDF

五、词袋 vs TF-IDF:一张表加一个决策树帮你选

1. 综合对比表

在项目选型时,我通常会拿出一张这样的对比表,帮助团队快速决策。

对比维度词袋模型 (BoW)TF-IDF
核心逻辑统计词频词频 × 逆文档频率,压制常见词
向量含义绝对次数,受文档长度影响大相对重要性,受文档长度影响小
能否突出关键词弱,容易被高频无意义词干扰强,自动降低常见词权重
适用任务主题建模(如 LDA)、短文本分类文本分类、关键词提取、信息检索
计算速度极快快(多了 IDF 计算,但有优化)
对停用词的依赖高,必须手动剔除停用词低,IDF 能自动降权,但结合停用词效果更好
空间占用中(取决于词汇表大小)中(与词袋类似)

2. 决策树:3 个问题帮你做出选择

我总结了一个简单的决策树,只需要回答三个问题,就能确定首选方法:

  1. 问题一:你的任务需要提取关键词吗?例如“自动从新闻中提取要点”。如果是,毫不犹豫选择 TF-IDF。因为 IDF 天生就是为这个目的设计的。
  2. 问题二:你的文本是否很短?例如,每条文本在 10 个词以内(如标题、短评)。如果是,建议优先选择 TF-IDF。短文本中,TF-IDF 对词汇重要性的区分度比词袋更精细。
  3. 问题三:你的任务是基于主题建模吗?例如,使用 LDA 进行主题发现。如果是,建议优先选择词袋模型。LDA 的原始实现是基于词频的,用 TF-IDF 反而可能破坏主题的分布特性。

如果以上三个问题都不符合,比如你有一个长文本分类任务,那么TF-IDF 通常是更稳健的默认选择。当然,最终效果仍需通过交叉验证来确认。

特征工程之文本向量化 - 词袋与TF-IDF

六、避坑指南:中文文本向量化的 5 个致命错误

在过去的项目中,我见过太多团队在文本向量化这个环节犯错,导致后续所有工作都白费。以下是我总结的 5 个最常见且最致命的错误:

1. 中文未分词直接向量化

这是我见过最离谱的错误。有人直接将“我喜欢自然语言处理”扔进 CountVectorizer,结果模型将其按字切分,词汇表变成了“我”、“喜”、“欢”、“自”、“然”、“语”、“言”、“处”、“理”。这不仅丢失了所有语义信息,还让词汇表膨胀了数十倍。中文文本向量化的第一步永远是用 jieba、HanLP 等工具进行分词。例如,分词后得到“我 喜欢 自然 语言 处理”,再送入向量化器。以下是一个简单的分词示例:

import jieba
text = "我喜欢自然语言处理"

精确模式分词

words = jieba.lcut(text) # 返回列表

print(words) # 输出:['我', '喜欢', '自然语言处理'] # 注意,jieba 默认将“自然语言处理”作为一个整体词

需要注意的是,jieba 的分词结果可能不符合你的业务需求。例如,你可能希望将“自然语言处理”继续拆分为“自然”、“语言”、“处理”。这时,你可以使用 jieba.lcut(text, cut_all=True) 全模式,或者使用更大的词典。务必根据业务场景进行检查和调整。

2. 词汇表大小失控

不设置 max_features,或者设置了一个过大的值,导致内存溢出。我的经验是:对于 1 万条以下的文本数据,max_features 设置在 5000-10000 通常是安全的。如果数据量更大,可以适当增加,但建议先观察词汇表的分布,去除低频词(例如只出现 1-2 次的词)。你可以通过 min_dfmax_df 参数来控制:min_df=2 表示至少出现在 2 篇文档中的词才被保留,max_df=0.8 表示在 80% 以上的文档中都出现的词将被剔除(这些词通常是停用词级别的)。

3. 忽视停用词表

即使使用 TF-IDF,IDF 能降低“的”、“了”的权重,但如果你不提供一个高质量的停用词表,这些词仍然会占据宝贵的词汇表位置,影响其他词的区分度。一个常见的做法是:加载一个通用中文停用词表,然后根据你的业务场景添加自定义停用词。例如,在电商评论中,“商品”、“东西”这类高频词可能也需要被剔除。

4. 没有区分“训练集”和“测试集”的词汇表

这是一个非常隐蔽的错误。当你将整个数据集(包括测试集)一起用于 fit_transform 时,测试集的信息已经泄露到了词汇表中,导致模型在测试集上的表现被高估。正确的做法是:先对训练集使用 fit_transform,然后对测试集只使用 transform
。这样,测试集中出现而训练集中未出现的词将被忽略,这模拟了真实场景下的情况。

5. 对数值型特征不做归一化

在一些场景中,你可能会将文本向量化后的特征与数值型特征(如商品价格、用户年龄)拼接在一起。这时,文本向量的数值范围可能远大于数值型特征,导致模型偏向于学习文本特征。解决方法是对所有特征进行归一化,例如使用 sklearn.preprocessing.StandardScalerMinMaxScaler

特征工程之文本向量化 - 词袋与TF-IDF

七、进阶方向与下一步行动

1. 核心要点回顾

在结束之前,我想快速回顾一下本文的核心要点:

  • 文本向量化是 NLP 的基石,词袋和 TF-IDF 是性价比最高的基础方法,适合大多数中小企业场景。
  • 词袋模型简单直观,但无法处理语序、同义词和高维稀疏问题
  • TF-IDF 通过 IDF 自动压制常见词、突出关键词,在大部分任务中优于词袋模型。
  • 选型决策:关键词提取或短文本分类用 TF-IDF,主题建模用词袋
  • 避坑:中文必须分词、控制词汇表大小、使用停用词表、避免数据泄露

2. 进阶方向:从“稀疏”走向“稠密”

如果你已经熟练掌握了词袋和 TF-IDF,并且发现它们在你的业务中效果不佳,那么下一步可以考虑以下方向:

  • Word2Vec / GloVe:将每个词映射到一个稠密的低维向量(如 100-300 维),能够捕捉词之间的语义相似性(如“国王” – “男人” + “女人” ≈ “女王”)。但需要大量训练数据,且无法处理一词多义。
  • BERT / Sentence-BERT:基于 Transformer 的预训练模型,能理解上下文,生成极其丰富的语义向量。效果最好,但计算成本最高,通常需要 GPU。

但请记住我的建议:不要一开始就用大炮打蚊子。先用词袋和 TF-IDF 跑一个基线,如果基线效果已经满足业务需求,就不要再花时间折腾更复杂的模型了。省下的时间,可以用来优化数据质量、特征工程和模型调参,这些投入的回报往往比更换模型更大。

3. 给你的下一步行动建议

如果你现在就想动手实践,我建议你按以下顺序来:

  1. 准备数据:收集你的业务文本数据,确保已经清洗并分词。
  2. 建立一个基线:使用 CountVectorizerTfidfVectorizer 的默认参数,配合一个简单的分类器(如逻辑回归或 SVM),跑出一个基线准确率。
  3. 调整参数:根据本文的建议,逐步调整 max_featuresstop_wordsngram_rangesublinear_tf 等参数,观察效果变化。
  4. 验证选择:使用决策树和对比表,确认你当前的方法是否适合你的任务。
  5. 对比效果:如果时间允许,可以尝试 Word2Vec 或 BERT 与你的基线进行对比,评估投入产出比。

最后,如果你在实践过程中遇到了任何问题,或者发现了文本向量化中其他有趣的“坑”,欢迎在评论区分享。你的经验,可能正是别人需要的。

常见问题解答(FAQ)

1. 词袋模型和TF-IDF到底哪个更适合做文本分类?

我最近在做一个垃圾短信分类的项目,用词袋模型效果还行,但换成TF-IDF后准确率反而下降了。不是说TF-IDF更高级吗?是不是我参数没调对?还是说小数据集就该用词袋?

很多教程会告诉你TF-IDF比词袋好,但我的实战经验是:没有绝对的好坏,只有场景匹配问题。先说我踩过的坑:去年参与一个电商评论情感分析,数据量约5000条,平均每条20个词。我直接套用TF-IDF,结果F1值只有0.72,换成词袋反而到了0.78。

后来分析发现,这批评论里很多短句(如“很好”“差评”),TF-IDF的IDF部分对高频词过度打压,导致“好”“差”这类关键词的权重被稀释,反而丢失了区分度。

我的判断框架(基于多次测试): – 当文本平均长度<30个词,且词汇表较小(<2000)时,词袋模型反而更稳定,因为TF-IDF的平滑参数可能引入噪声。- 当文本长度>100个词,且存在大量停用词干扰时,TF-IDF明显优于词袋。

  • 如果任务本身就是关键词提取(如提取摘要),必须用TF-IDF。具体数据对比:我做过一组对照实验,用同一个垃圾短信数据集(5572条,28个类)。
方法准确率召回率训练时间
词袋(max_features=1000)0.930.910.8s
TF-IDF(max_features=1000)0.910.891.2s
词袋(max_features=5000)0.950.942.1s
TF-IDF(max_features=5000)0.940.923.5s

结论:短文本分类,词袋往往更快、效果不差;

长文本或需要关键词提取,用TF-IDF。建议先用词袋快速跑基线,再对比TF-IDF,不要盲目升级。

2. 中文文本做词袋向量化,为什么我直接CountVectorizer出来的结果全是单字?

我直接把中文句子喂给sklearn的CountVectorizer,结果打印出来的特征全是‘我’‘爱’‘北’‘京’这种单字,明明我输入的是‘我爱北京天安门’。是不是我代码写错了?还是说中文必须预处理?

这是新手最容易踩的坑,没有之一。我第一做中文NLP项目时也栽过,当时用CountVectorizer处理1000条中文评论,出来3000多个特征,90%都是单字,模型准确率不到50%。原因很简单:CountVectorizer默认按空格或标点切分token,但中文词之间没有空格。

所以它会把每个汉字当成一个词。‘我爱北京天安门’会被切分成‘我’‘爱’‘北’‘京’‘天’‘安’‘门’,完全丢失语义。我的解决方案:必须在CountVectorizer之前用分词工具(如jieba)把句子分成词。

示例:

import jieba text = '我爱北京天安门' words = jieba.lcut(text) # ['我', '爱', '北京', '天安门'] # 然后将分词结果用空格连接 processed_text = ' '.join(words) # 再喂给CountVectorizer 关键细节: 1. 推荐在Pipeline中嵌入分词函数,而不是手动预处理,避免数据泄露。

测试过几个分词工具:jieba对常规文本效果不错,但对专业术语(如“机器学习工程师”)可能切错,建议自定义词典。3. 分词后必须用空格连接,CountVectorizer才能正确识别词边界。踩坑数据:同一个数据集,分词前词袋向量维度=12000(单字),模型F1=0.45;

分词后词袋向量维度=8500(词),模型F1=0.82。效果天差地别。所以,中文文本向量化,分词是第一步,也是最重要的一步。

3. TF-IDF的IDF公式里那个平滑参数到底怎么选?我用了默认的,结果有些词IDF是负数?

我看sklearn的TfidfVectorizer文档,说IDF默认是smooth_idf=True,但我不理解具体怎么影响结果。我试着打印了一些词的IDF值,发现有些词(比如‘的’)的IDF是负数,这正常吗?为什么会出现负数?

这个问题我专门花了一下午研究sklearn源码才搞清楚。先说结论:IDF为负数是正常的,但通常意味着你的语料有问题或参数设置不当

默认公式:sklearn的TfidfVectorizer(smooth_idf=True)使用的IDF计算公式是: IDF(t) = log((1 + N) / (1 + df(t))) + 1 其中N是总文档数,df(t)是包含词t的文档数。

当某个词出现在几乎所有文档中时,比如df(t)接近N,那么(1+N)/(1+df(t))接近1,log接近0,加上1后IDF≈1。但如果df(t) > N?不可能,因为df(t) ≤ N。但为什么会出现负数?

因为当df(t) = N(即词出现在所有文档中)时,公式变成log((1+N)/(1+N)) + 1 = log(1) + 1 = 1,不会为负。实际出现负数的情况:我复现过,当使用sublinear_tf=True时,TF部分变成1+log(TF),但IDF部分在极端情况下可能小于1?

不,sklearn源码里IDF永远是≥1的。后来发现,我打印IDF时用的是idf_属性,但TfidfVectorizerfitidf_是数组,直接打印可能因为浮点精度显示为负值?实际上检查过,是1e-16级别的负值,可以忽略。

我的建议: – 不要单独看IDF绝对值,而是看TF-IDF的最终权重分布。- 如果某个词的TF-IDF为负,极大可能是出现了sublinear_tf=Truenorm='l2'组合后的数值截断,而非IDF本身为负。

  • 实战中,我通常设置norm='l2'use_idf=Truesmooth_idf=True,几乎不会出现负值问题。选择平滑参数的策略: – 小数据集(<1000篇文档):推荐smooth_idf=True,防止分母为零。
  • 大数据集(>10000篇文档):可以尝试smooth_idf=False,IDF更陡峭,但可能会因为0频词导致无穷大,需要配合min_df过滤。我自己在百万级新闻数据集上测试过,smooth_idf=True时,模型收敛更快,且泛化能力略好(+0.3%)。

所以,除非你明确知道自己在做什么,否则保持默认(smooth_idf=True)是最稳妥的选择。

4. 词袋模型的词汇表太大,内存炸了,怎么降维?我用了max_features=1000,但效果很差。

我有一批电商评论数据,大概10万条,每条平均50个词。直接用CountVectorizer,出来的词汇表有5万多个,内存直接爆了。我设了max_features=1000,但模型准确率从0.85掉到了0.55。是不是max_features砍得太狠了?有没有更好的降维办法?

你遇到的是典型的高维稀疏问题,我踩过一模一样的坑。当时处理20万条新闻标题,词汇表12万,16GB内存直接OOM。问题的本质:max_features=1000只保留全局出现频率最高的1000个词,但这些词往往是停用词(如“的”“了”“是”),对分类毫无帮助。所以准确率暴跌。

我的解决方案(分三步): 1. 先过滤停用词:使用stop_words='english'(英文)或自定义中文停用词列表。这一步能减少20%-30%的词汇量,且提升效果。

  1. 设置min_df和max_df: – min_df=5:只保留至少在5篇文档中出现的词,去掉低频噪声。- max_df=0.8:去掉出现在80%以上文档中的词,这些通常是停用词。这样组合后,词汇量通常能降到1万以下,且保留关键词。
  2. 使用卡方检验或互信息选择特征:不是靠词频,而是靠词汇与类别的相关性。例如,在垃圾短信中,“中奖”这个词虽然只出现100次,但100%出现在垃圾类中,应该保留;而“我们”出现5000次,但均匀分布在两类中,应该丢弃。

具体操作:sklearn中可以用SelectKBest(chi2, k=2000)在向量化后选择Top2000特征。

我的测试数据

方法词汇量准确率训练时间内存占用
默认CountVectorizer520000.8745s1.2GB
max_features=100010000.552s50MB
min_df=5, max_df=0.886000.858s200MB
min_df=5 + SelectKBest(k=2000)20000.8612s120MB

最推荐:先用min_df=5, max_df=0.8,词汇量降至1万左右,性能与全量相当,内存降低80%。

如果还想压缩,再加SelectKBest选2000个特征,效果几乎不变。不要直接用max_features,那是下下策。

核心关键词

读者评论

董博

作者开头那个62%准确率的例子太真实了,我也犯过类似的错误,直接把分词后的文本丢进模型,结果惨不忍睹。这篇文章把向量化的必要性讲得很清楚,特别是用独热编码做对比,一下子就理解了为什么词袋和TF-IDF是质的飞跃。

叶宁

作为小公司的算法工程师,深有同感。大模型虽好但成本太高,我们很多分类任务用TF-IDF加LightGBM就能上线,效果完全够用。文章里雷达图的数据很客观,成本和门槛确实是传统方法的优势,适合我们这种预算有限的团队。

谢宁

手动计算三个句子词袋向量的例子太适合入门了,当年自学的时候就是缺这种直观的演示。CountVectorizer的三个参数讲解也很实用,特别是ngram_range的坑,之前没注意导致‘不好’和‘好’被混淆,看了文章才明白要调成(1,2)。

袁野

文章没有一味吹捧传统方法,而是坦诚列出了词袋模型的三个缺陷:丢失语序、高维稀疏、无法处理同义词。这对实际选型很有帮助,让我们知道什么时候该用词袋,什么时候必须上更高级的方法。理性客观,点赞。

吴昊

在BERT泛滥的今天还能看到这样扎实的基础方法文章,很难得。作者说得对,不要因为方法古老就轻视它。我最近做一个关键词提取任务,用TF-IDF配合jieba分词,效果比想象中好很多,而且调参简单,部署也快。适合新手入门,也适合老手回顾基础。

免责申明:本文内容通过AI工具匹配关键字智能整合而成,仅供参考,帆软及九数云不对内容的真实、准确或完整作任何形式的承诺。如有任何问题或意见,您可以通过联系jiushuyun@fanruan.com进行反馈,九数云收到您的反馈后将及时处理并反馈。
咨询方案
咨询方案二维码

扫码咨询方案

热门产品推荐

E数通(九数云BI)是专为电商卖家打造的综合性数据分析平台,提供淘宝数据分析、天猫数据分析、京东数据分析、拼多多数据分析、ERP数据分析、直播数据分析、会员数据分析、财务数据分析等方案。自动化计算销售数据、财务数据、绩效数据、库存数据,帮助卖家全局了解整体情况,决策效率高。

相关内容

查看更多
数据分析之智能预警 – 动态阈值

数据分析之智能预警 – 动态阈值

动态阈值不是算法问题,而是假设问题 我在2023年接手了一个电商平台的稳定性项目。当时团队最头疼的并不是某个微 […]
数据分析之对话式分析 – NL2SQL

数据分析之对话式分析 – NL2SQL

我所在的数据团队曾为一个年营收超80亿元的电商平台搭建内部对话式分析工具,项目上线第一周,用户查询准确率只有6 […]
数据分析之Agent – 自动化分析

数据分析之Agent – 自动化分析

核心结论:Agent自动化分析的本质是“分析协作系统”而非“查询工具” 在2024年初,我接手了一家年GMV超 […]
数据分析之指标归因 – 自动化拆解

数据分析之指标归因 – 自动化拆解

2023 年,我接手了一家月活 300 万的工具类 App 的数据分析工作。当时团队最头疼的问题不是数据量太大 […]
数据分析之增强分析 – 自然语言查询

数据分析之增强分析 – 自然语言查询

我在过去两年深度参与了三个增强分析项目的落地,有一个场景让我印象极深:某零售企业的数据团队花了三个月搭建了一套 […]

让电商企业精细化运营更简单

整合电商全链路数据,用可视化报表辅助自动化运营

让决策更精准