神经网络的原理虽然理解起来不难,但是要是想实现它,还是需要做一些工作的,并且有很多细节性的东西需要注意。通过参阅各种相关资料,以及参考网络上已有的资源,自己写了一个含有一个隐含层,且只能有一个输出单元的简单的BP网络,经过测试,达到了预期的效果。
需要说明的是,神经网络的每个输入都在[0,1]中,输出也在[0,1]中,在使用神经网络解决实际问题的时候,还需要对实际问题的输入输出进行归一化处理。另外,尽量不要使得神经网络的输入或输出接近于0或1,这样会影响拟合效果。
我用正弦函数进行了一次测试,效果如图所示:
以下是相关的代码:
1.神经网络代码
package pkg1;
import java.util.Scanner;
/*
*
*/
public class TestNeuro {
private int INPUT_DIM=1;
private int HIDDEN_DIM=20;
private double LEARNING_RATE=0.05;
double [][] input_hidden_weights=new double[INPUT_DIM][HIDDEN_DIM];
double [] hidden_output_weights=new double[HIDDEN_DIM];
double[] hidden_thresholds=new double[HIDDEN_DIM];
double output_threshold;
public static void main(String[]args){
Scanner in=new Scanner(System.in);
TestNeuro neuro=new TestNeuro(1,5);
neuro.initialize();
for(int i=0;i<10000;i++){
double[] input=new double[1];
input[0]=Math.random();
double expectedOutput=input[0]*input[0];
//System.out.println("input : "+input[0]+"\t\texpectedOutput : "+expectedOutput);
//System.out.println("predict before training : "+neuro.predict(input));
neuro.trainOnce(input, expectedOutput);
//System.out.println("predict after training : "+neuro.predict(input));
//in.next();
}
while(true){
//neuro.printLinks();
double[] input=new double[1];
input[0]=in.nextDouble();
double expectedOutput=in.nextDouble();
System.out.println("predict before training : "+neuro.predict(input));
neuro.trainOnce(input, expectedOutput);
System.out.println("predict after training : "+neuro.predict(input));
}
}
public TestNeuro(int input_dimension,int hidden_dimension){
this.INPUT_DIM=input_dimension;
this.HIDDEN_DIM=hidden_dimension;
this.initialize();
}
/**
* 打印出本神经元网络各层之间的连接权重,以及各个神经元上的阈值的信息。
*/
void print(){
System.out.println("隐含层阈值:");
for(int i=0;i<HIDDEN_DIM;i++){
System.out.print(hidden_thresholds[i]+" ");
}System.out.println();
System.out.println("输出层阈值:");
System.out.println(output_threshold);
System.out.println("连接权重:*********************");
System.out.println("输入层与隐含层的连接");
for(int i=0;i<INPUT_DIM;i++){
for(int j=0;j<HIDDEN_DIM;j++){
System.out.print(input_hidden_weights[i][j]+" ");
}System.out.println();
}
System.out.println("隐含层到输出层的连接");
for(int i=0;i<HIDDEN_DIM;i++){
System.out.print(hidden_output_weights[i]+" ");
}System.out.println();
System.out.println("*********************************");
}
/**
* 初始化,对所有的权值产生一个(0,1)之间的随机double型值
*/
void initialize(){
//输入层到隐含层的连接权重
for(int i=0;i<INPUT_DIM;i++){
for(int j=0;j<HIDDEN_DIM;j++){
input_hidden_weights[i][j]=Math.random();
}
}
//隐含层到输出层的连接权重
for(int i=0;i<HIDDEN_DIM;i++){
hidden_output_weights[i]=Math.random();
}
//隐含层的阈值
for(int i=0;i<HIDDEN_DIM;i++){
hidden_thresholds[i]=Math.random();
}
//输出层的阈值
output_threshold=Math.random();
}
/**
* 激励函数
* @param x
* @return
*/
double function(double x){
return 1/(1+Math.pow(Math.E, -x));
}
/**
* 给定一个输入,进行预测
* @param input
* @return
*/
double predict(double[]input){
double[] hiddenValues=new double[HIDDEN_DIM];
for(int i=0;i<hiddenValues.length;i++){
double sum=0;
for(int j=0;j<input.length;j++){
sum+=input[j]*input_hidden_weights[j][i];
}
sum+=hidden_thresholds[i];//再加上本神经元的阈值
hiddenValues[i]=function(sum);
}
double sum=0;
for(int i=0;i<HIDDEN_DIM;i++){
sum+=hiddenValues[i]*hidden_output_weights[i];
}
sum+=output_threshold;//输出层神经元的阈值
return function(sum);
}
/**
* 进行一次训练
* @param input
* @param expectedOutput
*/
void trainOnce(double[] input, double expectedOutput){
double[] hiddenValues=new double[HIDDEN_DIM];
double[] hiddenParams=new double[HIDDEN_DIM];
for(int i=0;i<hiddenValues.length;i++){
double sum=0;
for(int j=0;j<input.length;j++){
sum+=input[j]*input_hidden_weights[j][i];
}
sum+=hidden_thresholds[i];//
hiddenValues[i]=function(sum);
hiddenParams[i]=sum;
}
double sum=0;
for(int i=0;i<HIDDEN_DIM;i++){
sum+=hiddenValues[i]*hidden_output_weights[i];
}
sum+=output_threshold;//
double outputValue=function(sum);
double outputParam=sum;
//System.out.println("实际输出");
/*
* 调整权值和阈值
*/
for(int i=0;i<input.length;i++){
double factor=(expectedOutput-outputValue)*outputValue*(1-outputValue)*LEARNING_RATE*input[i];
for(int j=0;j<HIDDEN_DIM;j++){
double delta=factor*hidden_output_weights[j]*hiddenValues[j]*(1-hiddenValues[j]);
//System.out.println("输入层到隐含层连接的权重调整:delta = "+delta+"\t\t weight = "+input_hidden_weights[i][j]);
input_hidden_weights[i][j]+=delta;
}
}
double factor=(expectedOutput-outputValue)*outputValue*(1-outputValue)*LEARNING_RATE;
for(int i=0;i<hidden_thresholds.length;i++){
double delta=factor*hidden_output_weights[i]*hiddenValues[i]*(1-hiddenValues[i]);
hidden_thresholds[i]+=delta;
}
//System.out.println("hidden_output_weights : "+hidden_output_weights.length);
for(int i=0;i<hidden_output_weights.length;i++){
//w+=(exp-act)*df/dw
//df/dw=x(1-x)*hiddenj
double delta=factor*hiddenValues[i];
//System.out.println("隐含层到输出层连接的权值调整:delta = "+delta+"\t\t weight = "+hidden_output_weights[i]);
hidden_output_weights[i]+=delta;
}
double delta=(expectedOutput-outputValue)*outputValue*(1-outputValue)*LEARNING_RATE;
output_threshold+=delta;
if(Math.abs(outputValue-expectedOutput)>0.1){
//System.out.println(input[0]+"\t\t"+outputValue+"\t\t"+expectedOutput);
}
}
}
2.测试代码
package pkg1;
import java.awt.Graphics;
import java.util.Scanner;
import javax.swing.JFrame;
public class DisplayNeuro extends javax.swing.JPanel{
public static final int SIDE_LENGTH=200;
TestNeuro neuro;//=new TestNeuro();
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
DisplayNeuro dn=new DisplayNeuro();
JFrame jFrame=new JFrame();
jFrame.setBounds(100, 100, 300, 300);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.add(dn);
jFrame.setVisible(true);
TestNeuro neuro=new TestNeuro(1,20);
dn.neuro=neuro;
Scanner in=new Scanner(System.in);
dn.repaint();
for(int i=0;i<100000000;i++){
double[] input=new double[1];
input[0]=Math.random()/2+0.25;
double expectedOutput=(Math.sin(3.14*(input[0]-0.25)*2*4)+1)/8*3+0.125;
//System.out.println("input : "+input[0]+"\t\texpectedOutput : "+expectedOutput);
//System.out.println("predict before training : "+neuro.predict(input));
neuro.trainOnce(input, expectedOutput);
//System.out.println("predict after training : "+neuro.predict(input));
if(i%100000==0){
System.out.println("input please ");
//in.next();
//neuro.initialize();
dn.repaint();
}
}
}
@Override
public void paint(Graphics arg0) {
// TODO Auto-generated method stub
super.paint(arg0);
for(double x=0.25;x<0.75;x+=0.005){
double[] input=new double[1];
input[0]=x;
double y=neuro.predict(input);
arg0.fillRect((int)(x*SIDE_LENGTH), (int)((1-y)*SIDE_LENGTH), 1, 1);
}
}
}