反向传播算法不收敛求助:单隐藏层神经网络预测错误排查
你的代码存在多处核心逻辑错误,这些错误直接导致网络无法正确学习,以下是具体问题和修正方案:
1. 前向传播的致命错误:Z2计算误用Z1而非A1
在fit和predict方法中,计算输出层线性组合Z2时,你错误使用了隐藏层的原始线性输出Z1,而非经过sigmoid激活后的A1。这导致隐藏层的非线性变换完全失效,网络退化为线性模型,根本无法拟合非线性数据。
错误代码:
# fit方法中 Z2 = np.dot(Z1, self.output_weights) + self.output_bias # predict方法中 Z2 = np.dot(Z1, self.output_weights) + self.output_bias
修正后:
# fit方法中 Z2 = np.dot(A1, self.output_weights) + self.output_bias # predict方法中 Z2 = np.dot(A1, self.output_weights) + self.output_bias
2. 隐藏层权重更新的维度与梯度错误
self.hidden_weights的形状是(输入特征数, 隐藏节点数),而delta_hidden_layer是(隐藏节点数,)的向量。直接用delta_hidden_layer更新权重会触发维度不匹配错误,且正确的梯度应该是输入样本x_one与delta_hidden_layer的外积(每个输入特征对每个隐藏节点的梯度贡献)。
错误代码:
self.hidden_weights -= self.alpha*delta_hidden_layer
修正后:
self.hidden_weights -= self.alpha * np.outer(x_one, delta_hidden_layer)
3. 隐藏层偏置更新逻辑错误
隐藏层每个节点的偏置梯度就是对应位置的delta_hidden_layer值,不需要对error_hidden_layer求和——求和会抹除每个节点的独立梯度信息,导致偏置更新完全错误。
错误代码:
self.hidden_bias -= self.alpha*np.sum(error_hidden_layer)
修正后:
self.hidden_bias -= self.alpha * delta_hidden_layer
4. 输出层权重更新的梯度计算错误
self.output_weights的形状是(隐藏节点数, 1),正确的梯度应该是隐藏层激活输出A1与delta_output_layer的外积,这样才能匹配权重的维度。直接使用delta_output_layer更新会导致维度不匹配,且梯度计算完全错误。
错误代码:
self.output_weights -= self.alpha*delta_output_layer
修正后:
self.output_weights -= self.alpha * np.outer(A1, delta_output_layer)
5. 输出层偏置更新优化
输出层偏置的梯度就是delta_output_layer本身,不需要对原始error求和——delta_output_layer已经包含了误差与激活导数的乘积,是更准确的梯度值。
错误代码:
self.output_bias -= self.alpha*np.sum(error)
修正后:
self.output_bias -= self.alpha * delta_output_layer
修正后的完整代码
import numpy as np class ShallowNeuralNetwork: def sigmoid(self, val): return 1 / (1 + np.exp(-val)) def sigmoid_derivative(self, val): return val *(1 - val) def __init__(self, hidden_nodes, alpha, epochs): self.hidden_nodes = hidden_nodes self.alpha = alpha self.epochs = epochs self.hidden_weights = None self.hidden_bias = None self.output_weights = None self.output_bias = None def fit(self, X, y): self.hidden_weights = np.random.rand(X.shape[1], self.hidden_nodes) self.hidden_bias = np.random.rand(self.hidden_nodes) self.output_weights = np.random.rand(self.hidden_nodes,1) self.output_bias = np.random.rand(1) for _ in range(self.epochs): for x_one, y_one in zip(X, y): Z1 = np.dot(x_one, self.hidden_weights) + self.hidden_bias A1 = self.sigmoid(Z1) # 修正Z2的计算,使用A1而非Z1 Z2 = np.dot(A1, self.output_weights) + self.output_bias A2 = self.sigmoid(Z2) error = A2 - y_one delta_output_layer = error * self.sigmoid_derivative(A2) error_hidden_layer = np.dot(delta_output_layer, self.output_weights.T) delta_hidden_layer = error_hidden_layer * self.sigmoid_derivative(A1) # 修正隐藏层权重更新 self.hidden_weights -= self.alpha * np.outer(x_one, delta_hidden_layer) # 修正隐藏层偏置更新 self.hidden_bias -= self.alpha * delta_hidden_layer # 修正输出层权重更新 self.output_weights -= self.alpha * np.outer(A1, delta_output_layer) # 修正输出层偏置更新 self.output_bias -= self.alpha * delta_output_layer def predict(self, X): Z1 = np.dot(X, self.hidden_weights) + self.hidden_bias A1 = self.sigmoid(Z1) # 修正Z2的计算,使用A1而非Z1 Z2 = np.dot(A1, self.output_weights) + self.output_bias pred = self.sigmoid(Z2) return pred
内容的提问来源于stack exchange,提问作者Smitesh Patil

