1.逻辑回归回顾
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# -*- encoding:utf-8 -*-
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_predict
from numpy import shape
from sklearn import metrics
from sklearn.metrics import log_loss
import numpy as np
def read_data(path):
with open(path) as f :
lines=f.readlines()
lines=[eval(line.strip()) for line in lines]
X,y=zip(*lines)
X=np.array(X)
y=np.array(y)
return X,y
def curve(x_train,w,w0):
results=x_train.tolist()
for i in range(0,100):
x1=1.0*i/10
x2=-1*(w[0]*x1+w0)/w[1]
results.append([x1,x2])
results=["{},{}".format(x1,x2) for [x1,x2] in results]
return results
X_train,y_train=read_data("train_data")
X_test,y_test=read_data("test_data")
model = LogisticRegression()
model.fit(X_train, y_train)
print("w",model.coef_)
print("w0",model.intercept_)
y_pred = model.predict(X_test)
print(y_pred)
# y_pred=model.predict_proba(X_test)
# print(y_pred)
#loss=log_loss(y_test,y_pred)
#print ("KL_loss:",loss)
#loss=log_loss(y_pred,y_test)
#print ("KL_loss:",loss)
'''
curve_results=curve(X_train,model.coef_.tolist()[0],model.intercept_.tolist()[0])
with open("train_with_splitline","w") as f :
f.writelines("\n".join(curve_results))
'''

predict_proba是用来预测概率的。如果解开下面2行注释:
1
2
y_pred=model.predict_proba(X_test)
print(y_pred)
可以看到:

可以看到,应该是大于0.5就会有预测结果0,小于0.5预测结果就是1.
且每一行求和其实是1,因为是/非 是互斥的事件。
计算KL距离(即损失函数)的函数是log_loss:

注意:model.fit(X_train, y_train)就是模型不断学习的过程。即学习合适的参数\(w\)。
2.逻辑回归的损失函数BCE求导与梯度下降
二元逻辑回归(Binary Logistic Regression)的损失函数——二元交叉熵损失 (Binary Cross-Entropy Loss, BCE Loss) :
\[\mathcal{L}_{\text{BCE}}(P || Q) = - \sum_{k \in \{0, 1\}} P(Y=k) \log Q(Y=k) = - [y \log \hat{y} + (1 - y) \log (1 - \hat{y})]=\\ \sum_{i=1}^{n} \left[ y_i \log \frac{y_i}{f_i} + (1 - y_i) \log \frac{1 - y_i}{1 - f_i} \right]\]逻辑回归模型 (Hypothesis):
\[\hat{y}_i = \sigma(z_i) = \frac{1}{1 + e^{-z_i}}\]其中 \(z_i\) 是线性得分:
\[z_i = \mathbf{w}^T \mathbf{x}_i + b\](这里我们把偏置 \(b\) 视为 \(\mathbf{w}\) 中的 \(w_0\) 且 \(\mathbf{x}_i\) 扩展了 \(x_{i,0}\)=1维,简化为 \(\mathbf{w}^T \mathbf{x}_i\)。)
2.1求导
| 目标: 求损失函数 $$\mathcal{L}_{\text{BCE}}(P | Q)\(对权重向量\)\mathbf{w}\(中任一分量\)w_j\(的偏导数\)\frac{\partial L_i}{\partial w_j}$$。 |
使用链式法则,从 \(L_i\) 逐步向 \(w_j\) 追溯:
\[\frac{\partial L_i}{\partial w_j} = \frac{\partial L_i}{\partial \hat{y}_i} \cdot \frac{\partial \hat{y}_i}{\partial z_i} \cdot \frac{\partial z_i}{\partial w_j}\]【步骤 A】: \(\frac{\partial L_i}{\partial \hat{y}_i}\) (损失对预测概率的导数)
\[\frac{\partial L_i}{\partial \hat{y}_i} = - \left[ y_i \cdot \frac{1}{\hat{y}_i} + (1 - y_i) \cdot \frac{1}{1 - \hat{y}_i} \cdot (-1) \right]\] \[\frac{\partial L_i}{\partial \hat{y}_i} = - \left[ \frac{y_i}{\hat{y}_i} - \frac{1 - y_i}{1 - \hat{y}_i} \right]\] \[\frac{\partial L_i}{\partial \hat{y}_i} = - \left[ \frac{y_i (1 - \hat{y}_i) - \hat{y}_i (1 - y_i)}{\hat{y}_i (1 - \hat{y}_i)} \right]\] \[\frac{\partial L_i}{\partial \hat{y}_i} = - \left[ \frac{y_i - y_i \hat{y}_i - \hat{y}_i + y_i \hat{y}_i}{\hat{y}_i (1 - \hat{y}_i)} \right] = - \frac{y_i - \hat{y}_i}{\hat{y}_i (1 - \hat{y}_i)}\]【步骤 B】: \(\frac{\partial \hat{y}_i}{\partial z_i}\) (Sigmoid 函数对线性得分的导数)
Sigmoid 函数 \(\sigma(z) = \frac{1}{1 + e^{-z}}\) 的导数有一个非常简洁的形式:
\[\frac{\partial \sigma(z_i)}{\partial z_i} = \sigma(z_i) (1 - \sigma(z_i)) = \hat{y}_i (1 - \hat{y}_i)\]【步骤 C】: \(\frac{\partial z_i}{\partial w_j}\) (线性得分对权重的导数)
\[z_i = w_0 x_{i,0} + w_1 x_{i,1} + \dots + w_j x_{i,j} + \dots\] \[\frac{\partial z_i}{\partial w_j} = \frac{\partial}{\partial w_j} (\mathbf{w}^T \mathbf{x}_i) = x_{i,j}\]【步骤 D】: 合并结果 (最终梯度)
将 A、B、C 三个结果相乘:
\[\frac{\partial L_i}{\partial w_j} = \left[ - \frac{y_i - \hat{y}_i}{\hat{y}_i (1 - \hat{y}_i)} \right] \cdot \left[ \hat{y}_i (1 - \hat{y}_i) \right] \cdot \left[ x_{i,j} \right]\]观察到中间两项相乘可以抵消:
\[\frac{\partial L_i}{\partial w_j} = - (y_i - \hat{y}_i) x_{i,j} = (\hat{y}_i - y_i) x_{i,j}\]所以总体损失梯度如下:
对于整个训练集(所有 \(N\) 个样本),总损失 \(L(\mathbf{w}) = \frac{1}{N} \sum_{i=1}^{N} L_i(\mathbf{w})\) 对 \(w_j\) 的梯度为:
\[\frac{\partial L}{\partial w_j} = \frac{1}{N} \sum_{i=1}^{N} \frac{\partial L_i}{\partial w_j} = \frac{1}{N} \sum_{i=1}^{N} (\hat{y}_i - y_i) x_{i,j}\]2.2梯度下降逼近最优解
得到梯度后,就可以使用梯度下降法来迭代更新权重 \(\mathbf{w}\),以最小化总体损失 \(L(\mathbf{w})\)。
在每次迭代中,权重 \(\mathbf{w}\) 会沿着梯度的负方向进行更新。
-
更新公式:
\[\mathbf{w}_{\text{new}} = \mathbf{w}_{\text{old}} - \alpha \nabla_{\mathbf{w}} L(\mathbf{w}_{\text{old}})\] -
对单个权重 \(w_j\) 的更新:
\[w_{j, \text{new}} = w_{j, \text{old}} - \alpha \cdot \frac{1}{N} \sum_{i=1}^{N} (\hat{y}_i - y_i) x_{i,j}\]
其中 \(\alpha\) 是学习率 (Learning Rate),它控制了每一步更新的步长。
整个迭代过程如下:
-
初始化: 随机初始化权重向量 \(\mathbf{w}\)。
-
迭代循环 (直到收敛):
a. 计算预测值: 对于所有样本 \(i=1\) 到 \(N\),计算线性得分 \(z_i = \mathbf{w}^T \mathbf{x}_i\),并计算预测概率 \(\hat{y}_i = \sigma(z_i)\)。
b. 计算梯度: 计算每个权重 \(w_j\) 的平均梯度 \(\frac{\partial L}{\partial w_j} = \frac{1}{N} \sum_{i=1}^{N} (\hat{y}_i - y_i) x_{i,j}\)。
c. 更新权重: 使用更新公式 \(w_{j, \text{new}} = w_{j, \text{old}} - \alpha \cdot \frac{\partial L}{\partial w_j}\) 更新所有权重。
-
收敛: 当权重向量 \(\mathbf{w}\) 在后续迭代中的变化小于一个预设的阈值,或者达到最大迭代次数时,停止迭代。
这个梯度 \(\frac{1}{N} \sum_{i=1}^{N} (\hat{y}_i - y_i) x_{i,j}\) 直观地表示了“平均预测误差”乘以“输入特征”,模型会根据这个误差调整权重,使预测概率 \(\hat{y}_i\) 更接近真实标签 \(y_i\)。
3.逻辑回归不用MSE而用BCE
MSE均方误差:\(L_{MSE}=\frac{1}{N}\sum_{i=1}^N(f_i-y_i)^2\)
| BCE二元交叉熵损失:$$\mathcal{L}_{\text{BCE}}(P | Q) = - \sum_{k \in {0, 1}} P(Y=k) \log Q(Y=k) = - [y \log \hat{y} + (1 - y) \log (1 - \hat{y})]=\ \sum_{i=1}^{n} \left[ y_i \log \frac{y_i}{f_i} + (1 - y_i) \log \frac{1 - y_i}{1 - f_i} \right]$$ |
MSE求导:对于逻辑回归,模型预测为 \(\hat{y}_i = \sigma(z_i)\),代入 MSE 损失公式并对权重 \(w_j\) 求导:
\[L_{\text{MSE}}=\frac{1}{N}\sum_{i=1}^N(\hat{y}_i-y_i)^2\] \[\frac{\partial L_{\text{MSE}}}{\partial w_j} = \frac{2}{N} \sum_{i} (\hat{y}_i - y_i) \cdot \underbrace{\hat{y}_i (1 - \hat{y}_i)}_{\text{Sigmoid 导数}} \cdot x_{i,j}\]而BCE求导上面已经求过了:
\[\frac{\partial L_i}{\partial w_j} = - (y_i - \hat{y}_i) x_{i,j} = (\hat{y}_i - y_i) x_{i,j}\]为什么逻辑回归不用MSE而用BCE?
【1】若用MSE,如果初始权重 \(\mathbf{w}\) 被设置得非常大(在绝对值意义上),那么对于大多数样本 \(\mathbf{x}_i\):
\[|z_i| = |\mathbf{w}^T \mathbf{x}_i + b|\]\(z_i\) 的绝对值也会变得非常大。也就是说,当权重很大时,即使特征值 \(x_{i,j}\) 变化很小,也会导致得分 \(z_i\) 发生巨大的变化。
补充:Sigmoid 函数 \(\sigma(z) = \frac{1}{1 + e^{-z}}\) 具有以下特性:
\(z_i\) 的值 \(y_i=\frac{1}{1 + e^{-z_i}}\) 的值 \(y_i\) 接近 非常大的正数 (\(\gg 0\)) 趋近于 $\frac{1}{1 + 0}$ 1 非常大的负数 (\(\ll 0\)) 趋近于 $\frac{1}{1 + \infty}$ 0 接近 0 趋近于 $\frac{1}{1 + 1} = 0.5$ 0.5
| 由于初始权重 \(w\) 很大,使得大多数 \(z_i\) 的绝对值 $$ | z_i | $$ 非常大: |
- 如果 \(z_i\) 是一个非常大的正数(例如,100),则 \(\hat{y}_i \approx 1\)。
- 如果 \(z_i\) 是一个非常大的负数(例如,-100),则 \(\hat{y}_i \approx 0\)。
因此,当初始权重非常大时,模型对大部分样本的预测会非常自信地给出接近 0 或接近 1 的概率。
当 \(\hat{y}_i\) 非常接近 0 或 1 时,Sigmoid 导数项 \(\hat{y}_i (1 - \hat{y}_i)\) 的值会变得极小
即使模型预测错误(即 \((\hat{y}_i - y_i)\) 不为 0),由于 \(\hat{y}_i (1 - \hat{y}_i)\) 接近 0,整个梯度也会趋于 0。这就是梯度消失问题。
即:若初始化W非常大,此时还没有正确分类,模型就已经不太能够学到东西了。
而且其实就算不考虑极端的情况,本身\(f_i\)就很小,范围就是0到1,根据\(L_{MSE}== \frac{2}{N} \sum_{i} (\hat{y}_i - y_i) \cdot \underbrace{\hat{y}_i (1 - \hat{y}_i)}_{\text{Sigmoid 导数}} \cdot x_{i,j}\),即\({\hat{y}_i (1 - \hat{y}_i)}\)本身就很小,梯度更新本身就很小了。

【2】逻辑回归若用MSE,其曲线并不是一个凸函数,可能存在多个局部极小值的点,即非凸的损失曲面意味着它可能存在多个“小山谷”或“小坑”,梯度下降算法可能会收敛到一个局部极小值点,这个点的损失值大于全局最小值。此时就无法找到损失值最小的最佳模型了。
问:多选几个初始点w能不能避免 存在多个局部极小值的问题呢?
其实在机器学习的前沿领域,很多研究都是在讨论如何选初始点,这个策略叫做 Multi-start Optimization(多点启动优化)。但是达不到很好的效果。
而且维度如果很大,鞍点(Saddle Points)的数量急剧增加,成为主要的优化障碍。鞍点在某些方向是最小值,但在其他方向是最大值。SGD 可能会在鞍点附近停滞,因为所有方向的梯度都接近零,这比局部极小值更难逃逸。
而且在高维空间中,很多“足够好”的局部极小值点的损失值非常接近全局最优解。研究发现,许多局部极小值在泛化能力(即在测试集上的性能)上与全局最优解几乎没有区别。
在现代 ML 中,初始化研究的意义在于确保训练过程稳定、高效,并引导模型找到泛化能力强的局部最优解,而不是徒劳地去寻找理论上存在的、但在高维空间中难以捕捉的绝对全局最优解。
4.多分类任务-OVR
为每一个类别训练一个二元分类器,叫做One-vs-Rest (OvR)或者One-vs-All (OvA)。具体逻辑如下:
假设有 \(N\) 个类别,OvR 方法会训练 \(N\) 个独立的逻辑回归分类器:
-
训练 \(N\) 个分类器:
- 为每个类别 \(k \in \{1, 2, \ldots, N\}\) 训练一个二元分类器 \(C_k\)。
- 目标: \(C_k\) 的任务是判断一个样本是否属于类别 \(k\),或者说,输出一个概率。
-
构造数据集:
- 在训练 \(C_k\) 时,所有属于类别 $k$ 的样本被标记为正类(Positive, \(y=1\))。
- 所有不属于类别 \(k\) 的样本(即剩下的 \(N-1\) 个类别的样本)都被标记为负类(Negative, \(y=0\))。
-
预测:
- 当一个新样本 \(\mathbf{x}\) 输入时,它会被馈送到所有的 \(N\) 个分类器 \(C_1, C_2, \ldots, C_N\) 中。
- 每个分类器 \(C_k\) 都会输出一个概率 \(P(\mathbf{x} \in \text{Class } k)\)。
-
最终决策:
-
样本 \(\mathbf{x}\) 被最终分配给 输出概率最高的那个类别。
\[\text{Class}(\mathbf{x}) = \underset{k}{\operatorname{argmax}} \left( P(\mathbf{x} \in \text{Class } k) \right)\]
-
OvR概念简单,易于并行化;可以使用任何二元分类器(如 SVM、决策树等)。
这种方法适合工程,具有一定的开闭原则。
开闭原则是面向对象设计(OOD)的五大原则之一,它要求:
“软件实体(类、模块、函数等)应该是对扩展开放的,对修改封闭的。”
这意味着:当需要添加新功能(例如,增加一个新类别)时,应该通过扩展现有代码来实现,而不是修改已经稳定运行的代码。
OvR对扩展开放是因为:如果需要从 10 个类别增加到 11 个类别(比如新增一个“短裤”类别),只需要训练和部署第 11 个独立的二元分类器 \(C_{11}\)。你不需要触碰或重新训练那 10 个已经存在的、稳定运行的分类器 \(C_1\) 到 \(C_{10}\)。
而如果用softmax回归,必须修改模型的输出层(从 10 维改为 11 维),并且必须使用所有数据从头重新训练整个模型,因为所有权重都相互关联。
因此,在需要频繁增加新类别、追求模块化和服务高可用性的工程实践中,OvR 策略(尤其是使用轻量级模型时)确实展现出更高的工程价值和更好的可扩展性。
举例:更加形象的理解

对于上面的多分类任务,可以用下面的方式解决:
第一:三角形的分类器

第二:叉叉的分类器

第三:正方形的分类器

5.多分类任务-softmax回归

Softmax 回归可以看作是逻辑回归的推广(从二分类推广到多分类)。
5.1原理
Softmax 回归是假设多项分布的,多项分布可以理解为二项分布的扩展。投硬币是二项分布,掷骰子是多项分布。
多分类任务中,\(y\) 有多个可能的分类:\(y \in \{1, 2, 3, \ldots, k\}\),
每种分类对应的概率:\(\phi_1, \phi_2, \ldots, \phi_k\)。由于 \(\sum_{i=1}^{k} \phi_i = 1\),所以一般用 \(k-1\) 个参数 \(\phi_1, \phi_2, \ldots, \phi_{k-1}\)。其中:
- \[p(y = i; \phi) = \phi_i\]
- \[p(y = k; \phi) = 1 - \sum_{i=1}^{k-1} \phi_i\]
为了将多项分布表达为指数族分布,做以下工作:
- 定义 \(T(y) \in \mathbb{R}^{k-1}\) 它不再是一个数而是一个变量
-
引入指示函数:\(\mathbb{I}\{True\} = 1, \mathbb{I}\{False\} = 0\)
$E(T(y)_i) = p(y = i) = \phi_i$
为什么要将多项分布表达为指数族分布?
指数族分布 (Exponential Family Distribution) 是统计学中一类重要的概率分布家族(包括正态分布、伯努利分布、泊松分布、伽马分布等)。
要注意,学习过的线性回归中,变量服从高斯分布(正态分布),它属于指数族。逻辑回归变量服从伯努利分布,它也属于指数族。
现在对于多分类问题,它的输出是 \(k\) 种可能中的一种,服从多项分布。
指数族分布是广义线性模型(GLM) 这个“模型大厦”的基石。 GLM 提供了一个统一的建模流程:
- 线性部分(熟悉的 \(\theta^T x\)):所有模型都用它来计算“分数”。
- 连接函数(把分数转成概率或输出):这个函数是根据分布的性质自动推导出来的。
- 线性回归:连接函数是“恒等”(分数就是输出)。
- 逻辑回归:连接函数是 Sigmoid 函数。
- Softmax 回归:连接函数是 Softmax 函数。
通过这种统一,不需要为 Softmax 回归设计一套全新的理论,只是在 GLM 框架下,将随机分量从伯努利(二元)换成了多项(多分类)。
另外,在线性回归中用 MSE,在逻辑回归中用 BCE。这两种损失函数其实都是最大似然估计(MLE) 的结果。
- MSE 是高斯分布下 MLE 的结果。
- BCE 是伯努利分布下 MLE 的结果。
指数族分布有一个极好的数学性质:当用 MLE 的方法来构建损失函数时,这个损失函数(即对数似然函数)通常是凸函数(或凹函数)。这保证了在训练 Softmax 模型时,使用梯度下降等优化算法能够稳定、快速地找到最佳的模型参数,而不用担心陷入局部最优解。
所有指数族分布都可以写成统一的规范形式:
\[p(y; \eta) = b(y) \exp(\eta^T T(y) - a(\eta))\]其中,\(\eta\) 是自然参数(或规范参数),\(T(y)\) 是充分统计量,\(a(\eta)\) 是对数配分函数(用于确保概率之和为 1)。
一旦将多项分布(或任何其他分布)写成这种形式,就可以利用指数族分布的通用性质来推导其连接函数(link function)、均值和方差等,无需为每种分布从头开始推导。
解释:
【1】自然参数是什么?在所有 GLM 模型中,自然参数 \(\eta\) 总是由输入特征的线性组合得到的:
\[\eta = \theta^T x\]可以把它理解为数据特征 \(x\) 经过线性组合后得到的“原始分数”,这个分数直接决定了数据服从的概率分布的形状。
| 模型 | \(η=θ^Tx\) 的含义 | 原始参数 (μ 或 ϕ) |
|---|---|---|
| 线性回归 | 期望本身。 \(\eta\) 直接等于我们想要预测的连续值 \(\mu\)(假设 \(\sigma^2=1\))。 | 均值 \(\mu\) |
| 逻辑回归 | Log-Odds。 \(\eta\) 是用来衡量 \(\frac{P(y=1)}{P(y=0)}\) 这个比率的对数。 | 概率 \(\phi\) |
| Softmax 回归 | Log-Odds Ratio。 \(\eta_i\) 是衡量第 \(i\) 类相对于基准类 \(k\) 的对数几率比。\(\eta = \begin{bmatrix} \log(\phi_1/\phi_k) \\ \log(\phi_2/\phi_k) \\ \vdots \\ \log(\phi_{k-1}/\phi_k) \end{bmatrix}\) | 概率向量 \(\phi\) |
【2】什么是响应函数 (\(h(\cdot)\))?
将模型的原始分数 (\(\eta\)),转化为我们真正想预测和解释的概率、均值或计数(\(\mu\))。
\[\mu = h(\eta)\]| 模型 | 自然参数 η | 响应函数 μ=h(η) | 最终输出 μ |
|---|---|---|---|
| 线性回归 | \(\eta = \theta^T x\) | \(\mu = \eta\) (恒等函数) | 均值 \(\mu\) |
| 逻辑回归 | \(\eta = \text{Logit}(\phi)\) | \(\mu = \frac{e^\eta}{1+e^\eta}\) (Sigmoid 函数) | 概率 \(\phi\) |
| Softmax 回归 | \(\eta_i = \text{Log-Odds Ratio}\) | \(\mu_i = \frac{e^{\eta_i}}{\sum e^{\eta_j}}\) (Softmax 函数) | 概率 \(\phi_i\) |
简单来说,如果把 \(\theta^T x\) 看作原始的“火力值”,响应函数 \(h(\cdot)\) 就是将这个火力值转化为实际命中目标的概率(逻辑/Softmax 回归),或者实际的数值(线性回归)。
指数族分布
所有指数族分布都可以写成统一的规范形式:
\[p(y; \eta) = b(y) \exp(\eta^T T(y) - a(\eta))\]其中,\(b(y)\) 被称为残余项(Base Measure Term)或基测度,\(\eta\) 是自然参数(或规范参数),是一个只依赖于原分布参数(如 \(\mu\) 和 \(\sigma^2\))的函数,它不依赖于数据 $y$。\(T(y)\) 是充分统计量,只依赖于数据 \(y\)。\(a(\eta)\) 是对数配分函数(用于确保概率之和为 1)。在广义线性模型 (GLM) 中,主要通过最大似然估计(MLE) 来学习参数 \(\theta\)(它隐藏在 \(\eta\) 中)。
\(b(y)\) 一般包含原始概率分布函数(PDF 或 PMF)中所有与 \(y\) 相关但与模型参数 \(\eta\) 无关的项。
- 对于连续分布(如高斯分布),\(b(y)\) 通常包含像 \(\frac{1}{\sqrt{2\pi}}\) 或 \(\exp(-\frac{y^2}{2\sigma^2})\) 这样的因子。
- 对于某些离散分布(如伯努利分布),\(b(y)\) 可能只等于 \(1\)。对于泊松分布,它可能包含 \(\frac{1}{y!}\)。
以线性回归遵循的高斯分布为例,通常假设响应变量 \(y\) 服从均值为 \(\mu\)、方差为 \(\sigma^2\) 的高斯分布,即 \(y \sim N(\mu, \sigma^2)\)。为了简化,在 GLM 的推导中,我们通常假设 \(\sigma^2\) 是固定的常数(或为 1)。
高斯分布的概率密度函数 (PDF) 为:
\[p(y; \mu, \sigma^2) = \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{(y - \mu)^2}{2\sigma^2}\right)\]对上式进行代数展开,并将 \(\mu\) 作为参数:
\[\begin{aligned} p(y; \mu) &= \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{1}{2\sigma^2}(y^2 - 2y\mu + \mu^2)\right) \\ &= \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{y^2}{2\sigma^2}\right) \cdot \exp\left(\frac{2y\mu}{2\sigma^2} - \frac{\mu^2}{2\sigma^2}\right) \\ &= \left(\frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{y^2}{2\sigma^2}\right)\right) \cdot \exp\left(\left(\frac{\mu}{\sigma^2}\right)y - \left(\frac{\mu^2}{2\sigma^2}\right)\right)\end{aligned}\]通过与规范形式 \(p(y; \eta) = b(y) \exp(\eta^T T(y) - a(\eta))\) 对比,我们可以确定各部分:
注意:充分统计量\(T(y)\)只依赖于数据 \(y\)。而\(\eta\) 是一个只依赖于原分布参数(如 \(\mu\) 和 \(\sigma^2\))的函数,它不依赖于数据 \(y\)。所以\(T(y) = y\),\(\eta = \frac{\mu}{\sigma^2}\)
| 部分 | 高斯分布 (N(μ,σ2)) 的对应项 |
|---|---|
| 充分统计量 \(T(y)\) | \(T(y) = y\) |
| 自然参数 \(\eta\) | \(\eta = \frac{\mu}{\sigma^2}\) |
| 对数配分函数 \(a(\eta)\) | \(a(\eta) = \frac{\mu^2}{2\sigma^2} = \frac{(\eta\sigma^2)^2}{2\sigma^2} = \frac{1}{2}\sigma^2 \eta^2\) |
| 残余项 \(b(y)\) | \(b(y) = \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{y^2}{2\sigma^2}\right)\) |
自然参数 \(\eta\) 和均值 \(\mu\) 的关系是 \(\mu = \sigma^2 \eta\)。在高斯分布的标准 GLM 中,通常假设方差 \(\sigma^2\) 是常数,并且为了简化,我们常取 \(\sigma^2 = 1\)。
\[\text{如果}\ \sigma^2 = 1\ \text{,则}\ \eta = \frac{\mu}{1} = \mu\]所以,只有在这种特殊情况下(方差为 1),高斯分布的规范连接函数才是 \(\eta = \mu\)(即恒等连接)。
所以,在标准 GLM 中,响应函数就是 \(\mu = h(\eta) = \eta\),这正是线性回归的连接函数(恒等连接)。
再说逻辑回归(伯努利分布):响应变量 \(y\) 是二元的(0 或 1),服从均值为 \(\phi\) 的伯努利分布,即 \(y \sim \text{Bernoulli}(\phi)\)。
伯努利分布的概率质量函数 (PMF) 为:
\[p(y; \phi) = \phi^y (1 - \phi)^{1-y}\]转化为指数族规范形式
对上式进行代数重写:
\[\begin{aligned} p(y; \phi) &= \exp\left( \log\left(\phi^y (1 - \phi)^{1-y}\right) \right) \\ &= \exp\left( y \log(\phi) + (1-y) \log(1 - \phi) \right) \\ &= \exp\left( y \log(\phi) - y \log(1 - \phi) + \log(1 - \phi) \right) \\ &= \exp\left( y \log\left(\frac{\phi}{1 - \phi}\right) + \log(1 - \phi) \right)\end{aligned}\]通过与规范形式 $p(y; \eta) = b(y) \exp(\eta^T T(y) - a(\eta))$ 对比,我们可以确定各部分:
| 部分 | 伯努利分布 (Bernoulli(ϕ)) 的对应项 |
|---|---|
| 充分统计量 \(T(y)\) | \(T(y) = y\) |
| 自然参数 \(\eta\) | \(\eta = \log\left(\frac{\phi}{1 - \phi}\right)\) |
| 对数配分函数 \(a(\eta)\) | \(a(\eta) = -\log(1 - \phi) = \log\left(\frac{1}{1 - \phi}\right)\) |
| 残余项 \(b(y)\) | \(b(y) = 1\) |
-
自然参数 \(\eta\) 与均值 \(\phi\) 的关系(连接函数):
\[\eta = \log\left(\frac{\phi}{1 - \phi}\right)\]这就是著名的 Log-Odds 或 Logit 函数。
-
响应函数(反向):
将 \(\phi\) 解出来:
\[e^\eta = \frac{\phi}{1 - \phi} \implies e^\eta (1 - \phi) = \phi \implies e^\eta - \phi e^\eta = \phi\] \[e^\eta = \phi (1 + e^\eta) \implies \phi = \frac{e^\eta}{1 + e^\eta}\]这就是 Sigmoid 函数!
关键点: 逻辑回归的理论基础,正是通过将伯努利分布转化为指数族规范形式,自动推导出了 \(\phi = \frac{e^\eta}{1 + e^\eta}\) 这个 Sigmoid 函数作为它的响应函数。
通过这两个例子,可以看到,指数族分布的规范形式确实是统一各种回归模型的理论框架。对于多项分布(Softmax 回归),其过程与伯努利分布的推导非常相似,最终会自动推导出 Softmax 函数作为它的响应函数。
下面就是softmax回归的推导:
为了将多项分布纳入广义线性模型 (GLM) 框架,通常需要将其写成指数族分布的标准形式:
\[p(y; \eta) = b(y) \exp(\eta^T T(y) - a(\eta))\]在多分类问题中,单个数值 \(y\) 不足以直接作为 \(T(y)\)。因此,引入一个 \((k-1)\) 维的向量 $T(y)$,采用 “One-Hot Encoding”(独热编码)的思想:
如果 \(y=i\) (对于 \(i=1, \ldots, k-1\)),则 $T(y)$ 的第 \(i\) 个分量是1,其余分量是 0。即:
\[\begin{aligned} T(1) = \begin{bmatrix} 1 \\ 0 \\ \vdots \\ 0 \end{bmatrix}, & T(2) = \begin{bmatrix} 0 \\ 1 \\ \vdots \\ 0 \end{bmatrix}, T(3) = \begin{bmatrix} 0 \\ 0 \\ 1 \\ \vdots \\ 0 \end{bmatrix}, \ldots, \\ & T(k-1) = \begin{bmatrix} 0 \\ 0 \\ \vdots \\ 1 \\ 0 \end{bmatrix}, T(k) = \begin{bmatrix} 0 \\ 0 \\ \vdots \\ 0 \end{bmatrix} \end{aligned}\]如果 \(y=k\),则 \(T(k)\) 是一个全零向量 \(\mathbf{0}\)。
另外,引入指示函数和期望,指示函数 \(\mathbb{I}\{\cdot\}\) 用于将分类结果转化为数学表达式。
\(\mathbb{I}\{y=i\}\) 的值:如果分类结果是 \(i\),则为1;否则为0。
期望 \(E(T(y)_i)\):
\(T(y)_i\) 代表向量 \(T(y)\) 的第 $i$ 个分量。
\[E(T(y)_i) = \sum_{y \in \{1, \ldots, k\}} T(y)_i \cdot p(y) = 1 \cdot p(y=i) + 0 \cdot p(y \ne i) = p(y=i)\]这验证了 \(T(y)\) 向量的第 \(i\) 个分量的期望,正是第 \(i\) 类发生的概率 \(\phi_i\)(对于 \(i=1, \ldots, k-1\))。在 Softmax 回归的推导中,这个 \(E(T(y))\) 会被设为 \(\phi\),并与线性预测 \(\eta\) 通过连接函数联系起来。
多项分布 PMF(使用指示函数):
\[p(y; \phi) = \phi_1^{\mathbb{I}\{y=1\}} \phi_2^{\mathbb{I}\{y=2\}} \cdots \phi_k^{\mathbb{I}\{y=k\}}\]其中,\(\mathbb{I}\{y=i\}\) 是指示函数,只有当 \(y=i\) 时取 1,否则取 0。
由于 \(\phi_k = 1 - \sum_{i=1}^{k-1} \phi_i\),且指示函数的和 \(\sum_{i=1}^{k} \mathbb{I}\{y=i\} = 1\),所以 \(\mathbb{I}\{y=k\} = 1 - \sum_{i=1}^{k-1} \mathbb{I}\{y=i\}\)。
\[p(y; \phi) = \phi_1^{\mathbb{I}\{y=1\}} \phi_2^{\mathbb{I}\{y=2\}} \cdots \phi_k^{1 - \sum_{i=1}^{k-1} \mathbb{I}\{y=i\}}\]即\(p(y; \phi) = \phi_1^{T(y)_1} \phi_2^{T(y)_2} \cdots \phi_k^{1 - \sum_{i=1}^{k-1} T(y)_i}\)
取对数并写成 \(\exp(\cdot)\) 形式:
\[\begin{aligned} p(y; \phi) &= \exp\left( T(y)_1 \log(\phi_1) + T(y)_2 \log(\phi_2) + \cdots + \left(1 - \sum_{i=1}^{k-1} T(y)_i\right) \log(\phi_k) \right) \\ &= \exp\left( \sum_{i=1}^{k-1} T(y)_i \log(\phi_i) + \log(\phi_k) - \sum_{i=1}^{k-1} T(y)_i \log(\phi_k) \right)\end{aligned}\]整理成指数族形式(引入 \(\phi_k\) 作为分母):将所有 \(T(y)_i\) 对应的项归类,并提取 \(\log(\phi_k)\):
\[\begin{aligned} p(y; \phi) &= \exp\left( \sum_{i=1}^{k-1} T(y)_i \left(\log(\phi_i) - \log(\phi_k)\right) + \log(\phi_k) \right) \\ &= \exp\left( \sum_{i=1}^{k-1} T(y)_i \log\left(\frac{\phi_i}{\phi_k}\right) + \log(\phi_k) \right) \\ &= \exp\left( \begin{bmatrix} T(y)_1 \\ \vdots \\ T(y)_{k-1} \end{bmatrix}^T \begin{bmatrix} \log(\phi_1/\phi_k) \\ \vdots \\ \log(\phi_{k-1}/\phi_k) \end{bmatrix} - (-\log(\phi_k)) \right)\end{aligned}\]与指数族规范形式 \(p(y; \eta) = b(y) \exp(\eta^T T(y) - a(\eta))\) 进行比对,得到最终的模型参数:
| 参数 | 多项分布 (Softmax) 的对应项 | 解释 |
|---|---|---|
| \(T(y)\) | \(T(y) = \begin{bmatrix} \mathbb{I}\{y=1\} \\ \mathbb{I}\{y=2\} \\ \vdots \\ \mathbb{I}\{y=k-1\} \end{bmatrix}\) | 充分统计量,一个 \((k-1)\) 维的独热编码向量。 |
| \(\eta\) | \(\eta = \begin{bmatrix} \log(\phi_1/\phi_k) \\ \log(\phi_2/\phi_k) \\ \vdots \\ \log(\phi_{k-1}/\phi_k) \end{bmatrix}\) | 自然参数,一个 \((k-1)\) 维向量,每个分量是 Log-Odds Ratio。 |
| \(a(\eta)\) | \(a(\eta) = -\log(\phi_k)\) | 对数配分函数,确保概率总和为 1。 |
| \(b(y)\) | \(b(y) = 1\) | 残余项。对于多项分布(没有阶乘项),\(b(y)\) 恒为 1。 |
Softmax 回归的核心理论基础
- 自然参数 \(\eta\) 即 Log-Odds Ratio
自然参数 \(\eta_i\) 被定义为第 \(i\) 类相对于基准类 \(k\) 的对数几率比 (Log-Odds Ratio):
\[\eta_i = \log\left(\frac{\phi_i}{\phi_k}\right)\]- 推导出 Softmax 函数(响应函数)
从 \(\eta\) 的定义,我们可以反推出 Softmax 函数 \(\phi\):
-
步骤 1: 对 \(\eta_i\) 取指数:
\[e^{\eta_i} = \frac{\phi_i}{\phi_k} \quad \implies \quad \phi_i = \phi_k e^{\eta_i} \quad (\text{for } i=1, \ldots, k-1)\] -
步骤 2: 第 \(k\) 类可以定义为 \(\phi_k = \phi_k e^{\eta_k}\),其中 \(\eta_k\) 被隐含地设定为 \(0\)。
-
步骤 3: 利用概率总和为 1 的约束:\(\sum_{j=1}^{k} \phi_j = 1\)
\[\sum_{j=1}^{k} \phi_j = \sum_{j=1}^{k} \phi_k e^{\eta_j} = \phi_k \sum_{j=1}^{k} e^{\eta_j} = 1\]解出 \(\phi_k\):
\[\phi_k = \frac{1}{\sum_{j=1}^{k} e^{\eta_j}}\] -
步骤 4: 将 \(\phi_k\) 代回 \(\phi_i\) 的表达式,即将\(\phi_i = \phi_k e^{\eta_i} \quad (\text{for } i=1, \ldots, k-1)\)带入求和项
\(\sum_{j=1}^{k-1} \phi_j + \phi_k = 1\)中,得到\(\sum_{j=1}^{k-1} (\phi_k e^{\eta_j}) + \phi_k = 1\),从等式左边提取公因式 \(\phi_k\):\(\phi_k \left( \sum_{j=1}^{k-1} e^{\eta_j} + 1 \right) = 1\)
将括号里的项重新写成一个包含 \(k\) 个类别的求和:(注意,这里的 \(+1\) 其实代表了 \(e^{\eta_k}\),因为我们隐含地设定了 \(\eta_k = 0\) (\(e^0 = 1\))。)
\[\phi_k \left( \sum_{j=1}^{k} e^{\eta_j} \right) = 1\]现在,解出基准类别 $\phi_k$ 的表达式:
\[\phi_k = \frac{1}{\sum_{j=1}^{k} e^{\eta_j}} \quad\]最后一步,我们回到关系式 ($\phi_i = \phi_k e^{\eta_i}$),将我们刚刚解出的表达式 代入 \(\phi_k\) 的位置:
得到著名的 Softmax 函数:
\[\phi_i = \frac{e^{\eta_i}}{\sum_{j=1}^{k} e^{\eta_j}}\]
这一步是标准的归一化技巧:先用基准项(\(\phi_k\))表示所有其他项(\(\phi_i\)),然后利用所有项的和等于 1 的约束,解出基准项,最后代回得到通用公式。
Log-Odds Ratio(对数几率比) 是 Softmax 回归(和 Logit 回归)中 \(\eta\) 的核心意义。
- Odds (几率): \(\frac{P(\text{事件发生})}{P(\text{事件不发生})} = \frac{\phi}{1-\phi}\)。几率告诉我们事件发生的可能性是不发生的可能性的多少倍。
- Log-Odds (对数几率): \(\log(\frac{\phi}{1-\phi})\)。取对数是为了将范围 \([0, \infty)\) 映射到 \((-\infty, \infty)\),从而可以与线性模型 \(\theta^T x\) 相匹配。
在 Softmax 回归 中,面对 \(k\) 个类别,用的是 Ratio (比率):
\[\eta_i = \log\left(\frac{\phi_i}{\phi_k}\right)\]这意味着:
- 不再与“不发生”作比较,而是与一个选定的基准类别 \(k\) 作比较。
- 特殊意义: \(\eta_i\) 的数值直接告诉我们,第 \(i\) 类发生的对数几率,相对于第 \(k\) 类发生的对数几率,高出了多少。
如果 \(\theta_i\) 是特征 \(x_j\) 对应的模型系数,那么 \(\theta_i\) 代表着:当 \(x_j\) 增加 1 个单位时,第 \(i\) 类相对于基准类 \(k\) 的对数几率比 \(\eta_i\) 增加 \(\theta_i\) 个单位。
这种 Log-Odds Ratio 的形式,在统计学上是处理多项分布参数的最自然、最简洁的方式,也是确保 Softmax 模型能够被完美纳入指数族和 GLM 框架的关键。
注意:\(\eta_k = \log\left(\frac{\phi_k}{\phi_k}\right) = \log(1) = 0\)
将 \(\eta_k\) 设为 \(0\) 意味着第 \(k\) 类被选定为所有比较的基准类别。所有其他的线性预测器 \(\eta_1, \ldots, \eta_{k-1}\) 都被解释为相对于 \(\eta_k\) 的差异。
这种设置与处理多项分布参数时使用 \(k-1\) 个参数(因为 \(\sum \phi_i = 1\))的,最终只需要学习 \(k-1\) 组系数向量 \(\theta_1, \ldots, \theta_{k-1}\)。第 \(k\) 组系数 \(\theta_k\) 被隐含地设定为零向量(或被吸收进截距项)。
| 概率表示: Softmax 回归预测的是在给定输入 \(x\) 和模型参数 \(\theta\) 的条件下,输出为类别 \(i\) 的概率 $$p(y=i | x; \theta)\(,记为\)\phi_i$$。 |
响应函数: 根据指数族分布的推导(我们之前讨论过),概率 \(\phi_i\) 由自然参数 \(\eta_i\) 经 Softmax 函数转换得到:
\[\phi_i = \frac{e^{\eta_i}}{\sum_{j=1}^{k} e^{\eta_j}}\]线性预测子: Softmax 回归作为广义线性模型(GLM),其自然参数 \(\eta_i\) 由输入 \(x\) 的线性组合构成:
\[\eta_i = \theta_i^T x\]注意:这里 \(\theta_i\) 是一个与类别 \(i\) 相关的参数向量,整个模型有 \(k\) 个这样的向量 \(\theta_1, \theta_2, \ldots, \theta_k\)。
最终假设函数: 将线性预测子代入响应函数,得到了 Softmax 回归的最终假设函数 \(h_{\theta}(x)\),它输出一个 \(k\) 维的概率向量:
\[h_{\theta}(x) = \begin{cases} P(y=1|x) = \frac{e^{\theta_1^T x}}{\sum_{j=1}^{k} e^{\theta_j^T x}}, & y=1 \\ P(y=2|x) = \frac{e^{\theta_2^T x}}{\sum_{j=1}^{k} e^{\theta_j^T x}}, & y=2 \\ \vdots \\ P(y=k|x) = \frac{e^{\theta_k^T x}}{\sum_{j=1}^{k} e^{\theta_j^T x}}, & y=k \end{cases}\]\(h_{\theta}(x)\) 的输出 是一个向量 \([\phi_1, \phi_2, \ldots, \phi_k]^T\),其中 \(\sum_{i=1}^k \phi_i = 1\),且 \(0 \le \phi_i \le 1\)。
Softmax 是一个归一化指数函数:

假设我们有 \(k=3\) 个类别,输入 \(x\) 经过线性预测(即 \(\eta_i = \theta_i^T x\))后,得到了 \(k=3\) 个原始分数 \(z_1, z_2, z_3\):
| 步骤 | 原始分数 (自然参数 η) | 转换操作 | 结果值 (指数项 eη) | 最终概率 ϕi |
|---|---|---|---|---|
| 1. | \(z_1 = 3\) | \(e^{z_1}\) | \(e^3 \approx 20\) | \(y_1 = \frac{20}{23.75} \approx 0.88\) |
| 2. | \(z_2 = 1\) | \(e^{z_2}\) | \(e^1 \approx 2.7\) | \(y_2 = \frac{2.7}{23.75} \approx 0.12\) |
| 3. | \(z_3 = -3\) | \(e^{z_3}\) | \(e^{-3} \approx 0.05\) | \(y_3 = \frac{0.05}{23.75} \approx 0\) |
Softmax 机制:
- 1.指数化 (Exponentiation): 对每个原始分数 \(z_i\)(即 \(\eta_i\))取指数 \(e^{z_i}\)。将分数映射到 \((0, \infty)\) 的范围内,确保输出的概率是非负的。同时,指数函数会放大分数之间的差异,分数越高,其指数值增长得越快。
- 2.求和 (Normalization Term): 计算所有指数项的和 \(\sum_{j=1}^3 e^{z_j}\),确保所有概率之和为 1 的归一化因子(对应于指数族中的 \(e^{a(\eta)}\))。
-
3.归一化 (Division): 将每个指数项除以总和,得到最终的概率 $$y_i = P(C_i x)$$。
softmax输出的是概率分布,但其名称“Softmax”来源于它是一个平滑的、可微分的“argmax”函数的近似。它会给最大的输入分数(比如上面的\(z_1=3\))分配一个远大于其他分数的概率(\(0.88\)),从而放大最大值。
多分类交叉熵损失

Softmax 回归的损失函数是通过最大化数据样本的对数似然函数推导出来的,这个函数就是著名的多分类交叉熵损失 (Multiclass Cross-Entropy Loss)。
【1.似然函数 (Likelihood Function)】
假设有一个包含 \(m\) 个独立同分布 (i.i.d.) 训练样本的数据集 \(\{(x^{(1)}, y^{(1)}), (x^{(2)}, y^{(2)}), \ldots, (x^{(m)}, y^{(m)})\}\), 其中 \(y^{(i)} \in \{1, \ldots, k\}\)。
模型的参数是 \(\theta = \{\theta_1, \ldots, \theta_k\}\)。
似然函数 \(\mathcal{L}(\theta)\) 是观察到整个数据集的概率,根据独立性原则,它是每个样本概率的乘积:
\[\mathcal{L}(\theta) = P(y^{(1)}, \ldots, y^{(m)} | x^{(1)}, \ldots, x^{(m)}; \theta) = \prod_{i=1}^{m} P(y^{(i)} | x^{(i)}; \theta)\]| 对于单个样本 \((x, y)\),我们知道 $$P(y=j | x) = \phi_j\(。由于\)y\(只有一个取值,我们可以使用我们之前定义的指示函数\)\mathbb{I}{y=j}\(或充分统计量\)T(y)_j$$ 来简洁地表示这个概率: |
其中 \(\phi_j\) 是 Softmax 函数:\(\phi_j = \frac{e^{\theta_j^T x}}{\sum_{l=1}^{k} e^{\theta_l^T x}}\)。
将这个概率代入似然函数:
\[\mathcal{L}(\theta) = \prod_{i=1}^{m} \left[ \prod_{j=1}^{k} \left( \phi_j^{(i)} \right)^{\mathbb{I}\{y^{(i)}=j\}} \right]\]【2.对数似然函数 (Log-Likelihood Function)】
为了简化计算(将乘积转化为求和)并利用指数族分布的优点,我们取对数似然 \(\ell(\theta) = \log \mathcal{L}(\theta)\):
\[\ell(\theta) = \log \left( \prod_{i=1}^{m} \prod_{j=1}^{k} \left( \phi_j^{(i)} \right)^{\mathbb{I}\{y^{(i)}=j\}} \right)\] \[\ell(\theta) = \sum_{i=1}^{m} \sum_{j=1}^{k} \mathbb{I}\{y^{(i)}=j\} \log\left( \phi_j^{(i)} \right)\]【3.损失函数 (Loss Function)】
在机器学习中,我们通常采用最小化损失函数的方式来训练模型,而不是最大化对数似然函数。
损失函数 \(J(\theta)\) 被定义为负的平均对数似然函数:
\[J(\theta) = - \frac{1}{m} \ell(\theta) = - \frac{1}{m} \sum_{i=1}^{m} \sum_{j=1}^{k} \mathbb{I}\{y^{(i)}=j\} \log\left( \phi_j^{(i)} \right)\]【4.交叉熵的本质】
这个函数 \(J(\theta)\) 就是多分类交叉熵损失 (Multiclass Cross-Entropy Loss)。
为什么叫交叉熵?在信息论中,交叉熵 \(H(p, q)\) 衡量的是用分布 \(q\)(模型预测的概率 \(\phi\))来编码分布 \(p\)(真实标签 \(y\) 的概率分布)所需要的平均比特数。
真实标签 \(y\) 的概率分布 \(p\) 是一个独热编码的分布,例如,如果真实标签是类别 3,那么 \(p\) 就是 \([0, 0, 1, 0, \ldots]^T\)。
对于一个样本 \(i\),其真实概率分布 \(p^{(i)}\) 是 \(p_j^{(i)} = \mathbb{I}\{y^{(i)}=j\}\)。
样本 \(i\) 的交叉熵损失为:
\[J^{(i)}(\theta) = - \sum_{j=1}^{k} p_j^{(i)} \log\left( \phi_j^{(i)} \right)\]由于 \(p_j^{(i)}\) 只有在 \(j\) 等于真实标签 \(y^{(i)}\) 时才为1,其余为0 ,所以上式简化为:
\[J^{(i)}(\theta) = - \log\left( \phi_{y^{(i)}}^{(i)} \right)\]其中
- \(\phi_{y^{(i)}}^{(i)}\) 是模型预测的正确类别的概率。
- \(\log(\phi_{y^{(i)}}^{(i)})\) 随着正确概率的增大而增大(但仍为负数)。
- 最小化 \(J(\theta)\) 等同于最大化正确类别的预测概率 \(\phi_{y^{(i)}}^{(i)}\)。
最大化多项分布的对数似然,等价于最小化多分类交叉熵损失。这一损失函数具有凸性(由于指数族分布的良好性质),保证了模型可以通过梯度下降等优化算法稳定地找到全局最优参数。
损失函数的梯度
现在来推导 Softmax 回归损失函数(多分类交叉熵)相对于模型参数 \(\theta_j\) 的梯度。这个梯度是训练模型时使用梯度下降法的核心。
为了简洁,我们只推导单个样本的损失函数 \(J^{(i)}(\theta)\) 相对于某个特定类别 \(j\) 的参数向量 \(\theta_j\) 的梯度。
单个样本 \((x, y)\) 的损失函数(负对数似然)为:
\[J(\theta) = - \log\left( \phi_{y} \right)\]其中,\(\phi_{y}\) 是模型对真实类别 \(y\) 预测的概率。
\(\phi_j\) 是 Softmax 函数:
\[\phi_j = \frac{e^{\eta_j}}{\sum_{l=1}^{k} e^{\eta_l}}\]并且 \(\eta_j = \theta_j^T x\)。
我们的目标是计算损失函数 \(J(\theta)\) 对第 \(j\) 个类别的参数向量 \(\theta_j\) 的偏导数:
\[\nabla_{\theta_j} J(\theta) = \frac{\partial J(\theta)}{\partial \theta_j}\]\(J(\theta)\) 是关于 \(\phi_y\) 的函数,\(\phi_y\) 是关于 \(\eta_l\) 的函数,而 \(\eta_l\) 是关于 \(\theta_j\) 的函数,我们需要使用链式法则:
\[\frac{\partial J(\theta)}{\partial \theta_j} = \sum_{l=1}^{k} \left( \frac{\partial J(\theta)}{\partial \phi_l} \cdot \frac{\partial \phi_l}{\partial \eta_j} \cdot \frac{\partial \eta_j}{\partial \theta_j} \right)\]\(\frac{\partial J(\theta)}{\partial \phi_l}\) 只有在 \(l=y\) 时非零,所以简化为:
\[\frac{\partial J(\theta)}{\partial \theta_j} = \frac{\partial J(\theta)}{\partial \phi_y} \cdot \frac{\partial \phi_y}{\partial \eta_j} \cdot \frac{\partial \eta_j}{\partial \theta_j}\]然后计算各部分偏导数:
A. \(\frac{\partial J(\theta)}{\partial \phi_y}\) (损失函数对概率的导数)
\[J(\theta) = - \log(\phi_y)\] \[\frac{\partial J(\theta)}{\partial \phi_y} = \frac{\partial (-\log(\phi_y))}{\partial \phi_y} = - \frac{1}{\phi_y}\]B. \(\frac{\partial \eta_j}{\partial \theta_j}\) (自然参数对参数的导数)
\[\eta_j = \theta_j^T x\] \[\frac{\partial \eta_j}{\partial \theta_j} = x\](注意 \(\frac{\partial}{\partial \theta_j}\) 意味着对向量 \(\theta_j\) 求梯度,结果是向量 \(x\)。)
C. \(\frac{\partial \phi_y}{\partial \eta_j}\) (Softmax 对原始分数的导数)
这是最复杂的一步,需要根据 \(j\) 是否等于真实类别 \(y\) 进行分情况讨论:
Case 1: \(j = y\) (计算 \(\phi_y\) 对 \(\eta_y\) 的导数)
\[\phi_y = \frac{e^{\eta_y}}{\sum_{l=1}^{k} e^{\eta_l}}\]使用商法则 \(\left(\frac{u}{v}\right)' = \frac{u'v - uv'}{v^2}\),其中 \(u = e^{\eta_y}\),\(v = \sum_{l=1}^{k} e^{\eta_l}\)。
\[\frac{\partial \phi_y}{\partial \eta_y} = \frac{(e^{\eta_y}) \sum_{l=1}^{k} e^{\eta_l} - e^{\eta_y} (e^{\eta_y})}{\left(\sum_{l=1}^{k} e^{\eta_l}\right)^2}\] \[= \frac{e^{\eta_y}}{\sum_{l=1}^{k} e^{\eta_l}} - \frac{e^{\eta_y} e^{\eta_y}}{\left(\sum_{l=1}^{k} e^{\eta_l}\right)^2} = \phi_y - \phi_y \cdot \phi_y\] \[\frac{\partial \phi_y}{\partial \eta_y} = \phi_y (1 - \phi_y)\]Case 2: \(j \ne y\) (计算 \(\phi_y\) 对 \(\eta_j\) 的导数)
\[\frac{\partial \phi_y}{\partial \eta_j} = \frac{(0) \sum_{l=1}^{k} e^{\eta_l} - e^{\eta_y} (e^{\eta_j})}{\left(\sum_{l=1}^{k} e^{\eta_l}\right)^2}\] \[= - \frac{e^{\eta_y}}{\sum_{l=1}^{k} e^{\eta_l}} \cdot \frac{e^{\eta_j}}{\sum_{l=1}^{k} e^{\eta_l}} = - \phi_y \cdot \phi_j\] \[\frac{\partial \phi_y}{\partial \eta_j} = - \phi_y \phi_j\]现在,将 A, B, C 代回链式法则,同样分 \(j=y\) 和 \(j \ne y\) 两种情况。
\[\frac{\partial J(\theta)}{\partial \theta_j} = \frac{\partial J(\theta)}{\partial \phi_y} \cdot \frac{\partial \phi_y}{\partial \eta_j} \cdot \frac{\partial \eta_j}{\partial \theta_j}\]Case 1: \(j = y\) (真实类别的参数梯度)
\[\nabla_{\theta_y} J(\theta) = \underbrace{\left(- \frac{1}{\phi_y}\right)}_{\text{A}} \cdot \underbrace{\left(\phi_y (1 - \phi_y)\right)}_{\text{C1}} \cdot \underbrace{(x)}_{\text{B}}\] \[= - (1 - \phi_y) x = (\phi_y - 1) x\]Case 2: \(j \ne y\) (非真实类别的参数梯度)
\[\nabla_{\theta_j} J(\theta) = \underbrace{\left(- \frac{1}{\phi_y}\right)}_{\text{A}} \cdot \underbrace{\left(- \phi_y \phi_j\right)}_{\text{C2}} \cdot \underbrace{(x)}_{\text{B}}\] \[= \phi_j x\]可以用一个统一的公式来表达梯度 \(\nabla_{\theta_j} J(\theta)\):
\[\nabla_{\theta_j} J(\theta) = (\phi_j - \mathbb{I}\{y=j\}) x\]其中,\(\mathbb{I}\{y=j\}\) 是指示函数:如果 \(j\) 是真实类别 \(y\),则为1;否则为0。
【注意】括号内的项 \((\phi_j - \mathbb{I}\{y=j\})\) 是预测概率和真实标签之间的误差。
如果 \(j\) 是真实类别 \(y\): 误差是 \((\phi_y - 1)\)。由于 \(\phi_y < 1\),误差是负的。梯度下降会向增加 \(\phi_y\) 的方向调整 \(\theta_y\)。
如果 \(j\) 不是真实类别 \(y\): 误差是 \((\phi_j - 0) = \phi_j\)。梯度是正的。梯度下降会向减少 \(\phi_j\) 的方向调整 \(\theta_j\)。
模型的参数 \(\theta_j\) 的更新量正比于:
\[\text{误差} \times \text{输入特征}\]这个公式与线性回归和逻辑回归的梯度公式形式惊人地相似,展现了 GLM 框架的统一性。
有了这个梯度,我们就可以使用梯度下降(或其变体)来迭代更新所有 \(\theta_j\) 向量,直到损失函数收敛到最小值。
案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
# --- 1. 数据加载与预处理 ---
# 定义转换:将图像转换为张量,并归一化到 [0, 1]
transform = transforms.Compose([
transforms.ToTensor(), # 将 PIL Image 或 NumPy ndarray 转换为 Tensor
# PyTorch 的 ToTensor 默认会将像素值除以 255.0,实现归一化
])
# 加载 FashionMNIST 数据集
mnist_train = torchvision.datasets.FashionMNIST(
root='./data',
train=True,
download=True,
transform=transform
)
mnist_test = torchvision.datasets.FashionMNIST(
root='./data',
train=False,
download=True,
transform=transform
)
# 数据读取
batch_size = 256
train_data = DataLoader(mnist_train, batch_size=batch_size, shuffle=True)
test_data = DataLoader(mnist_test, batch_size=batch_size, shuffle=False)
# 标签对应的服饰名字
def get_text_labels(label):
text_labels = [
't-shirt', 'trouser', 'pullover', 'dress,', 'coat',
'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot'
]
# label 是一个 Tensor 或 NumPy 数组
return [text_labels[int(i)] for i in label]
# --- 2. 定义模型 ---
# 在 PyTorch 中,我们使用 nn.Module 来定义模型。
# Softmax 回归本质上是一个线性层。
class SoftmaxRegression(nn.Module):
def __init__(self, num_inputs, num_outputs):
super(SoftmaxRegression, self).__init__()
# 784 (输入特征) -> 10 (输出类别)
self.linear = nn.Linear(num_inputs, num_outputs)
def forward(self, x):
# x 的形状是 (batch_size, 1, 28, 28)
# 我们需要将其展平为 (batch_size, 784)
x = x.view(x.size(0), -1)
# 然后通过线性层 (这一步包含了 wx + b)
return self.linear(x)
# 初始化模型
num_inputs = 784 # 28 * 28
num_outputs = 10
net = SoftmaxRegression(num_inputs, num_outputs)
# --- 3. 定义损失函数和优化器 ---
# PyTorch 的 nn.CrossEntropyLoss 包含了 Softmax 运算和交叉熵计算,因此
# 模型的 forward 函数只需要输出未经 Softmax 的原始得分 (logits)。
# 损失函数: 交叉熵 (会自动应用 Softmax)
loss = nn.CrossEntropyLoss()
# 优化器: 随机梯度下降 (SGD)
learning_rate = 0.1
optimizer = optim.SGD(net.parameters(), lr=learning_rate)
# --- 4. 辅助函数 ---
def evaluate_accuracy(data_iter, net):
acc_sum, n = 0.0, 0
with torch.no_grad(): # 在测试阶段禁用梯度计算
for X, y in data_iter:
# net(X) 返回 logits (未经 Softmax 的得分)
# argmax(dim=1) 找到概率最高的类别
acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
n += y.shape[0]
return acc_sum / n
# --- 5. 训练模型 ---
epochs = 5
print(f"开始训练...")
for epoch in range(epochs):
train_loss_sum, train_acc_sum, n = 0.0, 0.0, 0
for X, y in train_data:
# 清除梯度
optimizer.zero_grad()
# 前向传播 (计算预测的 logits)
y_hat = net(X)
# 计算损失
l = loss(y_hat, y)
# 反向传播 (计算梯度)
l.backward()
# 更新模型参数
optimizer.step()
# 统计训练指标
train_loss_sum += l.item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
n += y.shape[0]
# 模型训练完之后进行测试
test_acc = evaluate_accuracy(test_data, net)
print("Epoch %d. Loss: %f, Train acc %f, Test acc %f" % (
epoch + 1, train_loss_sum / len(train_data), train_acc_sum / n, test_acc))
print(f"\n训练完成。")
# --- 6. 对新的样本进行标签预测 ---
# 取测试集前 9 个样本
# --- 6. 对新的样本进行标签预测 ---
num_samples = 9
# 从数据集中提取前 num_samples 个样本
# mnist_test[i] 返回的是 (Image Tensor, label scalar)
# 存储提取出的图像和标签
images = []
true_labels_list = []
for i in range(num_samples):
img_tensor, label_scalar = mnist_test[i]
images.append(img_tensor)
true_labels_list.append(label_scalar)
# 将图像列表堆叠成一个大的 Tensor (batch_size, channels, height, width)
data = torch.stack(images)
# 将标签列表转换为 Tensor
label = torch.tensor(true_labels_list)
print('\ntrue labels:')
# 使用新的 label Tensor
print(get_text_labels(label))
# 禁用梯度,进行预测
with torch.no_grad():
# 确保模型使用的是展平的输入 (784)
y_hat_logits = net(data)
# 找到预测概率最高的类别
predicted_labels = y_hat_logits.argmax(dim=1)
print('predicted labels:')
print(get_text_labels(predicted_labels.numpy()))

6.逻辑回归的线性不可分
例子:有4个坐标(0,0),(0,1),(1,0),(1,1),每个坐标对应了一个图形,能找得到一个边界区分下面的2类图形吗?
答:找不到。这就是线性不可分。
实际工程的项目,一定是线性不可分的场景更加多。

线性可分是指:在一个 $D$ 维特征空间中,如果存在一个 $D-1$ 维的超平面(例如,在二维空间中是一条直线),能够将两类样本完全、准确地划分开,那么称这两类样本是线性可分的。
上图是一个异或问题 (XOR)。这个问题的逻辑是:当且仅当 \(x_1\) 和 \(x_2\) 不同时,类别为 1 (三角形)。
此时用逻辑回归就不能解决这个问题了。逻辑回归(以及其他任何线性分类器,如感知机 Perceptron)的核心是找到一个线性决策边界 \(\mathbf{w}^T \mathbf{x} + b = 0\)。由于 XOR 问题是线性不可分的,这个模型无论如何优化权重 \(\mathbf{w}\) 和偏置 \(b\),都无法找到一条直线来正确地对所有四个点进行分类。
要解决像 XOR 这样的线性不可分问题,我们必须引入非线性。主要有两种策略:
- 策略 A:特征变换 (Feature Transformation)
- 策略 B:引入非线性模型 (多层神经网络)
6.1策略A-特征变换
特征变化是最直接的方法,目标是将数据映射到一个更高维的空间,在这个新空间中,数据变得线性可分。
核技巧的思路:原始特征: \(\mathbf{x} = (x_1, x_2)\),添加非线性特征, 即引入一个乘积特征 \(x_3 = x_1 x_2\),最终的新特征空间 \(\Phi(\mathbf{x})\): \((x_1, x_2, x_3)\)
| x=(x1,x2) | x3=x1x2 | Φ(x)=(x1,x2,x3) | 类别 |
|---|---|---|---|
| $(0, 0)$ | 0 | $(0, 0, 0)$ | 0 |
| $(0, 1)$ | 0 | $(0, 1, 0)$ | 1 |
| $(1, 0)$ | 0 | $(1, 0, 0)$ | 1 |
| $(1, 1)$ | 1 | $(1, 1, 1)$ | 0 |
特征由2维变成3维,在这个三维空间中,可以找到一个超平面(一个平面)来区分这两类点,从而解决线性不可分问题。
与2维相同的思路,寻找一组新的权重 \(\mathbf{w}' = (w_1, w_2, w_3)\) 和偏置 \(b'\)。
\[\mathbf{w}'^T \Phi(\mathbf{x}) + b' = 0\]展开形式(即平面的方程)就是:
\[w_1 x_1 + w_2 x_2 + w_3 (x_1 x_2) + b' = 0\]寻找最佳平面就是找到最优的参数集合 \((\mathbf{w}', b')\),使得这个平面能够在新空间中将两类样本准确地分开。
为了训练模型,需要定义一个损失函数 \(L(\mathbf{w}', b')\)。
- 对于逻辑回归,使用二元交叉熵损失。
- 对于线性 SVM,使用 Hinge Loss。
用逻辑回归解决的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
# --- 字体设置 ---
mpl.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "SimHei"]
mpl.rcParams["axes.unicode_minus"] = False
# --- 1. 准备 XOR 数据 ---
# 原始的 XOR 数据点
X_raw = torch.tensor([
[0., 0.],
[0., 1.],
[1., 0.],
[1., 1.]
], dtype=torch.float32)
# 对应的标签 (0: 圆形, 1: 三角形)
y = torch.tensor([0, 1, 1, 0], dtype=torch.float32).view(-1, 1)
# --- 2. 特征工程: 引入乘积特征 x1*x2 ---
# x3 = x1 * x2
x1_x2 = (X_raw[:, 0] * X_raw[:, 1]).unsqueeze(1) # 计算 x1*x2 并增加一个维度
# 拼接原始特征和新特征
# 新特征向量是 (x1, x2, x1*x2)
X_extended = torch.cat((X_raw, x1_x2), dim=1)
print("扩展后的特征 X_extended:\n", X_extended)
# --- 3. 定义逻辑回归模型 (使用扩展特征) ---
class LogisticRegressionExtended(nn.Module):
def __init__(self, num_inputs):
super(LogisticRegressionExtended, self).__init__()
# 3 (输入特征: x1, x2, x1*x2) -> 1 (输出: 概率)
self.linear = nn.Linear(num_inputs, 1)
# Sigmoid 激活函数通常在 BCEWithLogitsLoss 中隐式包含,
# 但为了 forward 明确输出概率,我们在这里加上
self.sigmoid = nn.Sigmoid()
def forward(self, x):
return self.sigmoid(self.linear(x))
# 模型初始化
num_extended_inputs = X_extended.shape[1] # 3
model = LogisticRegressionExtended(num_extended_inputs)
# --- 4. 定义损失函数和优化器 ---
# nn.BCELoss 需要模型直接输出概率 (0到1之间)
# nn.BCEWithLogitsLoss 更稳定,它内部包含 Sigmoid,所以模型forward不需要Sigmoid
# 这里我们的模型forward已经有Sigmoid,所以直接用BCELoss
criterion = nn.BCELoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)
# --- 5. 训练模型 ---
print("\n开始训练...")
epochs = 5000 # 训练更多轮次以确保收敛
for epoch in range(epochs):
optimizer.zero_grad()
outputs = model(X_extended)
loss = criterion(outputs, y)
loss.backward()
optimizer.step()
if (epoch + 1) % 1000 == 0:
print(f'Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}')
print("训练完成。\n")
# --- 6. 获取训练后的模型参数 ---
# 提取权重和偏置
# net.linear.weight 是一个 (1, num_inputs) 的张量
# net.linear.bias 是一个 (1,) 的张量
w1, w2, w3 = model.linear.weight.squeeze().tolist()
b = model.linear.bias.item()
print(f"最终学习到的权重 w = ({w1:.2f}, {w2:.2f}, {w3:.2f})")
print(f"最终学习到的偏置 b = {b:.2f}")
# --- 7. 可视化结果 ---
plt.figure(figsize=(8, 6))
# 绘制原始数据点
for i in range(len(X_raw)):
x1, x2 = X_raw[i, 0], X_raw[i, 1]
if y[i].item() == 0:
plt.scatter(x1, x2, marker='o', s=150, color='blue', edgecolor='black', label='类别 0 (圆形)' if i==0 else "")
else:
plt.scatter(x1, x2, marker='^', s=150, color='red', edgecolor='black', label='类别 1 (三角形)' if i==1 else "")
# 绘制决策边界曲线
# 定义一个网格来评估决策函数
x1_min, x1_max = -0.5, 1.5
x2_min, x2_max = -0.5, 1.5
xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max, 100),
np.linspace(x2_min, x2_max, 100))
# 计算决策函数的值 (Logits)
# Z = w1*x1 + w2*x2 + w3*x1*x2 + b
Z = w1 * xx1 + w2 * xx2 + w3 * (xx1 * xx2) + b
# 绘制决策边界 (Z=0 的等高线)
plt.contour(xx1, xx2, Z, levels=[0], colors='green', linewidths=2, linestyles='--')
plt.title('XOR 问题:通过特征扩展实现的非线性决策边界', fontsize=14)
plt.xlabel('$x_1$', fontsize=12)
plt.ylabel('$x_2$', fontsize=12)
plt.xlim(x1_min, x1_max)
plt.ylim(x2_min, x2_max)
plt.xticks([0, 1])
plt.yticks([0, 1])
plt.grid(True, linestyle=':', alpha=0.7)
plt.legend()
plt.show()
# --- 8. 验证预测结果 ---
with torch.no_grad():
predictions = model(X_extended)
predicted_classes = (predictions > 0.5).float()
print("\n模型预测结果:")
print("真实标签:", y.squeeze().tolist())
print("预测概率:", [f"{p.item():.2f}" for p in predictions.squeeze()])
print("预测类别:", predicted_classes.squeeze().tolist())

虽然是三维的,但是画图,看到的是投影的边界,看上去像双曲线。
以上的特征组合是\(x_1x_2\),但是特征组合的方式多种多样,可以是\(x_1^2\),也可以是\(x_2^2\),…,等等。
这就是特征工程的任务了,找到最好的某种特征的组合,得到最终的模型,使得分类任务完成的最好。
另外,要注意,对于0.5的边界,其实也不一定合理,因为分类器有一定的概率预测错误。比如癌症,建议边界卡的低一点。宁可让小概率得癌症的人多检查几次,也不要认为他没有得癌症回家了,这是需要编程自己根据实际情况调整边界的。
6.2策略B-引入非线性模型 (多层神经网络)
这是现代机器学习中最常用的方法:
- 多层感知机 (MLP): 在输入层和输出层之间添加一个或多个隐藏层,并在隐藏层中引入非线性激活函数(如 ReLU, Sigmoid)。
- 工作原理: 每个隐藏层可以被视为一个自动学习特征变换 Φ(x) 的模块。通过堆叠多个非线性层,模型能够学习到任意复杂的非线性决策边界,轻松解决 XOR 等问题。
7.GLM 模型的梯度更新形式都具有高度的统一性
来详细对比 Softmax 回归、逻辑回归和线性回归的单个样本的参数更新公式,看看它们的相似之处。
广义线性模型(GLM)理论中最核心的洞察之一:所有 GLM 模型的梯度更新形式都具有高度的统一性。
所有三种模型的参数更新都是基于 梯度下降法 的迭代更新规则:
\[\theta_{\text{new}} = \theta_{\text{old}} - \alpha \cdot \nabla_{\theta} J(\theta)\]其中 \(\alpha\) 是学习率,\(\nabla_{\theta} J(\theta)\) 是梯度(损失函数 \(J(\theta)\) 对参数 \(\theta\) 的偏导数)。
我们关注的重点是梯度项 \(\nabla_{\theta} J(\theta)\)。
Softmax 回归 (多分类)是多分类交叉熵损失 (负对数似然)。梯度公式如下:
\[\nabla_{\theta_j} J(\theta) = \underbrace{(\phi_j - \mathbb{I}\{y=j\})}_{\text{误差项}} \cdot \underbrace{x}_{\text{输入特征}}\]逻辑回归 (二分类)是 Softmax 回归在 \(k=2\) 时的特例。它只学习一个参数向量 \(\theta\)(通常 \(\theta_1\))。损失函数是二元交叉熵损失 (BCE)。梯度公式如下:
\[\nabla_{\theta} J(\theta) = \underbrace{(\phi - y)}_{\text{误差项}} \cdot \underbrace{x}_{\text{输入特征}}\]| 其中,\(\phi = h_{\theta}(x)\) 是模型预测 $$P(y=1 | x)\(的概率,而\)y$$ 是真实标签(0 或 1)。 |
线性回归 (连续值预测)的损失函数是MSE(均方误差),梯度公式是:
\[\nabla_{\theta} J(\theta) = \underbrace{(h_{\theta}(x) - y)}_{\text{误差项}} \cdot \underbrace{x}_{\text{输入特征}}\]其中,\(h_{\theta}(x) = \theta^T x\) 是模型的预测值,\(y\) 是真实值。
| 模型 | 损失函数 J(θ) | 梯度 ∇θJ(θ) | 误差项 (预测−真实) | 基础分布 |
|---|---|---|---|---|
| 线性回归 | MSE | \((h_{\theta}(x) - y) x\) | (预测值-真实值) | 高斯分布 |
| 逻辑回归 | BCE | \((\phi - y) x\) | \((\text{预测概率} - \text{真实标签})\) | 伯努利分布 |
| Softmax 回归 | 交叉熵 | \((\phi_j - \mathbb{I}\{y=j\}) x\) | \((\text{预测概率} - \text{真实标签})\) | 多项分布 |
所有 GLM 模型的梯度更新都遵循以下结构:
\[\nabla_{\theta} J(\theta) \propto (\text{预测值} - \text{观测值}) \times \text{输入特征}\]8.正向传播与反向传播
其实表达式计算的过程就是正向传播,利用梯度下降反复更新参数(权重)就是反向传播。
不论是线性回归、逻辑回归、softmax回归,其实都已经有了正向传播、反向传播的概念。
在多层神经网络出现之前,这些模型就被称为“单层网络”。它们的反向传播过程之所以看起来简单,是因为它们只有一个计算层(线性组合 \(\theta^T x\))和一个激活函数(Sigmoid 或 Softmax)
- 正向传播: \(x \to \theta^T x \to h(\theta^T x) \to J(\theta)\)
- 反向传播: \(J(\theta) \to \nabla_{\theta} J\) (通过链式法则) \(\to \theta_{\text{new}}\)
这两种传播机制是所有基于梯度学习的模型(包括深度学习)的根本。
9.采样的重要性
如果数据集极度不平衡(例如,欺诈检测、罕见疾病诊断、广告点击率),如果不进行采样,模型在训练时会过度关注多数类 0。模型会发现,只要它预测所有样本都是0,它就能达到一个看似很高的准确率(例如 99.9%),因为0的样本占了绝大多数。
虽然整体准确率高,但模型实际上失去了识别少数类 1(例如,真正欺诈的交易、真正患病的人)的能力。这在关键业务场景中是不可接受的。
所以需要采样,上采样(复制少数类样本)或下采样(减少多数类样本),有效提高少数类在损失计算中的权重,使其梯度能够充分指导模型的参数更新。
总结重点
1.逻辑回归损失函数与梯度下降,手推数学公式,即求导。说明逻辑回归迭代的过程。
2.KL距离(KL散度)与BCE之间的关系。并解释下图:

3.为什么逻辑回归不用MSE而用BCE?
4.softmax
补充-colab画图之显示中文字体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import matplotlib as mpl
import matplotlib.font_manager as fm
import os
# --- 1. 设置变量 ---
FONT_FILENAME = 'NotoSansCJKsc-Regular.otf'
DOWNLOAD_URL = "https://github.com/googlefonts/noto-cjk/raw/main/Sans/OTF/SimplifiedChinese/NotoSansCJKsc-Regular.otf"
# 找出 Matplotlib 的缓存/配置目录(兼容性写法)
try:
cache_dir = mpl.get_cachedir()
except AttributeError:
cache_dir = mpl.get_configdir()
# 定义下载和目标路径
source_file = FONT_FILENAME
font_destination = os.path.join(cache_dir, FONT_FILENAME)
print(f"目标缓存目录: {cache_dir}")
# --- 2. 检查并下载字体 ---
if not os.path.exists(source_file) or os.path.getsize(source_file) < 100:
print(f"正在重新下载 {FONT_FILENAME}...")
# 强制重新下载,覆盖可能存在的空文件
!wget {DOWNLOAD_URL} -O {source_file}
# 检查下载是否成功
if os.path.getsize(source_file) > 1000000: # 字体文件通常大于 1MB
print(f"✅ 文件下载成功,大小:{os.path.getsize(source_file) / 1024**2:.2f} MB")
# --- 3. 复制字体文件到目标路径 ---
# 使用 shutil.copy 以确保复制成功
import shutil
shutil.copyfile(source_file, font_destination)
print(f"✅ {FONT_FILENAME} 已成功复制到目标目录。")
# --- 4. 清理并重建 Matplotlib 缓存(关键) ---
# 清除旧的字体缓存文件(如果存在)
!rm -rf {cache_dir}/*.json
# 强制 Matplotlib 重新扫描并构建缓存
fm._load_fontmanager(try_read_cache=False)
print("✅ 字体管理器已强制重新加载和构建缓存。")
else:
print(f"❌ 错误:下载失败或文件大小异常 ({os.path.getsize(source_file)} 字节)。")
# 验证字体是否被管理器识别
if 'Noto Sans CJK SC' in [f.name for f in fm.fontManager.ttflist]:
print("✅ 验证成功:Matplotlib 字体管理器已识别 'Noto Sans CJK SC'")
else:
print("❌ 验证失败:字体管理器未识别该字体。")
如果验证失败,可以重启会话之后再执行一次。
如何还是显示“验证失败”,这通常是因为 font_manager 模块在重新初始化时,并没有真正将复制到缓存目录的文件视为有效的字体文件,或者读取路径有问题。可以尝试运行下面代码:Python 代码强制指定 Matplotlib 使用这个字体文件,绕过自动识别过程。(注意,最好还是重启下)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import os
# --- 1. 定义字体文件路径 ---
FONT_FILENAME = 'NotoSansCJKsc-Regular.otf'
# 找出 Matplotlib 的缓存/配置目录
try:
cache_dir = mpl.get_cachedir()
except AttributeError:
cache_dir = mpl.get_configdir()
# 目标路径就是我们复制到的路径
font_path = os.path.join(cache_dir, FONT_FILENAME)
# --- 2. 尝试将字体手动添加到字体管理器 ---
try:
# 使用 fontManager.addfont 明确告诉 Matplotlib 这个文件是一个字体
fm.fontManager.addfont(font_path)
print(f"✅ 字体文件 {FONT_FILENAME} 已通过 addfont 明确添加到管理器。")
except Exception as e:
print(f"❌ 字体手动添加失败: {e}")
# --- 3. 强制设置参数 (使用文件名作为字体名称) ---
# Matplotlib 有时会使用文件名作为字体名,我们使用正确的字体名 'Noto Sans CJK SC'
font_name = 'Noto Sans CJK SC'
# 检查字体是否已被识别 (重新检查,确保 addfont 有效)
if font_name in [f.name for f in fm.fontManager.ttflist]:
print("✅ 验证成功:Matplotlib 字体管理器已识别 'Noto Sans CJK SC'")
# 强制设置字体参数
mpl.rcParams["font.sans-serif"] = [font_name, 'SimHei'] # 将其设置为首选
mpl.rcParams["axes.unicode_minus"] = False
print(f"✅ Matplotlib 默认字体已设置为 {font_name}")
# --- 4. 运行一个快速测试图 ---
plt.figure(figsize=(2, 1))
plt.title("中文显示测试")
plt.text(0.5, 0.5, "测试文本:逻辑回归", ha='center', va='center')
plt.show()
else:
print(f"❌ 最终验证失败:字体管理器未识别 {font_name}。请确保安装代码运行完整。")
如果没有问题会显示:

以画sigmoid函数为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import matplotlib as mpl
# --- 【关键修复部分:强制刷新字体管理器】 ---
# 这一步尝试强制 Matplotlib 重新加载字体缓存,以确保新安装的字体生效。
# 确保 mpl 模块被正确导入
import matplotlib as mpl
# 尝试重建字体缓存(如果 Matplotlib 版本支持)
try:
# 强制重新加载字体管理器,不使用缓存
fm._load_fontmanager(try_read_cache=False)
print("ℹ️ Matplotlib 字体管理器已强制刷新。")
except Exception as e:
# 打印错误,但允许代码继续运行,以防版本兼容问题
print(f"⚠️ 字体管理器刷新失败: {e}")
# --- 【设置中文字体】 ---
# 使用思源黑体作为首选,并保留其他常用字体作为备用
plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "WenQuanYi Zen Hei", "SimHei"]
plt.rcParams["axes.unicode_minus"] = False # 解决负号显示的问题
print(f"ℹ️ 当前设置的字体列表: {plt.rcParams['font.sans-serif']}")
# --- 【验证字体是否可用】 ---
# 尝试查找首选字体,如果失败,会发出警告
try:
fm.findfont(plt.rcParams["font.sans-serif"][0], fallback_to_default=False)
print("✅ 中文字体验证通过。")
except Exception:
print("❌ 警告:首选中文字体仍未找到,可能显示乱码。")
# --- 【绘图逻辑】 ---
# 生成 x 值范围
x = np.linspace(-10, 10, 1000)
# 计算 Sigmoid 函数
y = 1 / (1 + np.exp(-x))
# 计算 Sigmoid 的导数:σ'(x) = σ(x)*(1-σ(x))
dy = y * (1 - y)
# 绘图
plt.figure(figsize=(10, 6))
plt.plot(x, y, label='Sigmoid 函数: σ(x) = 1/(1+e^{-x})', color='blue', linewidth=3)
plt.plot(x, dy, label="Sigmoid 导数: σ'(x) = σ(x)(1-σ(x))", color='red', linewidth=3, linestyle='--')
# 添加标题和标签
plt.title('Sigmoid 函数及其导数', fontsize=16)
plt.xlabel('x', fontsize=14)
plt.ylabel('y', fontsize=14)
# 添加网格、图例、零轴线
plt.grid(True, alpha=0.3)
plt.legend(fontsize=12)
plt.axhline(y=0, color='k', linewidth=0.8)
plt.axvline(x=0, color='k', linewidth=0.8)
plt.axhline(y=0.5, color='gray', linestyle=':', alpha=0.7, label='y=0.5')
plt.axhline(y=1, color='gray', linestyle=':', alpha=0.7)
plt.axhline(y=0, color='gray', linestyle=':', alpha=0.7)
# 设置坐标轴范围
plt.xlim(-10, 10)
plt.ylim(-0.1, 1.1)
# 显示图像
plt.tight_layout()
plt.show()
