ML-For-Beginners 实战:用逻辑回归预测南瓜颜色——二元分类全流程指南
【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners
本指南以 ML-For-Beginners 课程 Regression 章节的第 4 课(2-Regression/4-Logistic/README.md)为骨架,完整讲解如何使用 Scikit-learn 构建逻辑回归模型,预测南瓜是橙色还是白色("白色 / 非白色"二元分类)。你将掌握从数据清洗、特征与标签编码、Seaborn 可视化,到模型训练、混淆矩阵解读与 ROC 曲线评估的完整闭环,并能在配套的 Starter Notebook(2-Regression/4-Logistic/notebook.ipynb)与完整解法(2-Regression/4-Logistic/solution/notebook.ipynb)中复现每一步结果。
课程背景:为什么在"回归"章节讨论分类
在接触了线性回归、多项式回归并处理过美国南瓜批发价格数据(2-Regression/data/US-pumpkins.csv)之后,本课是 Regression 系列的最后一课。逻辑回归虽然名字里带"回归",但正如 Scikit-learn 官方文档所指出的,它本质上是一种基于线性模型的分类方法——本课之所以放在回归专题中讨论,更多是出于语言习惯上的便利。
逻辑回归的典型应用场景包括:
- 这块糖果是不是巧克力?
- 这种疾病是否具有传染性?
- 这位顾客是否会选择这个产品?
在本课程的具体项目中,我们要回答的问题是:给定一些变量,某个南瓜更可能是橙色(🎃)还是白色(👻)。南瓜数据中存在一个天然的二元类别列Color,因此非常适合用来演示逻辑回归。
趣味事实:白色南瓜有时被称为"幽灵南瓜",它们不易雕刻,因此不如橙色南瓜受欢迎,但外观很酷。所以我们的问题也可以表述为:"幽灵"还是"非幽灵"。
定义问题
在开始建模之前,先把任务形式化为一个二元问题:'White'(白)或 'Not White'(非白)。
数据集中其实还有一类 "striped"(条纹),但由于它的样本量极少,我们不使用它;而且一旦删除数据中的空值,这类样本自然就消失了。
逻辑回归的核心概念
逻辑回归与前面学过的线性回归有几个重要区别。
二元分类与连续预测
- 线性回归:预测连续值,例如根据南瓜产地和采收时间预测其价格会上涨多少。
- 逻辑回归:预测二元类别("白或非白")。
其他类型的逻辑回归
除了二元逻辑回归,还有两种扩展形式:
- 多项逻辑回归(Multinomial):涉及多于一个类别,例如"橙色、白色和条纹"。
- 有序逻辑回归(Ordinal):涉及有逻辑顺序的类别,例如按有限个尺寸等级(mini、sm、med、lg、xl、xxl)排序的南瓜。
变量不需要相关
还记得吗?线性回归在变量相关性较强时效果更好。逻辑回归恰恰相反——变量之间不需要相互对齐(相关)。这对本数据集很合适,因为南瓜数据中各变量间的相关性本就较弱。
需要大量干净数据
逻辑回归在数据量更大时给出的结果更准确;本课所用的小数据集(约 1000 行)对此任务而言并非最优,这一点需要牢记。
练习一:数据清洗
首先对数据做轻量清洗:删除空值行,并只选取部分需要的列。
columns_to_select = ['City Name','Package','Variety', 'Origin','Item Size', 'Color'] pumpkins = full_pumpkins.loc[:, columns_to_select] pumpkins.dropna(inplace=True)其中full_pumpkins来自 Starter Notebook 中的加载逻辑:full_pumpkins = pd.read_csv('../data/US-pumpkins.csv')(数据实际存放于 2-Regression/data/US-pumpkins.csv,原始数据共 26 列,包含 City Name、Type、Package、Variety、Grade、Date、价格等多类字段)。清洗后我们保留了 6 个字段,其中Color即标签列。
随时可以查看新的 DataFrame:
pumpkins.info从解法 Notebook(2-Regression/4-Logistic/solution/notebook.ipynb)中可以看到清洗后的数据形如:
City Name Package Variety Origin Item Size Color 2 BALTIMORE 24 inch bins HOWDEN TYPE DELAWARE med ORANGE 3 BALTIMORE 24 inch bins HOWDEN TYPE VIRGINIA med ORANGE 4 BALTIMORE 24 inch bins HOWDEN TYPE MARYLAND lge ORANGE练习二:用 Seaborn 绘制类别图(catplot)
本课引入一个新的可视化库Seaborn,它构建在之前用过的 Matplotlib 之上。Seaborn 提供了一些很实用的数据可视化方式,例如在类别图中对比每种Variety(品种)与Color(颜色)的数据分布。
使用catplot函数,并为每个南瓜类别(橙色或白色)指定颜色映射:
import seaborn as sns palette = { 'ORANGE': 'orange', 'WHITE': 'wheat', } sns.catplot( data=pumpkins, y="Variety", hue="Color", kind="count", palette=palette, )通过观察这张图,可以直观看出颜色数据与品种之间的关系。对应的示例图保存在 2-Regression/4-Logistic/images/pumpkins_catplot_1.png。
练习三:数据预处理——特征与标签编码
南瓜数据集的所有列都是字符串值。人类处理类别数据很直观,但机器学习算法只擅长处理数字。**编码(Encoding)**是数据预处理中非常重要的一步:它让我们在不丢失信息的前提下,把类别数据转换成数值数据。好的编码是构建好模型的前提。
特征编码有两种主要类型:
1. 序数编码器(OrdinalEncoder)
适用于有序变量(ordinal variables)——数据遵循逻辑顺序的类别变量,例如本数据集中的Item Size列。它会建立映射,让每个类别用一个数字表示,该数字即该类别在列中的顺序。
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)解法 Notebook 中先通过
pumpkins['Item Size'].unique()查看该列的全部取值:['med', 'lge', 'sml', 'xlge', 'med-lge', 'jbo', 'exjbo'],这正是上面item_size_categories中定义顺序的依据。
2. 类别编码器(OneHotEncoder)
适用于名义变量(nominal variables)——数据没有逻辑顺序的类别变量,例如本数据集中除Item Size外的所有特征。它执行的是 One-Hot 编码:每个类别用一列二进制列表示,南瓜属于该类别时编码为 1,否则为 0。
from sklearn.preprocessing import OneHotEncoder categorical_features = ['City Name', 'Package', 'Variety', 'Origin'] categorical_encoder = OneHotEncoder(sparse_output=False)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')让输出直接以 pandas DataFrame 形式返回。从解法 Notebook 的输出可以看到,编码后的特征表列名形如ord__Item Size、cat__City Name_BALTIMORE、cat__Origin_VIRGINIA等,最终是48 列(1 列序数编码 + 47 列 One-Hot 编码)。
4. 编码标签:LabelEncoder
编码标签时,我们使用 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'])5. 合并特征与标签
编码完成后,将特征与标签合并进新的 DataFrameencoded_pumpkins:
encoded_pumpkins = encoded_features.assign(Color=encoded_label)此时encoded_pumpkins共49 列(48 个特征列 + 1 个 Color 标签列)。解法 Notebook 还演示了反向映射验证:list(label_encoder.inverse_transform([0, 1]))返回['ORANGE', 'WHITE'],确认 0 代表橙色、1 代表白色。
练习四:分析变量之间的关系
数据预处理完成后,可以分析特征与标签之间的关系,判断模型根据特征预测标签的效果。最佳分析方式是绘制数据。我们再次使用 Seaborn 的catplot,在类别图中可视化Item Size、Variety与Color三者的关系。为了画图更清晰,这里使用编码后的Item Size列与未编码的Variety列:
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}")参数说明:
kind="box"表示绘制箱线图;orient="h"水平方向;sharex=False允许各子图独立 x 轴范围;xlim=(0,6)将横轴限制在 0 到 6,正好覆盖Item Size的 7 个序数编码值(0~6)。结果图见 2-Regression/4-Logistic/images/pumpkins_catplot_2.png。
使用 Swarm Plot(蜂群图)
由于Color是二元类别(白或非白),它需要一种专门的可视化方法。你可以用 Seaborn 将变量并排可视化,尝试用 "swarm"(蜂群)图展示值的分布:
palette = { 0: 'orange', 1: 'wheat' } sns.swarmplot(x="Color", y="ord__Item Size", data=encoded_pumpkins, palette=palette)结果见 2-Regression/4-Logistic/images/swarm_2.png。
注意:上述代码可能会产生一条警告,因为 Seaborn 难以把这么多数据点完整表示在一个蜂群图中。一个可行方案是使用size参数减小标记尺寸,但要注意这会影响图的可读性。
解法 Notebook 中的做法是:warnings.filterwarnings(action='ignore', category=UserWarning, module='seaborn')并在swarmplot中同时传入hue="Color"以保持调色板一致性。需要强调的是,忽略警告并非最佳实践,这里只是为了保证绘图的可读性而做出的权衡;正常开发中应优先通过调整参数(如减小 marker 尺寸)来解决问题。
数学原理:Sigmoid 函数与最大似然
逻辑回归建立在**最大似然(Maximum Likelihood)**概念之上,核心工具是Sigmoid 函数。Sigmoid 函数在图上呈 "S" 形,它接受一个值并将其映射到 0 与 1 之间,其曲线也被称为"逻辑曲线"。公式如下:
其中:Sigmoid 的中点位于 x = 0 处,L 是曲线的最大值,k 是曲线的陡峭程度。
判类规则是:如果函数的输出大于 0.5,则将对应标签归为二元选择中的类别 "1";否则归类为 "0"。
构建逻辑回归模型
在 Scikit-learn 中,构建这样一个二元分类模型出乎意料地简单。
1. 选择变量并划分训练/测试集
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)X是除Color外的所有编码特征,y是编码后的标签。test_size=0.2表示留出 20% 的数据用于测试,random_state=0保证结果可复现。
2. 训练模型并输出评估结果
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 行数据,这个表现并不差:
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 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 1] F1-score: 0.7457627118644068其中测试集共 199 个样本:类别 0(橙色)166 个、类别 1(白色)33 个。整体准确率 0.92,加权平均 F1 为 0.92。
用混淆矩阵理解模型表现
虽然可以通过打印上述各项输出获得评估报告,但使用**混淆矩阵(Confusion Matrix)**能更直观地理解模型的表现。
🎓 混淆矩阵(又称"误差矩阵")是一种表格,它表达模型预测的真/假阳性和真/假阴性,从而衡量预测的准确性。
调用confusion_matrix():
from sklearn.metrics import confusion_matrix confusion_matrix(y_test, predictions)查看模型的混淆矩阵:
array([[162, 4], [ 11, 22]])在 Scikit-learn 中,混淆矩阵的行(axis 0)是真实标签,列(axis 1)是预测标签:
| 0 | 1 | |
|---|---|---|
| 0 | TN | FP |
| 1 | FN | TP |
假设模型要在"白色"和"非白色"两个二元类别之间对南瓜分类:
- 模型预测为"非白",实际也属于"非白"类别 →True Negative(TN),即左上角数字 162。
- 模型预测为"白",实际属于"非白"类别 →False Negative(FN),即左下角数字 11。
- 模型预测为"非白",实际属于"白"类别 →False Positive(FP),即右上角数字 4。
- 模型预测为"白",实际也属于"白"类别 →True Positive(TP),即右下角数字 22。
可以想象,我们期望 True Positives 和 True Negatives 数量更大、False Positives 和 False Negatives 数量更小,这意味着模型表现更好。
混淆矩阵如何关联精确率与召回率
回顾前面打印的分类报告:精确率(precision)为 0.85,召回率(recall)为 0.67。利用混淆矩阵中的数字可以验证:
精确率 = tp / (tp + fp) = 22 / (22 + 4) = 0.8461538461538461 召回率 = tp / (tp + fn) = 22 / (22 + 11) = 0.6666666666666666与报告中的 0.85、0.67 一致(保留两位小数)。
评估指标速查
借助混淆矩阵中 TP/TN 与 FP/FN 的映射,回顾此前出现过的术语:
- 精确率(Precision):TP / (TP + FP),检索到的实例中相关实例所占比例(即哪些标签被正确标记)。
- 召回率(Recall):TP / (TP + FN),被检索到的相关实例所占比例,无论是否正确标记。
- F1 分数:(2 × precision × recall) / (precision + recall),精确率与召回率的加权平均,1 最好、0 最差。
- Support:每个被检索标签的出现次数。
- 准确率(Accuracy):(TP + TN) / (TP + TN + FP + FN),对一个样本正确预测标签的百分比。
- 宏平均(Macro Avg):对每个标签计算指标的未加权平均,不考虑标签不平衡。
- 加权平均(Weighted Avg):对每个标签计算指标的平均值,通过按 support(每个标签的真实实例数)加权来考虑标签不平衡。
思考题:如果希望模型减少 False Negatives 的数量,应该重点关注哪个指标?(提示:召回率 Recall)
用 ROC 曲线评估模型
再做一个可视化,看看所谓的 "ROC" 曲线:
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()用 Matplotlib 绘制模型的Receiver Operating Characteristic(ROC)曲线。ROC 曲线常用于从真阳性与假阳性的角度观察分类器的输出:典型 ROC 曲线以 Y 轴为真阳性率(True Positive Rate),X 轴为假阳性率(False Positive Rate)。因此,曲线的陡峭程度以及中线与曲线之间的间隔很重要:你希望曲线快速上升并越过对角线。本例中,起初有一些假阳性,随后曲线正确地向上并越过了对角线:
最后,使用 Scikit-learn 的roc_auc_scoreAPI 计算实际的"曲线下面积"(AUC):
auc = roc_auc_score(y_test,y_scores[:,1]) print(auc)结果为0.9749908725812341。由于 AUC 的取值范围是 0 到 1,我们希望得到高分——一个 100% 正确预测的模型 AUC 为 1;就本例而言,这个模型的表现相当不错。
总结与延伸
恭喜!至此你已完成了 ML-For-Beginners 课程中全部的回归(Regression)课程,并用逻辑回归独立完成了"预测南瓜颜色"的二元分类任务,覆盖了数据清洗、类别编码、可视化探索、模型训练与评估(分类报告、混淆矩阵、ROC/AUC)的完整流程。
在后续的分类课程中,你将继续学习如何迭代改进模型分数。当前模型约 1000 行数据即可达到 0.92 的准确率与 0.975 的 AUC,这说明在特征选择与编码到位的情况下,逻辑回归对小数据集同样能给出不错的基准结果。
挑战
关于逻辑回归还有更多值得探索的内容,但最好的学习方式就是动手实验。找一个适合此类分析的数据集,用它构建一个模型,看看你能学到什么。
本课对应的官方作业见 2-Regression/4-Logistic/assignment.md:回到原始数据,尝试使用全部数据(清洗并标准化)来构建逻辑回归模型。评分标准为:提交一个解释充分且表现良好的 Notebook(优秀)、表现勉强合格(合格)、表现欠佳或缺失(待改进)。
复习与自学
建议阅读 Stanford 大学关于逻辑回归实际应用的论文开头部分,思考此前学过的各类回归任务中,哪些更适合使用哪一种回归/分类方法——哪种方案效果最好?另外,本课还提供了 R 语言版本的完整实现,可参考 2-Regression/4-Logistic/solution/R/lesson_4.html;Scikit-learn 版本的逐单元可运行代码见 2-Regression/4-Logistic/solution/notebook.ipynb。
【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考