核心要点

  • 先讲模型:z=Xw+b,再过 sigmoid,输出正类概率

  • 损失用二元交叉熵,本质是伯努利分布的负对数似然

  • 关键梯度要会写:dw=(1/N)Xᵀ(p−y),db=mean(p−y)

  • 代码重点不是花哨,而是 sigmoid 分段稳定、log 加 eps、必要时加 L2

标准回答

一、先讲模型形式

逻辑回归不是“回归”,它通常用来做二分类:先算线性分数 z = Xw + b,再过 sigmoid 得到正类概率 p = sigmoid(z)

二、再讲训练目标

损失用二元交叉熵,本质上就是伯努利分布的负对数似然。它比 MSE 更适合配 sigmoid,因为梯度更干净、优化更稳定。

三、最后讲代码关键

推导后最重要的结论是:对 z 的梯度就是 p − y,所以 dw=(1/N)Xᵀ(p−y),db=mean(p−y)。实现时注意 sigmoid 要分段写,防 exp 溢出;log 里要加 eps,防 log(0)。代码如下:

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))

常见误区

⚠️ 常见踩坑

误区一:sigmoid 直接裸写。 z 很大或很小时 exp 可能溢出,要分段实现。误区二:log 不加 eps。 p 接近 0 或 1 时会出现 -inf。误区三:用 MSE 当标准损失。 虽然能跑,但配 sigmoid 优化更差,标准做法是交叉熵。

追问

追问 1为什么用交叉熵而不是 MSE?

可以说:交叉熵和 sigmoid 是一对更自然的组合。从概率角度,它是伯努利分布的负对数似然;从优化角度,梯度能化成 p−y,比较稳定。MSE 也能算,但和 sigmoid 组合后在饱和区梯度容易很小,训练会慢,也更难优化。

追问 2如何扩展到多分类?

很自然:把 sigmoid 换成 softmax,把二元交叉熵换成多类交叉熵。权重从一个向量变成 D×C 的矩阵,每个样本输出 C 个类别概率。梯度形式仍然很漂亮,基本就是 p−onehot(y)。

🔗 相似问题

同一考点的不同问法,换着练更稳

延伸学习

按主题分类的相关资源,便于系统复习