You need to enable JavaScript to run this app. 自实现前馈神经网络求解XOR时输出相近随机值,照搬参考代码仍训练失效
优惠活动
大模型
产品
解决方案
定价
更多

自实现前馈神经网络求解XOR时输出相近随机值,照搬参考代码仍训练失效

训练异常的核心根因
  • 反向传播循环边界错误,隐藏层权重从未更新。以2-2-1结构的XOR网络为例,整个网络一共仅2组可训练权重(输入层→隐藏层、隐藏层→输出层),原代码写的循环为for i in range(2, len(self.weights))。由于range函数是左闭右开区间,这个循环的实际执行范围是空集——代码仅能更新输出层的权重和偏置,所有隐藏层、输入层连接的权重从初始化完成后就从未发生过任何变化。这直接解释了你观察到的所有共性现象:不同随机初始化下输出高度相似、权重更新幅度极小、无论调整多少训练轮次都无法学到非线性边界、简单线性分类任务也无法收敛。
  • 梯度更新方向完全写反。原代码计算得到损失对权重的梯度后,使用+=执行权重更新。而梯度下降算法需要沿着损失减小的方向调整参数,也就是需要用当前权重减去“梯度值*学习率”,原写法实际是朝着增大损失的方向调整参数,会直接导致网络要么在最优点附近来回振荡(对应你观察到的误差曲线无明显下降、整体振荡的特征),要么直接跑到sigmoid激活函数的饱和区,把所有输出压到接近0或1的固定值(比如学习率设为100时输出全为e-20级别的极小值,就是权重被反方向调整到让sigmoid输入达到极大值、触发饱和的典型表现)。
  • 前向传播存储逻辑冗余。代码前向传播过程仅存储了各层的加权和z,没有存储各层的激活输出a,导致梯度计算时需要重复调用激活函数,既增加了冗余计算,也提高了逻辑出错的概率。
代码修正方案
  1. 修正反向传播的循环边界:将原循环代码for i in range(2, len(self.weights))改为for i in range(2, len(self.weights)+1),确保所有隐藏层的连接权重都能被遍历更新。以2-5-5-1结构的三层权重网络为例,修正后循环会依次遍历更新倒数第二层、最靠近输入层的权重,完成全网络的梯度回传。
  2. 修正梯度更新的方向符号:将原代码中所有针对权重、偏置的+=更新逻辑改为-=,确保参数沿着减小损失的方向调整。
  3. 优化前向传播的存储逻辑:新增激活值列表存储每层的输出,避免重复计算激活函数。在backprop方法开头初始化激活值集合self.aCollection = [a],每计算完一层的激活值后将其追加到集合中,后续计算权重梯度时直接从集合中取对应层的激活值即可,无需重复调用激活函数计算。
  4. 合理配置超参数:修正逻辑后,2-2-1结构的网络使用学习率0.5~1,训练500~1000轮即可稳定收敛到XOR的正确分类结果,无需设置过高的学习率(如学习率100极易触发sigmoid的饱和区,导致训练发散)。
  5. 优化激活函数的实现:将原activationPrime中重复定义的lambda逻辑替换为直接复用静态的activation方法,减少重复代码。

参考修正后的核心反向传播片段:

def backprop(self, a, o):
    o = np.array(o)
    self.zCollection = []
    self.aCollection = [a]  # 存储各层激活值,首元素为原始输入
    # 前向传播
    for w,b in zip(self.weights, self.biases):
        z = np.matmul(w, a) + b
        self.zCollection.append(z)
        a = self.activation(z)
        self.aCollection.append(a)
    
    # 计算输出层误差并更新参数
    error = (a - o) * self.activationPrime(self.zCollection[-1])
    self.weights[-1] -= np.matmul(error, self.aCollection[-2].T) * self.rate
    self.biases[-1] -= error * self.rate
    
    # 回传更新隐藏层参数
    for i in range(2, len(self.weights)+1):
        error = np.multiply(self.weights[-i+1].T * error, self.activationPrime(self.zCollection[-i]))
        self.weights[-i] -= np.matmul(error, self.aCollection[-i-1].T) * self.rate
        self.biases[-i] -= error * self.rate

@staticmethod
def activationPrime(x):
    s = NeuralNetwork.activation(x)
    return s * (1 - s)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.25 04:15:03