sigmoid导数在反向传播中的应用及Python神经网络权重更新问题
一、sigmoid导数在反向传播中的应用位置
你实现的sigmoid_derr函数核心作用是计算每层的误差敏感度delta,也就是当前层的输出变化会多大程度影响最终的总误差,它是反向传播里每层必须用到的计算因子:
- 输出层:先算出预测值和真实值的误差,再乘
sigmoid_derr(当前层输出),得到输出层的delta,作为输出层权重调整的核心参考 - 隐藏层:先拿到后一层传递过来的误差,同样乘
sigmoid_derr(当前层输出)得到隐藏层自身的delta
你可以简单理解为:sigmoid导数是给每层的误差打“调整系数”,因为sigmoid输出接近0或1的时候导数值非常小,代表这个位置的神经元输出已经接近饱和,不需要大幅调整对应权重。
二、神经网络权重的更新逻辑
权重更新的核心目标是让总误差不断变小,所以调整方向要和误差方向相反,还要乘以学习率控制每次调整的幅度,避免调整幅度过大错过最优值。
简化的可直接套用的权重更新公式(不需要额外微积分知识):新权重 = 旧权重 - 学习率 * (当前层输入矩阵转置 点乘 当前层的delta)
三、现有代码的错误点和修正方案
主要问题:
- 权重更新方向错误:你当前代码用了
+=直接加调整量,相当于往误差变大的方向调整权重,所以输出会卡在固定的异常值 - 权重更新的计算参数错误:不应该用
层.output.T参与矩阵乘,应该用层.input.T,因为权重是连接输入和当前层的,调整量要和输入的大小挂钩 - 权重初始化维度不匹配:原代码的权重维度设置反了,会导致矩阵乘法逻辑和反向传播的维度适配出错
- 输入的维度没有统一:单样本输入是一维数组,容易触发numpy的广播机制导致计算偏差
修正后的可运行代码
import numpy as np INPUT = 'INPUT' HIDDEN = 'HIDDEN' OUTPUT = 'OUTPUT' class Layer: def __init__(self, neurons, connections, type): self.neurons = neurons self.connections = connections # 权重维度调整为[输入特征数, 输出神经元数],适配矩阵乘法逻辑 self.weights = np.random.randn(connections, neurons) * 0.01 self.type = type self.learning_rate = 0.1 def sigmoid(self, value): return 1.0 / (1.0 + np.exp(-value)) def sigmoid_derr(self, value): return value * (1 - value) def feed_forward(self, input): # 输入统一转为二维矩阵,避免维度广播错误 self.input = np.array(input, ndmin=2) self.pre_activation = np.dot(self.input, self.weights) self.output = self.sigmoid(self.pre_activation) return self.output def backprop(self, expected=[], prevError=None): if self.type == OUTPUT: self.error = np.subtract(expected, self.output) self.delta = np.multiply(self.error, self.sigmoid_derr(self.output)) self.error = np.dot(self.delta, self.weights.T) else: self.delta = np.multiply(prevError, self.sigmoid_derr(self.output)) self.error = np.dot(self.delta, self.weights.T) # 层初始化参数:第一个参数为当前层输出神经元数,第二个为输入特征数 l1 = Layer(3, 2, type=INPUT) l2 = Layer(4, 3, type=HIDDEN) l3 = Layer(2, 4, type=OUTPUT) for i in range(50000): # 前向传播 l1.feed_forward([0.26, 0.87]) l2.feed_forward(l1.output) l3.feed_forward(l2.output) # 反向传播 l3.backprop(expected=[0.12, 0.92]) l2.backprop(prevError=l3.error) l1.backprop(prevError=l2.error) # 权重更新 l3.weights -= l3.learning_rate * np.dot(l3.input.T, l3.delta) l2.weights -= l2.learning_rate * np.dot(l2.input.T, l2.delta) l1.weights -= l1.learning_rate * np.dot(l1.input.T, l1.delta) if i % 5000 == 0: print(f"迭代次数{i},输出:{l3.output}")
运行后可以看到输出会逐渐收敛到你设定的目标值[0.12, 0.92]。
内容的提问来源于stack exchange,提问作者IRL135
相关产品推荐
相关产品推荐

