如何在基于Moons数据集的ANN中添加隐藏层及解决低精度问题
解决双隐藏层ANN精度过低的问题
看起来你在把单隐藏层扩展到双隐藏层时遇到了梯度传播或参数初始化的问题,50%精度基本相当于随机猜测,说明模型根本没在学习。我先给你修正后的双隐藏层核心代码,再拆解问题根源和调试步骤。
修正后的双隐藏层核心代码
1. 前向传播(新增第二层隐藏层)
def forward_propagation(X, W1, b1, W2, b2, W3, b3): forward_params = {} # 第一层隐藏层 Z1 = np.dot(W1, X.T) + b1 A1 = relu(Z1) # 第二层隐藏层(新增) Z2 = np.dot(W2, A1) + b2 A2 = relu(Z2) # 输出层 Z3 = np.dot(W3, A2) + b3 A3 = sigmoid(Z3) forward_params = { "Z1": Z1, "A1": A1, "Z2": Z2, "A2": A2, "Z3": Z3, "A3": A3, } return forward_params
2. 反向传播(链式法则从输出层往回推)
def backward_propagation(forward_params, X, Y): A3 = forward_params["A3"] Z3 = forward_params["Z3"] A2 = forward_params["A2"] Z2 = forward_params["Z2"] A1 = forward_params["A1"] Z1 = forward_params["Z1"] data_size = Y.shape[1] # 输出层梯度 dZ3 = A3 - Y dW3 = np.dot(dZ3, A2.T) / data_size db3 = np.sum(dZ3, axis=1, keepdims=True) / data_size # 用keepdims避免形状广播错误 # 第二层隐藏层梯度(新增) dZ2 = np.dot(W3.T, dZ3) * prime_relu(Z2) dW2 = np.dot(dZ2, A1.T) / data_size db2 = np.sum(dZ2, axis=1, keepdims=True) / data_size # 第一层隐藏层梯度 dZ1 = np.dot(W2.T, dZ2) * prime_relu(Z1) dW1 = np.dot(dZ1, X) / data_size db1 = np.sum(dZ1, axis=1, keepdims=True) / data_size grads = { "dZ3": dZ3, "dW3": dW3, "db3": db3, "dZ2": dZ2, "dW2": dW2, "db2": db2, "dZ1": dZ1, "dW1": dW1, "db1": db1, } return grads
3. 主函数(新增第二层参数初始化与更新)
def two_hidden_layers_model(X, y, epochs=10000, learning_rate=0.01): np.random.seed(0) input_size = X.shape[1] output_size = 1 hidden_layer_nodes = 4 # 改用He初始化(更适配ReLU激活函数) W1 = np.random.randn(hidden_layer_nodes, input_size) * np.sqrt(2 / input_size) b1 = np.zeros((hidden_layer_nodes, 1)) W2 = np.random.randn(hidden_layer_nodes, hidden_layer_nodes) * np.sqrt(2 / hidden_layer_nodes) b2 = np.zeros((hidden_layer_nodes, 1)) W3 = np.random.randn(output_size, hidden_layer_nodes) * np.sqrt(2 / hidden_layer_nodes) b3 = np.zeros((output_size, 1)) loss_history = [] for i in range(epochs): forward_params = forward_propagation(X, W1, b1, W2, b2, W3, b3) A3 = forward_params["A3"] loss = loss_function(A3, y) grads = backward_propagation(forward_params, X, y) # 更新所有参数 W1 -= learning_rate * grads["dW1"] b1 -= learning_rate * grads["db1"] W2 -= learning_rate * grads["dW2"] b2 -= learning_rate * grads["db2"] W3 -= learning_rate * grads["dW3"] b3 -= learning_rate * grads["db3"] if i % 1000 == 0: loss_history.append(loss) print(f"Iteration {i}: Loss = {loss:.4f}") return W1, b1, W2, b2, W3, b3
精度过低的核心原因&调试步骤
形状不匹配导致参数更新错误
你原来的单隐藏层代码中用reshape修复了db1的形状,但双隐藏层时很容易漏掉db2、db3的维度保持。用keepdims=True可以确保偏置的梯度始终是(n,1)的形状,和偏置参数本身形状一致,避免广播错误导致参数更新完全失效。初始化方式不适配ReLU
你之前用的Xavier初始化更适合sigmoid/tanh,ReLU激活函数会“杀死”一半神经元,改用He初始化(乘以sqrt(2/输入维度))能让每层激活值的方差保持稳定,避免梯度消失。训练轮数&学习率不足
双隐藏层模型比单隐藏层需要更多训练轮数(建议从10000开始),同时学习率0.003可能太小,试试0.01或0.005,也可以加入学习率衰减(比如每5000轮学习率乘以0.9)。反向传播逻辑错误
一定要验证反向传播的梯度是否正确:写一个数值梯度函数,对每个参数做微小扰动计算损失变化,和反向传播得到的梯度对比,误差应该在1e-7以内,否则说明链式法则的推导有bug。ReLU导数是否正确
检查你的prime_relu函数:def prime_relu(Z): return np.where(Z > 0, 1, 0)如果导数写错了,梯度传播直接失效,模型根本学不到东西。
内容的提问来源于stack exchange,提问作者Dametime
相关产品推荐
相关产品推荐

