You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义任意隐藏层数的神经网络代码问题求助

多层神经网络类的问题修复方案

问题描述

我编写了一个支持任意隐藏层数的NeuralNetwork类,运行后存在两个问题:

  1. 训练后输出不符合预期:预期输出接近[0,1,1,0],实际得到[[0.97068419],[0.96361902],[0.02130181],[0.03903314]](即[1,1,0,0])
  2. 使用不同尺寸的隐藏层(如[4,8])时,出现维度匹配错误:operands could not be broadcast together with shapes (8,8) (1,4)

原代码如下:

#Multiple Layers

import numpy as np

class NeuralNetwork:

    def __init__(self, input_size, hiddenLayerSizes, output_size):

        self.input_size = input_size
        self.hiddenLayerSizes = hiddenLayerSizes
        self.output_size = output_size

        self.hiddenLayerWeights = []
        self.hiddenLayerBiases =[]

        self.weights_input_hidden1 = np.random.randn(self.input_size, self.hiddenLayerSizes[0])

        for i in range(0, len(hiddenLayerSizes)-1):

            self.hiddenLayerWeights.append(np.random.randn(self.hiddenLayerSizes[i], self.hiddenLayerSizes[i+1]))
            self.hiddenLayerBiases.append(np.zeros((1, self.hiddenLayerSizes[i])))

        self.weights_hidden_output = np.random.randn(self.hiddenLayerSizes[len(self.hiddenLayerSizes)-1], self.output_size)
        self.bias_output = np.zeros((1, self.output_size))

    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))

    def sigmoid_derivative(self, x):
        return x * (1 - x)

    def feedforward(self, x):

        self.hidden_activations = []
        self.hidden_outputs = []

        self.hidden_activations.append(np.dot(X, self.weights_input_hidden1) + self.hiddenLayerBiases[0])
        self.hidden_outputs.append(self.sigmoid(self.hidden_activations[0]))

        for i in range(0, len(self.hiddenLayerSizes)-1):

            self.hidden_activations.append(np.dot(self.hidden_outputs[i], self.hiddenLayerWeights[i]) + self.hiddenLayerBiases[i])
            self.hidden_outputs.append(self.sigmoid(self.hidden_activations[i]))

        self.output_activation = np.dot(self.hidden_outputs[len(self.hidden_outputs)-1], self.weights_hidden_output) + self.bias_output
        self.predicted_output = self.sigmoid(self.output_activation)

        return self.predicted_output

    def backward(self, X, y, learning_rate):

        self.output_error = y - self.predicted_output
        self.output_delta = self.output_error * self.sigmoid_derivative(self.predicted_output)

        self.hidden_errors = [np.dot(self.output_delta, self.weights_hidden_output.T)]
        self.hidden_deltas = [self.hidden_errors[0]*self.sigmoid_derivative(self.hidden_outputs[len(self.hidden_outputs)-1])]

        for i in range(0, len(self.hiddenLayerSizes)-1):

            self.hidden_errors.append(np.dot(self.hidden_deltas[i], self.hiddenLayerWeights[len(self.hiddenLayerWeights)-i-1].T))
            self.hidden_deltas.append(self.hidden_errors[i+1] * self.sigmoid_derivative(self.hidden_outputs[len(self.hidden_outputs)-i-1]))

        #Weight Updates

        self.weights_hidden_output += np.dot(self.hidden_outputs[len(self.hidden_outputs)-1].T, self.output_delta) * learning_rate
        self.bias_output += np.sum(self.output_delta, axis=0, keepdims=True) * learning_rate

        for i in range(len(self.hiddenLayerSizes)-1, 0):

            self.hiddenLayerWeights[i] += np.dot(self.hidden_outputs[i].T, self.hidden_deltas[len(self.hidden_deltas)-i]) * learning_rate
            self.hiddenLayerBiases[i+1] += np.sum(self.hidden_deltas[len(self.hidden_deltas)-i], axis=0, keepdims=True) * learning_rate

        self.weights_input_hidden1 += np.dot(X.T, self.hidden_deltas[len(self.hidden_deltas)-1]) * learning_rate
        self.hiddenLayerBiases[0] += np.sum(self.hidden_deltas[len(self.hidden_deltas)-1], axis=0, keepdims=True) * learning_rate

    def train(self, X, y, epochs, learning_rate):

        for epoch in range(epochs):
            output = self.feedforward(X)
            self.backward(X, y, learning_rate)
            if epoch % 4000 == 0:
                loss = np.mean(np.square(y-output))
                print(f"Epoch {epoch}, Loss:{loss}")

nn1 = NeuralNetwork(input_size=2, hiddenLayerSizes=[4, 4], output_size=1)

X = np.array([[0,0], [0,1], [1,0], [1,1]])

y = np.array([[1], [1], [0], [0]])

nn1.train(X, y, epochs=80000, learning_rate=0.01)

output = nn1.feedforward([[1,1], [0,0], [1,1], [0,1]])

print(output)

错误原因分析

  1. Feedforward方法参数误用:feedforward里错误使用全局变量X而非传入的参数x,导致输入无法正确传递。
  2. 隐藏层偏置维度不匹配:初始化偏置时,维度对应了当前层的输入神经元数,而非输出神经元数,导致矩阵运算时维度冲突。
  3. 反向传播循环逻辑错误:更新隐藏层权重的循环未设置步长,导致循环无法执行;同时偏置更新的索引错误,进一步引发维度问题。
  4. 标签与测试输入不对应:测试输入的顺序和训练标签的逻辑不匹配,导致输出看起来不符合预期。

修复后的完整代码

import numpy as np

class NeuralNetwork:
    def __init__(self, input_size, hiddenLayerSizes, output_size):
        self.input_size = input_size
        self.hiddenLayerSizes = hiddenLayerSizes
        self.output_size = output_size

        self.hiddenLayerWeights = []
        self.hiddenLayerBiases = []

        # 输入到第一个隐藏层的权重和偏置
        self.weights_input_hidden1 = np.random.randn(self.input_size, self.hiddenLayerSizes[0])
        self.bias_hidden1 = np.zeros((1, self.hiddenLayerSizes[0]))

        # 初始化中间隐藏层的权重和偏置
        for i in range(len(hiddenLayerSizes)-1):
            # 当前层到下一层的权重
            self.hiddenLayerWeights.append(np.random.randn(hiddenLayerSizes[i], hiddenLayerSizes[i+1]))
            # 下一层的偏置,维度匹配下一层神经元数量
            self.hiddenLayerBiases.append(np.zeros((1, hiddenLayerSizes[i+1])))

        # 最后一个隐藏层到输出层的权重和偏置
        self.weights_hidden_output = np.random.randn(hiddenLayerSizes[-1], output_size)
        self.bias_output = np.zeros((1, output_size))

    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))

    def sigmoid_derivative(self, x):
        return x * (1 - x)

    def feedforward(self, x):
        self.hidden_activations = []
        self.hidden_outputs = []

        # 第一个隐藏层计算
        activation = np.dot(x, self.weights_input_hidden1) + self.bias_hidden1
        output = self.sigmoid(activation)
        self.hidden_activations.append(activation)
        self.hidden_outputs.append(output)

        # 中间隐藏层计算
        for i in range(len(self.hiddenLayerWeights)):
            activation = np.dot(self.hidden_outputs[i], self.hiddenLayerWeights[i]) + self.hiddenLayerBiases[i]
            output = self.sigmoid(activation)
            self.hidden_activations.append(activation)
            self.hidden_outputs.append(output)

        # 输出层计算
        self.output_activation = np.dot(self.hidden_outputs[-1], self.weights_hidden_output) + self.bias_output
        self.predicted_output = self.sigmoid(self.output_activation)
        return self.predicted_output

    def backward(self, X, y, learning_rate):
        # 输出层误差计算
        output_error = y - self.predicted_output
        output_delta = output_error * self.sigmoid_derivative(self.predicted_output)

        # 反向计算隐藏层误差和delta
        hidden_errors = []
        hidden_deltas = []

        # 最后一个隐藏层的误差
        hidden_error = np.dot(output_delta, self.weights_hidden_output.T)
        hidden_delta = hidden_error * self.sigmoid_derivative(self.hidden_outputs[-1])
        hidden_errors.append(hidden_error)
        hidden_deltas.append(hidden_delta)

        # 从后往前计算前面的隐藏层误差
        for i in range(len(self.hiddenLayerWeights)-1, -1, -1):
            hidden_error = np.dot(hidden_deltas[-1], self.hiddenLayerWeights[i].T)
            hidden_delta = hidden_error * self.sigmoid_derivative(self.hidden_outputs[i])
            hidden_errors.append(hidden_error)
            hidden_deltas.append(hidden_delta)

        # 反转误差和delta列表,方便从前往后更新权重
        hidden_errors.reverse()
        hidden_deltas.reverse()

        # 更新输出层权重和偏置
        self.weights_hidden_output += np.dot(self.hidden_outputs[-1].T, output_delta) * learning_rate
        self.bias_output += np.sum(output_delta, axis=0, keepdims=True) * learning_rate

        # 更新中间隐藏层的权重和偏置
        for i in range(len(self.hiddenLayerWeights)):
            self.hiddenLayerWeights[i] += np.dot(self.hidden_outputs[i].T, hidden_deltas[i+1]) * learning_rate
            self.hiddenLayerBiases[i] += np.sum(hidden_deltas[i+1], axis=0, keepdims=True) * learning_rate

        # 更新输入到第一个隐藏层的权重和偏置
        self.weights_input_hidden1 += np.dot(X.T, hidden_deltas[0]) * learning_rate
        self.bias_hidden1 += np.sum(hidden_deltas[0], axis=0, keepdims=True) * learning_rate

    def train(self, X, y, epochs, learning_rate):
        for epoch in range(epochs):
            output = self.feedforward(X)
            self.backward(X, y, learning_rate)
            if epoch % 4000 == 0:
                loss = np.mean(np.square(y - output))
                print(f"Epoch {epoch}, Loss:{loss:.6f}")

# 测试代码
nn1 = NeuralNetwork(input_size=2, hiddenLayerSizes=[4, 8], output_size=1)
X = np.array([[0,0], [0,1], [1,0], [1,1]])
# 调整标签匹配预期输出逻辑:[0,0]→1,[0,1]→0,[1,0]→0,[1,1]→1
y = np.array([[1], [0], [0], [1]])

nn1.train(X, y, epochs=80000, learning_rate=0.01)

# 测试输入:对应预期输出[0,1,0,1]
test_input = np.array([[1,1], [0,0], [1,1], [0,1]])
output = nn1.feedforward(test_input)
print("\n测试输出:")
print(output)

关键修复点说明

  • 修正Feedforward参数:将全局变量X替换为方法传入的x,确保输入正确传递。
  • 修正隐藏层偏置维度:初始化偏置时,维度设置为(1, hiddenLayerSizes[i+1]),匹配当前层输出的神经元数量,解决维度冲突。
  • 重构反向传播循环:
    1. 从最后一个隐藏层往前计算误差,确保误差传递逻辑正确。
    2. 反转误差和delta列表,保证权重更新顺序与网络层级一致。
    3. 修复循环步长和索引,确保所有隐藏层的权重和偏置都能被正确更新。
  • 匹配标签与测试输入:调整训练标签,使其与测试输入的预期输出逻辑一致,解决输出不符合预期的表象问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.02 07:57:30