深度学习之零碎知识点__举例解释原理
2026/9/16 22:01:17 网站建设 项目流程

一.为什么规范化会加快收敛速度


1.绘制未标准化数据的等高线图
#等高线可以直观的展示损失函数在不同参数组合的取值情况,等高线上的每个点对应的损失函数值是相等的,就像地图上的等高线表示相同海拔高度一样
2.未标准化,不同特征的尺度差异很大,在某一方向上的变化率大很多,会导致等高线在不同参数方向的拉伸程度不同,呈现出扁长的椭圆形。在扁长的椭圆形的等高线中,梯度方向会在不同参数方向上频繁摆动,使得新路径的方向呈现锯齿状,为了达到最优解,需要得带多次,所以收敛速度慢

二.为什么参数更新完,参数梯度清零

1. 批次的Loss计算

问题loss是批次中的总loss吗?
  • 答案loss是当前批次(batch)的平均损失,不是总损失。

    • 计算方式:

      loss = -(y_batch * torch.log(y_pred) + (1-y_batch)*torch.log(1-y_pred)).mean()
      • y_batchy_pred的形状均为(batch_size,)

      • .mean()对所有样本的损失求平均(等价于总损失除以batch_size)。

举例说明

假设batch_size=3,且:

  • y_batch = [1.0, 0.0, 1.0](真实标签)

  • y_pred = [0.9, 0.2, 0.8](预测概率)

则损失计算过程:

  1. 对每个样本计算交叉熵:

    • 样本1:-1.0 * log(0.9) ≈ 0.1054

    • 样本2:-(1-0.0) * log(1-0.2) ≈ 0.2231

    • 样本3:-1.0 * log(0.8) ≈ 0.2231

  2. 求平均

    loss = (0.1054 + 0.2231 + 0.2231) / 3 ≈ 0.1839

2. 参数更新的具体过程

问题param -= lr * param.grad是基于哪个参数计算的梯度?
  • 答案:梯度是基于当前批次数据计算的,参数更新是立即生效的(即下一批次使用更新后的参数)。

    # 手动梯度下降(不借助optimizer) with torch.no_grad(): for param in model.parameters(): param -= lr * param.grad param.grad.zero_() # 清空当前批次的梯度
    • 流程

    • 梯度清零param.grad.zero_()清空梯度,防止影响下一批次。

    • 参数更新param -= lr * param.grad直接修改参数值。

    • 反向传播:计算当前批次梯度param.grad

    • 前向传播:用当前参数计算y_pred

具体例子

假设模型参数初始值:

  • w = [0.0, 0.0],b = 0.0
    学习率lr = 0.1

第一批次数据

  1. 计算梯度w.grad = [0.5, -0.3],b.grad = 0.2

  2. 更新参数:

    python

    w -= 0.1 * [0.5, -0.3] → w = [-0.05, 0.03] b -= 0.1 * 0.2 → b = -0.02
  3. 梯度清零:w.grad = [0.0, 0.0],b.grad = 0.0

第二批次数据

  • 使用更新后的参数w = [-0.05, 0.03],b = -0.02继续计算。


3.param.grad.zero_()的作用

问题:为什么要清零梯度?
  • 原因:PyTorch默认会累积梯度param.grad += new_gradients)。如果不清零:

    • 第一批次梯度:w.grad = [0.5, -0.3]

    • 第二批次梯度:w.grad = [0.5, -0.3] + [new_grad]→ 梯度错误累积!

  • 正确做法:每批次更新后立即清零梯度:

    python

    with torch.no_grad(): for param in model.parameters(): param -= lr * param.grad # 用当前梯度更新参数 param.grad.zero_() # 清零梯度

4. 参数更新可视化

批次参数w当前梯度w.grad更新后的w
1[0.0, 0.0][0.5, -0.3][-0.05, 0.03]
2[-0.05, 0.03][0.2, 0.1][-0.07, 0.02]
3[-0.07, 0.02][-0.1, 0.4][-0.06, -0.02]

5. 完整代码流程总结

  1. 前向传播:用当前参数计算批次预测值y_pred

  2. 损失计算:求批次内所有样本损失的平均值。

  3. 反向传播:计算param.grad(梯度基于当前批次数据)。

  4. 参数更新

    • param -= lr * param.grad(立即更新参数)。

    • param.grad.zero_()(清空梯度,防止影响下一批次)

6. 梯度与参数的关系

1.参数是变量,比如线性回归中的w,b,而在求解时会将参数初始化为0矩阵,或者随机初始化,即参数的初始值。

2.梯度,是损失函数对参数求导,比如y=x^2,对x求导后为dy/dx=2x,求x的梯度需要在求导后带入x初始值(eg,x初始=1.0),则x的梯度,x.gard=2*1=2,更新梯度x(new)=x-lr*x,则新的X会作为下一次求导后,带入的初始值。在下一次求导前,首先需要将x的梯度清零。一方面,本身就是要新的loss对参数求导,比如对参数的求导公式是固定的(同一模型中)此处以y=x^2,对x求导后为dy/dx=2x为例,导数公式一直为x.grad=2x.只是因为更新后的x不一样,所以每次会计算出不同的x.grad.

4.反向传播

每次求导后的公式都一样,只是代入不同的参数值计算梯度吗?”

  • 是的,但不止如此

    1. 求导公式固定:梯度公式(如 ∂L/∂w)的形式由模型和损失函数决定,与参数值无关。

    2. 梯度值动态变化:实际计算的梯度值取决于:

      • 当前参数值(影响 y^ 的计算)。

      • 输入数据(当前批次的 x 和 y)。

4. 具体例子说明

假设:

  • 参数初始值:w=[0.0,0.0], b=0.0。

  • 第一批次数据

    • 输入 x=[1.0,2.0],标签 y=1。

    • 计算过程:

      1. 前向传播:

        z=0×1.0+0×2.0+0=0,y^=σ(0)=0.5z=0×1.0+0×2.0+0=0,y^​=σ(0)=0.5
      2. 梯度计算:

        ∂L∂w1=(0.5−1)×1.0=−0.5∂w1​∂L​=(0.5−1)×1.0=−0.5∂L∂w2=(0.5−1)×2.0=−1.0∂w2​∂L​=(0.5−1)×2.0=−1.0∂L∂b=0.5−1=−0.5∂b∂L​=0.5−1=−0.5
      3. 梯度值:

        w.grad=[−0.5,−1.0],b.grad=−0.5w.grad=[−0.5,−1.0],b.grad=−0.5
  • 参数更新后(学习率 lr=0.1lr=0.1):

    w=[0.0,0.0]−0.1×[−0.5,−1.0]=[0.05,0.1],b=−0.05w=[0.0,0.0]−0.1×[−0.5,−1.0]=[0.05,0.1],b=−0.05
  • 第二批次数据

    • 用新参数 w=[0.05,0.1],b=−0.05 重新计算梯度,结果不同。

5. 反向传播的动态性
批次参数 w输入 x预测 y^梯度 w.grad
1[0.0,0.0][1.0,2.0]0.5[−0.5,−1.0]
2[0.05,0.1][−1.0,1.0]0.49[0.51,−0.49]
  • 梯度公式相同,但梯度值因参数和数据变化。

6. 代码验证
w = torch.tensor([0.0, 0.0], requires_grad=True) b = torch.tensor(0.0, requires_grad=True) # 第一批次 X_batch = torch.tensor([[1.0, 2.0]]) y_batch = torch.tensor([1.0]) y_pred = torch.sigmoid(X_batch @ w + b) loss = - (y_batch * torch.log(y_pred) + (1-y_batch)*torch.log(1-y_pred) loss.backward() print("第一批次梯度:", w.grad) # 输出: tensor([-0.5000, -1.0000]) # 参数更新 with torch.no_grad(): w -= 0.1 * w.grad w.grad.zero_() # 第二批次(用更新后的参数) X_batch = torch.tensor([[-1.0, 1.0]]) y_pred = torch.sigmoid(X_batch @ w + b) loss.backward() print("第二批次梯度:", w.grad) # 输出: tensor([0.5100, -0.4900])
7. 总结
  • 梯度公式固定:由模型结构和损失函数决定。

  • 梯度值动态变化:依赖当前参数值和输入数据。

  • 反向传播的作用:自动计算当前参数和数据下的梯度值,指导参数更新。

    • 在 Python 中,只有定义了__call__方法的对象才能被直接调用(如函数或实现了__call__的类)。

    • 你的LogisticRegression类可能没有实现__call__,而是定义了forward方法。

  • 正确的调用方式

    • 如果模型是 PyTorch 的nn.Module子类,应该通过forward方法或直接调用实例(但需继承nn.Module并正确实现)。

    • class LogisticRegression(nn.Module):
    • 如果是从零实现的类,需显式调用forward

    • # 必须显式调用 forward model = LogisticRegression(2) y_pred = model.forward(x_batch) # 正确:调用 forward 方法

      8.一些公式

    • 偏置项的作用

      在机器学习模型中,偏置项(bias term)用于调整模型的输出基准。对于线性模型(如 Softmax 回归),偏置项允许决策边界不强制经过原点。
      数学形式:

      z=xW+bz=xW+b

      其中:

    • x∈R784x∈R784:输入特征(展平后的图像像素)

    • W∈R784×10W∈R784×10:权重矩阵

    • b∈R10b∈R10:偏置向量

    • X_train = np.hstack([X_train, np.ones((X_train.shape[0], 1))]) # [60000, 784] → [60000, 785]
    • np.ones((X_train.shape[0], 1))
      生成形状为[60000, 1]的全1矩阵,代表偏置项。

      python

      # 示例输出(前3行): [[1.], [1.], [1.]]
    • np.hstack
      水平拼接原特征矩阵和全1列,将偏置项合并到特征中。

      python

      # 拼接前:X_train.shape = [60000, 784] # 拼接后:X_train.shape = [60000, 785]
    • 3. 数学等价性

      合并后的操作等价于:

      z=[x 1][W b]T(转置)=xW+bz
    • 合并后的权重矩阵
      W′∈R785×10W′∈R785×10,其中最后一行是偏置向量 bb。

    • 4. 数字示例

      假设某样本原始特征(已归一化):

      python

      x = [0.1, 0.5, 0.3, ..., 0.8] # 长度784 x_with_bias = [0.1, 0.5, 0.3, ..., 0.8, 1.0] # 长度785
    • 5. 为什么需要显式添加偏置项?
    • 模型简化:将 WW 和 bb 合并为单一矩阵 W′W′,便于统一计算。

    • 代码实现:避免在梯度计算中单独处理 bb。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询