前馈神经网络的简单数学原理以及Java实现

深度神经网络(DNN)

​ 深度神经网络(DNN)是含有多层隐含层的神经网络,即:输入层+多层隐含层+输出层.

整个网络的训练过程为:初始化网络数据、【n,计算各层输出值+反向计算误差+反向更新权重和偏量】

public interface IbpBase {//DNN框架
	public abstract void init(int[] numberOfLayer,double r);//初始化参数
	public abstract double[] computeOut(double[] inp);//逐层计算输出
	public abstract void backPropagation(double[] target);//反向计算误差并更新权值
}

一、初始化网络数据

​ 算法需要初始化四个量:各节点的值,各网络连接的权值,各节点的误差以及学习参数。

​ 网络连接的权值可以设计成一个三维数组(多个矩阵),第一维是连接的起始层网络层数,第二维是连接的节点标号,第三维是连接的终点标号。此外,可以在各层的权值矩阵添加一行作为偏量。

二、 计算各层输出值

​ 神经网络由多个神经节点组成,对于隐含层和输出层的每一个节点而言,输出值计算方式为:


o u t = F ( ∑ w x + b ) out=F(\sum{wx+b}) out=F(wx+b)
​ 其中F为激活函数,x为输入,w和b分别为权值和偏量。

public double[] computeOut(double[] inp) {//采用function:sigmoid
		// TODO 自动生成的方法存根
		for(int i=1;i<layer.length;++i) {
			for(int k=0;k<layer[i].length;++k) {
				double z=weight[i-1][layer[i-1].length][k];//偏量
				for(int j=0;j<layer[i-1].length;++j) {
					layer[i-1][j]=i==1?inp[j]:layer[i-1][j];//初始化输入层
					z+=weight[i-1][j][k]*layer[i-1][j];
				}
				layer[i][k]=Function.sigmoid(z);
			}
		}
		return layer[layer.length-1];
	}

三、反向计算误差并更新权值和偏量

​ 采用平方型误差函数计量误差:
E = 0.5 ∗ ∑ ( O k − t k ) 2 E=0.5*\sum{(O_k-t_k)^2} E=0.5(Oktk)2
​ 算法的更新过程沿着梯度方向进行,
∂ E ∂ w = ( O k − t k ) O k ( 1 − O k ) O j \frac{\partial E}{\partial w}=(O_k-t_k)O_k(1-O_k)O_j wE=(Oktk)Ok(1Ok)Oj

∂ E ∂ b = ( O k − t k ) O j \frac{\partial E}{\partial b}=(O_k-t_k)O_j bE=(Oktk)Oj

​ 因此,误差的更新公式为:
e r r o r [ i ] [ j ] = ∑ k ( e r r o r [ i + 1 ] [ k ] ∗ w [ i ] [ j ] [ k ] ) ∗ l a y e r [ i ] [ j ] ∗ ( 1 − l a y e r [ i ] [ j ] ) error[i][j]=\sum_k{(error[i+1][k]*w[i][j][k])}*layer[i][j]*(1-layer[i][j]) error[i][j]=k(error[i+1][k]w[i][j][k])layer[i][j](1layer[i][j])
​ 权值的更新公式为:
w [ i ] [ j ] [ k ] + = r a t e ∗ e r r o r [ i + 1 ] [ k ] ∗ l a y e r [ i ] [ j ] w[i][j][k]+=rate*error[i+1][k]*layer[i][j] w[i][j][k]+=rateerror[i+1][k]layer[i][j]
​ 偏量的更新公式:
w [ i ] [ j + 1 ] [ k ] = r a t e ∗ e r r o r [ i + 1 ] [ k ] w[i][j+1][k]=rate*error[i+1][k] w[i][j+1][k]=rateerror[i+1][k]

public void backPropagation(double[] target) {//自倒数第二层开始反向计算误差并更新权值和偏量
		// TODO 自动生成的方法存根
		int i=layer.length-1;
		//先计算最后一层的误差
		for(int j=0;j<layer[i].length;++j)
			error[i][j]=layer[i][j]*(1-layer[i][j])*(target[j]-layer[i][j]);
		while(i-->0){
			//误差和权重同时计算
			for(int j=0;j<layer[i].length;++j) {
				double err=0.0;
				for(int k=0;k<layer[i+1].length;++k) {
					err+=weight[i][j][k]*error[i+1][k];
					weight[i][j][k]+=rate*error[i+1][k]*layer[i][j];
					if(j==layer[i].length-1) //调整偏量
						weight[i][j+1][k]=rate*error[i+1][k];
				}
				error[i][j]=err*layer[i][j]*(1.0-layer[i][j]);
			}
		}
	}

四、激活函数

激活函数sigmoid(z)

s i g m o i d ( z ) = 1 1 + e − z sigmoid(z)=\frac{1}{1+e^{-z}} sigmoid(z)=1+ez1

​ sigmoid(z)+交叉熵损失函数可以对算法进行改进,以提高收敛速度。交叉熵损失函数为:
E = − t k l n x − ( 1 − t k ) l n ( 1 − x ) E=-t_klnx-(1-t_k)ln(1-x) E=tklnx(1tk)ln(1x)
​ 其对权值w的偏导数(梯度)为:
∂ E ∂ w = ( O k − t k ) O j \frac{\partial E}{\partial w}=(O_k-t_k)O_j wE=(Oktk)Oj

### 激活函数tanh(z)

t a n h ( z ) = e z − e − z e z + e − z = 2 s i g m o i d ( 2 z ) − 1 tanh(z)=\frac{e^z-e^{-z}}{e^z+e^{-z}}=2sigmoid(2z)-1 tanh(z)=ez+ezezez=2sigmoid(2z)1

​ 在使用神经网络完成数据分类的操作时,使用tanh函数作为激活函数可能会提高分类的精度。

附录代码

package bp;

import bp.base.IbpBase;
import tool.Function;

//神经网络基本实现:sigmoid+平方误差
//本程序在出现三个迭代变量时,通常i代表层数,j代表某层的节点,k代表下一层的节点
public class BPModel implements IbpBase{
	private double[][] layer;//输出值
	private double[][][] weight;//权值,【n】【a】【b】,n<总层数-1
	private double[][] error;//误差
	private double rate;//学习系数
	
	public BPModel(int[] numberOfLayer,double r) {
		init(numberOfLayer, r);
	}
	
	@Override
	public void init(int[] numberOfLayer,double r) {
		// TODO 自动生成的方法存根
		rate=r;
		int n=numberOfLayer.length;//模型的层数
		layer=new double[n][];
		weight=new double[n-1][][];
		error=new double[n][];
		for(int i=0;i<n;++i) {
			layer[i]=new double[numberOfLayer[i]];
			error[i]=new double[numberOfLayer[i]];
			if(i<n-1) {
				weight[i]=new double[numberOfLayer[i]+1][numberOfLayer[i+1]];
				for(int j=0;j<numberOfLayer[i]+1;++j)
					for(int k=0;k<numberOfLayer[i+1];++k)
						weight[i][j][k]=Math.random();//随机初始化权值和偏量,最后一列为偏量
			}
		}
	}

	@Override
	public double[] computeOut(double[] inp) {//采用function:sigmoid
		// TODO 自动生成的方法存根
		for(int i=1;i<layer.length;++i) {
			for(int k=0;k<layer[i].length;++k) {
				double z=weight[i-1][layer[i-1].length][k];//偏量
				for(int j=0;j<layer[i-1].length;++j) {
					layer[i-1][j]=i==1?inp[j]:layer[i-1][j];//初始化输入层
					z+=weight[i-1][j][k]*layer[i-1][j];
				}
				layer[i][k]=Function.sigmoid(z);
			}
		}
		return layer[layer.length-1];
	}

	@Override
	public void backPropagation(double[] target) {//自倒数第二层开始反向计算误差并更新权值和偏量
		// TODO 自动生成的方法存根
		int i=layer.length-1;
		//先计算最后一层的误差
		for(int j=0;j<layer[i].length;++j)
			error[i][j]=layer[i][j]*(1-layer[i][j])*(target[j]-layer[i][j]);
		while(i-->0){
			//误差和权重同时计算
			for(int j=0;j<layer[i].length;++j) {
				double err=0.0;
				for(int k=0;k<layer[i+1].length;++k) {
					err+=weight[i][j][k]*error[i+1][k];
					weight[i][j][k]+=rate*error[i+1][k]*layer[i][j];
					if(j==layer[i].length-1) //调整偏量
						weight[i][j+1][k]=rate*error[i+1][k];
				}
				error[i][j]=err*layer[i][j]*(1.0-layer[i][j]);
			}
		}
	}
	
	public void trainModel(double[][] inp,double[][] tar,int n) {
		while(n-->0) {
			for(int i=0;i<inp.length;++i)
				train(inp[i], tar[i]);
		}
	}
	
	private void train(double[] inp,double[] tar) {
		computeOut(inp);
		backPropagation(tar);
	}
	
	public double reportModel(double[][] inp,double[][] tar,double p) {
		int count=0;
		for(int i=0;i<inp.length;++i) {
			double[] result=computeOut(inp[i]);
			System.out.println(tar[i][0]+"\t"+result[0]);
			if(compare(result, tar[i], p)) ++count;
		}
		return count/(double)inp.length;
	}
	
	private boolean compare(double[] a1,double[] a2,double p) {//平方误差小于p则算正确
		double err=0.0;
		for(int i=0;i<a1.length;++i)
			err+=0.5*Math.pow(a1[i]-a2[i],2.0);
		if(err<p) return true;
		else return false;
	}
}

package bp.test;

import java.io.IOException;
import java.util.ArrayList;

import bp.BPModel;
import tool.ReadData;

public class BaseTest {

	public static void listSplit(ArrayList<ArrayList<String>> data,ArrayList<ArrayList<String>> d1,ArrayList<ArrayList<String>> d2,double p) {
		for(ArrayList<String> val : data)
			if(Math.random()<p) d1.add(val);
			else d2.add(val);
	}
	
	public static void dataTransfer(ArrayList<ArrayList<String>> d,double[][] in,double[][] tar) {
		for(int i=0;i<d.size();++i) {
			tar[i][0]=Double.parseDouble(d.get(i).get(d.get(i).size()-1));
			for(int j=0;j<d.get(i).size()-1;++j)
				in[i][j]=Double.parseDouble(d.get(i).get(j));
		}
	}
	
	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
		//divorce.txt,AutismAdultDataPlus.txt,StudentAcademicsPerformance.txt
		String path="divorce.txt";
		ArrayList<ArrayList<String>> data=ReadData.readDataFile(path);
		ArrayList<ArrayList<String>> d1=new ArrayList<ArrayList<String>>();
		ArrayList<ArrayList<String>> d2=new ArrayList<ArrayList<String>>();
		listSplit(data, d1, d2, 0.8);
		int row=d1.size(),col=d1.get(0).size()-1;
		double[][] target=new double[row][1];
		double[][] inp=new double[row][col];
		dataTransfer(d1, inp, target);
		int t=1;//隐含层的数目
		int[] layers=new int[t+2];
		layers[0]=col;
		layers[t+1]=1;
		for(int i=1;i<t+1;++i) layers[i]=col-i*(col-2)/(t+1);
		BPModel bpModel=new BPModel(layers, 0.8);
		bpModel.trainModel(inp, target, 10000);
		
		
		row=d2.size();
		col=d2.get(0).size()-1;
		double[][] target2=new double[row][1];
		double[][] inp2=new double[row][col];
		dataTransfer(d2, inp2, target2);
		System.out.println(bpModel.reportModel(inp2, target2, 0.1));
	}

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值