1.一层的感知机(Perceptron)是无法学习非线性的二分函数的
2.多层的神经网络含有一个或多个隐藏层,其权重的调整需要借助输出层的反向传播回输出层的方式来调整,这就是BP神经网络。
3.使用S形函数来做激活函数(导数容易计算,保证输出在0~1之间)
4.误差梯度的计算:激活函数的导数与神经元的输出误差的乘积,即:
经数学推导后可得,
(1)初始化:
权值w及阈值b的值应在[-2.4/Fi,2.4/Fi]之间,Fi为神经元i的输入总数,如本例中为2;
(2)激活
计算隐含层神经元的实际输出:
计算输出层神经元的实际输出:
(3)权重训练
输出层神经元的权重:
隐藏层神经元的权重:
(4)反复迭代,直到误差平方和满足一定要求(因为有的误差是正、有的负,用平方来衡量误差大小。似乎绝对值更好??)
5.本例中的神经网络图
6.简单代码实现:
import java.util.Random;
public class XorANN {
//计算输出值
/*
x1、x2:输入值
w1、w2:对应输入值权重
b:控制阈值,应仔细选择
返回值:输出值
*/
public double caclOutput(double x1,double x2,double w1,double w2,double b)
{
return x1*w1+x2*w2-b;
}
//使用S型函数做为激活函数sigmoid
/*
* out:输出的值
* 返回值:结果
*/
public double activatorSigmoid(double out)
{
return 1.0/(1.0+Math.pow(Math.E,-out));
}
//计算误差
/*
* out:输出的结果值,real:真实值
*/
public double caclError(double out,double real)
{
return real-out;
}
//更新权重
/*
* x:之前的输入值,w:对应权重,err:误差,a:学习率
*/
public double updateWeight(int x,double w,int err,double a)
{
if(x==0)
return w;
else
if(err>0)
return w+a;
else if(err<0)
return w-a;
else
return w;
}
//返回一个指定区间的随机小数
/*
* 根据Hankin 2008的方法设置随机权重
* 权重范围为:[-2.4/Fi,2.4/Fi]
* nextDouble方法返回[0,1]之间的随机小数
* nextDouble-0.5方法返回[-0.5,0.5]之间的随机小数
* 2*t*(r.nextDouble()-0.5)则返回[-t,t]之间的随机小数
*/
public double getRandom(int n,Random r)
{
double t=2.4/(double)n;
return 2*t*(r.nextDouble()-0.5);
}
//结果函数
public int Xor(double x1,double x2,double w13,double w14,double w23,double w24,double w35,double w45,double b3,double b4,double b5)
{
double o3=caclOutput(x1, x2, w13, w23, b3);
double o4=caclOutput(x1, x2, w14, w24, b4);
double out3=activatorSigmoid(o3);
double out4=activatorSigmoid(o4);
double o5=caclOutput(out3, out4, w35, w45, b5);
double out=activatorSigmoid(o5);
if(out>0.99) return 1;
else return 0;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
final double esp=0.00001;
XorANN xor=new XorANN();
int[][] input= {{0,0},{0,1},{1,0},{1,1}};
int[] real= {1,0,0,1};
Random r=new Random();
double w13=xor.getRandom(2,r);
double w14=xor.getRandom(2,r);
double w23=xor.getRandom(2,r);
double w24=xor.getRandom(2,r);
double w35=xor.getRandom(2,r);
double w45=xor.getRandom(2,r);
double b3=0.8; //b3、b4、b5为阈值
double b4=-0.1;
double b5=0.3;
double a=0.1; //学习率
double s=0.1; //损失值
int p=0; //迭代次数
while(s>esp)
{
s=0.0;
p++;
for(int i=0;i<input.length;i++)
{
double o3=xor.caclOutput(input[i][0], input[i][1], w13, w23, b3);
double o4=xor.caclOutput(input[i][0], input[i][1], w14, w24, b4);
double out3=xor.activatorSigmoid(o3);
double out4=xor.activatorSigmoid(o4);
double o5=xor.caclOutput(out3, out4, w35, w45, b5);
double out5=xor.activatorSigmoid(o5);
double err=xor.caclError(out5, real[i]);
double d5=out5*(1-out5)*err; //输出层误差梯度
//更新输出层的权重
w35+=a*out3*d5;
w45+=a*out4*d5;
double d3=out3*(1-out3)*d5*w35; //隐藏层误差梯度
double d4=out4*(1-out4)*d5*w45; //隐藏层误差梯度
//更新隐藏层权重
w13+=a*input[i][0]*d3;
w14+=a*input[i][0]*d4;
w23+=a*input[i][1]*d3;
w24+=a*input[i][1]*d4;
//更新阈值
b3+=a*(-1)*d3;
b4+=a*(-1)*d4;
b5+=a*(-1)*d5;
//计算误差平方和
s+=err*err;
}
}
//测试
System.out.println("总共迭代次数"+p);
System.out.println("w13="+w13+",w14="+w14+"w23="+w23+",w24="+w24+"w35="+w35+",w45="+w45+"b3="+b3+",b4="+b4+",b5="+b5);
System.out.println("1 xor 1 ="+xor.Xor(1, 1, w13, w14, w23, w24, w35, w45, b3, b4, b5));
System.out.println("1 xor 0 ="+xor.Xor(1, 0, w13, w14, w23, w24, w35, w45, b3, b4, b5));
System.out.println("0 xor 1 ="+xor.Xor(0, 1, w13, w14, w23, w24, w35, w45, b3, b4, b5));
System.out.println("0 xor 0 ="+xor.Xor(0, 0, w13, w14, w23, w24, w35, w45, b3, b4, b5));
}
}