标准回答
逻辑回归在线性打分 z=Xw+b 上套 Sigmoid 得到概率,用二元交叉熵作损失,本质是对伯努利分布做最大似然估计。一个漂亮的结论是:交叉熵对 z 求导后,梯度恰好化简为 (p−y),于是 dw=(1/N)Xᵀ(p−y),和线性回归同形。实现上要点是 Sigmoid 的数值稳定(按 z 正负分段)与 log 加 epsilon 防溢出。代码如下:
python
import numpy as np
def sigmoid(z):
# 数值稳定的 sigmoid:按 z 正负分段,避免 exp 溢出
out = np.empty_like(z, dtype=float)
pos = z >= 0
out[pos] = 1.0 / (1.0 + np.exp(-z[pos]))
ez = np.exp(z[~pos])
out[~pos] = ez / (1.0 + ez)
return out
def logistic_regression(X, y, lr=0.1, epochs=1000, l2=0.0):
N, D = X.shape
w = np.zeros(D)
b = 0.0
eps = 1e-12
for _ in range(epochs):
p = sigmoid(X @ w + b) # 前向:预测概率
# 二元交叉熵梯度(化简后等于 p - y)
grad = p - y
dw = (X.T @ grad) / N + l2 * w # 加 L2 正则
db = grad.sum() / N
w -= lr * dw
b -= lr * db
return w, b
def bce_loss(X, y, w, b):
p = sigmoid(X @ w + b)
eps = 1e-12
return float(-np.mean(y * np.log(p + eps) + (1 - y) * np.log(1 - p + eps)))
if __name__ == '__main__':
rng = np.random.default_rng(0)
X0 = rng.normal(-1, 1, (100, 2)); X1 = rng.normal(2, 1, (100, 2))
X = np.vstack([X0, X1]); y = np.array([0.0] * 100 + [1.0] * 100)
w, b = logistic_regression(X, y, lr=0.5, epochs=2000)
pred = (sigmoid(X @ w + b) >= 0.5).astype(int)
print('acc=', round((pred == y).mean(), 3))
print('loss=', round(bce_loss(X, y, w, b), 4))常见误区
⚠️ 常见踩坑
直接写 1/(1+np.exp(-z)) 在 z 为大负数时 exp 溢出为 inf;以及 log(p) 在 p=0 或 1 时变 −inf,必须加 epsilon。还有人误把 MSE 当逻辑回归损失,虽能跑但损失非凸、收敛差,标准做法是交叉熵。
追问
追问 1:为什么用交叉熵而不是 MSE?
配合 Sigmoid 时,交叉熵对参数是凸的且梯度为 (p−y),不会因 Sigmoid 饱和而消失;而 MSE 与 Sigmoid 组合非凸、在饱和区梯度趋近 0,导致训练慢、易陷入坏局部最优。交叉熵也正是伯努利分布的负对数似然。
追问 2:如何扩展到多分类?
把 Sigmoid 换成 Softmax、二元交叉熵换成多类交叉熵,即 Softmax 回归(多项逻辑回归)。权重变为 D×C 矩阵,每个样本输出各类概率,梯度仍为 (p−onehot(y)),结构高度一致。
延伸学习
与本题相关的知识库文章、术语、工具与行业资讯。