一句话总结: 邮件分类是文本分类的经典场景,
TfidfVectorizer(抑制停用词)+MultinomialNB(多项式朴素贝叶斯)是工业基线三件套。
环境要求
- Python 3.14+
- scikit-learn 1.9+
- pandas 3.0+
- numpy 2.4+
- jupyter 1.1+
- notebook 7.5+
- nbconvert 7.17+
一、为什么 TF-IDF + 朴素贝叶斯是文本分类默认组合
| 组件 | 作用 |
|---|---|
| TfidfVectorizer | 将文本转为向量,自动压低停用词权重 |
| MultinomialNB | 多项式朴素贝叶斯,假设特征条件独立,文本分类效果稳健 |
| Pipeline | 串联向量化 + 分类器,生产部署必备 |
优势: 训练快、可解释、对小数据集友好(50 篇文档也能跑)。
二、数据加载
import pandas as pd
data_train = pd.read_csv("email_data_train.csv") # 仅 "text" 列
target_train = pd.read_csv("email_target_train.csv") # 仅 "target" 列(0/1)
数据结构: 二分类邮件数据(正常/垃圾,或不同主题),50 篇训练样本。
三、转换为 List 格式
注意: sklearn 向量化器要求输入是可迭代的字符串序列。
data_train = data_train["text"].tolist()
target_train = target_train["target"].tolist()
大规模数据提示: 百万级文本用 HashingVectorizer 替代,无需存储词表,节省内存。
四、CountVectorizer 词频向量化
核心: 构建词表,每篇文档转成"该词出现次数"的向量。
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer() # 默认:小写 + 按空格切分
data_train_vectorized = vectorizer.fit_transform(data_train)
输出: 稀疏矩阵 (50, 3225) = 50 篇文档 × 3225 个词。
# 50 篇文档有 3225 个不同的词
print(vectorizer.get_feature_names_out()[1000:1100]) # 查看部分词表
print(len(vectorizer.get_feature_names_out())) # 3225
⚠️ sklearn 1.0+: 用
get_feature_names_out()替代get_feature_names()(已删除)。
五、训练朴素贝叶斯分类器
MultinomialNB 适用场景: 特征是离散计数(词频、TF-IDF),特别适合文本分类。
核心假设: 特征条件独立(朴素之处)。实践中即使不满足也常常 work。
from sklearn.naive_bayes import MultinomialNB
from sklearn import set_config
set_config(display='text') # 关闭 diagram 渲染
classifier = MultinomialNB()
classifier.fit(data_train_vectorized, target_train)
六、预测新文档(关键:fit_transform vs transform)
# 待预测的 2 篇新邮件
docs = [
'The graphic designer requires a good processor to work',
'Flights into space'
]
# ⚠️ 关键:用训练时的 vectorizer 做 transform,不能重新 fit
data_new = vectorizer.transform(docs)
data_pred = classifier.predict(data_new)
# → [0, 1]
核心原则:
| 阶段 | 调用的方法 | 作用 |
|---|---|---|
| 训练 | fit_transform | 学习词表 + 转换 |
| 测试/预测 | transform | 复用词表 |
⚠️ 这是文本分类的"高频踩坑点",必须牢记!
七、TF-IDF 向量化(更优)
核心思想: TF-IDF = 词频(TF)× 逆文档频率(IDF)
- TF: 词在当前文档中出现的频率
- IDF: 词在多少篇文档中出现过的"倒数",出现越普遍权重越低
优势: 自动降低"全场停用词"(the、is)的权重,突出"有区分度"的关键词。
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()
data_train_vectorized = vectorizer.fit_transform(data_train)
相同接口,效果通常更优。
八、Count vs TF-IDF 对比
| 维度 | CountVectorizer | TfidfVectorizer |
|---|---|---|
| 权重策略 | 单纯词频 | 词频 × IDF |
| 停用词 | 默认不过滤 | 自动抑制 |
| 适用场景 | 短文本、词频本身有意义 | 长文档、关键词主导 |
| 计算开销 | 小 | 略大 |
| 与朴素贝叶斯搭配 | 一般 | 通常更好 |
结论: 工业默认组合是 TfidfVectorizer + MultinomialNB。
九、完整 Pipeline 部署
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
# Pipeline 串联向量化 + 分类器
pipe = Pipeline([
('tfidf', TfidfVectorizer()),
('clf', MultinomialNB())
])
pipe.fit(data_train, target_train)
pipe.predict(docs_new) # 直接用 Pipeline 预测
优势:
- 训练/预测时自动处理 fit/transform 顺序
- 一行 pickle 保存,生产部署方便
十、关键要点
- fit_transform vs transform: 训练用前者,测试/预测用后者(必记)
- TfidfVectorizer 更优: 与朴素贝叶斯搭配效果更好
- get_feature_names_out: sklearn 1.0+ 新 API
- 稀疏矩阵: CSR 格式只存非零值,文本场景省 90% 内存
- Pipeline 必备: 生产部署的"标配"
十一、进阶路径
| 阶段 | 工具 |
|---|---|
| 基线 | TF-IDF + MultinomialNB |
| 进阶 | + N-gram (1,2) + 停用词 |
| 工业 | + Pipeline + GridSearchCV 调参 |
| 深度学习 | Word2Vec / BERT Embedding |
中文场景标准三件套:
import jieba
def seg_text(text):
return " ".join(jieba.lcut(text))
texts_seg = [seg_text(t) for t in texts]
# TfidfVectorizer + MultinomialNB
十二、常见陷阱
- ❌ 测试集重新 fit_transform: 词表不一致,预测完全错位
- ❌ 中文未分词: 整句成 1 个 token,模型失效
- ❌ 忘记用 Pipeline: 手动管 fit/transform 顺序易出错
- ❌ 调旧版 API:
get_feature_names()在 sklearn ≥ 1.0 已删除 - ❌ 训练集太小: 50 篇可能欠拟合,工业建议 ≥ 10000 篇
十三、调参与超参数
from sklearn.model_selection import GridSearchCV
param_grid = {
'tfidf__ngram_range': [(1, 1), (1, 2)],
'tfidf__min_df': [1, 2, 5],
'clf__alpha': [0.1, 0.5, 1.0] # NB 平滑参数
}
grid = GridSearchCV(pipe, param_grid, cv=5, scoring='accuracy')
grid.fit(data_train, target_train)
print(grid.best_params_)
关键参数:
| 参数 | 含义 | 推荐 |
|---|---|---|
ngram_range | N-gram 范围 | (1, 2) 性价比最高 |
min_df / max_df | 词频阈值 | min_df=2 过滤低频噪声 |
stop_words | 停用词 | ‘english’ 或自定义列表 |
alpha (NB) | 拉普拉斯平滑 | 0.1-1.0,小数据集用 1.0 |

1732

被折叠的 条评论
为什么被折叠?



