神经网络反向传播实现问题

我已经阅读了不少关于神经网络及其通过反向传播进行训练的内容,主要是通过这门Coursera课程,并从这里这里进行了额外的阅读。我以为自己已经很好地掌握了核心算法,但当我尝试构建一个通过反向传播训练的神经网络时,效果却不尽如人意,我不确定原因是什么。

代码是用C++编写的,目前还没有进行向量化处理。

我想构建一个简单的2输入神经元、1隐藏神经元、1输出神经元的网络来模拟AND函数。只是为了在转向更复杂的例子之前理解这些概念的工作原理。当我手动输入权重和偏置值时,我的前向传播代码是有效的。

float NeuralNetwork::ForwardPropagte(const float *dataInput){        int number = 0; // 将输入数据写入输入层        for ( auto & node : m_Network[0])        {            node->input = dataInput[number++];        }        // 对于网络中的每一层        for ( auto & layer : m_Network)        {            // 对于层中的每个神经元            for (auto & neuron : layer)            {                float activation;                if (layerIndex != 0)                {                   neuron->input += neuron->bias;                   activation = Sigmoid( neuron->input);                } else {                    activation = neuron->input;                }                for (auto & pair : neuron->outputNeuron)                {                    pair.first->input += static_cast<float>(pair.second)*activation;                }            }        }        return Sigmoid(m_Network[m_Network.size()-1][0]->input);}

这些变量的命名有些不太好,但基本上,neuron->outputNeuron是一个对的向量。第一个是指向下一神经元的指针,第二个是权重值。neuron->input是神经网络方程中的“z”值,即所有权重*激活值 + 偏置的总和。Sigmoid函数如下所示:

float NeuralNetwork::Sigmoid(float value) const{    return 1.0f/(1.0f + exp(-value));}

这两个函数似乎按预期工作。在网络上的每次传递后,所有’z’或’neuron->input’值都会重置为零(或在反向传播后)。

然后我按照下面的伪代码训练网络。训练代码会运行多次。

for trainingExample=0 to m // m = 训练样本数   执行前向传播计算hyp(x)   计算最后一层的成本delta         delta = y - hyp(x)   使用输出层的delta计算所有层的delta   遍历网络根据此值调整权重   重置网络

实际代码如下:

void NeuralNetwork::TrainNetwork(const std::vector<std::pair<std::pair<float,float>,float>> & trainingData){    for (int i = 0; i < 100; ++i)    {        for (auto & trainingSet : trainingData)        {            float x[2] = {trainingSet.first.first,trainingSet.first.second};            float y      = trainingSet.second;            float estimatedY = ForwardPropagte(x);            m_Network[m_Network.size()-1][0]->error = estimatedY - y;            CalculateError();            RunBackpropagation();            ResetActivations();        }    }}

反向传播函数如下所示:

void NeuralNetwork::RunBackpropagation(){    for (int index = m_Network.size()-1; index >= 0; --index)    {        for(auto &node : m_Network[index])        {            // 再次说明,"outputNeuron"是下一层神经元及其相关权重的列表            for (auto &weight : node->outputNeuron)            {                weight.second += weight.first->error*Sigmoid(node->input);            }            node->bias = node->error; // 我不知道如何调整偏置,一些公式似乎指向这个。是否正确?        }    }}

成本计算如下:

void NeuralNetwork::CalculateError(){    for (int index = m_Network.size()-2; index > 0; --index)    {        for(auto &node : m_Network[index])        {            node->error = 0.0f;            float sigmoidPrime = Sigmoid(node->input)*(1 - Sigmoid(node->input));            for (auto &weight : node->outputNeuron)            {                node->error += (weight.first->error*weight.second)*sigmoidPrime;            }        }    }   }

我随机化权重并在数据集上运行它:

    x = {0.0f,0.0f} y =0.0f    x = {1.0f,0.0f} y =0.0f    x = {0.0f,1.0f} y =0.0f    x = {1.0f,1.0f} y =1.0f 

当然,我不应该用同一数据集进行训练和测试,但我只是想让基本的反向传播算法运行起来。当我运行这段代码时,我看到权重/偏置如下:

Layer 0    Bias 0.111129    NeuronWeight 0.058659    Bias -0.037814    NeuronWeight -0.01820Layer 1    Bias 0.016230    NeuronWeight -0.104935Layer 2    Bias 0.080982

训练集运行后,输出层的delta的均方误差看起来像这样:

Error: 0.156954Error: 0.152529Error: 0.213887Error: 0.305257Error: 0.359612Error: 0.373494Error: 0.374910Error: 0.374995Error: 0.375000... 一直保持这个值...

最终的权重看起来像这样:(它们总是大致达到这个值)

Layer 0    Bias 0.000000    NeuronWeight 15.385233    Bias 0.000000    NeuronWeight 16.492933Layer 1    Bias 0.000000    NeuronWeight 293.518585Layer 2    Bias 0.000000

我承认,这可能看起来是学习神经网络的一种迂回方式,而且当前的实现非常不优化。但有人能发现我做出了无效假设,或者实现或公式有误的地方吗?

编辑

感谢关于偏置值的反馈,我停止将偏置应用于输入层,并停止将输入层通过Sigmoid函数。此外,我的Sigmoid prime函数是无效的。但网络仍然无法正常工作。我已经更新了上面的错误和输出,显示现在的情况。


回答:

我解决了我的问题(除了上述初始的偏置/Sigmoid prime问题)。我开始从权重中减去,而不是添加到权重中。在我查阅的资料中,他们在delta值计算中有一个负号,而我没有,但我保留了他们将否定值添加到权重的格式。此外,我对如何处理权重感到困惑,并误读了一个来源,该来源说将其分配给错误。现在我明白了直觉是将其视为正常权重,但乘以偏置常数1而不是z。在我添加这些更改后,迭代训练集大约1000次可以模拟简单的位运算表达式,如OR和AND。

Related Posts

L1-L2正则化的不同系数

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

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

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

f1_score metric in lightgbm

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

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

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

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

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

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

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

发表回复

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