bp简介
github 原文:https://github.com/amace-lzo/top-algorithm-set/wiki/bpnn_BPNeuralNetwork
BP神经网络(Back Propagation Neural Network)是一种基于BP算法的人工神经网络,其使用BP算法进行权值与阈值的调整。在20世纪80年代,几位不同的学者分别开发出了用于训练多层感知机的反向传播算法,David Rumelhart和James McClelland提出的反向传播算法是最具影响力的。其包含BP的两大主要过程,即工作信号的正向传播与误差信号的反向传播,分别负责了神经网络中输出的计算与权值和阈值更新。工作信号的正向传播是通过计算得到BP神经网络的实际输出,误差信号的反向传播是由后往前逐层修正权值与阈值,为了使实际输出更接近期望输出。
(1)工作信号正向传播。输入信号从输入层进入,通过突触进入隐含层神经元,经传递函数运算后,传递到输出层,并且在输出层计算出输出信号传出。当工作信号正向传播时,权值与阈值固定不变,神经网络中每层的状态只与前一层的净输出、权值和阈值有关。若正向传播在输出层获得到期望的输出,则学习结束,并保留当前的权值与阈值;若正向传播在输出层得不到期望的输出,则在误差信号的反向传播中修正权值与阈值。
(2)误差信号反向传播。在工作信号正向传播后若得不到期望的输出,则通过计算误差信号进行反向传播,通过计算BP神经网络的实际输出与期望输出之间的差值作为误差信号,并且由神经网络的输出层,逐层向输入层传播。在此过程中,每向前传播一层,就对该层的权值与阈值进行修改,由此一直向前传播直至输入层,该过程是为了使神经网络的结果与期望的结果更相近。
? 当进行一次正向传播和反向传播后,若误差仍不能达到要求,则该过程继续下去,直至误差满足精度,或者满足迭代次数等其他设置的结束条件。
面向matlab工具箱的神经网络理论与应用(第三版) 第66页
文章图片
训练代码:(代码+解释)
初始化
public BPModel trainBP(BPParameter bpParameter, Matrix inputAndOutput) throws Exception {ActivationFunction activationFunction = bpParameter.getActivationFunction();
int inputCount = bpParameter.getInputLayerNeuronCount();
int hiddenCount = bpParameter.getHiddenLayerNeuronCount();
int outputCount = bpParameter.getOutputLayerNeuronCount();
double normalizationMin = bpParameter.getNormalizationMin();
double normalizationMax = bpParameter.getNormalizationMax();
double step = bpParameter.getStep();
double momentumFactor = bpParameter.getMomentumFactor();
double precision = bpParameter.getPrecision();
int maxTimes = bpParameter.getMaxTimes();
if(inputAndOutput.getMatrixColCount() != inputCount + outputCount){
throw new Exception("神经元个数不符,请修改");
}
- 我们去看一下BPParameter 类,如图:设定了一些默认值
文章图片
- 默认的激活函数是:sigmoid,而且提供的也只有sigmoid
sigmoid函数也叫Logistic函数,用于隐层神经元输出,取值范围为(0,1),它可以将一个实数映射到(0,1)的区间,可以用来做二分类。在特征相差比较复杂或是相差不是特别大时效果比较好。
Sigmoid函数由下列公式定义
文章图片
其对x的导数可以用自身表示:
文章图片
Sigmoid函数的图形如S曲线
文章图片
- 包含隐藏层权值w1和输出层权值w2
// 初始化权值
Matrix weightIJ = initWeight(inputCount, hiddenCount);
Matrix weightJP = initWeight(hiddenCount, outputCount);
- 方法是生成-1~1之间的随机数
文章图片
- 包括隐藏层阈值b1和输出层阈值b2
// 初始化阈值
Matrix b1 = initThreshold(hiddenCount);
Matrix b2 = initThreshold(outputCount);
- 神经网络是模仿大脑的神经元,当外界刺激达到一定的阀值时,神经元才会受刺激,影响下一个神经元
- 方法也是-1~1之间的随机数
文章图片
// 动量项
Matrix deltaWeightIJ0 = new Matrix(inputCount, hiddenCount);
Matrix deltaWeightJP0 = new Matrix(hiddenCount, outputCount);
Matrix deltaB10 = new Matrix(1, hiddenCount);
Matrix deltaB20 = new Matrix(1, outputCount);
- 动量项反应了以前积累的调整经验,对于t时刻的调整起阻尼作用。当误差曲面出现骤然起伏时,可以减小振荡趋势,提高收敛速度。
- 动量项奖励一致的梯度,惩罚不一致的梯度,类似于对梯度作了归一化,也可以认为是利用了某种形式的高阶信息。因此即使梯度的某些分量很小,对带动量的GD variants影响也不会很大。
- 通俗的讲,就是修改权值和阈值的变化量,使其和上一次的变化量有关。
文章图片
- 其中,▲x(k+1):第k+1次的权值、阈值改变量;▲x(k+1):第k次的权值、阈值变化量;α:学习速率;E:误差函数;x:权值/阈值
截取输入矩阵和输出矩阵
// 截取输入矩阵和输出矩阵
Matrix input = inputAndOutput.subMatrix(0,inputAndOutput.getMatrixRowCount(),0,inputCount);
Matrix output = inputAndOutput.subMatrix(0,inputAndOutput.getMatrixRowCount(),inputCount,outputCount);
- 把输入数据分成两部分,一部分是权值,一部分是目标值
// 归一化
Map inputAfterNormalize = MatrixUtil.normalize(input, normalizationMin, normalizationMax);
input = (Matrix) inputAfterNormalize.get("res");
Map outputAfterNormalize = MatrixUtil.normalize(output, normalizationMin, normalizationMax);
output = (Matrix) outputAfterNormalize.get("res");
文章图片
工作信号正向传播:以
f2(f1(x1 * w1 + b1)*w2+ b2))
为例
- f1(x)、f2(x)是激活函数;w1,b1隐含层的权值、阈值;w2,b2输出层的权值、阈值;x1:输入矢量
int times = 1;
double E = 0;
//误差
while (times < maxTimes) {
/*-----------------正向传播---------------------*/
// 隐含层输入
Matrix jIn = input.multiple(weightIJ);
// 扩充阈值
Matrix b1Copy = b1.extend(2,jIn.getMatrixRowCount());
// 加上阈值
jIn = jIn.plus(b1Copy);
// 隐含层输出
Matrix jOut = computeValue(jIn,activationFunction);
// 输出层输入
Matrix pIn = jOut.multiple(weightJP);
// 扩充阈值
Matrix b2Copy = b2.extend(2, pIn.getMatrixRowCount());
// 加上阈值
pIn = pIn.plus(b2Copy);
// 输出层输出
Matrix pOut = computeValue(pIn,activationFunction);
// 计算误差
Matrix e = output.subtract(pOut);
E = computeE(e);
//误差
// 判断是否符合精度
if (Math.abs(E) <= precision) {
System.out.println("满足精度");
break;
}
隐含层输入 相当于
x1 * w1
文章图片
扩充阈值 相当于把单行的b扩充为n行(n:输入数据的个数)的b,用于构造b1
文章图片
加上阈值 相当于内层的
x1 * w1 + b1
文章图片
隐含层输出
- 上一步的值代入激活函数计算结果,相当于内层的
f1(x1 * w1 + b1)
文章图片
- Logistic函数的计算:
文章图片
- 同隐含层输入,外层的
X2 * W2
- 同上一个扩充阀域,可得出 b2
- 外层的
X2 * W2+ b2
,其中x2 = x1 * w1+ b1
- 上一步的值通过激活函数计算结果,相当于外层的
f2(X2 * W2 + b2)
代码:
- t-a
文章图片
- 1/2 * ( t-a )^2
文章图片
文章图片
- 是否比给定精度小
文章图片
/*-----------------反向传播---------------------*/
// J与P之间权值修正量
Matrix deltaWeightJP = e.multiple(step);
deltaWeightJP = deltaWeightJP.pointMultiple(computeDerivative(pIn,activationFunction));
deltaWeightJP = deltaWeightJP.transpose().multiple(jOut);
deltaWeightJP = deltaWeightJP.transpose();
// P层神经元阈值修正量
Matrix deltaThresholdP = e.multiple(step);
deltaThresholdP = deltaThresholdP.transpose().multiple(computeDerivative(pIn, activationFunction));
// I与J之间的权值修正量
Matrix deltaO = e.pointMultiple(computeDerivative(pIn,activationFunction));
Matrix tmp = weightJP.multiple(deltaO.transpose()).transpose();
Matrix deltaWeightIJ = tmp.pointMultiple(computeDerivative(jIn, activationFunction));
deltaWeightIJ = input.transpose().multiple(deltaWeightIJ);
deltaWeightIJ = deltaWeightIJ.multiple(step);
// J层神经元阈值修正量
Matrix deltaThresholdJ = tmp.transpose().multiple(computeDerivative(jIn, activationFunction));
deltaThresholdJ = deltaThresholdJ.multiple(-step);
if (times == 1) {
// 更新权值与阈值
weightIJ = weightIJ.plus(deltaWeightIJ);
weightJP = weightJP.plus(deltaWeightJP);
b1 = b1.plus(deltaThresholdJ);
b2 = b2.plus(deltaThresholdP);
}else{
// 加动量项
weightIJ = weightIJ.plus(deltaWeightIJ).plus(deltaWeightIJ0.multiple(momentumFactor));
weightJP = weightJP.plus(deltaWeightJP).plus(deltaWeightJP0.multiple(momentumFactor));
b1 = b1.plus(deltaThresholdJ).plus(deltaB10.multiple(momentumFactor));
b2 = b2.plus(deltaThresholdP).plus(deltaB20.multiple(momentumFactor));
}deltaWeightIJ0 = deltaWeightIJ;
deltaWeightJP0 = deltaWeightJP;
deltaB10 = deltaThresholdJ;
deltaB20 = deltaThresholdP;
times++;
}
【bp神经网络】以下计算变化量都是根据偏导计算,公式在下图中(公式太难敲了,略)
- J与P之间权值修正量
计算△w2
=dE/dw2 - P层神经元阈值修正量
计算△b2
=dE/db2 - I与J之间的权值修正量
- 计算
△w1
=dE/dw1 - J层神经元阈值修正量
计算△b1
=dE/db1
文章图片
// BP神经网络的输出
BPModel result = new BPModel();
result.setInputMax((Matrix) inputAfterNormalize.get("max"));
result.setInputMin((Matrix) inputAfterNormalize.get("min"));
result.setOutputMax((Matrix) outputAfterNormalize.get("max"));
result.setOutputMin((Matrix) outputAfterNormalize.get("min"));
result.setWeightIJ(weightIJ);
result.setWeightJP(weightJP);
result.setB1(b1);
result.setB2(b2);
result.setError(E);
result.setTimes(times);
result.setBpParameter(bpParameter);
System.out.println("循环次数:" + times + ",误差:" + E);
return result;
}
- 保存了权值及目标值的最大值、最小值,w1,w2,b1,b2,误差,训练次数,bp的参数
推荐阅读
- dex 转 smali
- 【Java面试】Kafka 怎么避免重复消费
- 基于Java web的校园电动车租赁系统idea版本+eclipse版本+论文+答辩ppt+包安装配置+代码讲解+开题报告
- 内核|linux(使用yum安装_首次使用Linux:30个安装案例)
- 【Java面试】为什么引入偏向锁、轻量级锁,介绍下升级流程
- java|怎样画一张人见人爱的系统架构图
- 操作系统|[转] 张凌 ARM体系架构
- nacos|nacos介绍及使用
- spring部分内容|springAOP定义一个切面,切入日志