首页
/ ML for Beginners 实战:Logistic Regression 作业全解——用完整南瓜数据集构建并评估逻辑回归模型

ML for Beginners 实战:Logistic Regression 作业全解——用完整南瓜数据集构建并评估逻辑回归模型

2026-09-06 15:03:17作者:钟日瑜

本篇围绕 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 NamePackageVarietyOrigin 都没有逻辑顺序,采用 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

目标列 ColorLabelEncoder 归一化为 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 TYPEBLUE TYPE 等品种基本全是白色,而 HOWDEN TYPEPIE 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。

ROC 曲线

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 自查

把上面的步骤汇总成作业交付的自查清单:

  1. 数据:从 US-pumpkins.csv 读取全量数据 → 选 6 列 → dropna()(实测剩 991 行);
  2. 编码Item Size 用 OrdinalEncoder,其余 4 个类别列用 OneHotEncoder,经 ColumnTransformer 统一输出;Color 用 LabelEncoder 并在 Notebook 中写明 0/1 的语义映射
  3. 探索:至少一张 Seaborn 分类图,并说明你观察到的品种-颜色关系;
  4. 建模train_test_split(test_size=0.2, random_state=0) 划分后训练 LogisticRegression
  5. 评估:输出 classification_reportconfusion_matrix、ROC 曲线与 AUC,并用混淆矩阵数值手动推导 Precision/Recall,解释类别不均衡对 Recall 的影响;
  6. 解释:每个关键单元格都有 Markdown 说明——这是与“Needs Improvement”拉开差距的核心。

10. 参考资源

课程正文最后还提出了一个开放性挑战:找一个适合逻辑回归的新数据集独立建模。完成本作业后,可以按同样的“清洗 → 编码 → 划分 → 训练 → 混淆矩阵/ROC 评估”流程迁移到任意二分类问题上——这正是逻辑回归作为经典 ML 入门模型的通用价值。

登录后查看全文
热门项目推荐
相关项目推荐