BP神经网络

来源:互联网 发布:上海一橙网络报修电话 编辑:程序博客网 时间:2024/05/14 02:11

原文:http://fantasticinblur.iteye.com/blog/1465497

一、实验目的

实现一个BP神经网络

二、实验内容及原理

2.1 BP神经网络基本原理

     BP网络模型处理信息的基本原理是:输入信号Xi通过中间节点(隐层点)作用于输出节点,经过非线形变换,产生输出信号Yk,网络训练的每个样本包括输入向量X和期望输出量t,网络输出值Y与期望输出值t之间的偏差,通过调整输入节点与隐层节点的联接强度取值Wij和隐层节点与输出节点之间的联接强度Tjk以及阈值,使误差沿梯度方向下降,经过反复学习训练,确定与最小误差相对应的网络参数(权值和阈值),训练即告停止。此时经过训练的神经网络即能对类似样本的输入信息,自行处理输出误差最小的经过非线形转换的信息。

2.2 BP神经网络模型

BP网络模型包括其输入输出模型、作用函数模型、误差计算模型和自学习模型。

(1)节点输出模型

隐节点输出模型:Oj=f(∑Wij×Xi-q j)    (1)

输出节点输出模型:Yk=f(∑Tjk×Oj-q k) (2)

f-非线形作用函数;q -神经单元阈值。
 

典型BP网络结构模型

2)作用函数模型

作用函数是反映下层输入对上层节点刺激脉冲强度的函数又称刺激函数,一般取为(0,1)内连续取值Sigmoid函数:                        f(x)=1/(1+e-x)                   (3)

(3)误差计算模型

误差计算模型是反映神经网络期望输出与计算输出之间误差大小的函数:

                    Ep=1/2×∑(tpi-Opi)2                (4)

tpi- i节点的期望输出值;Opi-i节点计算输出值。

(4)自学习模型

  神经网络的学习过程,即连接下层节点和上层节点之间的权重拒阵Wij的设定和误差修正过程。BP网络有师学习方式-需要设定期望值和无师学习方式-只需输入模式之分。自学习模型为

                       △Wij(n+1)= h ×Фi×Oj+a×△Wij(n) (5)

h -学习因子;Фi-输出节点i的计算误差;Oj-输出节点j的计算输出;a-动量因子。

2.3 BP网络模型的缺陷分析及优化策略

(1)学习因子h 的优化

采用变步长法根据输出误差大小自动调整学习因子,来减少迭代次数和加快收敛速度。

 h =h +a×(Ep(n)- Ep(n-1))/ Ep(n) a为调整步长,0~1之间取值 (6)

(2)隐层节点数的优化

     隐节点数的多少对网络性能的影响较大,当隐节点数太多时,会导致网络学习时间过长,甚至不能收敛;而当隐节点数过小时,网络的容错能力差。利用逐步回归分析法并进行参数的显著性检验来动态删除一些线形相关的隐节点,节点删除标准:当由该节点出发指向下一层节点的所有权值和阈值均落于死区(通常取±0.1、±0.05等区间)之中,则该节点可删除。最佳隐节点数L可参考下面公式计算:

L=(m+n)1/2+c (7)

m-输入节点数;n-输出节点数;c-介于1~10的常数。

(3)输入和输出神经元的确定

利用多元回归分析法对神经网络的输入参数进行处理,删除相关性强的输入参数,来减少输入节点数。

 

三、实验代码

BP.java

package BP;

 

 

import java.util.Random;

 

/**

 * BPNN.

 *

 * @author RenaQiu

 *

 */

public class BP {

/**

 * input vector.

 */

private final double[] input;

/**

 * hidden layer.

 */

private final double[] hidden;

/**

 * output layer.

 */

private final double[] output;

/**

 * target.

 */

private final double[] target;

 

/**

 * delta vector of the hidden layer .

 */

private final double[] hidDelta;

/**

 * output layer of the output layer.

 */

private final double[] optDelta;

 

/**

 * learning rate.

 */

private final double eta;

/**

 * momentum.

 */

private final double momentum;

 

/**

 * weight matrix from input layer to hidden layer.

 */

private final double[][] iptHidWeights;

/**

 * weight matrix from hidden layer to output layer.

 */

private final double[][] hidOptWeights;

 

/**

 * previous weight update.

 */

private final double[][] iptHidPrevUptWeights;

/**

 * previous weight update.

 */

private final double[][] hidOptPrevUptWeights;

 

public double optErrSum = 0d;

 

public double hidErrSum = 0d;

 

private final Random random;

 

/**

 * Constructor.

 * <p>

 * <strong>Note:</strong> The capacity of each layer will be the parameter

 * plus 1. The additional unit is used for smoothness.

 * </p>

 *

 * @param inputSize

 * @param hiddenSize

 * @param outputSize

 * @param eta

 * @param momentum

 * @param epoch

 */

public BP(int inputSize, int hiddenSize, int outputSize, double eta,

double momentum) {

 

input = new double[inputSize + 1];

hidden = new double[hiddenSize + 1];

output = new double[outputSize + 1];

target = new double[outputSize + 1];

 

hidDelta = new double[hiddenSize + 1];

optDelta = new double[outputSize + 1];

 

iptHidWeights = new double[inputSize + 1][hiddenSize + 1];

hidOptWeights = new double[hiddenSize + 1][outputSize + 1];

 

random = new Random(19881211);

randomizeWeights(iptHidWeights);

randomizeWeights(hidOptWeights);

 

iptHidPrevUptWeights = new double[inputSize + 1][hiddenSize + 1];

hidOptPrevUptWeights = new double[hiddenSize + 1][outputSize + 1];

 

this.eta = eta;

this.momentum = momentum;

}

 

private void randomizeWeights(double[][] matrix) {

for (int i = 0, len = matrix.length; i != len; i++)

for (int j = 0, len2 = matrix[i].length; j != len2; j++) {

double real = random.nextDouble();

matrix[i][j] = random.nextDouble() > 0.5 ? real : -real;

}

}

 

/**

 * Constructor with default eta = 0.25 and momentum = 0.3.

 *

 * @param inputSize

 * @param hiddenSize

 * @param outputSize

 * @param epoch

 */

public BP(int inputSize, int hiddenSize, int outputSize) {

this(inputSize, hiddenSize, outputSize, 0.25, 0.9);

}

 

/**

 * Entry method. The train data should be a one-dim vector.

 *

 * @param trainData

 * @param target

 */

public void train(double[] trainData, double[] target) {

loadInput(trainData);

loadTarget(target);

forward();

calculateDelta();

adjustWeight();

}

 

/**

 * Test the BPNN.

 *

 * @param inData

 * @return

 */

public double[] test(double[] inData) {

if (inData.length != input.length - 1) {

throw new IllegalArgumentException("Size Do Not Match.");

}

System.arraycopy(inData, 0, input, 1, inData.length);

forward();

return getNetworkOutput();

}

 

/**

 * Return the output layer.

 *

 * @return

 */

private double[] getNetworkOutput() {

int len = output.length;

double[] temp = new double[len - 1];

for (int i = 1; i != len; i++)

temp[i - 1] = output[i];

return temp;

}

 

/**

 * Load the target data.

 *

 * @param arg

 */

private void loadTarget(double[] arg) {

if (arg.length != target.length - 1) {

throw new IllegalArgumentException("Size Do Not Match.");

}

System.arraycopy(arg, 0, target, 1, arg.length);//结果存到target1-5位中

}

 

/**

 * Load the training data.

 *

 * @param inData

 */

private void loadInput(double[] inData) {

if (inData.length != input.length - 1) {//inData.length=32

throw new IllegalArgumentException("Size Do Not Match.");

}

System.arraycopy(inData, 0, input, 1, inData.length);//将32位二进制复制到input中的1-33位

}

 

/**

 * Forward.

 *

 * @param layer0

 * @param layer1

 * @param weight

 */

private void forward(double[] layer0, double[] layer1, double[][] weight) {

// threshold unit.

layer0[0] = 1.0;

for (int j = 1, len = layer1.length; j != len; ++j) {

double sum = 0;

for (int i = 0, len2 = layer0.length; i != len2; ++i)

sum += weight[i][j] * layer0[i];

layer1[j] = sigmoid(sum);

}

}

 

/**

 * Forward.

 */

private void forward() {

forward(input, hidden, iptHidWeights);

forward(hidden, output, hidOptWeights);

}

 

/**

 * Calculate output error.

 */

private void outputErr() {

double errSum = 0;

for (int idx = 1, len = optDelta.length; idx != len; ++idx) {

double o = output[idx];

optDelta[idx] = o * (1d - o) * (target[idx] - o);

errSum += Math.abs(optDelta[idx]);

}

optErrSum = errSum;

}

 

/**

 * Calculate hidden errors.

 */

private void hiddenErr() {

double errSum = 0;

for (int j = 1, len = hidDelta.length; j != len; ++j) {

double o = hidden[j];

double sum = 0;

for (int k = 1, len2 = optDelta.length; k != len2; ++k)

sum += hidOptWeights[j][k] * optDelta[k];

hidDelta[j] = o * (1d - o) * sum;

errSum += Math.abs(hidDelta[j]);

}

hidErrSum = errSum;

}

 

/**

 * Calculate errors of all layers.

 */

private void calculateDelta() {

outputErr();

hiddenErr();

}

 

/**

 * Adjust the weight matrix.

 *

 * @param delta

 * @param layer

 * @param weight

 * @param prevWeight

 */

private void adjustWeight(double[] delta, double[] layer,

double[][] weight, double[][] prevWeight) {

 

layer[0] = 1;

for (int i = 1, len = delta.length; i != len; ++i) {

for (int j = 0, len2 = layer.length; j != len2; ++j) {

double newVal = momentum * prevWeight[j][i] + eta * delta[i]

* layer[j];

weight[j][i] += newVal;

prevWeight[j][i] = newVal;

}

}

}

 

/**

 * Adjust all weight matrices.

 */

private void adjustWeight() {

adjustWeight(optDelta, hidden, hidOptWeights, hidOptPrevUptWeights);

adjustWeight(hidDelta, input, iptHidWeights, iptHidPrevUptWeights);

}

 

/**

 * Sigmoid.

 *

 * @param val

 * @return

 */

private double sigmoid(double val) {

return 1d / (1d + Math.exp(-val));

}

}

 

Test.java

package BP;

 

import java.io.IOException;

import java.util.ArrayList;

import java.util.List;

import java.util.Random;

 

public class Test {

 

/**

 * @param args

 * @throws IOException

 */

public static void main(String[] args) throws IOException {

BP bp = new BP(32, 15, 4);

//随机生成1000个数放入数组

Random random = new Random();

List<Integer> list = new ArrayList<Integer>();

for (int i = 0; i != 1000; i++) {

int value = random.nextInt(1000);

list.add(value);

}

 

for (int i = 0; i != 200; i++) {

for (int value : list) {

/*real数组存放value最终结果

 * [1,0,0,0]代表>=50奇数,[0,1,0,0]代表>=50偶数,[0,0,1,0]代表<50奇数,[0,0,0,1]代表<50偶数。

 */

double[] real = new double[4];

if (value >= 50)

if ((value & 1) == 1)

real[0] = 1;

else

real[1] = 1;

else if ((value & 1) == 1)

real[2] = 1;

else

real[3] = 1;

/*

 * 将value数字转化为32位二进制向量

 */

double[] binary = new double[32];

int index = 31;

do {

binary[index--] = (value & 1);

value >>>= 1;//>>>= 右移赋值,左边空出的位以0填充

} while (value != 0);

 

bp.train(binary, real);

}

}

 

System.out.println("训练完毕,下面请输入一个0-1000的数字,神经网络将自动判断它是否>50,奇数还是偶数。");

 

while (true) {

byte[] input = new byte[10];

System.in.read(input);

Integer value = Integer.parseInt(new String(input).trim());

int rawVal = value;

double[] binary = new double[32];

int index = 31;

do {

binary[index--] = (value & 1);

value >>>= 1;

} while (value != 0);

 

double[] result = bp.test(binary);

 

double max = -Integer.MIN_VALUE;

int idx = -1;

 

for (int i = 0; i != result.length; i++) {

if (result[i] > max) {

max = result[i];

idx = i;

}

}

 

switch (idx) {

case 0:

System.out.format("%d是一个>=50的奇数\n", rawVal);

break;

case 1:

System.out.format("%d是一个>=50的偶数\n", rawVal);

break;

case 2:

System.out.format("%d是一个<50的奇数\n", rawVal);

break;

case 3:

System.out.format("%d是一个<50的偶数\n", rawVal);

break;

}

}

}

 

}

四、实验结果

大部分会产生正确结果:如下

 

 

 

 

运行数十次后,发现偶尔会产生错误结果;

如:

 

可加大训练次数来提高准确率,神经网络会根据传入的训练集不断反馈错误,从而调整权重矩阵,所以训练次数越多,准确率越高。

原创粉丝点击