ML-For-Beginners 回归模块指南:基于南瓜价格数据集构建线性与逻辑回归模型
本文基于 ML-For-Beginners 课程库的 2-Regression 模块,系统讲解如何用 Scikit-learn 完成回归建模的完整流程:从配置本地机器学习环境、清洗和可视化 USDA 南瓜市场价格数据,到训练线性回归、多项式回归和逻辑回归模型,并掌握 RMSE、决定系数、混淆矩阵与 ROC 曲线等核心评估手段。读完本篇,你可以独立复现该模块四节课的全部实操,并理解每一步背后的数学原理与代码实现。
模块主题与数据集背景
回归(Regression)模型的核心能力是发现变量之间的关系,并据此预测连续数值——例如长度、温度、年龄或价格。在分析数据点的过程中,模型会逐步揭示特征与目标之间的关联规律。
ML-For-Beginners 选择了北美南瓜市场作为贯穿本模块的实战案例:在北美,南瓜常在万圣节被雕刻成"杰克灯"(Jack-o'-lanterns),围绕这种蔬菜的市场数据天然适合用回归模型来分析。模块的原始设定文档位于 Regression 模块 README,其中明确了学习主线——理解线性回归与逻辑回归的区别,并掌握何时应该选择其中哪一种。
整个模块共享一份真实公开数据集 US-pumpkins.csv:
- 数据来源是美国农业部(USDA)发布的 Specialty Crops Terminal Markets Standard Reports 终端市场标准报告,属于公共领域数据;
- 数据集包含 1758 行(约 1757 条记录)美国各城市南瓜终端市场报价,字段包括
City Name(城市)、Package(包装规格)、Variety(品种)、Grade(等级)、Date(日期)、Low Price/High Price(低/高报价)、Origin(产地)、Item Size(大小)、Color(颜色)等 20 多个列; - 数据原始形态较为"混乱":存在大量空值、字符串与数值混杂、以及像
Package这样"bushel"(蒲式耳)、"bins"(箱)、"sacks"(袋)混合的规格列——这正是本模块刻意保留的:课程要教你把一份不规整的真实数据一步步加工成可以建模的结构。
模块结构与四节课程
按照 模块 README 中的课程目录,本模块共四节,每节都配有可运行的 notebook.ipynb、R 语言版本讲义和课后作业:
| 课程 | 主题 | 核心内容 |
|---|---|---|
| 1-Tools | 工具准备 | Python、VS Code、Jupyter Notebook、Scikit-learn 安装与首个线性回归模型 |
| 2-Data | 数据管理 | Pandas 数据清洗、蒲式耳价格归一化、Matplotlib/Seaborn 可视化 |
| 3-Linear | 线性与多项式回归 | 最小二乘法原理、相关性分析、One-hot 编码、四种模型对比 |
| 4-Logistic | 逻辑回归 | 二分类预测南瓜颜色、特征编码、混淆矩阵、ROC/AUC 评估 |
每节课后还附有 作业文件(如 Linear 课程要求自选题材复现线性/多项式回归并论证选型理由),以及 R 语言版讲义,方便不同技术栈的读者对照学习。
第一课:搭建机器学习环境并训练首个模型
回归建模的第一步是把本地开发环境配置到位。Tools 课程给出的四步准备流程是:
- 安装 Python:确保本机有 Python 环境(多数系统已自带)。由于不同工具链可能要求不同 Python 版本,建议在虚拟环境(venv)中工作,避免版本冲突;
- 安装 Visual Studio Code:本课程所有笔记本都在 VS Code 中运行,需先配置好 Python 开发支持;
- 安装 Scikit-learn:注意必须使用 Python 3,Apple M1 芯片的 Mac 有额外的安装说明;
- 安装 Jupyter Notebook:数据科学家最常用
.ipynb笔记本文件作为交互式工作环境,代码块与 Markdown 说明可以交错书写,非常适合实验性、研究性的机器学习任务。
环境就绪后,课程立刻用 Scikit-learn 内置的糖尿病数据集完成了一次最小闭环,这也是理解 Scikit-learn "load → split → fit → predict" 工作流的最佳样本。糖尿病数据集包含 442 条样本、10 个特征变量(age 年龄、bmi 体质指数、bp 平均血压、s1 T-细胞等),目标值 y 是疾病进展程度(disease progression)——这正是"给定患者指标组合,预测一个数值"的典型回归场景。
完整代码如下(源自 Tools 课程 notebook 教程):
# 1. 导入依赖
import matplotlib.pyplot as plt
import numpy as np
from sklearn import datasets, linear_model, model_selection
# 2. 加载糖尿病数据集。return_X_y=True 表示返回 (X, y) 元组:
# X 为 442x10 的特征矩阵,y 为回归目标
X, y = datasets.load_diabetes(return_X_y=True)
print(X.shape) # (442, 10)
print(X[0])
# 3. 只取第 3 列(BMI)作为特征,并 reshape 成 2D 数组供绘图/建模
X = X[:, 2]
X = X.reshape((-1, 1))
# 4. 按 67%/33% 划分训练集与测试集
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, test_size=0.33)
# 5. 训练线性回归模型:fit() 是几乎所有 ML 库中训练模型的通用入口
model = linear_model.LinearRegression()
model.fit(X_train, y_train)
# 6. 用测试集生成预测值
y_pred = model.predict(X_test)
# 7. 绘图:散点 + 回归线
plt.scatter(X_test, y_test, color='black')
plt.plot(X_test, y_pred, color='blue', linewidth=3)
plt.xlabel('Scaled BMIs')
plt.ylabel('Disease Progression')
plt.title('A Graph Plot Showing Diabetes Progression Against BMI')
plt.show()
几个值得注意的工程细节:
X.reshape((-1, 1))中的-1让 NumPy 自动推断行数,把一维特征向量变成 N×1 的二维矩阵——线性回归的输入必须是二维数组,每一行对应一条样本的特征向量;train_test_split的test_size=0.33表示 33% 数据留作验证,确保模型评估基于训练时未见过的数据;model.fit()/model.predict()构成了 Scikit-learn 估算器(estimator)API 的核心约定,后续课程中的所有模型(包括逻辑回归)都沿用这套接口。
课程同时点出了回归方法选择的第一性原则:想预测数值(例如根据年龄估计身高),用线性回归;想预测类别归属(例如判断某种菜系是否为素食),用逻辑回归。这一区分贯穿整个模块,也决定了后面两节课的走向。该课作业 assignment.md 要求改用 Scikit-learn 内置的 Linnerud 数据集(20 名中年男性的三组锻炼数据与三组生理指标),自行描述如何建模"腰围与仰卧起坐次数"的关系,是检验是否理解多目标回归的好练习。
第二课:把"混乱"的南瓜数据清洗成可建模结构
Data 课程处理的核心问题是:如何为"预测给定月份南瓜的售价"这一回归问题构造数据。打开 US-pumpkins.csv 会立刻发现原始数据的问题——空值、字符串与数值混杂、包装规格不统一。课程给出的完整清洗流水线是:
第一步:读取数据并检查缺失。
import pandas as pd
pumpkins = pd.read_csv('../data/US-pumpkins.csv')
pumpkins.head() # 查看前 5 行
pumpkins.isnull().sum() # 统计各列缺失值数量
第二步:只保留任务需要的列。 使用 loc 按行、列索引从原 DataFrame 中提取子集(: 表示所有行):
columns_to_select = ['Package', 'Low Price', 'High Price', 'Date']
pumpkins = pumpkins.loc[:, columns_to_select]
第三步:构造目标列。 用低/高报价的平均值作为单条记录的价格,并把日期(美式 MM/DD/YYYY 格式)转换为月份:
price = (pumpkins['Low Price'] + pumpkins['High Price']) / 2
month = pd.DatetimeIndex(pumpkins['Date']).month
第四步:解决"蒲式耳规格不一致"问题。 这是本课最有代表性的数据洞察:南瓜的计价单位五花八门——按 1 1/9 蒲式耳箱、1/2 蒲式耳、按个(EACH)、按箱(PER BIN)出售。原始数据中凡 Unit of Sale 为 'EACH' 或 'PER BIN' 的记录,其 Package 也相应按英寸/箱/个计价。为了统一,课程用字符串过滤只保留按蒲式耳计价的记录:
pumpkins = pumpkins[pumpkins['Package'].str.contains('bushel', case=True, regex=True)]
过滤后只剩约 415 行数据。由于蒲式耳本身是体积单位(一蒲式耳番茄约 56 磅,而菠菜只有约 20 磅),不同规格箱(1 1/9 bushel 与 1/2 bushel)对应的价格不能直接比较,需要按规格归一化为"每蒲式耳价格":
new_pumpkins = pd.DataFrame({'Month': month, 'Package': pumpkins['Package'],
'Low Price': pumpkins['Low Price'],
'High Price': pumpkins['High Price'], 'Price': price})
new_pumpkins.loc[new_pumpkins['Package'].str.contains('1 1/9'), 'Price'] = price / (1 + 1/9)
new_pumpkins.loc[new_pumpkins['Package'].str.contains('1/2'), 'Price'] = price / (1/2)
课程还留了一个分析性思考:为什么按半蒲式耳出售的南瓜(多为小型南瓜)单价明显更贵?——因为一个半蒲式耳箱里能装下的小南瓜数量远多于大南瓜,"空心"利用率更高。这一案例说明:理解数据的业务含义与清洗技术同等重要。
可视化:从 Matplotlib 到 Seaborn
清洗完成后,课程用两组工具库做探索性可视化,其目的不只是"画得好看",而是判断数据是否适合某种机器学习方法——例如散点若近似落在一条直线上,就是线性回归的良好候选信号。
Matplotlib 部分先画散点图(按月份分布的价格点),再画按月均价的分组柱状图:
import matplotlib.pyplot as plt
# 基础散点:展示性有限,只是把点铺开
plt.scatter(price, month)
plt.show()
# 按月取均值的柱状图:信息量显著更高
new_pumpkins.groupby(['Month'])['Price'].mean().plot(kind='bar')
plt.ylabel("Pumpkin Price")
柱状图揭示了一个清晰结论:南瓜均价在 9 月和 10 月达到峰值——与万圣节采购季节吻合。
Seaborn 是构建在 Matplotlib 之上、专为统计可视化设计的库,能直接接收 Pandas DataFrame 并套用美观的默认样式,代码量大幅减少:
import seaborn as sns
# 关系图(散点):直接传列名与 DataFrame
sns.relplot(x="Price", y="Month", data=new_pumpkins)
# 切换为折线图,自动带置信区间阴影
sns.relplot(x="Price", y="Month", kind="line", data=new_pumpkins)
# 分类柱状图:自动完成分组聚合
sns.catplot(x="Month", y="Price", data=new_pumpkins, kind="bar")
# 相关性热图:一次看清所有数值列两两之间的相关强度
correlations = new_pumpkins[['Month', 'Low Price', 'High Price', 'Price']].corr()
sns.heatmap(correlations, annot=True, cmap="coolwarm")
热图给出了一个反直觉但关键的教学点:Low Price 与 High Price 几乎完全相关,而 Month 与价格的线性相关却很弱——尽管柱状图明明显示出 9、10 月的季节性峰值。原因是相关系数只度量直线型关系,无法捕捉季节性这类非线性模式。因此课程强调:在决定建模特征之前,应同时查看热图与分组图,两种视角互为补充。
第三课:线性回归、多项式回归与特征工程
Linear 课程是全模块数学浓度最高的一课,围绕"预测南瓜价格随时间如何变化"训练了四个逐步改进的模型,最终把模型质量提升到 97% 的决定系数。
最小二乘回归的数学基础
线性回归要画出的是一条"最佳拟合线"(line of best fit),其数学表达为:
Y = a + bX
其中 X 是解释变量(explanatory variable),Y 是因变量(dependent variable),b 是斜率,a 是 Y 轴截距(即 X = 0 时 Y 的取值)。这条线由**最小二乘法(Least-Squares Regression)**确定:对每个数据点,测量它到回归线的垂直距离(残差 residual),然后把所有残差平方求和,寻找使这个总和最小的那条线。
残差为什么要平方?课程给出两个理由:
- 幅度优先于方向:-5 与 +5 的误差应同等对待,平方使所有值变为正数;
- 惩罚离群点:平方放大了大误差的权重,迫使直线更贴近离群较远的点。
映射回南瓜问题:"预测某月每蒲式耳南瓜价格"中,X 是价格、Y 是销售月份(课程中的建模取向)。
相关性:先找值得建模的关系
建模前先验证相关性。课程加载了预清洗好的 new_pumpkins 数据框(含 ID、Month、DayOfYear、Variety、City、Package、Low Price、High Price、Price 列),其中 DayOfYear 由如下表达式计算:
day_of_year = pd.to_datetime(pumpkins['Date']).apply(lambda dt: (dt - datetime(dt.year, 1, 1)).days)
直接计算全量数据的相关系数,结果并不理想:
print(new_pumpkins['Month'].corr(new_pumpkins['Price'])) # 约 -0.15
print(new_pumpkins['DayOfYear'].corr(new_pumpkins['Price'])) # 约 -0.17
全量数据中 Month/DayOfYear 与价格的相关性都很弱,因为不同品种的价格区间形成了明显的簇。课程用按品种着色的散点图验证了这一点:
ax = None
colors = ['red', 'blue', 'green', 'yellow']
for i, var in enumerate(new_pumpkins['Variety'].unique()):
df = new_pumpkins[new_pumpkins['Variety'] == var]
ax = df.plot.scatter('DayOfYear', 'Price', ax=ax, c=colors[i], label=var)
结论:品种对价格的影响远大于销售日期。于是聚焦到单个品种 PIE TYPE 重新计算相关性,得到约 -0.27——此时训练预测模型才有意义。线性回归对缺失值不鲁棒,所以先做 pie_pumpkins.dropna(inplace=True) 清洗(或用列均值填充)。
简单线性回归的完整训练流程
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
# 分离特征与目标,注意 reshape(-1, 1) 保证输入是 N×1 二维数组
X = pie_pumpkins['DayOfYear'].to_numpy().reshape(-1, 1)
y = pie_pumpkins['Price']
# 训练/测试集划分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
# 训练
lin_reg = LinearRegression()
lin_reg.fit(X_train, y_train)
# 系数解读:lin_reg.coef_ 约 -0.017(每天约降 2 美分)
# 截距解读:lin_reg.intercept_ 约 21(年初的基准价格)
# 用 RMSE 评估
pred = lin_reg.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, pred))
print(f'RMSE: {rmse:3.3} ({rmse/np.mean(pred)*100:3.3}%)')
# 决定系数(coefficient of determination)
score = lin_reg.score(X_train, y_train)
print('Model determination: ', score)
评估结果的解读要点:
- RMSE(均方根误差)约为 2 美元,占预测均值的 ~17%——对于"单变量线性"而言精度不够;
- 决定系数(
score)约 0.06:取值为 0 表示模型完全没用到输入信息(退化为输出均值的"最劣线性预测器"),为 1 表示完美预测。0.06 说明单靠日期解释不了多少价格波动。
多项式回归:拟合非线性关系
当变量间关系无法用直线表达时(价格显然会随季节波动),可以尝试多项式回归——它创建一条曲线来拟合非线性数据。Scikit-learn 的管道 API(pipeline)可以把"特征变换 + 回归"串成单一估算器:
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
pipeline = make_pipeline(PolynomialFeatures(2), LinearRegression())
pipeline.fit(X_train, y_train)
pred = pipeline.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, pred))
print(f'RMSE: {rmse:3.3} ({rmse/np.mean(pred)*100:3.3}%)')
score = pipeline.score(X_train, y_train)
print('Model determination: ', score)
PolynomialFeatures(2) 表示加入输入数据的全部二阶多项式:单变量时只是 DayOfYear²(得到一个带极值的抛物线);若是两个变量 X、Y,则会扩展出 X²、XY、Y²。画平滑拟合曲线时用 np.linspace 生成均匀取值的输入范围,而不是直接画无序的测试散点(否则会画出锯齿线):
X_range = np.linspace(X_test.min(), X_test.max(), 100).reshape(-1, 1)
y_range = pipeline.predict(X_range)
plt.scatter(X_test, y_test)
plt.plot(X_range, y_range)
多项式模型把 RMSE 略降到 2.73(17.0%)、决定系数提升到 0.08,改善有限——课程由此引出真正有效的方向:把更多特征放进模型。有趣的是拟合出的曲线最低点出现在万圣节前后,与"节日后需求回落"的直觉吻合。
类别特征编码:从 8% 到 97% 的关键一跃
要让同一个模型预测所有品种的价格,必须处理 Variety 这类非数值列。课程对比了两种编码方式:
- 数值编码(按品种建索引表):不推荐用于线性回归,因为回归会把索引的数值本身当作连续量乘系数,而索引序号与价格之间显然不存在线性关系;
- One-hot 编码(独热编码):把
Variety列拆成每品种一列的 0/1 矩阵,回归模型会为每个品种学到独立的系数,代表该品种的"附加价格"。
pd.get_dummies(new_pumpkins['Variety'])
只把 One-hot 化的品种作为特征训练线性回归,决定系数就跳到 ~77%。再把 Month、City、Package 等数值与类别特征用 join 合并进同一特征矩阵:
X = pd.get_dummies(new_pumpkins['Variety']) \
.join(new_pumpkins['Month']) \
.join(pd.get_dummies(new_pumpkins['City'])) \
.join(pd.get_dummies(new_pumpkins['Package']))
y = new_pumpkins['Price']
全特征线性模型的 RMSE 降至 2.84(10.5%),决定系数达 0.94。
组合拳:全特征 + 多项式回归
最终把 One-hot 类别特征、数值特征与多项式管道组合起来,完整代码见 Linear 课程:
# 训练数据(同上一节组合特征)
X = pd.get_dummies(new_pumpkins['Variety']) \
.join(new_pumpkins['Month']) \
.join(pd.get_dummies(new_pumpkins['City'])) \
.join(pd.get_dummies(new_pumpkins['Package']))
y = new_pumpkins['Price']
# 训练/测试划分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
# 搭建并训练管道
pipeline = make_pipeline(PolynomialFeatures(2), LinearRegression())
pipeline.fit(X_train, y_train)
# 预测与评估
pred = pipeline.predict(X_test)
rmse = mean_squared_error(y_test, pred, squared=False)
print(f'RMSE: {rmse:3.3} ({rmse/pred.mean()*100:3.3}%)')
score = pipeline.score(X_train, y_train)
print('Model determination: ', score)
课程给出的四个模型横向对比表,是理解"特征工程比模型复杂度更重要"的最好证据:
| 模型 | RMSE | 决定系数 |
|---|---|---|
DayOfYear 线性 |
2.77(17.2%) | 0.07 |
DayOfYear 多项式 |
2.73(17.0%) | 0.08 |
Variety 线性 |
5.24(19.7%) | 0.77 |
| 全特征线性 | 2.84(10.5%) | 0.94 |
| 全特征多项式 | 2.23(8.25%) | 0.97 |
从单变量线性到全特征多项式,模型质量从 7% 提升到 97%——其中最大的贡献来自正确选择并编码特征,而非单纯增加模型阶数。该课作业(Build a Model)要求自选数据集完整复现这套流程,并在笔记本中论证技术选型与精度结论。
第四课:逻辑回归——用"回归"做二分类
Logistic 课程回答了一个常见的命名困惑:逻辑回归虽然名字里带"回归",本质上是一种基于线性方法的分类技术(课程原文也注明"仅为语言习惯才把它归入回归章节")。它用于发现模式并预测二元类别——例如"这个南瓜是白色还是非白色"(白色南瓜因不易雕刻,俗称"ghost pumpkin")。
任务定义与数据前提
- 南瓜数据集里的
Color列存在ORANGE、WHITE,以及少量striped。因样本太少,striped在去空后自然消失,任务被简化为二分类:White or Not White; - 与线性回归"变量越相关效果越好"相反,逻辑回归不要求变量强相关,对弱相关数据也能工作;
- 但逻辑回归依赖大量干净数据,本数据集约千行数据并非理想规模,评估时需注意这一点。
数据准备只保留分类任务需要的列并去空:
columns_to_select = ['City Name', 'Package', 'Variety', 'Origin', 'Item Size', 'Color']
pumpkins = full_pumpkins.loc[:, columns_to_select]
pumpkins.dropna(inplace=True)
先用 Seaborn 的 catplot 按 Variety × Color 画计数图,直观查看品种与颜色的分布关系:
import seaborn as sns
palette = {'ORANGE': 'orange', 'WHITE': 'wheat'}
sns.catplot(
data=pumpkins, y="Variety", hue="Color", kind="count",
palette=palette,
)
特征与标签编码
机器学习算法只吃数字,字符串类别必须编码。课程演示了两种编码器的分工:
- 有序编码器(OrdinalEncoder):适合存在自然顺序的变量,如
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)
- 独热编码器(OneHotEncoder):适合无自然顺序的名义变量(城市、包装、品种、产地)。每个类别变成一个 0/1 二元列:
from sklearn.preprocessing import OneHotEncoder
categorical_features = ['City Name', 'Package', 'Variety', 'Origin']
categorical_encoder = OneHotEncoder(sparse_output=False)
- 用 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)
- 标签用 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_features.assign(Color=encoded_label)
编码后还可以用 Seaborn 的箱线图(kind="box")按品种分行比较 Item Size 与 Color 的分布,或用 swarmplot 观察二值类别的散点分布(数据点过多时需调小 size 参数以保证可读性)。
Sigmoid:逻辑回归的数学核心
🧮 逻辑回归基于**最大似然(maximum likelihood)**思想,核心是 Sigmoid 函数——一条 S 形曲线,把任意实数映射到 0 与 1 之间。若函数输出大于 0.5,该样本被判为类别 '1',否则为 '0'。这就是"线性组合 + 非线性映射"如何产出一个概率化分类边界的完整逻辑。
训练模型与评估指标
建模过程沿用了第一节课建立的 Scikit-learn 范式,只是把 LinearRegression 换成 LogisticRegression:
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score, classification_report
from sklearn.linear_model import LogisticRegression
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)
model = LogisticRegression()
model.fit(X_train, y_train)
predictions = model.predict(X_test)
print(classification_report(y_test, predictions))
print('F1-score: ', f1_score(y_test, predictions))
课程给出的实际运行结果(约 200 条测试样本):
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(非白色)166 条,1(白色)仅 33 条,因此白色类别的召回率只有 0.67。这正是引入混淆矩阵的动机:
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 |
由此可以精确复现分类报告中的指标,理解每个数字的来源:
- Precision = TP / (TP + FP) = 22 / (22 + 4) ≈ 0.846:模型判为白色的样本里,真正是白色的比例;
- Recall = TP / (TP + FN) = 22 / (22 + 11) ≈ 0.667:真实白色南瓜被找出来的比例;
- F1-score = 2 × precision × recall / (precision + recall):两者的调和平均,最好为 1、最劣为 0;
- Support:每个标签在测试集中的真实样本数;
- Accuracy = (TP + TN) / (TP + TN + FP + FN):整体判对比例(此处 0.92,但被多数类抬高了,不能单独作为依据);
- Macro Avg:各标签指标的不加权平均,忽略类别不平衡;Weighted Avg:按各标签 support 加权的平均,反映不平衡的真实影响。
课程留了一个关键思考题:如果业务上希望减少漏报(false negative,比如漏检白色南瓜),应该盯住哪个指标?答案是 Recall——这体现了"选评估指标取决于业务代价"的通用原则。
ROC 曲线与 AUC
最后一步用 ROC 曲线(Receiving 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()
再用 roc_auc_score 计算曲线下面积:
auc = roc_auc_score(y_test, y_scores[:, 1])
print(auc) # 0.9749908725812341
AUC 取值 0~1,越接近 1 越好(100% 正确的模型 AUC 为 1)。0.975 的 AUC 说明在约千行小数据集上,这个逻辑回归模型已经"相当不错"——尽管混淆矩阵显示白色类别召回偏低,两者并不矛盾:AUC 度量的是排序能力(把正例排在负例前面的概率),而分类报告度量的是固定 0.5 阈值下的硬分类表现。本课作业(Retrying this regression)要求回到完整南瓜数据(清洗与标准化后)重建逻辑回归模型,正是检验能否把"数据规模"这一变量控制起来的练习。
模块要点总结与学习路径
回顾 回归模块 的完整脉络,可以提炼出四条可迁移的建模经验:
- 环境与范式先行:Python + 虚拟环境 + VS Code + Jupyter 笔记本 + Scikit-learn 的组合,配合
fit/predict/score的统一估算器 API,是传统机器学习(classic ML)的标准工作流;本模块刻意回避神经网络与深度学习(课程说明这部分内容留给 AI for Beginners 课程线); - 数据质量决定模型上限:蒲式耳归一化、按品种聚焦、去空值、One-hot 与有序编码——南瓜案例中每次数据处理的改进都直接体现在决定系数从 0.06 到 0.97 的跃升上;
- 先看图再建模:散点判断线性趋势、柱状图发现季节性、热图度量线性相关强度——三种视图互补,相关系数捕捉不到非线性模式;
- 按问题类型选模型:预测连续数值用线性/多项式回归(目标:最小化误差,看 RMSE 与决定系数);预测类别归属用逻辑回归(本质是分类,看混淆矩阵、Precision/Recall、F1 与 ROC/AUC,并警惕类别不平衡)。
模块所有代码都可以在各课程目录下的 notebook.ipynb 中直接打开运行(例如 Tools 笔记本、Data 笔记本、Linear 笔记本、Logistic 笔记本),需要 R 语言对照的读者可查阅各课 solution/R 目录下的 .Rmd 讲义(如 lesson_3.Rmd)。完成四节课与对应作业后,你已经具备了进入下一阶段——分类(Classification)模块——的全部前置能力。
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 StartedRust0624
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

