ML-For-Beginners 回归单元实战:用逻辑回归预测南瓜颜色的二分类模型
本篇指南基于 ML-For-Beginners 课程仓库回归单元(2-Regression)的最后一课,完整讲解如何用 Scikit-learn 的逻辑回归(Logistic Regression)解决二分类问题:给定一批美国南瓜的市场数据,预测每个南瓜的颜色属于"白色"还是"非白色"。文章覆盖从数据清洗、Seaborn 可视化、特征与标签编码,到模型训练、混淆矩阵解读与 ROC/AUC 评估的全流程,读者可以按步骤复现一个可运行的二分类流水线。
1. 为什么在"回归"单元讲二分类
逻辑回归是经典机器学习中用于预测二分类类别的核心技术。典型问题形式是:"这颗糖果含巧克力吗?""这种病传染吗?""这个客户会选这个产品吗?"——答案都是"是/否"。
逻辑回归虽然名字里带"回归",但按 Scikit-learn 的模块划分它本质上是一种基于线性方法的分类算法,课程把它放在回归单元里只是出于术语上的便利。在 ML-For-Beginners 中,下一课组(4-Classification)会继续介绍其他分类方法,本篇则是回归单元的收官课。
与线性回归相比,逻辑回归有几个重要区别:
- 预测对象不同:线性回归输出连续值(例如根据产地和收获时间预测南瓜价格涨幅);逻辑回归输出一个二分类预测("白色或非白色")。
- 变量不要求强相关:线性回归更喜欢强相关的变量,逻辑回归则相反——变量之间不必对齐。这对本课数据很合适,因为南瓜各特征之间的相关性并不算强。
- 需要大量干净数据:数据越多,逻辑回归的准确度越高。本课数据集只有约 1000 多行有效数据,对这项任务并不算理想,这是评估结果时需要记住的前提。
1.1 其他类型的逻辑回归
除二分类外,逻辑回归还有两类变体:
- 多项(Multinomial):存在多个无序类别,例如"橙色、白色、条纹色";
- 有序(Ordinal):类别之间存在逻辑顺序,例如南瓜按有限个尺寸排序(mini、sm、med、lg、xl、xxl)。
1.2 定义问题:白色 vs 非白色
课程使用的数据集是美国南瓜批发市场数据,仓库中为 US-pumpkins.csv(共 1757 行、26 列)。从实际数据看,Color 列只出现 ORANGE、WHITE、STRIPED 三种取值;其中 STRIPED(条纹)样本很少,且一旦移除空值后基本消失,因此课程将其简化为二分类问题:
- 分类目标:
White(白色)或Not White(非白色); - 趣味背景:白色南瓜常被称为"幽灵南瓜"(ghost pumpkins),不易雕刻,不如橙色南瓜常见,所以问题也可以表述为"Ghost 还是 Not Ghost"。
课程配套的起点笔记本是 notebook.ipynb,其中已预置了加载数据的第一步代码:
import pandas as pd
import numpy as np
full_pumpkins = pd.read_csv('../data/US-pumpkins.csv')
full_pumpkins.head()
另外,本课同时提供 R 语言版本,包括 lesson_4.html、lesson_4.Rmd 与 lesson_4-R.ipynb,适合 R 学习者对照学习。
2. 清洗数据:选列与去空值
逻辑回归依赖干净的数据,第一步是从完整数据框中挑选有用列并删除缺失值:
columns_to_select = ['City Name', 'Package', 'Variety', 'Origin', 'Item Size', 'Color']
pumpkins = full_pumpkins.loc[:, columns_to_select]
pumpkins.dropna(inplace=True)
清洗后可以通过 pumpkins.info 查看数据框的新结构(列名、非空数量、类型)。保留下来的 Color 就是本模型要预测的目标类别。
3. 可视化:用 Seaborn 绘制分类图
本课引入基于 Matplotlib 的 Seaborn 库(sns),它是课程中第一次使用的统计可视化库。例如用 catplot 按南瓜品种(Variety)对比颜色(Color)的分布,并为每种颜色指定固定色板:
import seaborn as sns
palette = {
'ORANGE': 'orange',
'WHITE': 'wheat',
}
sns.catplot(
data=pumpkins, y="Variety", hue="Color", kind="count",
palette=palette,
)
从图中可以直接观察到 Color 与 Variety 之间的关联:某些品种(如 HOWDEN WHITE TYPE)几乎全是白色,而 HOWDEN TYPE 则以橙色为主——这种结构性差异正是逻辑回归能够学到"颜色规律"的原因。
✅ 思考题:观察这张分类计数图,你还能设想哪些有趣的探索方向?
4. 数据预处理:特征与标签编码
南瓜数据集的所有列都是字符串。人类直观理解类别型数据,但机器不是——机器学习算法只擅长处理数字。因此**编码(encoding)**是预处理阶段中至关重要的一步:在不丢失信息的前提下把类别数据转成数值数据,好的编码方式直接决定模型质量的上限。
课程给出了两种主要编码器:
4.1 有序编码器(OrdinalEncoder)
适合有序变量——类别之间存在逻辑顺序,例如 Item Size 列。它把每个类别映射为序号。实际数据中该列共 7 个取值,从最小到最大依次为 sml, med, med-lge, lge, xlge, jbo, exjbo,与下面的类别定义一致:
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)
✅ 思考题:对
Item Size使用有序编码器有什么好处?(提示:保留了尺寸间"更小→更大"的序关系,且参数量远少于独热编码。)
4.2 独热编码器(OneHotEncoder)
适合名义变量——类别之间没有顺序,例如 City Name、Package、Variety、Origin(实际数据中品种有 10 种、产地有 24 种)。独热编码把每个类别展开为一个二值列:属于该类别为 1,否则为 0。
from sklearn.preprocessing import OneHotEncoder
categorical_features = ['City Name', 'Package', 'Variety', 'Origin']
categorical_encoder = OneHotEncoder(sparse_output=False)
4.3 用 ColumnTransformer 合并多路编码
ColumnTransformer 把多个编码器组合成一步,并各自作用于对应列:
from sklearn.compose import ColumnTransformer
ct = ColumnTransformer(transformers=[
('ord', ordinal_encoder, ordinal_features),
('cat', categorical_encoder, categorical_features)
])
ct.set_output(transform='pandas')
encoded_features = ct.fit_transform(pumpkins)
注意 ct.set_output(transform='pandas'):它让输出仍是带列名的 DataFrame,后续可以直接引用 ord__Item Size 这类带前缀的编码列。
4.4 标签编码与合并
标签用 Scikit-learn 的 LabelEncoder 规范化为 0 到 n_classes-1(这里即 0 和 1)的整数:
from sklearn.preprocessing import LabelEncoder
label_encoder = LabelEncoder()
encoded_label = label_encoder.fit_transform(pumpkins['Color'])
最后把编码后的特征与标签合并进新的数据框 encoded_pumpkins:
encoded_pumpkins = encoded_features.assign(Color=encoded_label)
5. 分析变量间关系:箱体分类图与蜂群图
预处理完成后,课程建议先"看图建模",直观判断特征对标签的区分力。再次使用 catplot,把 Item Size(使用编码后的数值列)、Variety(保持原始文本作分面行)和 Color 画成横向箱体图:
palette = {
'ORANGE': 'orange',
'WHITE': 'wheat',
}
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}")
由于 Color 是二分类变量,课程还推荐用**蜂群图(swarm plot)**展示两个颜色取值下尺寸分布的形态:
palette = {
0: 'orange',
1: 'wheat'
}
sns.swarmplot(x="Color", y="ord__Item Size", data=encoded_pumpkins, palette=palette)
注意:上述代码可能产生警告——Seaborn 在单张蜂群图中渲染如此多的数据点会失败或互相叠压。可以调小 size 参数缓解,但需权衡图形的可读性。
5.1 数学原理:Sigmoid 函数与极大似然
🧮 Show Me The Math
逻辑回归基于**极大似然(maximum likelihood)**思想,并使用 Sigmoid 函数。Sigmoid 曲线呈"S"形,把一个任意实数映射到 0 与 1 之间,其曲线也叫"逻辑曲线":
其中曲线中点位于 x=0 处,L 是曲线的最大值,k 控制曲线的陡峭程度。函数输出大于 0.5 时,样本被划入二分类中的"1"类;否则归为"0"类。
6. 构建模型:训练与初步评估
在 Scikit-learn 中构建二分类模型非常直接。
第 1 步:选定进入模型的变量,并用 train_test_split() 划分训练集与测试集(留出 20% 作测试集,random_state=0 保证结果可复现):
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)
第 2 步:调用 fit() 训练,对测试集做 predict(),并打印分类报告与 F1 分数:
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))
课程的运行结果(测试集约 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
Predicted labels: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 ... 1 1]
F1-score: 0.7457627118644068
考虑到有效数据仅约 1000 行,这个结果并不算差:0 类(非白色)的精确率/召回率都很高,1 类(白色)的召回率只有 0.67,说明少数类的漏报是主要短板。
7. 深入理解:混淆矩阵
分类报告给出了各指标数值,但要真正理解模型行为,**混淆矩阵(confusion matrix)**更直观:
from sklearn.metrics import confusion_matrix
confusion_matrix(y_test, predictions)
课程的输出:
array([[162, 4],
[ 11, 22]])
在 Scikit-learn 中,行(轴 0)是真实标签,列(轴 1)是预测标签,对应关系为:
| 预测 0 | 预测 1 | |
|---|---|---|
| 实际 0 | TN=162 | FP=4 |
| 实际 1 | FN=11 | TP=22 |
以"预测南瓜是否白色"为例:
- 预测"非白色"且实际就是非白色 → 真负(TN),左上角数字;
- 预测"白色"但实际非白色 → 假负(FN),课程将其解释为左下角数字;
- 预测"非白色"但实际是白色 → 假正(FP),右上角数字;
- 预测"白色"且实际就是白色 → 真正(TP),右下角数字。
TP、TN 越大、FP、FN 越小,模型越好。
7.1 从混淆矩阵手算 Precision 与 Recall
课程用矩阵中的数字验证了分类报告里的精确率与召回率:
Precision = tp / (tp + fp) = 22 / (22 + 4) = 0.8461538461538461
Recall = tp / (tp + fn) = 22 / (22 + 11) = 0.6666666666666666
✅ Q:根据混淆矩阵,模型表现如何?A:不算差——真负数量可观,但存在少量假负。 ✅ 思考题:如果你想减少模型的假负数量,应该重点盯住哪个指标?(提示:Recall。)
课程随后用 TP/TN/FP/FN 重新定义了报告中的每个术语:
- Precision(精确率):TP/(TP + FP),被检索到的实例中相关实例的占比(哪些标签被正确标出);
- Recall(召回率):TP/(TP + FN),被检索到的相关实例占比(无论标记好坏);
- f1-score:(2 × precision × recall) / (precision + recall),精确率与召回率的调和平均,最好为 1、最差为 0;
- Support:每个标签实际出现的次数;
- Accuracy:(TP + TN)/(TP + TN + FP + FN),样本中被正确预测的百分比;
- Macro Avg:对各标签指标取不加权平均,不考虑类别不平衡;
- Weighted Avg:按各标签的 support 加权平均,考虑类别不平衡。
8. 评估模型:ROC 曲线与 AUC
最后一个可视化是 ROC 曲线(Receiver Operating Characteristic),用于从"真阳率 vs 假阳率"的角度审视分类器输出:
from sklearn.metrics import roc_curve, roc_auc_score
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
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()
几点细节值得注意:
model.predict_proba(X_test)返回每个类别的预测概率,y_scores[:,1]取"1 类(白色)"的概率作为打分输入roc_curve;- ROC 曲线横轴是假阳率(FPR)、纵轴是真阳率(TPR),虚线是从 (0,0) 到 (1,1) 的随机猜测基准线;
- 曲线的陡峭程度以及它与基准线之间围成的面积是关键:理想曲线应尽早向上凸起并越过基准线。本课模型起初存在少量假阳,随后正确地向上越过基准线。
最后用 Scikit-learn 的 roc_auc_score 计算曲线下面积(AUC):
auc = roc_auc_score(y_test, y_scores[:,1])
print(auc)
课程输出的结果是 0.9749908725812341。AUC 取值在 0 到 1 之间,100% 正确的模型 AUC 为 1,因此 0.975 说明这个在仅千行数据上训练的逻辑回归模型"相当不错"。
9. 练习与延伸:用完整数据重做这个回归
课程课后作业(assignment.md)的要求是:
课上你只使用了南瓜数据的一个子集。现在回到原始数据,把全部数据清洗、标准化之后,重新构建一个逻辑回归模型。
评估标准是:提交一个笔记本,模型讲解充分且表现良好(优秀)、能基本运行(合格)、或模型表现不佳/缺失(需改进)。
在动手之前,可以先利用仓库中的原始数据了解全貌:US-pumpkins.csv 有 26 列,除本课选用的 6 列外,还包含 Type、Sub Variety、Grade、Date、Low Price/High Price、Origin District、Environment、Crop、Repack 等字段,Color 列存在 ORANGE、WHITE、STRIPED 三种取值,且大量行带有空值——这正是"先用 dropna() 清洗、再选列"这一流程的由来。
课程给出的自研延伸建议是:找一份适合二分类分析的数据集,用逻辑回归从零建模,记录学到了什么。学习路径上,本课结束后即可进入仓库的下一课组 4-Classification(入口),那里会系统讲解如何迭代改进分类模型的各项得分。
本篇小结:围绕 2-Regression/4-Logistic/README.md 这一课,本文完整复现了 ML-For-Beginners 中逻辑回归的实战流程——从南瓜数据的选列清洗、Seaborn 分类图与蜂群图,到 OrdinalEncoder/OneHotEncoder/ColumnTransformer/LabelEncoder 的编码流水线、LogisticRegression 训练,再到混淆矩阵手算 Precision/Recall 与 ROC/AUC 评估。所有代码均可在配套笔记本 notebook.ipynb 中对照运行,R 语言版本见 solution/R/。
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




