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

为何在TensorFlow中需对网络权重与偏置执行两次更新?

问题解释:两次参数更新的原因及代码问题

问题描述

我用TensorFlow实现了DenseLayer和DenseNetwork类,但搞不懂为什么要对权重和偏置做两次更新:一次在DenseLayer的backward方法里,另一次在DenseNetwork的train方法里。一开始以为是笔误,结果移除第二次更新后损失就不再下降了,想知道这是为什么。

实现代码

class DenseLayer(tf.Module):
    def __init__(self, input_size, output_size):
        super(DenseLayer, self).__init__()
        self.input_size = input_size
        self.output_size = output_size

        self.weights = tf.Variable(tf.random.normal([input_size, output_size], stddev=0.01))
        self.biases = tf.Variable(tf.zeros([output_size]))

    def __call__(self, inputs):
        return self.forward(inputs)

    def forward(self, inputs):
        self.last_input = inputs
        self.z = tf.matmul(inputs, self.weights) + self.biases
        return self.z

    def backward(self, d_L_d_out, learning_rate):
        with tf.GradientTape(persistent=True) as tape:
            d_L_d_inputs = tf.matmul(d_L_d_out, tf.transpose(self.weights))

        d_L_d_weights = tape.gradient(d_L_d_inputs, self.weights)
        d_L_d_biases = tape.gradient(d_L_d_inputs, self.biases)

        self.weights.assign_sub(learning_rate * d_L_d_weights)
        self.biases.assign_sub(learning_rate * d_L_d_biases)

        return d_L_d_inputs

class DenseNetwork(tf.Module):
    def __init__(self):
        super(DenseNetwork, self).__init__()
        self.layers = []

    def add_layer(self, layer):
        self.layers.append(layer)

    def __call__(self, inputs):
        return self.forward(inputs)


    def forward(self, inputs):
        for layer in self.layers:
            inputs = layer.forward(inputs)
        return inputs

    def backward(self, d_L_d_out, learning_rate):
        for layer in reversed(self.layers):
            d_L_d_out = layer.backward(d_L_d_out, learning_rate)

    def train(self, inputs, targets, learning_rate, epochs):
        for epoch in range(epochs):
            loss = 0
            with tf.GradientTape(persistent=True) as tape:
                output = self.forward(inputs)
                loss = tf.reduce_mean(tf.square(output - targets))

            grads = tape.gradient(loss, self.trainable_variables)
            for i, layer in enumerate(self.layers):
              layer.biases.assign(layer.biases - learning_rate*grads[2*i])
              layer.weights.assign(layer.weights - learning_rate * grads[2*i+1])

            if (epoch + 1) % 10 == 0:
                tf.print(f"Epoch {epoch+1} - Loss: {loss}")

    def loss(self, output, target):
        return tf.reduce_mean(tf.square(output - target))

核心原因:第一次更新完全错误

你遇到的问题本质是DenseLayer的backward方法里的梯度计算逻辑完全错误,第一次更新根本没有起到正确的反向传播优化作用,只有train方法里的第二次更新是有效的。

具体分析:

  1. 正确的全连接层梯度计算:
    • 权重梯度应该是tf.matmul(tf.transpose(self.last_input), d_L_d_out)(输入的转置乘输出的损失梯度)
    • 偏置梯度应该是tf.reduce_sum(d_L_d_out, axis=0)(输出损失梯度在样本维度求和)
  2. 你的backward方法错误点:
    你用GradientTape包裹d_L_d_inputs = tf.matmul(d_L_d_out, tf.transpose(self.weights)),然后求d_L_d_inputs对权重的梯度——这个梯度和“损失对权重的梯度”完全无关,只是一个数学上的偏导数值,用它更新参数等于瞎调整,根本无法让损失下降。
  3. 为什么移除第二次更新后损失不下降:
    因为第一次更新是无效的,只有train方法里通过GradientTape追踪整个前向传播和损失计算得到的梯度才是正确的,这部分更新才真正在优化模型。现在两次更新并存时,第二次正确的更新抵消了第一次错误更新的影响,模型还能正常优化;一旦去掉第二次,只剩下错误的更新,模型自然无法学习。

修正方案

你需要二选一,统一参数更新的逻辑:

方案1:保留train方法的自动梯度更新,去掉backward里的参数更新

修改DenseLayer的backward方法,只负责计算传递给上一层的梯度,不更新参数:

def backward(self, d_L_d_out):
    # 只计算对输入的梯度,传递给上一层
    d_L_d_inputs = tf.matmul(d_L_d_out, tf.transpose(self.weights))
    return d_L_d_inputs

train方法保持不变,继续用自动梯度更新参数。

方案2:用手动计算梯度的方式在backward里更新参数,去掉train里的更新

修正DenseLayer的backward方法,用正确的手动梯度计算更新参数:

def backward(self, d_L_d_out, learning_rate):
    # 正确计算权重和偏置的梯度
    d_L_d_weights = tf.matmul(tf.transpose(self.last_input), d_L_d_out)
    d_L_d_biases = tf.reduce_sum(d_L_d_out, axis=0)
    # 更新参数
    self.weights.assign_sub(learning_rate * d_L_d_weights)
    self.biases.assign_sub(learning_rate * d_L_d_biases)
    # 计算对输入的梯度,传递给上一层
    d_L_d_inputs = tf.matmul(d_L_d_out, tf.transpose(self.weights))
    return d_L_d_inputs

然后修改train方法,调用backward完成更新:

def train(self, inputs, targets, learning_rate, epochs):
    for epoch in range(epochs):
        with tf.GradientTape() as tape:
            output = self.forward(inputs)
            loss = self.loss(output, targets)
        # 计算损失对模型输出的梯度
        d_L_d_out = tape.gradient(loss, output)
        # 反向传播更新所有层的参数
        self.backward(d_L_d_out, learning_rate)
        
        if (epoch + 1) % 10 == 0:
            tf.print(f"Epoch {epoch+1} - Loss: {loss}")

内容的提问来源于stack exchange,提问作者unit 1991

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 20:07:14