1403贝叶斯算法应用——文本分类

🎯 教学目标与重难点…

【三维目标】

  • 📚 知识目标:掌握 sklearn.naive_bayesMultinomialNBGaussianNB 的区别与适用场景;理解文本分类的完整流程(中文分词 → 向量化 → 训练 → 评估)。
  • ⚙️ 能力目标:能够使用 CountVectorizerTfidfVectorizer 将文本转换为词向量;能够使用 MultinomialNB 训练文本分类模型并评估性能;能够对预测结果进行简单的错误分析。
  • 💡 素养目标:建立“数据预处理 → 模型训练 → 模型评估 → 模型优化”的标准机器学习项目流程意识;理解朴素贝叶斯在文本分类中的独特优势(简单、快速、效果好)。

【重点与难点】

  • 🟢 教学重点:使用 CountVectorizer + MultinomialNB 完成文本分类;中文文本的分词处理(jieba 库的使用)。
  • 🟡 教学难点:理解 Pipeline(管道)在文本分类中的作用;理解拉普拉斯平滑(Laplace Smoothing)避免零概率问题的原理。

📌 一、 课程导入(5 分钟)

回顾与连接

  • 上节课学习了词向量(如何把文本变成数字)和贝叶斯分类器的数学形式。
  • 今天的任务:在 Python 中让这一切真实运行起来

展示“成品”: 课前运行一段完整的文本分类代码,当场演示:

  1. 输入一句中文文本(如“双十一全场五折”)。
  2. 模型实时输出分类结果(垃圾/正常)及对应的概率值。

核心疑问

  • 代码只有十几行,怎么做到的?
  • 背后的原理是什么?

本节课结构

  1. 中文分词(jieba)—— 让计算机“认识”中文词
  2. 向量化(CountVectorizer)—— 文本 → 数字矩阵
  3. 训练(MultinomialNB)—— 统计概率
  4. 预测与评估 —— 看效果
  • 👨‍🏫 教师活动:在 Jupyter Notebook 中实时演示完整的文本分类代码(使用预先准备的短信数据集);让学生现场“出题”输入任意文本,模型即时返回分类结果。
  • 🧑‍🎓 学生活动:观察代码运行过程,记录关键步骤;尝试输入不同的测试文本(如“恭喜您中奖了”、“明天下午三点开会”),观察模型输出的变化。

用“代码演示 + 实时预测”建立直观感受,让学生先看到结果再学习原理,符合“结果导向”的学习规律。现场互动环节增强参与感。


📖 二、 解决问题过程(一):中文分词与文本预处理

1. 为什么中文需要分词?

  • 英文天然有空格作为分隔符(如 “I love you” → [“I”, “love”, “you”])。
  • 中文文本没有空格:“我爱中国” → 需要切分为 [“我”, “爱”, “中国”]。
  • 分词工具jieba(结巴分词),最流行的 Python 中文分词库。

2. jieba 分词基础用法

 1import jieba
 2
 3text = "我爱北京天安门"
 4words = jieba.lcut(text)  # lcut 返回列表
 5print(words)  # ['我', '爱', '北京', '天安门']
 6
 7# 全模式分词(召回更多词,可能有冗余)
 8words_full = jieba.lcut(text, cut_all=True)
 9print(words_full)  # ['我', '爱', '北京', '天安门'](本例无冗余)
10
11# 搜索引擎模式(对长词进一步切分)
12text2 = "北京大学计算机系"
13words_search = jieba.lcut_for_search(text2)
14print(words_search)  # ['北京', '大学', '北京大学', '计算', '算机', '计算机', '系']

3. 文本预处理的完整流程

步骤 操作 示例
① 原始文本 用户输入的原始字符串 “双11全场5折!免费领取优惠券”
② 中文分词 使用 jieba 切分 [‘双11’, ‘全场’, ‘5折’, ‘免费’, ‘领取’, ‘优惠券’]
③ 去停用词 去掉“的”、“了”、“是”等无意义词 本例无停用词,保留
④ 向量化 转为词频向量 [1, 1, 1, 1, 1, 1, …](长度=词典大小)

4. 停用词(Stop Words)

  • 定义:在文本中高频出现但对分类没有实际贡献的词。
  • 中文停用词示例:的、了、是、在、和、与、等。
  • 作用:减少特征维度,提高模型效率和准确率。
  • 👨‍🏫 教师活动:在 Jupyter 中逐行运行 jieba 的分词示例;展示一份常见中文停用词表;说明“去停用词”的必要性。
  • 🧑‍🎓 学生活动:在自己的环境中安装 jieba(pip install jieba),尝试切分不同的中文句子;观察不同分词模式(精确/全/搜索引擎)的输出差异。

中文分词是中文 NLP 特有的预处理步骤。通过 jieba 的三种模式对比,让学生理解“分词粒度”对后续建模的影响。停用词过滤的引入为后续向量化降维做铺垫。


💻 三、 解决问题过程(二):向量化与贝叶斯训练

1. 完整代码演示(使用 sklearn 内置数据集)

 1import jieba
 2import pandas as pd
 3from sklearn.feature_extraction.text import CountVectorizer
 4from sklearn.naive_bayes import MultinomialNB
 5from sklearn.model_selection import train_test_split
 6from sklearn.metrics import accuracy_score, classification_report
 7
 8# ---------- 1. 准备训练数据 ----------
 9# 为了演示,手动构造简单数据集(实际使用时建议从文件读取)
10data = [
11    ("免费领取优惠券,点击链接", "垃圾"),
12    ("恭喜您获得一等奖,请回复确认", "垃圾"),
13    ("尊敬的会员,本月积分已更新", "垃圾"),
14    ("明天下午三点会议室开会", "正常"),
15    ("请尽快提交项目报告", "正常"),
16    ("关于年终总结的安排通知", "正常"),
17    ("您的快递已送达请查收", "正常"),
18    ("限时抢购,全场五折起", "垃圾"),
19    ("感谢您的购买,欢迎下次光临", "正常"),
20    ("亲爱的用户,您的验证码是123456", "正常"),
21]
22df = pd.DataFrame(data, columns=['text', 'label'])
23
24# ---------- 2. 定义分词函数 ----------
25def chinese_cut(text):
26    return ' '.join(jieba.lcut(text))
27
28# 对所有文本进行分词
29df['cut_text'] = df['text'].apply(chinese_cut)
30print("分词后的数据:")
31print(df[['text', 'cut_text']])
32
33# ---------- 3. 划分训练集和测试集 ----------
34X_train, X_test, y_train, y_test = train_test_split(
35    df['cut_text'], df['label'], test_size=0.3, random_state=42
36)
37
38# ---------- 4. 向量化(文本 → 词频矩阵) ----------
39vectorizer = CountVectorizer()
40X_train_vec = vectorizer.fit_transform(X_train)
41X_test_vec = vectorizer.transform(X_test)
42
43print(f"\n训练集向量形状:{X_train_vec.shape}")  # (样本数, 词典大小)
44print(f"词典中的词:{vectorizer.get_feature_names_out()}")
45
46# ---------- 5. 训练朴素贝叶斯模型 ----------
47model = MultinomialNB(alpha=1.0)  # alpha=1 为拉普拉斯平滑
48model.fit(X_train_vec, y_train)
49
50# ---------- 6. 预测与评估 ----------
51y_pred = model.predict(X_test_vec)
52print(f"\n测试集准确率:{accuracy_score(y_test, y_pred):.4f}")
53print("\n分类报告:")
54print(classification_report(y_test, y_pred))
55
56# ---------- 7. 测试单条新文本 ----------
57new_text = "恭喜你中奖了,赶快领取"
58new_text_cut = chinese_cut(new_text)
59new_vec = vectorizer.transform([new_text_cut])
60pred = model.predict(new_vec)[0]
61prob = model.predict_proba(new_vec)[0]
62
63print(f"\n新文本:{new_text}")
64print(f"分词后:{new_text_cut}")
65print(f"预测类别:{pred}")
66print(f"各类别概率:{dict(zip(model.classes_, prob))}")

2. 代码解析

步骤 关键代码 作用
分词 jieba.lcut(text)' '.join() 将中文切词后拼成空格分隔的字符串(sklearn 要求)
向量化 CountVectorizer().fit_transform() 构建词典并生成词频矩阵
训练 MultinomialNB(alpha=1.0).fit() 统计每个类别下每个词的条件概率(拉普拉斯平滑)
预测 model.predict() + predict_proba() 输出类别和各类别概率

3. 拉普拉斯平滑(Laplace Smoothing)

  • 问题:如果测试集中出现了一个在训练集中从未见过的词,P(词|类别) = 0,导致整个后验概率为 0。
  • 解决方案:在分子上加 α(通常为 1),分母上加 α × 词典大小。
  • 公式:P(词_i|C) = (count(词_i, C) + α) / (total_words_C + α × V)
  • 效果:避免零概率,让模型对未知词更“宽容”。
  • 👨‍🏫 教师活动:逐行运行代码,在每个关键步骤后暂停并解释输出;展示 X_train_vec.toarray() 的稀疏矩阵内容,让学生看到实际的数字表示。
  • 🧑‍🎓 学生活动:在本地复现代码;尝试修改训练数据(增加/删减样本),观察准确率的变化;测试自己的新文本(如“今晚一起吃饭吧”),查看模型预测结果。

通过完整可运行的代码,将前两次课的所有理论概念(先验、条件概率、词向量、贝叶斯公式)一次性“兑现”为实际工作流程。让学生体验“理论→代码→结果”的完整闭环。


✍️ 四、 解决问题过程(三):实战任务——垃圾分类器

📝 任务:训练一个完整的垃圾分类模型…

背景与题目

假设你接到一个任务:开发一个简单的“垃圾分类”文本分类器,输入一段物品描述文本,输出其所属类别(可回收/厨余/有害/其他)。

任务要求

  1. 自行构造数据集(至少 20 条样本,4 个类别各 5 条以上)。
  2. 使用 jieba 分词 + CountVectorizer 向量化。
  3. 使用 MultinomialNB 训练模型。
  4. 测试至少 3 条新文本,输出预测类别和各类别概率。
  5. 输出模型的分类报告(classification_report)。

参考数据(可直接使用)

文本 类别
矿泉水瓶 易拉罐 废纸 可回收
剩饭 剩菜 果皮 茶叶渣 厨余
电池 过期药品 油漆 有害
烟蒂 砖瓦 陶瓷碎片 其他
报纸 纸箱 塑料瓶 可回收
菜叶 鸡蛋壳 骨头 厨余
温度计 农药瓶 废灯管 有害
灰渣 尿不湿 卫生巾 其他
🔍 查看参考代码…
 1import jieba
 2import pandas as pd
 3from sklearn.feature_extraction.text import CountVectorizer
 4from sklearn.naive_bayes import MultinomialNB
 5from sklearn.model_selection import train_test_split
 6from sklearn.metrics import accuracy_score, classification_report
 7
 8# 数据
 9data = [
10    ("矿泉水瓶 易拉罐 废纸", "可回收"),
11    ("报纸 纸箱 塑料瓶", "可回收"),
12    ("玻璃瓶 金属 旧衣服", "可回收"),
13    ("剩饭 剩菜 果皮", "厨余"),
14    ("菜叶 鸡蛋壳 骨头", "厨余"),
15    ("茶叶渣 咖啡渣 果核", "厨余"),
16    ("电池 过期药品 油漆", "有害"),
17    ("温度计 农药瓶 废灯管", "有害"),
18    ("废机油 含铅废物", "有害"),
19    ("烟蒂 砖瓦 陶瓷碎片", "其他"),
20    ("灰渣 尿不湿 卫生巾", "其他"),
21    ("墙纸 玻璃纤维", "其他"),
22]
23
24df = pd.DataFrame(data, columns=['text', 'label'])
25
26def cut_text(text):
27    return ' '.join(jieba.lcut(text))
28
29df['cut_text'] = df['text'].apply(cut_text)
30
31X_train, X_test, y_train, y_test = train_test_split(
32    df['cut_text'], df['label'], test_size=0.3, random_state=42
33)
34
35vec = CountVectorizer()
36X_train_vec = vec.fit_transform(X_train)
37X_test_vec = vec.transform(X_test)
38
39model = MultinomialNB(alpha=1.0)
40model.fit(X_train_vec, y_train)
41
42y_pred = model.predict(X_test_vec)
43print(f"准确率:{accuracy_score(y_test, y_pred):.4f}")
44print(classification_report(y_test, y_pred))
45
46# 测试
47test_texts = ["旧报纸和塑料瓶", "没吃完的西瓜皮", "用完的电池"]
48for text in test_texts:
49    cut = cut_text(text)
50    vec_text = vec.transform([cut])
51    pred = model.predict(vec_text)[0]
52    prob = model.predict_proba(vec_text)[0]
53    print(f"\n{text}{pred} ({dict(zip(model.classes_, prob))})")

📝 五、 课堂小结(5 分钟)

flowchart LR
    root["💻 贝叶斯文本分类(第三次课)"]

    subgraph C1["🔧 文本预处理"]
        direction TB
        A1["jieba 中文分词"]
        A2["去停用词(可选)"]
    end

    subgraph C2["📊 向量化"]
        direction TB
        B1["CountVectorizer"]
        B2["文本 → 词频矩阵"]
    end

    subgraph C3["🧮 模型训练"]
        direction TB
        C1_node["MultinomialNB"]
        C2_node["统计 P(词|类别) + 拉普拉斯平滑"]
    end

    subgraph C4["📈 评估与预测"]
        direction TB
        D1["predict / predict_proba"]
        D2["准确率 / 分类报告"]
    end

    root --> C1
    root --> C2
    root --> C3
    root --> C4

    style root fill:#2e7d32,stroke:#1b5e20,color:#fff,stroke-width:2px,rx:8px,ry:8px
    style C1 fill:#e3f2fd,stroke:#1e88e5,stroke-width:1px
    style C2 fill:#fff3e0,stroke:#ff9800,stroke-width:1px
    style C3 fill:#f3e5f5,stroke:#9c27b0,stroke-width:1px
    style C4 fill:#e8f5e9,stroke:#43a047,stroke-width:1px

✏️ 随堂检测与互动练习

点击展开:随堂测试题(带解析)

一、 单选题

  1. 在中文文本分类中,使用 jieba.lcut(text) 对文本进行分词,其返回的数据类型是?
  • A. 字符串(str)
  • B. 列表(list)
  • C. 字典(dict)
  • D. 元组(tuple)
【答案】

【解析】Bjieba.lcut() 返回一个列表,列表中的每个元素是一个词。jieba.cut() 返回的是生成器,而 lcut 的 “l” 代表 “list”。

  1. CountVectorizerfit_transform() 方法返回的数据类型是?
  • A. 稠密矩阵(numpy array)
  • B. 稀疏矩阵(scipy.sparse matrix)
  • C. 列表
  • D. 字典
【答案】

【解析】B。由于词频矩阵通常是高维且稀疏的(大部分位置为 0),CountVectorizer 返回稀疏矩阵以节约内存。

  1. 拉普拉斯平滑(Laplace Smoothing)在朴素贝叶斯中的作用是?
  • A. 提高模型训练速度
  • B. 避免因未登录词导致的条件概率为零
  • C. 减少特征数量
  • D. 自动进行特征选择
【答案】

【解析】B。拉普拉斯平滑通过在分子和分母上添加常数,确保即使某个词在训练集中未出现,其条件概率也不会为 0。

二、 代码填空题

题目:现有已训练的 MultinomialNB 模型 nb_model 和已拟合的 CountVectorizer 向量化器 vectorizer。请补充代码,对一条新文本 new_text 进行预测,并输出预测类别。

1# 1. 对新文本进行分词(使用 jieba)
2words = jieba.___________(new_text)
3# 2. 将分词结果拼接为空格分隔的字符串
4cut_text = ' '.join(words)
5# 3. 向量化
6vec = vectorizer.__________([cut_text])
7# 4. 预测
8pred = nb_model.__________(vec)[0]
9print(pred)
【答案】
  1. lcut
  2. transform
  3. predict

📮 六、 课后作业与拓展

📮 课后作业…
  1. 基础作业:完成课堂上的“垃圾分类器”代码,提交 .ipynb 文件。
  2. 数据集扩展作业:将垃圾分类的训练数据扩展至 40 条以上(每类至少 10 条),重新训练并对比扩展前后的准确率变化。
  3. TF-IDF 对比实验:将 CountVectorizer 替换为 TfidfVectorizer,对比两个模型在同一数据集上的准确率差异,写下你的观察结论。
  4. 新任务作业:找一个自己感兴趣的二分类文本任务(如“美团评论的好评/差评分类”),自行收集至少 30 条标注数据,训练一个朴素贝叶斯分类器。
  5. 拓展阅读:查阅资料,了解朴素贝叶斯的另外两种变体——GaussianNB(高斯朴素贝叶斯)和 BernoulliNB(伯努利朴素贝叶斯),总结它们分别适用于什么类型的数据。

📋 七、 板书设计

🛠️ 板书设计…
 1💻 朴素贝叶斯 – 第三次课:代码实战
 2
 3一、 中文分词(jieba)
 4    · pip install jieba
 5    · jieba.lcut("中文文本") → ['中文', '文本']
 6
 7二、 向量化(CountVectorizer)
 8    · 构建词典:fit()
 9    · 文本→向量:transform()
10    · 输出:词频矩阵(稀疏矩阵)
11
12三、 贝叶斯模型(MultinomialNB)
13    · 适用:离散特征(词频/计数)
14    · 核心:MultinomialNB(alpha=1.0)  ← 拉普拉斯平滑
15
16四、 完整流程(代码记忆)
17    ① 文本 → ② 分词 → ③ 向量化 → ④ 训练 → ⑤ 预测
18
19五、 常用评估指标
20    accuracy_score(y_true, y_pred)      # 准确率
21    classification_report(...)          # 精确率/召回率/F1

本课用到的单词

英文术语 发音(美式) 中文释义 专业语境解释
Tokenization /ˌtoʊ.kə.naɪˈzeɪ.ʃən/ 分词 将连续文本切分为独立的词单元(Token)的过程。
Count Vectorizer /kaʊnt ˈvek.tə.raɪ.zər/ 计数向量化器 Scikit-learn 中将文本转为词频矩阵的工具。
Sparse Matrix /spɑːrs ˈmeɪ.trɪks/ 稀疏矩阵 大部分元素为 0 的矩阵,用于高效存储词频数据。
Multinomial NB /ˌmʌl.tiˈnoʊ.mi.əl ˌnaɪvˈbeɪz/ 多项式朴素贝叶斯 适用于离散特征(如词频)的朴素贝叶斯变体。
Laplace Smoothing /ləˈplɑːs ˈsmuː.ðɪŋ/ 拉普拉斯平滑 在概率估计中附加伪计数,避免零概率问题的技术。
Pipeline /ˈpaɪ.pˌlaɪn/ 管道 将多个数据处理步骤串联为一个整体的工作流。
Stop Words /stɑːp wɜːrdz/ 停用词 在文本分析中无信息量的高频词,通常在预处理时移除。