ML for Beginners 实战:Logistic Regression 作业全解——用完整南瓜数据集构建并评估逻辑回归模型
本篇围绕 ML-For-Beginners 课程《2-Regression/4-Logistic》配套的作业文档 assignment.md 展开。该作业要求学习者跳出课程示例中使用的数据子集,回到原始的美国南瓜数据集,完成清洗、标准化(编码)后,用全部数据训练一个逻辑回归(Logistic Regression)二分类模型,并交付一份解释清晰、表现良好的 Notebook。读完本篇,你将掌握该作业要求的完整技术链路:数据清洗、序数/独热编码、训练集划分、模型训练、混淆矩阵与 ROC/AUC 评估,并了解如何对照官方评分标准(Rubric)自查作业质量。
1. 作业任务与评分标准
原始作业文档(英文版见 2-Regression/4-Logistic/assignment.md)给出的指令是:
In the lesson, you used a subset of the pumpkin data. Now, go back to the original data and try to use all of it, cleaned and standardized, to build a Logistic Regression model.
(在课程示例中,你只使用了南瓜数据的一个子集。现在,请回到原始数据,把全部数据经过清洗和标准化处理后,用来构建一个逻辑回归模型。)
配套的评分标准(Rubric)如下,完成作业时应逐项对照:
| 标准 | 优秀(Exemplary) | 合格(Adequate) | 需改进(Needs Improvement) |
|---|---|---|---|
| 交付物 | 提交一份带有解释详尽且模型表现良好的 Notebook | 提交一份模型基本可用的 Notebook | 提交的 Notebook 模型表现差或没有模型 |
也就是说,作业的核心交付物是一份 可运行的 Jupyter Notebook,其中必须体现:完整数据的读取、清洗、编码(standardized 在这里指完成特征编码与类别标签规范化)、建模与评估的全过程,并配有充分的文字解释。
2. 为什么是南瓜数据与逻辑回归
该作业隶属第 2 章 Regression 的最后一课 4-Logistic。课程选择南瓜数据中的 Color 列作为二分类目标,因为它是数据集中天然存在的二元类别:橙色(ORANGE)还是白色(WHITE)。白色南瓜俗称 "ghost pumpkins"(幽灵南瓜),因此问题也可以表述为 “Ghost 或 Not Ghost”。
这里有一个概念要点(课程原文强调):逻辑回归本质上是分类方法,而非回归。线性回归预测连续值(例如南瓜的价格会涨到多少),逻辑回归预测二元类别(例如这颗南瓜是不是白色)。它之所以归入 Regression 章节,只是命名上的历史习惯。此外,与线性回归偏好高相关变量不同,逻辑回归并不要求特征之间强相关;但它对数据量有要求——数据越多越干净,结果越准。这正是本作业要求“用全部清洗后的数据”而非课程子集的原因。
原始数据文件为 2-Regression/data/US-pumpkins.csv,共 1756 行记录、30 余列,包含价格、产地、品种、尺寸、颜色、环境、质量、外观等字段。数据集中还存在少量 striped(条纹)颜色记录,但样本极少,且会在去掉空值后消失,因此课程将其排除,仅保留二分类口径 White / Not White。
3. 第一步:回到原始数据并清洗
作业的起点是丢弃课程子集,改用全量数据。课程 Notebook notebook.ipynb 第一个代码单元格即加载全量数据:
import pandas as pd
import numpy as np
full_pumpkins = pd.read_csv('../../data/US-pumpkins.csv')
full_pumpkins.head()
接下来按作业要求做两件事:只保留建模需要的列、删除含空值的行:
# 只保留建模所需列(含目标列 Color)
columns_to_select = ['City Name', 'Package', 'Variety', 'Origin', 'Item Size', 'Color']
pumpkins = full_pumpkins.loc[:, columns_to_select]
# 删除任意为空的行
pumpkins.dropna(inplace=True)
pumpkins.info()
用仓库中的真实数据核验这一清洗步骤:全文件共 1756 行,选择上述 6 列并执行 dropna() 后,剩余 991 行。目标列分布如下:
| Color | 行数 |
|---|---|
| ORANGE | 835 |
| WHITE | 156 |
Item Size 的取值分布为:sml 273、lge 197、xlge 166、med 146、med-lge 100、jbo 97、exjbo 12;Variety 前三名为 HOWDEN TYPE(531)、PIE TYPE(191)、MINIATURE(130)。注意类别存在明显不均衡(橙:白 ≈ 5.4:1),这一点会直接影响后续 Recall 与混淆矩阵的解读(见第 7 节)。
提示:课程 README 中提到“只有大约 1000 行数据”,与这里实测的 991 行一致——课程示例本身就是在这份全量清洗数据上运行的,所谓“子集”是指课程讲解过程中展示的部分行。
4. 第二步:特征与标签编码(Standardized)
作业中的 “standardized” 落到操作上就是编码:把字符串类别转成机器可用的数字,且不丢失信息。数据集所有列都是字符串,而机器学习算法只认数字,因此编码是预处理的必经环节。课程(及其 解答 Notebook)使用三类编码器:
(1)序数编码器 OrdinalEncoder —— 用于有逻辑顺序的 Item Size
尺寸列的取值存在天然的从大到小/从小到大顺序,因此按序数编码,每个类别映射为其在有序列表中的序号(0~6):
from sklearn.preprocessing import OrdinalEncoder
item_size_categories = [['sml', 'med', 'med-lge', 'lge', 'xlge', 'jbo', 'exjbo']]
ordinal_features = ['Item Size']
ordinal_encoder = OrdinalEncoder(categories=item_size_categories)
jbo(jumbo)与 exjbo(extra jumbo)排在末尾,表示特大号南瓜。用序数编码保留了“大小”这一语义,避免独热编码把有序信息摊平成无序哑变量。
(2)独热编码器 OneHotEncoder —— 用于无顺序的类别特征
City Name、Package、Variety、Origin 都没有逻辑顺序,采用 one-hot 编码:每个取值展开为一列 0/1 指示列:
from sklearn.preprocessing import OneHotEncoder
categorical_features = ['City Name', 'Package', 'Variety', 'Origin']
categorical_encoder = OneHotEncoder(sparse_output=False)
(3)ColumnTransformer 统一编排两种编码器
from sklearn.compose import ColumnTransformer
ct = ColumnTransformer(transformers=[
('ord', ordinal_encoder, ordinal_features),
('cat', categorical_encoder, categorical_features)
])
ct.set_output(transform='pandas') # 让输出保持带列名的 DataFrame,便于后续对齐
encoded_features = ct.fit_transform(pumpkins)
注意列名前缀 ord__ 与 cat__:前者是 ColumnTransformer 按命名空间自动加的前缀,例如编码后的尺寸列名为 ord__Item Size,后续绘图会直接引用它。
(4)标签编码 LabelEncoder
目标列 Color 用 LabelEncoder 归一化为 0~n_classes-1 的整数标签:
from sklearn.preprocessing import LabelEncoder
label_encoder = LabelEncoder()
encoded_label = label_encoder.fit_transform(pumpkins['Color'])
# 合并特征与标签
encoded_pumpkins = encoded_features.assign(Color=encoded_label)
由于 LabelEncoder 按字母序映射,本数据中 ORANGE → 0、WHITE → 1(可用 label_encoder.inverse_transform([0, 1]) 验证)。在作业 Notebook 中务必记录这个映射,评分标准里“well-explained”一项,标签语义映射就是必须解释清楚的细节之一。
5. 第三步:可视化探索(用 Seaborn)
编码之后、建模之前,作业要求先观察特征与标签的关系。课程引入 Seaborn(构建在 Matplotlib 之上)做分类绘图。
(1)各品种下颜色构成的计数图
import seaborn as sns
palette = {
'ORANGE': 'orange',
'WHITE': 'wheat',
}
sns.catplot(
data=pumpkins, y="Variety", hue="Color", kind="count",
palette=palette,
)
从这张图可以直接看到:HOWDEN WHITE TYPE、BLUE TYPE 等品种基本全是白色,而 HOWDEN TYPE、PIE TYPE 几乎全是橙色——品种与颜色高度相关,这从先验上预示逻辑回归应有不错的表现。
(2)品种 × 尺寸 × 颜色的箱线图
用编码后的 ord__Item Size 做横轴,按 Variety 分面:
pumpkins['Item Size'] = encoded_pumpkins['ord__Item Size']
g = sns.catplot(
data=pumpkins,
x="Item Size", y="Color", row='Variety',
kind="box", orient="h",
sharex=False, margin_titles=True,
height=1.8, aspect=4, palette=palette,
)
g.set(xlabel="Item Size", ylabel="").set(xlim=(0, 6))
g.set_titles(row_template="{row_name}")
(3)Swarm 图
palette = {0: 'orange', 1: 'wheat'}
sns.swarmplot(x="Color", y="ord__Item Size", data=encoded_pumpkins, palette=palette)
课程特别提示:由于数据点较多,Swarm 图可能产生“点放不下”的警告,可通过 size 参数缩小标记来缓解,但会牺牲可读性——在作业 Notebook 中说明你如何权衡这一点,也是加分的解释性内容。
原理上,逻辑回归依赖 sigmoid(logistic)函数做最大似然估计:它把任意实数输入压缩到 (0, 1) 区间,函数值超过 0.5 判为类别 1(本数据中即 WHITE),否则判为 0。
6. 第四步:划分数据并训练模型
from sklearn.model_selection import train_test_split
X = encoded_pumpkins[encoded_pumpkins.columns.difference(['Color'])] # 全部编码特征
y = encoded_pumpkins['Color'] # 标签
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=0
)
test_size=0.2 即 80/20 划分;random_state=0 固定随机种子,保证结果可复现——在作业中写明这一点能体现严谨性。
训练与预测:
from sklearn.metrics import f1_score, classification_report
from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
model.fit(X_train, y_train)
predictions = model.predict(X_test)
print(classification_report(y_test, predictions))
print('Predicted labels: ', predictions)
print('F1-score: ', f1_score(y_test, predictions))
课程文档中给出的评分卡示例(1000 行规模数据、20% 测试集,199 条测试样本):
precision recall f1-score support
0 0.94 0.98 0.96 166
1 0.85 0.67 0.75 33
accuracy 0.92 199
macro avg 0.89 0.82 0.85 199
weighted avg 0.92 0.92 0.92 199
F1-score: 0.7457627118644068
按本报告卡可以读出:模型对橙色(0 类)几乎不误判(recall 0.98),但对白色(1 类)的召回只有 0.67——少数类是模型的短板,这与 5.4:1 的类别不均衡直接相关。作业中若能把这一现象解释清楚,就满足了 Rubric 中 “well-explained” 的要求。
7. 第五步:混淆矩阵与指标推导
from sklearn.metrics import confusion_matrix
confusion_matrix(y_test, predictions)
# array([[162, 4],
# [ 11, 22]])
Scikit-learn 的混淆矩阵约定:行为真实标签,列为预测标签。
| 预测 0 | 预测 1 | |
|---|---|---|
| 真实 0 | TN = 162 | FP = 4 |
| 真实 1 | FN = 11 | TP = 22 |
对应课程中的四象限定义(以 white / not-white 为例):预测非白且真实非白 → TN(左上 162);预测白而真实非白 → FP(右上 4);预测非白而真实白 → FN(左下 11);预测白且真实白 → TP(右下 22)。
由矩阵直接推出报告卡中的指标:
- Precision = TP / (TP + FP) = 22 / (22 + 4) ≈ 0.846
- Recall = TP / (TP + FN) = 22 / (22 + 11) ≈ 0.667
- F1 = 2·P·R / (P + R) ≈ 0.746
- Accuracy = (TP + TN) / (TP + TN + FP + FN) = 184 / 199 ≈ 0.92
其余两行指标:Macro Avg 是各类别指标的无加权平均,忽略类别不均衡;Weighted Avg 按各类别 support(真实样本数)加权,反映整体样本层面表现。课程留了一个思考题:如果你希望减少误报(false negatives),应盯住 Recall。
8. 第六步:ROC 曲线与 AUC
from sklearn.metrics import roc_curve, roc_auc_score
import matplotlib.pyplot as plt
y_scores = model.predict_proba(X_test)
fpr, tpr, thresholds = roc_curve(y_test, y_scores[:, 1])
fig = plt.figure(figsize=(6, 6))
plt.plot([0, 1], [0, 1], 'k--') # 随机基线
plt.plot(fpr, tpr)
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
plt.show()
auc = roc_auc_score(y_test, y_scores[:, 1])
print(auc)
# 0.9749908725812341
ROC 曲线横轴为 FPR、纵轴为 TPR,越快速“抬头并拱过”对角虚线的曲线越好;AUC(0~1)越大越好。课程实测 AUC ≈ 0.975,说明模型的区分能力相当强——注意 AUC 高与白色类 Recall 偏低可以并存:概率排序很好,但在 0.5 这个固定阈值下少数类仍会被误判一部分。在作业中区分这两者(阈值效应 vs 排序能力),是“well-performing 且 well-explained”的体现。
9. 交付清单:对照 Rubric 自查
把上面的步骤汇总成作业交付的自查清单:
- 数据:从 US-pumpkins.csv 读取全量数据 → 选 6 列 →
dropna()(实测剩 991 行); - 编码:
Item Size用 OrdinalEncoder,其余 4 个类别列用 OneHotEncoder,经 ColumnTransformer 统一输出;Color用 LabelEncoder 并在 Notebook 中写明 0/1 的语义映射; - 探索:至少一张 Seaborn 分类图,并说明你观察到的品种-颜色关系;
- 建模:
train_test_split(test_size=0.2, random_state=0)划分后训练LogisticRegression; - 评估:输出
classification_report、confusion_matrix、ROC 曲线与 AUC,并用混淆矩阵数值手动推导 Precision/Recall,解释类别不均衡对 Recall 的影响; - 解释:每个关键单元格都有 Markdown 说明——这是与“Needs Improvement”拉开差距的核心。
10. 参考资源
- 课程正文(含完整代码与输出):2-Regression/4-Logistic/README.md
- 练习用起始 Notebook:2-Regression/4-Logistic/notebook.ipynb
- 官方解答 Notebook(含完整运行结果):2-Regression/4-Logistic/solution/notebook.ipynb
- 原始数据集:2-Regression/data/US-pumpkins.csv
- 作业原文(英/阿语):2-Regression/4-Logistic/assignment.md、translations/ar/2-Regression/4-Logistic/assignment.md
课程正文最后还提出了一个开放性挑战:找一个适合逻辑回归的新数据集独立建模。完成本作业后,可以按同样的“清洗 → 编码 → 划分 → 训练 → 混淆矩阵/ROC 评估”流程迁移到任意二分类问题上——这正是逻辑回归作为经典 ML 入门模型的通用价值。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00


