手写二分类神经网络大数据集下损失停滞不收敛问题排查
你手写的二分类网络(隐藏层ReLU、输出层Sigmoid、二分类交叉熵损失、小批量梯度下降优化)小数据集可收敛、大数据集损失卡在0.7(随机猜测水平),代码存在6处明确逻辑错误,逐处说明:
错误1:输入归一化未生效
你写了MinMaxScaler将输入归一化到0-1区间并存为X_scale,但拆分数据集时直接使用了未归一化的原始数组X,还注释掉了调用X_scale拆分的代码。未归一化的特征尺度差异极大,会导致梯度更新步长完全失控,数据规模越大收敛越困难。
修正:拆分训练/验证/测试集时使用归一化后的X_scale替换原始X。错误2:反向传播批次样本数取值错误
反向传播函数中m = Y.shape[1]写法完全错误:传入的标签Y被reshape为(batch_size, 1)形状,Y.shape[1]恒等于1,根本不是当前批次的样本数量,导致梯度缩放系数完全错误。小数据集时梯度量级偏差还能偶然收敛,数据量变大后梯度方向完全失真。
修正:将该行改为m = Y.shape[0],取当前批次的实际样本数。错误3:ReLU导数计算传参错误
反向传播计算隐藏层误差时,你给ReLU导数传入的是激活后的输出a1、a2,但ReLU导数是对激活前的线性输出z求导,应该传入z1、z2。传入激活值会导致所有z<0位置的导数计算错误,直接截断梯度传播路径。
修正:将self.Relu_Derivative(self.a2)改为self.Relu_Derivative(self.z2),self.Relu_Derivative(self.a1)改为self.Relu_Derivative(self.z1)。错误4:偏置项梯度更新维度错误
你直接将形状为(batch_size, 神经元数)的误差矩阵作为偏置梯度更新参数,而偏置是每个神经元对应一个标量,梯度应该沿批次维度聚合后再更新。直接用矩阵更新会触发numpy广播机制,把偏置的维度和数值完全搞乱,数据量越大错误累积越快。
修正:偏置更新时沿批次维度求均值,参考代码:self.b1 = self.b1 - lr * np.mean(self.error_term_hidden1, axis=0) self.b2 = self.b2 - lr * np.mean(self.error_term_hidden2, axis=0) self.b3 = self.b3 - lr * np.mean(self.error_term_output, axis=0)错误5:训练时标签reshape硬编码错误
训练循环中你写死y = np.reshape(np.array(batch[1]),(32, 1)),但小批量生成逻辑里最后一个批次是凑不满32个样本的剩余数据,硬编码reshape会直接打乱标签顺序、甚至触发维度不匹配报错,数据量越大不完整批次带来的标签错误对训练的干扰越强。
修正:去掉硬编码的32,按batch实际长度reshape:y = np.array(batch[1]).reshape(-1, 1)错误6:最后一个小批量切片索引错误
生成小批量时,取最后剩余样本的切片起始索引写为n_minibatches * minibatch_size - 1,会和上一个批次的最后一个样本重复,导致每个epoch都存在样本重复训练、部分样本漏训的问题,数据量越大,样本重复/遗漏带来的梯度偏差越明显。
修正:去掉索引的-1偏移,参考代码:Lat_X_minibatch = X_shuffled[n_minibatches * minibatch_size:, :] Last_Y_minibatch = Y_shuffled[n_minibatches * minibatch_size:]
额外优化建议
当前权重初始化用0.01*np.random.randn对于3层ReLU网络来说初始值过小,会导致前期梯度传播太慢,可以换成He初始化(权重按np.random.randn(n_in, n_out) * np.sqrt(2/n_in)生成),收敛速度会明显提升,但这不是损失卡在0.7的核心原因,优先修复上述6个逻辑错误即可正常收敛。
内容的提问来源于stack exchange,提问作者that annoying kid

