WARNING
🧪 Beta公测版本提示:教程主体已完成,正在优化细节,欢迎大家提Issue反馈问题或建议。
s03 逻辑回归 — demo.py 代码详解
运行方式
cd docs/ml/foundations/logistic-regression/code
python demo.py代码逐段详解
第1步:导入库 — 每个库是做什么的
import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression as SklearnLRos:操作文件路径,创建images/目录numpy:数值计算核心。关键用法:np.exp()指数函数(Sigmoid 和 Softmax 的核心),np.clip()裁剪数值范围防止溢出,np.log()对数函数(交叉熵损失),np.argmax()取最大值索引,np.unique()统计类别数matplotlib:绘图,包括函数曲线、散点图、决策边界热力图、等高线图load_iris(sklearn):加载经典的 Iris(鸢尾花)数据集——150 个样本,4 个特征,3 个类别train_test_split(sklearn):按比例(80/20)随机划分训练集和测试集sklearn.linear_model.LogisticRegression:sklearn 的实现,用作基准对比
第2步:Sigmoid 函数 — 从实数到概率
def sigmoid(z: np.ndarray) -> np.ndarray:
z_clipped = np.clip(z, -500, 500)
return 1.0 / (1.0 + np.exp(-z_clipped))Sigmoid 函数的数学形式:
Sigmoid 的核心性质:
- 值域:
,天然适合解释为概率。当 时 ,当 时 - 对称性:
,得分取反,概率互补 - 导数:
,可以用自身表达——这让反向传播计算异常简便 - 中心点:
,对应决策边界
数值稳定技巧:np.clip(z, -500, 500) 将
第3步:Softmax 函数 — 从得分到概率分布
def softmax(z: np.ndarray) -> np.ndarray:
z_stable = z - np.max(z, axis=1, keepdims=True)
exp_z = np.exp(z_stable)
return exp_z / np.sum(exp_z, axis=1, keepdims=True)Softmax 将
三个关键性质:
- 非负性:每个输出
- 归一性:所有输出之和
(构成一个合法的概率分布) - 保序性:如果
,则
数值稳定技巧:z - np.max(z, axis=1, keepdims=True) 在每行(每个样本)减去该行的最大值。这等价于分子分母同除 axis=1 表示沿列方向(对每个样本的所有类别做 max),keepdims=True 保持维度以便广播。
第4步:二分类逻辑回归模型
4.1 模型定义
逻辑回归 = 线性模型 + Sigmoid:
这个值被解释为"给定输入
预测概率 _predict_proba():
def _predict_proba(self, X):
z = X @ self.w + self.b # 线性组合 (n,)
return sigmoid(z) # 通过 Sigmoid 得到概率 (n,)X @ self.w 是矩阵乘法
类别预测 predict():
def predict(self, X, threshold=0.5):
proba = self._predict_proba(X)
return (proba >= threshold).astype(int)当预测概率
4.2 损失函数:二元交叉熵
为什么不继续用 MSE?两个致命问题:
- Sigmoid 的饱和特性导致梯度消失(当预测值接近 0 或 1 且标签相反时,MSE 的梯度极小)
- MSE + Sigmoid 在参数空间中是非凸的,存在多个局部最小值
解决方案是二元交叉熵(Binary Cross-Entropy):
这个公式设计得非常精妙:当
代码实现:
def _compute_loss(self, X, y):
y_pred = self._predict_proba(X)
n = len(y)
eps = 1e-15
y_pred = np.clip(y_pred, eps, 1 - eps) # 防止 log(0)
loss = -(1.0 / n) * np.sum(
y * np.log(y_pred) + (1 - y) * np.log(1 - y_pred)
)
return lossnp.clip(y_pred, eps, 1 - eps) 将预测概率限制在
4.3 梯度:数学之美的巅峰
Sigmoid + 交叉熵组合的梯度有一个惊人简洁的形式。经过数学推导(链式法则 + Sigmoid 导数性质):
这被称为"黄金组合"——损失对原始得分
代码实现:
def _compute_gradients(self, X, y):
y_pred = self._predict_proba(X)
n = len(y)
errors = y_pred - y # (n,) 预测误差
dw = (1.0 / n) * (X.T @ errors) # (d,) 权重梯度
db = (1.0 / n) * np.sum(errors) # 标量 偏置梯度
return dw, dbX.T @ errors 是矩阵乘法:
4.4 训练循环
def fit(self, X, y, verbose=True):
self.w = np.random.randn(n_features) * 0.01
self.b = 0.0
for epoch in range(self.max_epochs):
loss = self._compute_loss(X, y) # 前向 + 损失
dw, db = self._compute_gradients(X, y) # 反向传播
self.w -= self.learning_rate * dw # 参数更新
self.b -= self.learning_rate * db与线性回归的训练循环结构完全相同——这揭示了机器学习的一个统一框架:
- 前向传播:计算
- 计算损失:
- 反向传播:求
- 更新参数:
不同模型只是换用了不同的
第5步:多分类 Softmax 回归
二分类
- 权重:从向量
变为矩阵 ,每个类别有自己的权重向量 - 偏置:从标量
变为向量 ,每个类别有自己的偏置 - 激活:从 Sigmoid 变为 Softmax
- 损失:从二元交叉熵变为多分类交叉熵
多分类交叉熵损失:
由于真实标签是 one-hot 编码(只有正确类别的位置为 1,其余为 0),实际计算时只需取正确类别位置的
def _compute_loss(self, X, y):
proba = self._predict_proba(X) # (n, K)
proba = np.clip(proba, eps, 1 - eps)
loss = -(1.0 / n) * np.sum(np.log(proba[np.arange(n), y]))
return lossproba[np.arange(n), y] 是高级索引(fancy indexing):对每个样本
One-hot 编码在梯度计算中扮演关键角色:
y_onehot = np.zeros((n, self.n_classes))
y_onehot[np.arange(n), y] = 1
errors = proba - y_onehoty_onehot 中,每行只有一个位置为 1(真实类别),其余为 0。errors = proba - y_onehot 的结果是:正确类别位置为
第6步:可视化
6.1 Sigmoid 函数曲线
在
- 红色虚线标注
和 ——这是决策边界的位置 - 灰色虚线标注渐近线
和 ——Sigmoid 的值域下界和上界 - 绿色区域标注
的"正类区域",红色区域标注 的"负类区域"
6.2 决策边界与概率热力图
这是逻辑回归最直观的可视化。代码使用 np.meshgrid() 在二维平面上创建 contourf() 填充颜色:
- 蓝色区域:模型输出低概率,倾向于预测负类
- 红色区域:模型输出高概率,倾向于预测正类
- 绿色轮廓线:
的等概率线,即决策边界
颜色渐变展示了模型的"置信度"——远离决策边界的区域颜色更深(更确信),决策边界附近的区域颜色较浅(不确定)。
第7步:模型评估 — 混淆矩阵
对于分类问题,仅看准确率可能不够。代码计算了**混淆矩阵(Confusion Matrix)**的四个元素:
| 预测正类 (1) | 预测负类 (0) | |
|---|---|---|
| 真实正类 (1) | TP (真正例) | FN (假负例) |
| 真实负类 (0) | FP (假正例) | TN (真负例) |
- 准确率:
,所有预测中正确的比例 - 从混淆矩阵可以进一步计算精确率
、召回率 、F1 分数等更细致的指标
关键概念速查表
| 概念 | 数学形式 | 代码位置 | 关键说明 |
|---|---|---|---|
| Sigmoid | sigmoid() | 实数→(0,1)概率映射 | |
| Softmax | softmax() | 得分→概率分布 | |
| 交叉熵 (二元) | _compute_loss() | 分类问题的标准损失 | |
| 黄金梯度 | _compute_gradients() | Sigmoid导数被约掉 | |
| 决策边界 | 可视化 | ||
| One-hot 编码 | 正确类别=1,其余=0 | _compute_gradients() | 多分类梯度的关键 |
| 混淆矩阵 | TP/TN/FP/FN | main() | 分类问题精细化评估 |
源码位置
clone 后打开(相对仓库根目录):
docs/ml/foundations/logistic-regression/code/demo.py