1404 综合练习

Part A:给定数据集的精讲精练(共 6 题)

练习 1:“天气预测 —— 贝叶斯定理手算与代码验证”

🎯 目标:通过手算 + 代码验证,深刻理解贝叶斯定理的每个组成部分。 📂 数据集:给定下面的“天气与打球”数据表(10 条记录)

【数据表】 :某同学记录了过去 10 天的天气情况和是否去打球的决策。

日期 天气(Outlook) 温度(Temp) 是否打球(Play)
D1 晴天(Sunny) 热(Hot) 否(No)
D2 晴天(Sunny) 热(Hot) 否(No)
D3 阴天(Overcast) 热(Hot) 是(Yes)
D4 雨天(Rainy) 温和(Mild) 是(Yes)
D5 雨天(Rainy) 凉爽(Cool) 是(Yes)
D6 雨天(Rainy) 凉爽(Cool) 否(No)
D7 阴天(Overcast) 凉爽(Cool) 是(Yes)
D8 晴天(Sunny) 温和(Mild) 否(No)
D9 晴天(Sunny) 凉爽(Cool) 是(Yes)
D10 雨天(Rainy) 温和(Mild) 是(Yes)

【详细操作步骤(Step-by-Step)】

第 1 步:统计先验概率

先填写以下计数:

类别 计数 概率 P(类别)
Play = Yes ____ 天 ____ / 10 = ____
Play = No ____ 天 ____ / 10 = ____

第 2 步:统计条件概率(似然)

在“打球=是”的条件下,各种天气出现的次数和概率:

| 天气 | Yes 组中计数 | P(天气 | Yes) | | :— | :— | :— | | Sunny | ____ | ____ / ____ = ____ | | Overcast | ____ | ____ / ____ = ____ | | Rainy | ____ | ____ / ____ = ____ |

在“打球=否”的条件下,各种天气出现的次数和概率:

| 天气 | No 组中计数 | P(天气 | No) | | :— | :— | :— | | Sunny | ____ | ____ / ____ = ____ | | Overcast | ____ | ____ / ____ = ____ | | Rainy | ____ | ____ / ____ = ____ |

第 3 步:计算证据因子 P(天气=Sunny)

$$P(\text{Sunny}) = P(\text{Sunny}|\text{Yes}) \times P(\text{Yes}) + P(\text{Sunny}|\text{No}) \times P(\text{No}) = \_\_\_\_ $$

第 4 步:使用贝叶斯定理计算后验概率

当天气为 Sunny 时,打球的概率:

$$P(\text{Yes}|\text{Sunny}) = \frac{P(\text{Sunny}|\text{Yes}) \times P(\text{Yes})}{P(\text{Sunny})} = \frac{\_\_\_\_ \times \_\_\_\_}{\_\_\_\_} = \_\_\_\_$$

当天气为 Sunny 时,不打球的概率:

$$P(\text{No}|\text{Sunny}) = \frac{P(\text{Sunny}|\text{No}) \times P(\text{No})}{P(\text{Sunny})} = \frac{\_\_\_\_ \times \_\_\_\_}{\_\_\_\_} = \_\_\_\_$$

第 5 步:做出决策

当天气为 Sunny 时,P(Yes|Sunny) = ____,P(No|Sunny) = ____。

因为 ____ > ,所以预测为:****(打球/不打球)。

第 6 步:代码验证

 1import numpy as np
 2from sklearn.naive_bayes import CategoricalNB
 3
 4# 编码:Sunny=0, Overcast=1, Rainy=2 ; Yes=1, No=0
 5X = np.array([[0,0], [0,0], [1,0], [2,1], [2,2], [2,2], [1,2], [0,1], [0,2], [2,1]])
 6y = np.array([0, 0, 1, 1, 1, 0, 1, 0, 1, 1])
 7
 8model = CategoricalNB(alpha=1.0)  # 拉普拉斯平滑
 9model.fit(X, y)
10
11# 预测天气=Sunny 时是否打球(注意要传入二维数组)
12pred = model.predict([[0, 0]])[0]
13prob = model.predict_proba([[0, 0]])[0]
14print(f"预测:{'打球' if pred == 1 else '不打球'}")
15print(f"P(Yes|Sunny)={prob[1]:.4f}, P(No|Sunny)={prob[0]:.4f}")

代码输出的结果是否与你手算的一致?______(填“是”或“否”,如有差异请说明原因)

练习 2:“先验敏感性分析 —— 当先验概率发生变化”

🎯 目标:理解先验概率对后验概率的影响程度,培养“先验选择需谨慎”的工程意识。 📂 数据集:沿用练习 1 的“天气与打球”数据。

【详细操作步骤(Step-by-Step)】

第 1 步:回顾练习 1 中计算出的条件概率

从练习 1 中抄录以下数值:

  • P(Sunny | Yes) = ____
  • P(Sunny | No) = ____
  • P(Yes) = ____(原始先验)
  • P(No) = ____(原始先验)

第 2 步:改变先验概率 —— 场景 A(极端乐观)

假设这位同学是个“球痴”,无论天气如何都倾向于打球。设定先验为:

  • P(Yes) = 0.9
  • P(No) = 0.1

重新计算:

  • P(Sunny) = P(Sunny|Yes) × 0.9 + P(Sunny|No) × 0.1 = ____
  • P(Yes|Sunny) = P(Sunny|Yes) × 0.9 / P(Sunny) = ____
  • P(No|Sunny) = P(Sunny|No) × 0.1 / P(Sunny) = ____

此时,晴天时预测为:____(打球/不打球)

第 3 步:改变先验概率 —— 场景 B(极端悲观)

假设这位同学是个“宅男”,无论如何都不想出门。设定先验为:

  • P(Yes) = 0.1
  • P(No) = 0.9

重新计算:

  • P(Sunny) = P(Sunny|Yes) × 0.1 + P(Sunny|No) × 0.9 = ____
  • P(Yes|Sunny) = P(Sunny|Yes) × 0.1 / P(Sunny) = ____
  • P(No|Sunny) = P(Sunny|No) × 0.9 / P(Sunny) = ____

此时,晴天时预测为:____(打球/不打球)

第 4 步:填写对比表格

| 先验设置 | P(Yes) | P(No) | P(Yes|Sunny) | P(No|Sunny) | 预测结果 | | :— | :— | :— | :— | :— | :— | | 原始(数据统计) | 0.5 | 0.5 | ____ | ____ | ____ | | 场景 A(乐观) | 0.9 | 0.1 | ____ | ____ | ____ | | 场景 B(悲观) | 0.1 | 0.9 | ____ | ____ | ____ |

第 5 步:回答以下问题

  1. 在场景 A 中,即使 P(Sunny|Yes) 较低,后验 P(Yes|Sunny) 仍然很高。这说明了什么?
  2. 如果这是一个实际项目,先验概率应该从哪里获取?
  3. 当先验数据不足时,通常假设 P(类别) 相等(均匀先验)。这种做法的潜在风险是什么?

练习 3:“从零构建词袋模型 —— 手动分词与向量化”

🎯 目标:通过手动操作,彻底理解“文本 → 词频向量”的完整过程。 📂 数据集:以下 4 条短文本(关于“体育”和“科技”两类)

文档编号 文本内容 类别
D1 足球比赛精彩绝伦 体育
D2 篮球巨星夺得冠军 体育
D3 人工智能改变世界 科技
D4 程序员编写代码 科技

【详细操作步骤(Step-by-Step)】

第 1 步:中文分词

使用 jieba 对每条文本进行分词(也可手动切分),将结果填写在下表中。

文档 分词结果(用空格分隔)
D1 ______
D2 ______
D3 ______
D4 ______

第 2 步:构建词典

汇总所有文档中出现的不重复词语,建立词典并编号。

编号 词语
1 ______
2 ______
3 ______
4 ______
5 ______
6 ______
7 ______
8 ______

词典大小 V = ______(共 ____ 个词)

第 3 步:生成词频向量(Count Vector)

对每个文档,统计词典中每个词在该文档中出现的次数,填入下表。

文档 词1 词2 词3 词4 词5 词6 词7 词8
D1 ____ ____ ____ ____ ____ ____ ____ ____
D2 ____ ____ ____ ____ ____ ____ ____ ____
D3 ____ ____ ____ ____ ____ ____ ____ ____
D4 ____ ____ ____ ____ ____ ____ ____ ____

第 4 步:生成 One-Hot 向量(二值向量)

将上表中的所有非零值改为 1,得到 One-Hot 向量。

文档 词1 词2 词3 词4 词5 词6 词7 词8
D1 ____ ____ ____ ____ ____ ____ ____ ____
D2 ____ ____ ____ ____ ____ ____ ____ ____
D3 ____ ____ ____ ____ ____ ____ ____ ____
D4 ____ ____ ____ ____ ____ ____ ____ ____

第 5 步:代码验证

 1import jieba
 2from sklearn.feature_extraction.text import CountVectorizer
 3
 4docs = [
 5    "足球比赛精彩绝伦",
 6    "篮球巨星夺得冠军",
 7    "人工智能改变世界",
 8    "程序员编写代码"
 9]
10
11def cut_text(text):
12    return ' '.join(jieba.lcut(text))
13
14cut_docs = [cut_text(d) for d in docs]
15print("分词结果:")
16for i, d in enumerate(cut_docs):
17    print(f"D{i+1}: {d}")
18
19vec = CountVectorizer()
20X = vec.fit_transform(cut_docs)
21print(f"\n词典:{vec.get_feature_names_out()}")
22print(f"\n词频矩阵:\n{X.toarray()}")

代码输出的词典和矩阵是否与你手动构建的一致?如有差异,请分析原因(可能是 jieba 的分词结果与你的手动分词不同)。

练习 4:【独立实战】“垃圾短信分类 —— 完整项目流程”

🎯 目标:使用真实(或模拟)的短信数据,完成从数据加载到模型评估的完整项目。 📂 数据集:使用 sklearn 内置的 fetch_20newsgroups 的子集,或自行构造短信数据。

任务要求

  1. 准备数据:自行构造或从网络收集 50 条短信(25 条垃圾,25 条正常),保存为 CSV 格式(两列:text, label)。

  2. 完整代码实现

    • 使用 pandas 读取数据。
    • 使用 jieba 进行中文分词。
    • 使用 CountVectorizer 进行向量化(设置 max_features=500 限制特征数)。
    • 使用 train_test_split 划分训练集和测试集(test_size=0.2)。
    • 使用 MultinomialNB 训练模型。
    • 输出测试集准确率和分类报告。
  3. 测试与展示

    • 输入 5 条新短信(自己编写),展示预测结果和各类别概率。
    • 从测试集中找出 2 条被分错的样本,分析可能的原因。
  4. 提交物

    • 完整代码(.ipynb.py)。
    • CSV 数据文件。
    • 运行结果截图。

练习 5:【独立实战】“CountVectorizer vs TfidfVectorizer —— 谁是文本分类的更好选择?”

🎯 目标:通过控制变量实验,对比两种向量化方法在文本分类任务中的表现差异。 📂 数据集:练习 4 中构造的短信数据集。

任务要求

  1. 实验设计

    • 使用完全相同的数据划分(固定 random_state=42)。
    • 使用完全相同的模型(MultinomialNB(alpha=1.0))。
    • 唯一变量:向量化方法(CountVectorizer vs TfidfVectorizer)。
  2. 执行实验

     1from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
     2
     3# CountVectorizer 实验
     4cv = CountVectorizer(max_features=500)
     5X_train_cv = cv.fit_transform(X_train_cut)
     6X_test_cv = cv.transform(X_test_cut)
     7model_cv = MultinomialNB()
     8model_cv.fit(X_train_cv, y_train)
     9acc_cv = model_cv.score(X_test_cv, y_test)
    10
    11# TfidfVectorizer 实验
    12tv = TfidfVectorizer(max_features=500)
    13X_train_tv = tv.fit_transform(X_train_cut)
    14X_test_tv = tv.transform(X_test_cut)
    15model_tv = MultinomialNB()
    16model_tv.fit(X_train_tv, y_train)
    17acc_tv = model_tv.score(X_test_tv, y_test)
    18
    19print(f"CountVectorizer 准确率:{acc_cv:.4f}")
    20print(f"TfidfVectorizer 准确率:{acc_tv:.4f}")
  3. 输出对比表格

向量化方法 特征数 训练集准确率 测试集准确率
CountVectorizer ____ ____ ____
TfidfVectorizer ____ ____ ____
  1. 分析报告(100 字以内):
    • 哪种方法在你的数据集上表现更好?
    • 分析可能的原因(提示:数据集中是否存在大量停用词/通用词?)

练习 6:【独立实战】“超参数探索 —— alpha 值对模型性能的影响”

🎯 目标:理解拉普拉斯平滑参数 α(alpha)的作用,并通过实验找到最佳取值。 📂 数据集:自行选择(建议使用练习 4 的短信数据集或 sklearn 内置的 fetch_20newsgroups

任务要求

  1. 背景知识MultinomialNB(alpha=α) 中的 alpha 是拉普拉斯平滑参数。

    • alpha=0:无平滑,未见过的词会导致概率为 0。
    • alpha=1:标准拉普拉斯平滑(默认值)。
    • alpha>1:更强的平滑,概率分布更均匀。
    • alpha<1:较弱的平滑,更依赖训练数据中的频率。
  2. 实验设计

    • 固定向量化方法(建议使用 TfidfVectorizer)。
    • alpha = [0, 0.01, 0.1, 0.5, 1.0, 2.0, 5.0, 10.0] 范围内进行实验。
    • 记录每个 alpha 值对应的测试集准确率。
  3. 绘制曲线

    • 横轴:alpha 值(使用对数刻度,因为 alpha 跨度大)。
    • 纵轴:测试集准确率。
    • 标注出最佳 alpha 值。
  4. 分析

    • alpha=0 时是否出现错误(提示:某些测试词的 P=0)?
    • 在当前数据集上,推荐的 alpha 值是多少?
    • 如果你有 100 万条训练数据,你认为 alpha 应该调大还是调小?为什么?

Part B:不给定数据集的开放设计挑战(共 3 题)

练习 7:【造数据挑战】“美食评论情感分类 —— 自建数据集与模型训练”

背景:某餐饮平台想要自动识别用户评论的情感倾向(好评/差评),以便快速了解客户满意度。你需要自建数据集并训练朴素贝叶斯分类器。

任务要求

  1. 自行构造数据

    • 编写 30 条关于餐厅/美食的中文评论。
    • 其中 15 条为好评(如“味道很棒”、“服务热情”、“环境优雅”等)。
    • 其中 15 条为差评(如“太难吃了”、“价格太贵”、“上菜太慢”等)。
    • 将数据保存为 CSV 格式(列:text, label)。
  2. 模型训练

    • 使用 jieba 分词。
    • 使用 TfidfVectorizer 向量化。
    • 使用 MultinomialNB 训练模型。
    • 划分训练集(70%)和测试集(30%)。
  3. 模型评估

    • 输出测试集准确率。
    • 输出分类报告(精确率、召回率、F1 分数)。
  4. 测试新评论

    • 编写 3 条新的评论(1 条明显好评、1 条明显差评、1 条中性/模糊)。
    • 展示模型对每条评论的预测结果和概率。
  5. 错误分析

    • 找出至少 1 条被错分的评论。
    • 分析错分原因(如:用词模糊、训练数据不足、特征选择不当等)。

练习 8:【造数据挑战】“多分类新闻标题分类器”

背景:某新闻聚合 App 需要将新闻标题自动归类为:体育、科技、财经、娱乐 四个类别之一。

任务要求

  1. 自行构造数据

    • 每个类别至少编写 10 条新闻标题(共 ≥ 40 条)。
    • 数据保存为 CSV(列:title, category)。
  2. 模型训练(完整流程):

    • 中文分词(jieba)。
    • 向量化(从 CountVectorizerTfidfVectorizer 中选择一个,并说明理由)。
    • 训练 MultinomialNB
    • 评估模型(准确率 + 分类报告)。
  3. 多分类概率分析

    • 选一条测试标题,输出它在 4 个类别上的完整概率分布(predict_proba)。
    • 用柱状图可视化这个概率分布。
    • 如果最高概率与第二高概率的差值很小(如 0.35 vs 0.32),说明什么问题?
  4. 数据量实验

    • 仅使用每类 5 条数据训练一个模型(使用 random_state=42 随机抽取子集)。
    • 使用完整数据训练另一个模型。
    • 对比两个模型的准确率,讨论数据量对朴素贝叶斯的影响。

练习 9:【综合设计挑战】“甲方需求 —— 工单自动分派系统”

背景:某 IT 服务公司每天收到大量客户报修/咨询工单。需要开发一个自动分类系统,将工单分派给对应的处理部门(3 个部门:硬件维修、软件支持、网络运维)。

【甲方需求书】

项目 内容
数据情况 公司提供 200 条已标注的历史工单,但数据格式不规范,部分工单的标题和描述混在一起。
类别 3 类:硬件维修(如“电脑无法开机”、“打印机卡纸”)、软件支持(如“Excel 崩溃”、“系统蓝屏”)、网络运维(如“无法上网”、“VPN 连不上”)。
硬性要求 ① 分类准确率 ≥ 85%;② 必须能输出“置信度”(即各类别概率),方便人工复核;③ 模型需要在 0.5 秒内完成单条工单分类。
软性要求 最好能分析出每个类别中最具有区分度的 Top 5 关键词,供业务方参考。

任务要求

  1. 自行生成数据

    • 模拟 200 条工单数据(可以重复使用一些模板,但要保证每个类别至少 50 条)。
    • 可以适当加入一些“边界案例”(如描述模糊的工单),增加分类难度。
  2. 模型选型论证

    • 从以下模型中选择最合适的一个MultinomialNBGaussianNBBernoulliNB
    • 说明为什么选它,为什么放弃另外两个(从特征类型角度分析:文本词频属于离散计数数据,适用 MultinomialNB)。
  3. 完整实现

    • 数据加载与预处理(文本清洗、分词、去停用词)。
    • 向量化(TfidfVectorizerCountVectorizer)。
    • 模型训练与评估。
    • 输出准确率和各类别的精确率/召回率。
  4. 交付物

    • 模型文件:保存训练好的模型和向量化器(使用 joblib.dump)。
    • 分类函数:封装一个 predict_ticket(text) 函数,输入工单文本,输出“类别 + 各类别概率”。
    • 特征分析:使用 model.feature_log_prob_ 提取每个类别中概率最高的 Top 5 关键词,输出到表格中。
    • 速度测试:测量单条文本的预测耗时,确认 < 0.5 秒。
  5. 扩展挑战(加分项)

    • 尝试使用 Pipeline 将“向量化 + 模型”封装为一个整体流程。
    • 使用 GridSearchCValpha 进行调优,并比较调优前后的准确率变化。

📌 使用指南

练习题 难度 建议课时 对应教案 带步骤
练习 1 ★☆☆ 0.5 课时 第一次课(14.1) ✅ 是
练习 2 ★★☆ 0.5 课时 第二次课(14.2.1) ✅ 是
练习 3 ★★☆ 0.5 课时 第二次课(14.2.2) ✅ 是
练习 4 ★★☆ 1 课时 第三次课(14.3) ❌ 否
练习 5 ★★☆ 1 课时 第三次课(14.3) ❌ 否
练习 6 ★★★ 1 课时 第三次课(14.3) ❌ 否
练习 7 ★★☆ 1 课时 综合(14.2-14.3) ❌ 否
练习 8 ★★★ 1.5 课时 综合(全章) ❌ 否
练习 9 ★★★★ 2 课时 综合(全章 + 工程化) ❌ 否

建议

  • 练习 1-3 作为 课后作业 布置(每次课后布置 1 题),巩固基础概念。
  • 练习 4-6 作为 实训课任务,让学生独立完成代码实现。
  • 练习 7-8 作为 小组项目(2 人一组),培养协作能力。
  • 练习 9 作为 期末大作业/机考题目,综合考察全章知识 + 工程化能力。
  • 练习 1-3:重点检查手算结果是否正确,公式是否用对。
  • 练习 4-6:重点检查代码是否可运行,是否记录了实验数据并做出了分析。
  • 练习 7-8:重点检查数据构造是否合理、选型论证是否充分。
  • 练习 9:重点检查是否满足了甲方所有需求(准确率 ≥ 85%、速度 < 0.5s、概率输出、特征分析)。