反向传播算法的实现

Backpropagation Algorithm Implementation

本文关键字:实现 算法 传播      更新时间:2023-10-16

我正在关注这篇文章。我使用这篇文章来理解逻辑,但我使用structs以不同的方式实现了它。

问题

问题是它永远不会收敛到期望的输出。我没有得到我想要的输出。这意味着权重没有正确更新。如果我对网络进行足够多次的训练,输出就会停止变化,这意味着权重不会更新,因此网络认为它得到了正确的权重,但输出却显示出相反的情况。

每个神经元都有一个CCD_ 2的数组。权重是Path的一个属性,Neurons可以将值"向下"发送到Path,然后到达另一侧的Neuron

下面是代码。。。

const double e =2.7182818284;

神经元:

struct Neuron{
double value;   //Local input
double bias;
double gradient;
double out; //Output value
Path *p;    //path array 
int nc; //number of paths/connections belonging to *this

};

路径:

struct Path{
double weight;
double prevDelta;
double delta;
int nid;    //neuron id in *n
};

CCD_ 7包含CCD_。Paths用整数nid标识Neurons,该整数是相邻Layer中的Neuron阵列的索引。

层:

struct Layer{
int nneurons;   //number of neurons in cluster/layer
Neuron *n;   //Local Neuron array to reference
Layer *neighbor;

void Transfer(int nid)  //compute target Neuron's input and assign it to it
{
double valueOut=0;
Neuron *temp;
temp=&n[nid];
//for each connection, send w*v to paired neuron
for(int i=0; i<n[nid].nc; i++)
{
valueOut=temp->p[i].weight * temp->out;
//neuron nid(as referenced by p[]), which is in the other layer, receives the value
neighbor->n[temp->p[i].nid].value+=valueOut;
}

}
void Initialize(int size)
{
nneurons=size;
n=new Neuron[nneurons];
for(int i=0; i<nneurons; i++)
{
n[i].value=0.0;
n[i].bias=1.0;
n[i].out=0.0;
}
}
void FormConnections(Layer& nl)//with neighboring layer
{
neighbor=&nl;
int nCon=neighbor->nneurons;
for(int i=0; i<nneurons; i++)
{
n[i].nc=nCon;
n[i].p=new Path[nCon];
//neuron 'i' will link its paths to neurons in the other layer
for(int ii=0; ii<n[i].nc; ii++)
{
n[i].p[ii].weight=1.0;
n[i].p[ii].prevDelta=0.0;
n[i].p[ii].nid=ii;
}
}
}
};

大脑(神经网络):

class Brain{
public:
double eta;
double alpha;
Layer   input,
hidden,
output;
double *target;
void GetInput(double* in){
for(int i=0; i<input.nneurons; i++)
input.n[i].value=in[i];
}
void GetDesiredOutput(double* t)
{
target=t;
}
void Initialize(int inputsize, int hiddensize, int outputsize)
{
input.Initialize(inputsize);
hidden.Initialize(hiddensize);
output.Initialize(outputsize);
input.FormConnections(hidden);
hidden.FormConnections(output);
}
void BP()
{
//Calculate gradients for output
for(int i=0; i<output.nneurons; i++)
{output.n[i].gradient=(target[i] - output.n[i].out) * (1 - output.n[i].out) * (1 + output.n[i].out);}
Neuron* temp;
for(int i=0; i<hidden.nneurons; i++)
{
temp=&hidden.n[i];
temp->gradient=0;
//for each connection...
for(int ii=0; ii<hidden.n[i].nc; ii++)
{
//temp's gradient gets values in the form w1*g2 + w2*g2 + ... + wn*gn,
//where w is the weight of p that leads to output.n[i] from temp(hidden), and g
//is the gradient of that output at p[CurrentConnection].nid
temp->gradient+= temp->p[ii].weight * output.n[temp->p[ii].nid].gradient;
}
//Multiply the resultant sums with d/dx S(x)
temp->gradient*= (temp->out)*(1-temp->out);

}
/
/---------------------------------------------------------------------------
//Calculate delta
for(int i=0; i<input.nneurons; i++)
{
temp=&input.n[i];
//temp->bias=eta*temp->gradient;
for(int ii=0; ii<input.n[i].nc; ii++)
{   
temp->p[ii].delta=eta* hidden.n[temp->p[ii].nid].gradient* temp->value;
temp->p[ii].weight=temp->p[ii].prevDelta*alpha+temp->p[ii].delta;
temp->p[ii].prevDelta=temp->p[ii].delta;
}
}
for(int i=0; i<hidden.nneurons; i++)
{
temp=&hidden.n[i];
temp->bias=eta*temp->gradient;
for(int ii=0; ii<hidden.n[i].nc; ii++)
{   temp->p[ii].delta=eta* output.n[temp->p[ii].nid].gradient* temp->value;
temp->p[ii].weight=temp->p[ii].prevDelta*alpha+temp->p[ii].delta;
temp->p[ii].prevDelta=temp->p[ii].delta;
}
}
for(int i=0; i<output.nneurons; i++)
{
temp=&output.n[i];
temp->bias=eta*temp->gradient;
}
Zero(hidden);
Zero(output);
}
void Process()
{
for(int i=0; i<input.nneurons; i++)
{   input.n[i].out=input.n[i].value;
input.Transfer(i);//transfer each neuron data in input to hidden
}
for(int i=0; i<hidden.nneurons; i++)
{
hidden.n[i].out=Sigmoid(hidden.n[i].value + hidden.n[i].bias);
hidden.Transfer(i);
}
for(int i=0; i<output.nneurons; i++)
{
output.n[i].out=HyperTan(output.n[i].value + output.n[i].bias);
cout<<"Output "<<i<<": "<<output.n[i].out<<endl;
}

}
void Zero(Layer &l){ for(int i=0; i<l.nneurons; i++) l.n[i].value=0.0;}
void Randomize(Layer &l)
{
for(int i=0; i<l.nneurons; i++)
{
for(int ii=0; ii<l.n[i].nc; ii++)
{
l.n[i].p[ii].weight=rand()%100/10;
}
}
}
Brain(){eta=0.9; alpha=0.4;}
double Sigmoid(double x)
{
if (x < -45.0) return 0.0;
else if (x > 45.0) return 1.0;
else return (1.0 / (1.0 + pow(e, -x)));
}
double HyperTan(double x)
{
if (x < -10.0) return -1.0;
else if (x > 10.0) return 1.0;
else return tanh(x);
}
};

一个典型的程序是:

Brain b;
double data[]={1.0,2.0, 3.0};
double ddata[]={-0.25,0.14};

b.Initialize(3,4,2);
b.GetDesiredOutput(ddata);
b.GetInput(data);

b.Process();
b.BP();

示例:η=0.9,alpha=0.4对于输入1.0、2.0、3.0

我得到:

-0.1117471和0.0661122

预期输出为:

-0.25,0.14

更新(2013年12月25日):问题是计算隐藏到输出权重的增量值,使用这些新的增量更新权重时出现了额外的错误,这些都发生在同一个for循环中。我只是简单地分配了新的权重,而我应该将它们添加到以前的权重中。

问题在于计算权重的delta值,使用这些新的delta更新权重时出现了额外的错误,这些都发生在同一个for循环中。我只是简单地分配了新的权重,而我应该将它们添加到以前的权重中。正确的增量计算和权重分配:

for(int i=0; i<input.nneurons; i++)
{
temp=&input.n[i];

for(int ii=0; ii<input.n[i].nc; ii++)
{   
temp->p[ii].delta=eta* hidden.n[temp->p[ii].nid].gradient* temp->out;
temp->p[ii].weight+=temp->p[ii].prevDelta*alpha +temp->p[ii].delta;
temp->p[ii].prevDelta=temp->p[ii].delta;

}
}

对于隐藏输出:

for(int i=0; i<hidden.nneurons; i++)
{
temp=&hidden.n[i];
temp->bd=eta*temp->gradient;
temp->bias+=temp->bd+ alpha*temp->pbd;
temp->pbd=temp->bd;
for(int ii=0; ii<hidden.n[i].nc; ii++)
{   
temp->p[ii].delta=eta* output.n[temp->p[ii].nid].gradient* temp->out;
temp->p[ii].weight+=temp->p[ii].prevDelta*alpha+ temp->p[ii].delta;
temp->p[ii].prevDelta=temp->p[ii].delta;

}
}