神经网络反向传播实现问题
Neural Network Backpropagation implementation issues
我已经阅读了很多关于神经网络的文章并使用反向传播训练它们,主要是 this Coursera course, with additional reading from here and here。我以为我对核心算法有相当扎实的掌握,但我尝试建立一个反向传播训练的神经网络并没有完全成功,我不确定为什么。
代码是 C++,目前还没有向量化。
我想构建一个简单的 2 个输入神经元、1 个隐藏神经元、1 个输出神经元网络来模拟 AND 函数。只是为了在进入更复杂的示例之前了解这些概念是如何工作的当我手动编写权重和偏差的值时,我的前向传播代码起作用了。
float NeuralNetwork::ForwardPropagte(const float *dataInput)
{
int number = 0; // Write the input data into the input layer
for ( auto & node : m_Network[0])
{
node->input = dataInput[number++];
}
// For each layer in the network
for ( auto & layer : m_Network)
{
// For each neuron in the layer
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”值,所有wights*activation + bais之和。 Sigmoid 由以下公式给出:
float NeuralNetwork::Sigmoid(float value) const
{
return 1.0f/(1.0f + exp(-value));
}
这两个似乎按预期工作。通过网络后,所有 'z' 或 'neuron->input' 值都重置为零(或在反向传播之后)。
然后我按照下面的伪代码训练网络。训练代码运行多次
for trainingExample=0 to m // m = number of training examples
perform forward propagation to calculate hyp(x)
calculate cost delta of last layer
delta = y - hyp(x)
use the delta of the output to calculate delta for all layers
move over the network adjusting the weights based on this value
reset network
实际代码在这里:
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])
{
// Again where the "outputNeuron" is a list of the next layer of neurons and associated weights
for (auto &weight : node->outputNeuron)
{
weight.second += weight.first->error*Sigmoid(node->input);
}
node->bias = node->error; // I'm not sure how to adjust the bias, some of the formulas seemed to point to this. Is it correct?
}
}
}
成本计算方式:
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
当然我不应该使用相同的数据集进行训练和测试,但我只是想获得基本的反向传播算法并 运行ning。当我 运行 这段代码时,我看到 weights/biases 如下:
Layer 0
Bias 0.111129
NeuronWeight 0.058659
Bias -0.037814
NeuronWeight -0.018420
Layer 1
Bias 0.016230
NeuronWeight -0.104935
Layer 2
Bias 0.080982
训练集 运行s 和 delta[outputLayer] 的均方误差看起来像:
Error: 0.156954
Error: 0.152529
Error: 0.213887
Error: 0.305257
Error: 0.359612
Error: 0.373494
Error: 0.374910
Error: 0.374995
Error: 0.375000
... remains at this value for ever...
最后的权重看起来像:(它们总是大致达到这个值)
Layer 0
Bias 0.000000
NeuronWeight 15.385233
Bias 0.000000
NeuronWeight 16.492933
Layer 1
Bias 0.000000
NeuronWeight 293.518585
Layer 2
Bias 0.000000
我承认这似乎是学习神经网络的一种迂回方式,而且(目前)实现非常不理想。但是有谁能发现我的假设无效,或者实施或公式有误吗?
编辑
感谢您对偏差值的反馈,我停止将它们应用于输入层并停止通过 sigmoid 函数传递输入层。另外我的 Sigmoid 素数函数无效。但网络仍然无法正常工作。我已经用现在发生的事情更新了上面的错误和输出。
正如lejilot所说,你有很多偏见。
最后一层不需要偏置,它是一个输出层,偏置必须连接到它的输入,但不能连接到它的输出。看看下面的图片:
在这张图片中你可以看到每一层只有一个偏差,除了最后一层,那里不需要偏差。
Here you can read 一种非常直观的神经网络方法。它在Python中,但它可以帮助您更好地理解神经网络的一些概念。
我解决了我的问题(除了上面最初的 biases/signed 主要问题)。我开始减去而不是增加权重。在我查看的资料中,他们在 delta 值计算中有一个负号,我没有,但我保留了他们将负值添加到权重的格式。此外,我对如何处理重量感到困惑,并且误读了一个消息来源,该消息来源称将其分配给错误。我现在看到直觉是将其视为正常权重,但乘以偏差常数 1 而不是 z。在我添加这些更改后,迭代训练集约 1000 次可以对简单的按位表达式(如 OR 和 AND)建模。
我已经阅读了很多关于神经网络的文章并使用反向传播训练它们,主要是 this Coursera course, with additional reading from here and here。我以为我对核心算法有相当扎实的掌握,但我尝试建立一个反向传播训练的神经网络并没有完全成功,我不确定为什么。
代码是 C++,目前还没有向量化。
我想构建一个简单的 2 个输入神经元、1 个隐藏神经元、1 个输出神经元网络来模拟 AND 函数。只是为了在进入更复杂的示例之前了解这些概念是如何工作的当我手动编写权重和偏差的值时,我的前向传播代码起作用了。
float NeuralNetwork::ForwardPropagte(const float *dataInput)
{
int number = 0; // Write the input data into the input layer
for ( auto & node : m_Network[0])
{
node->input = dataInput[number++];
}
// For each layer in the network
for ( auto & layer : m_Network)
{
// For each neuron in the layer
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”值,所有wights*activation + bais之和。 Sigmoid 由以下公式给出:
float NeuralNetwork::Sigmoid(float value) const
{
return 1.0f/(1.0f + exp(-value));
}
这两个似乎按预期工作。通过网络后,所有 'z' 或 'neuron->input' 值都重置为零(或在反向传播之后)。
然后我按照下面的伪代码训练网络。训练代码运行多次
for trainingExample=0 to m // m = number of training examples
perform forward propagation to calculate hyp(x)
calculate cost delta of last layer
delta = y - hyp(x)
use the delta of the output to calculate delta for all layers
move over the network adjusting the weights based on this value
reset network
实际代码在这里:
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])
{
// Again where the "outputNeuron" is a list of the next layer of neurons and associated weights
for (auto &weight : node->outputNeuron)
{
weight.second += weight.first->error*Sigmoid(node->input);
}
node->bias = node->error; // I'm not sure how to adjust the bias, some of the formulas seemed to point to this. Is it correct?
}
}
}
成本计算方式:
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
当然我不应该使用相同的数据集进行训练和测试,但我只是想获得基本的反向传播算法并 运行ning。当我 运行 这段代码时,我看到 weights/biases 如下:
Layer 0
Bias 0.111129
NeuronWeight 0.058659
Bias -0.037814
NeuronWeight -0.018420
Layer 1
Bias 0.016230
NeuronWeight -0.104935
Layer 2
Bias 0.080982
训练集 运行s 和 delta[outputLayer] 的均方误差看起来像:
Error: 0.156954
Error: 0.152529
Error: 0.213887
Error: 0.305257
Error: 0.359612
Error: 0.373494
Error: 0.374910
Error: 0.374995
Error: 0.375000
... remains at this value for ever...
最后的权重看起来像:(它们总是大致达到这个值)
Layer 0
Bias 0.000000
NeuronWeight 15.385233
Bias 0.000000
NeuronWeight 16.492933
Layer 1
Bias 0.000000
NeuronWeight 293.518585
Layer 2
Bias 0.000000
我承认这似乎是学习神经网络的一种迂回方式,而且(目前)实现非常不理想。但是有谁能发现我的假设无效,或者实施或公式有误吗?
编辑
感谢您对偏差值的反馈,我停止将它们应用于输入层并停止通过 sigmoid 函数传递输入层。另外我的 Sigmoid 素数函数无效。但网络仍然无法正常工作。我已经用现在发生的事情更新了上面的错误和输出。
正如lejilot所说,你有很多偏见。
最后一层不需要偏置,它是一个输出层,偏置必须连接到它的输入,但不能连接到它的输出。看看下面的图片:
在这张图片中你可以看到每一层只有一个偏差,除了最后一层,那里不需要偏差。
Here you can read 一种非常直观的神经网络方法。它在Python中,但它可以帮助您更好地理解神经网络的一些概念。
我解决了我的问题(除了上面最初的 biases/signed 主要问题)。我开始减去而不是增加权重。在我查看的资料中,他们在 delta 值计算中有一个负号,我没有,但我保留了他们将负值添加到权重的格式。此外,我对如何处理重量感到困惑,并且误读了一个消息来源,该消息来源称将其分配给错误。我现在看到直觉是将其视为正常权重,但乘以偏差常数 1 而不是 z。在我添加这些更改后,迭代训练集约 1000 次可以对简单的按位表达式(如 OR 和 AND)建模。