多层感知器中的反向传播算法陷入困境

我想实现一个多层感知器(MLP)来解决异或问题。我已经为此绞尽脑汁了很长时间,因为我不是一个出色的科学家,我希望能完全理解这个程序的每一行代码。

我先从几个问题开始:

  1. 是否有针对神经网络的特定调试方法?(例如预定义的已知权重可以引导到预期的解决方案)
  2. 下面的反向传播算法(伪代码)是否正确
  3. 偏置是否应该包含在这个算法中?如果不包含,偏置是否有必要解决异或问题?如果需要,每个神经元、每层还是整个网络应该有一个偏置?
  4. 我的Python实现是否正确?如果不正确,是反向传播、前向传播、缺少偏置还是其他原因?

任何回应都非常受欢迎。这不是“作业”,但如果你非常担心这一点,我会很高兴得到一些好的建议。我还没有正确使用numpy,因为我更像是一个开发者而不是科学家,有时在矩阵方程上遇到困难(我目前正在努力解决)。因此,我很难完全理解我找到的不同实现方式。


编辑:算法现在运行良好,我将突出显示伪代码/Python实现中存在的问题:

理论部分

  • 伪代码在权重调整部分是错误的(我编辑了代码,用WRONG标记了该行并进行了修复)。我使用了输出层输出,而应该使用输入值
  • 实际上,解决异或问题是不需要偏置的,只需要一个隐藏层。这里有趣的是,只有在隐藏层有4个或更多神经元时才有效,而3个不行。然而,经过基准测试,添加偏置可以显著加速达到最优解的时间(加速5倍或更多)

Python实现

  • 权重被随机初始化在0到1之间,而不是-1到1之间,这样是无法工作的

最后,这个资源对理解“神经元魔法”很有帮助。它没有帮助我调试反向传播,但现在很明显,如果没有更多的理论知识,我永远无法让它工作。如果你和我一样,更多的是开发者而不是科学家,我强烈建议你查看一下。


基于Virginie MATHIVET的《开发者的artificial intelligence》一书,这里是反向传播算法的伪代码(这是一本法语书,抱歉翻译不好):

While stop criteria is not achieved:    Initialize d(i)    For each example:        Compute output value s(i)                          #1        For each output neuron weight:                     #2            d(i) = s(i) * (1 - s(i)) * (y(i) - s(i))        EndFor        For each hidden neuron weight:            sum = 0            For each link towards output neuron k:                sum += d(k) * w(i->k)            EndFor            d(i) = o(i) * (1 - o(i)) * sum        EndFor        For each weight of the network:            If link towards output neuron:                w(i) += rate * d(i) * o(i)            Else                w(i) += rate * d(i) * s(i) # WRONG: s(i) should be input(i)            EndIf        EndFor    EndForEndWhile

在上面,d(x)是增量,o(x)是隐藏层输出,s(x)是输出层输出,y(x)是预期输出,w(x)是网络权重。

我理解第#1行是前向传播,而#2行(包括)之后的行是关于反向传播的。因此,正确的算法可以写成:

While stop criteria is not achieved:    Initialize d(i)    For each example:        output = Forward propagation with example inputs                  #1        Backpropagation of the error between output and expected output   #2    EndForEndWhile

我的问题是,算法似乎在反向传播和权重变化中陷入循环,例如,这里是隐藏层的输出:

[Epoch 0, inputs (1.0, 1.0)]: hidden outputs: None[Epoch 0, inputs (0.0, 0.0)]: hidden outputs: [ 0.7755638   0.64556638  0.68163599][Epoch 0, inputs (1.0, 0.0)]: hidden outputs: [ 0.5  0.5  0.5][Epoch 0, inputs (0.0, 1.0)]: hidden outputs: [ 0.60747218  0.58975313  0.55246625][Epoch 1, inputs (1.0, 1.0)]: hidden outputs: [ 0.68911554  0.55079694  0.62718831][Epoch 1, inputs (1.0, 0.0)]: hidden outputs: [ 0.77449528  0.64107552  0.67770194][Epoch 1, inputs (0.0, 0.0)]: hidden outputs: [ 0.60728756  0.58957687  0.55230354][Epoch 1, inputs (0.0, 1.0)]: hidden outputs: [ 0.5  0.5  0.5][Epoch 2, inputs (0.0, 0.0)]: hidden outputs: [ 0.68877278  0.54872848  0.6254074 ][Epoch 2, inputs (1.0, 0.0)]: hidden outputs: [ 0.5  0.5  0.5][Epoch 2, inputs (1.0, 1.0)]: hidden outputs: [ 0.60700878  0.58812487  0.5509695 ][Epoch 2, inputs (0.0, 1.0)]: hidden outputs: [ 0.77344667  0.63591436  0.67311723][Epoch 3, inputs (0.0, 0.0)]: hidden outputs: [ 0.68856723  0.54708942  0.62400827][Epoch 3, inputs (1.0, 0.0)]: hidden outputs: [ 0.5  0.5  0.5]

将示例随机排序不会改变任何事情。此外,我尝试了所有学习率(0.05到0.95),结果相同,所以我认为这不是关于坏的收敛问题。以下是我的Python实现:

def sigmoid(x):    return 1 / (1 + np.exp(-x))def dsigmoid(y):    return y * (1.0 - y)class NeuralNetwork:    def __init__(self, nb_inputs, nb_hidden, nb_outputs, learning_rate):        self.nb_inputs = nb_inputs        self.nb_hidden = nb_hidden        self.nb_outputs = nb_outputs        self.learning_rate = learning_rate        self.output_deltas = None        self.output_weights = np.random.random((nb_outputs, nb_hidden)) # WRONG: should be between -1 and 1, not 0 and 1        self.outputs = None        self.hidden_deltas = None        self.hidden_weights = np.random.random((nb_hidden, nb_inputs)) # WRONG: should be between -1 and 1, not 0 and 1        self.hidden_outputs = None    def forward_propagation(self, inputs):        self.hidden_outputs = np.zeros((self.nb_hidden,))        self.outputs = np.zeros((self.nb_outputs,))        # get outputs for hidden layer        for i in range(self.nb_hidden):            aggregated = sum([inputs[j] * self.hidden_weights[i][j] for j in range(self.nb_inputs)])            self.hidden_outputs[i] = sigmoid(aggregated)        # get inputs for output layer        for i in range(self.nb_outputs):            aggregated = sum([self.hidden_outputs[j] * self.output_weights[i][j] for j in range(self.nb_hidden)])            self.outputs[i] = sigmoid(aggregated)    def backpropagation(self, expected_outputs):        # find deltas for output layer         for i in range(self.nb_outputs):            for j in range(self.nb_hidden):                self.output_deltas[i][j] = dsigmoid(self.outputs[i]) * (expected_outputs[i] - self.outputs[i])        # find deltas for hidden layer        for i in range(self.nb_hidden):            for j in range(self.nb_inputs):                total = 0.0                for k in range(self.nb_outputs):                    total += self.output_deltas[k][i] * self.output_weights[k][i]                self.hidden_deltas[i][j] = dsigmoid(self.hidden_outputs[i]) * total        # change weights for output layer        for i in range(self.nb_outputs):            for j in range(self.nb_hidden):                self.output_weights[i][j] += self.learning_rate * self.output_deltas[i][j] * self.outputs[i] # WRONG: should be self.hidden_outputs[j]        # change weights for inputs layer        for i in range(self.nb_hidden):            for j in range(self.nb_inputs):                self.hidden_weights[i][j] += self.learning_rate * self.hidden_deltas[i][j] * self.hidden_outputs[i] # WRONG: should be inputs[j]    def train(self, data, nb_iterations):        for i in range(nb_iterations):            # Init deltas            self.output_deltas = np.zeros((self.nb_outputs, self.nb_hidden), dtype=np.float64)            self.hidden_deltas = np.zeros((self.nb_hidden, self.nb_inputs), dtype=np.float64)            # Train on examples with different orders            for inputs, expected_output in sorted(data.items(), key=lambda x: random.random()):                expected_outputs = np.array([expected_output])                self.forward_propagation(inputs)                self.backpropagation(expected_outputs) # WRONG: need inputs: backpropagation(inputs, expected_outputs)    def predict(self, inputs):        self.forward_propagation(inputs)        return self.outputs

回答:

我已经为此绞尽脑汁了很长时间,因为我不是一个出色的科学家,我希望能完全理解这个程序的每一行代码。

你得到了我的回答。继续这样做,你会做得很棒!

免责声明:部分回答。我可能会随着时间的推移进行改进。

是否有针对神经网络的特定调试方法?(例如预定义的已知权重可以引导到预期的解决方案)

通常没有。由于权重的数量,在大多数实际案例中几乎是不可能的。你仍然可以尝试监控神经元活动(你代码中的aggregated变量),例如连续两次向网络发送相同的输入,看看它是否在“学习”,即它是否越来越接近正确的值。

下面的反向传播算法(伪代码)是否正确?

我会试着检查。这是你写的吗?

偏置是否应该包含在这个算法中?如果不包含,偏置是否有必要解决异或问题?如果需要,每个神经元、每层还是整个网络应该有一个偏置?

通常每个神经元有一个偏置。如果你只是忽略它也没有问题。

我的Python实现是否正确?如果不正确,是反向传播、前向传播、缺少偏置还是其他原因?

我会检查。缺少偏置是可以的。

作为补充,我推荐这个在线“书籍”:http://neuralnetworksanddeeplearning.com/chap1.html 里面有很多数学,不要在数学上花太多时间,试着理解概念。我觉得它非常教学性。

希望这能帮到你(一点)
pltrdy

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注