我一直在阅读有关神经网络的信息并用backprogpagation进行训练,主要是this Coursera course,还有来自here和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”值,是所有wieghts *激活bais的总和 . Sigmoid由下式给出:
float NeuralNetwork::Sigmoid(float value) const
{
return 1.0f/(1.0f + exp(-value));
}
这两个似乎按预期工作 . 在通过网络之后,所有'z'或'神经元 - >输入'值被重置为零(或者在反向传播之后) .
然后我按照下面的psudo代码训练网络 . 培训代码多次运行 .
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
当然,我不应该使用相同的数据集进行训练和测试,但我只是想让基本的反向传播算法运行起来 . 当我运行此代码时,我看到权重/偏差如下:
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
运行训练集并且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主要功能无效 . 但网络仍然无法正常工作 . 我已经更新了上面的错误和输出,现在发生了什么 .
2 回答
正如lejilot所说,那里有很多偏见 . 您不需要输出图层,并且必须将偏置连接到其输入,而不是连接到其输出 . 看一下下图:
在此图像中,您可以看到每层只有一个偏差,除了最后一个偏差,不需要偏差 .
Here you can read一种非常直观的神经网络方法 . 它是在Python中,但它可以帮助您更好地理解神经网络的一些概念 .
我解决了我的问题(超出上面的最初偏见/签署的主要问题) . 我开始减去而不是增加权重 . 在我看到的来源中,他们在delta值计算中有一个减号我没有,但我保留了他们的格式,即将负值添加到权重中 . 另外,我很困惑如何处理重量并误读了一个来源,它将其分配给错误 . 我现在看到直觉将它视为正常体重,但乘以偏差常数1而不是z . 在我添加了这些更改后,迭代训练集~1000次可以模拟简单的按位表达式,如OR和AND .