You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Python推导log loss梯度下降算法及基础实现代码

对数损失(Log Loss)梯度下降推导与Python实现

基础符号定义

二分类任务下的符号约定如下:

  • 训练集总样本数:$n$
  • 单样本特征向量:$x_i \in \mathbb{R}^d$,$d$为特征维度
  • 单样本真实标签:$y_i \in {0,1}$,1代表正类,0代表负类
  • 模型待学习参数:权重向量$w \in \mathbb{R}^d$,偏置项$b$
  • 模型输出的正类预测概率:$\hat{y}_i = \sigma(w^T x_i + b)$,其中$\sigma$为sigmoid激活函数,定义为$\sigma(z) = \frac{1}{1+e^{-z}}$,输出范围固定在(0,1)之间

对数损失定义

二分类场景下的对数损失(即二元交叉熵损失),训练集平均损失公式为:
$$L(w,b) = -\frac{1}{n}\sum_{i=1}^n \left[ y_i \log(\hat{y}_i) + (1-y_i)\log(1-\hat{y}_i) \right]$$
对数损失是典型的凸函数,梯度下降求解时可以收敛到全局最优值,不会陷入局部极小点

梯度推导过程

推导核心用链式法则,先利用sigmoid函数的导数性质简化计算:sigmoid的导数满足$\sigma'(z) = \sigma(z)(1-\sigma(z))$,推导步骤如下:

  1. 先计算单样本损失对预测概率$\hat{y}_i$的偏导:
    $$\frac{\partial L_i}{\partial \hat{y}_i} = -\frac{y_i}{\hat{y}_i} + \frac{1-y_i}{1-\hat{y}_i}$$
  2. 引入线性输出项$z_i = w^T x_i + b$,结合sigmoid导数,计算损失对$z_i$的偏导:
    $$
    \begin{align*}
    \frac{\partial L}{\partial z_i} &= \frac{\partial L_i}{\partial \hat{y}_i} \cdot \frac{\partial \hat{y}_i}{\partial z_i} \
    &= \left( -\frac{y_i}{\hat{y}_i} + \frac{1-y_i}{1-\hat{y}_i} \right) \cdot \hat{y}_i(1-\hat{y}_i) \
    &= -y_i(1-\hat{y}_i) + (1-y_i)\hat{y}_i \
    &= \hat{y}_i - y_i
    \end{align*}
    $$
    可以看到化简后梯度形式非常简洁,就是预测概率与真实标签的差值,这是逻辑回归实现高效的核心原因
  3. 进一步推导对参数$w$和$b$的平均梯度:
  • 对权重$w$的梯度:$\frac{\partial z_i}{\partial w} = x_i$,因此全训练集平均梯度为$\frac{\partial L}{\partial w} = \frac{1}{n}\sum_{i=1}^n (\hat{y}_i - y_i)x_i$
  • 对偏置$b$的梯度:$\frac{\partial z_i}{\partial b} = 1$,因此全训练集平均梯度为$\frac{\partial L}{\partial b} = \frac{1}{n}\sum_{i=1}^n (\hat{y}_i - y_i)$
  1. 梯度下降参数更新规则:

每轮迭代沿梯度的反方向更新参数,抵消梯度上升的影响,其中$\alpha$为预设的学习率:
$$w = w - \alpha \cdot \frac{\partial L}{\partial w}$$
$$b = b - \alpha \cdot \frac{\partial L}{\partial b}$$

基础Python实现

实现仅依赖numpy做矩阵运算,完全对应上述推导逻辑,无框架黑盒:

import numpy as np

def sigmoid(z):
    # 数值稳定版sigmoid,避免z为绝对值极大的负数时出现指数溢出
    return np.where(z >= 0, 
                    1 / (1 + np.exp(-z)), 
                    np.exp(z) / (1 + np.exp(z)))

def compute_log_loss(y_true, y_pred):
    # 计算平均对数损失,加1e-8避免log(0)的数值错误
    n = len(y_true)
    loss = -1/n * np.sum(
        y_true * np.log(y_pred + 1e-8) + (1 - y_true) * np.log(1 - y_pred + 1e-8)
    )
    return loss

def gradient_descent_train(X, y, lr=0.1, epochs=1000):
    """
    梯度下降训练最小化对数损失
    参数说明:
        X: 特征矩阵,shape=(样本数n, 特征维度d)
        y: 真实标签数组,shape=(n,)
        lr: 学习率
        epochs: 迭代轮数
    返回:训练得到的w、b,以及每轮迭代的损失记录
    """
    n, d = X.shape
    # 参数全0初始化即可
    w = np.zeros(d)
    b = 0.0
    loss_history = []

    for epoch in range(epochs):
        # 前向计算预测概率
        z = np.dot(X, w) + b
        y_pred = sigmoid(z)
        # 记录当前轮损失
        current_loss = compute_log_loss(y, y_pred)
        loss_history.append(current_loss)
        # 计算梯度
        dw = 1/n * np.dot(X.T, (y_pred - y))
        db = 1/n * np.sum(y_pred - y)
        # 更新参数
        w -= lr * dw
        b -= lr * db
        # 每100轮打印训练状态
        if (epoch + 1) % 100 == 0:
            print(f"Epoch {epoch+1}/{epochs}, Log Loss: {current_loss:.4f}")
    
    return w, b, loss_history

def predict_proba(X, w, b):
    # 输出正类预测概率
    z = np.dot(X, w) + b
    return sigmoid(z)

def predict(X, w, b, threshold=0.5):
    # 按指定阈值输出分类结果
    proba = predict_proba(X, w, b)
    return (proba >= threshold).astype(int)

测试示例

可以用简单的线性可分数据集验证实现效果:

from sklearn.datasets import make_classification
from sklearn.metrics import accuracy_score

# 生成1000个二分类样本,特征维度为2
X, y = make_classification(n_samples=1000, n_features=2, n_redundant=0, random_state=42)
# 模型训练
w, b, loss_history = gradient_descent_train(X, y, lr=0.1, epochs=1000)
# 预测与评估
y_pred = predict(X, w, b)
print(f"训练集分类准确率: {accuracy_score(y, y_pred):.4f}")

核心实现注意点

  • 没有直接使用原生sigmoid公式,而是做了数值稳定性处理,避免大数值输入下的溢出问题
  • 计算对数项时加入了1e-8的极小值,防止预测值完全为0或1时出现log(0)的非法运算
  • 梯度计算完全对应推导公式,用numpy矩阵运算替代Python原生循环,执行效率更高
  • 全0初始化即可满足训练要求,不需要复杂的参数初始化策略

内容的提问来源于stack exchange,提问作者ashfaq ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 02:01:04