用java写bp神经网络(二)

接上篇。

Net和Propagation具备后,我们就可以训练了。训练师要做的事情就是,怎么把一大批样本分成小批训练,然后把小批的结果合并成完整的结果(批量/增量);什么时候调用学习师根据训练的结果进行学习,然后改进网络的权重和状态;什么时候决定训练结束。

那么这两位老师儿长的什么样子,又是怎么做到的呢?

public interface Trainer {
    public void train(Net net,DataProvider provider);
}

public interface Learner {
    public void learn(Net net,TrainResult trainResult);
}

所谓Trainer即是给定数据,对指定网络进行训练;所谓Learner即是给定训练结果,然后对指定网络进行权重调整。

下面给出这两个接口的简单实现。

Trainer

Trainer实现简单的批量训练功能,在给定的迭代次数后停止。代码示例如下。

public class CommonTrainer implements Trainer {
	int ecophs;
	Learner learner;
	List<Double> costs = new ArrayList<>();
	List<Double> accuracys = new ArrayList<>();
	int batchSize = 1;

	public CommonTrainer(int ecophs, Learner learner) {
		super();
		this.ecophs = ecophs;
		this.learner = learner == null ? new MomentAdaptLearner() : learner;
	}

	public CommonTrainer(int ecophs, Learner learner, int batchSize) {
		this(ecophs, learner);
		this.batchSize = batchSize;
	}

	public void trainOne(final Net net, DataProvider provider) {
		final Propagation propagation = new Propagation(net);
		DoubleMatrix input = provider.getInput();
		DoubleMatrix target = provider.getTarget();
		final int allLen = target.columns;
		final int[] nodesNum = net.getNodesNum();
		final int layersNum = net.getLayersNum();

		List<DoubleMatrix> inputBatches = this.getBatches(input);
		final List<DoubleMatrix> targetBatches = this.getBatches(target);

		final List<Integer> batchLen = MatrixUtil.getEndPosition(targetBatches);

		final BackwardResult backwardResult = new BackwardResult(net, allLen);

          // 分批并行训练
		Parallel.For(inputBatches, new Parallel.Operation<DoubleMatrix>() {
			@Override
			public void perform(int index, DoubleMatrix subInput) {
				ForwardResult subResult = propagation.forward(subInput);
				DoubleMatrix subTarget = targetBatches.get(index);
				BackwardResult backResult = propagation.backward(subTarget,
						subResult);

				DoubleMatrix cost = backwardResult.cost;
				DoubleMatrix accuracy = backwardResult.accuracy;
				DoubleMatrix inputDeltas = backwardResult.getInputDelta();

				int start = index == 0 ? 0 : batchLen.get(index - 1);
				int end = batchLen.get(index) - 1;
				int[] cIndexs = ArraysHelper.makeArray(start, end);

				cost.put(cIndexs, backResult.cost);

				if (accuracy != null) {
					accuracy.put(cIndexs, backResult.accuracy);
				}

				inputDeltas.put(ArraysHelper.makeArray(0, nodesNum[0] - 1),
						  cIndexs, backResult.getInputDelta());

				for (int i = 0; i < layersNum; i++) {
					DoubleMatrix gradients = backwardResult.gradients.get(i);
					DoubleMatrix biasGradients = backwardResult.biasGradients
							.get(i);

  					DoubleMatrix subGradients = backResult.gradients.get(i)
							.muli(backResult.cost.columns);
					DoubleMatrix subBiasGradients = backResult.biasGradients
							.get(i).muli(backResult.cost.columns);
					gradients.addi(subGradients);
					biasGradients.addi(subBiasGradients);
				}
			}
		});
         // 求均值
		for(DoubleMatrix gradient:backwardResult.gradients){
			gradient.divi(allLen);
		}
		for(DoubleMatrix gradient:backwardResult.biasGradients){
			gradient.divi(allLen);
		}

		// this.mergeBackwardResult(backResults, net, input.columns);
		TrainResult trainResult = new TrainResult(null, backwardResult);

		learner.learn(net, trainResult);

		Double cost = backwardResult.getMeanCost();
		Double accuracy = backwardResult.getMeanAccuracy();
		if (cost != null)
			costs.add(cost);
		if (accuracy != null)
			accuracys.add(accuracy);

  		System.out.println(cost);
		System.out.println(accuracy);
	}

	@Override
	public void train(Net net, DataProvider provider) {
		for (int i = 0; i < this.ecophs; i++) {
			this.trainOne(net, provider);
		}

	}
}

Learner

Learner是具体的调整算法,当梯度计算出来后,它负责对网络权重进行调整。调整算法的选择直接影响着网络收敛的快慢。本文的实现采用简单的动量-自适应学习率算法。

其迭代公式如下:

$$W(t+1)=W(t)+\Delta W(t)$$

$$\Delta W(t)=rate(t)(1-moment(t))G(t+1)+moment(t)\Delta W(t-1)$$

$$rate(t+1)=\begin{cases} rate(t)\times 1.05 & \mbox{if } cost(t)<cost(t-1)\\ rate(t)\times 0.7 & \mbox{else if } cost(t)<cost(t-1)\times 1.04\\ 0.01 & \mbox{else} \end{cases}$$

$$moment(t+1)=\begin{cases} 0.9 & \mbox{if } cost(t)<cost(t-1)\\ rate(t)\times 0.7 & \mbox{else if } cost(t)<cost(t-1)\times 1.04\\ 1-0.9 & \mbox{else} \end{cases}$$

示例代码如下:

public class MomentAdaptLearner implements Learner {

	Net net;
	double moment = 0.9;
	double lmd = 1.05;
	double preCost = 0;
	double eta = 0.01;
	double currentEta=eta;
	double currentMoment=moment;
	TrainResult preTrainResult;

	public MomentAdaptLearner(double moment, double eta) {
		super();
		this.moment = moment;
		this.eta = eta;
		this.currentEta=eta;
		this.currentMoment=moment;
	}

	@Override
	public void learn(Net net, TrainResult trainResult) {
		if (this.net == null)
			init(net);

		BackwardResult backwardResult = trainResult.backwardResult;
		BackwardResult preBackwardResult = preTrainResult.backwardResult;
		double cost=backwardResult.getMeanCost();
		this.modifyParameter(cost);
		System.out.println("current eta:"+this.currentEta);
		System.out.println("current moment:"+this.currentMoment);
		for (int j = 0; j < net.getLayersNum(); j++) {
			DoubleMatrix weight = net.getWeights().get(j);
			DoubleMatrix gradient = backwardResult.gradients.get(j);

			gradient = gradient.muli(currentEta * (1 - this.currentMoment)).addi(
					preBackwardResult.gradients.get(j).muli(this.currentMoment));
			preBackwardResult.gradients.set(j, gradient);

			weight.subi(gradient);

			DoubleMatrix b = net.getBs().get(j);
			DoubleMatrix bgradient = backwardResult.biasGradients.get(j);

			bgradient = bgradient.muli(currentEta * (1 - this.currentMoment)).addi(
					preBackwardResult.biasGradients.get(j).muli(this.currentMoment));
			preBackwardResult.biasGradients.set(j, bgradient);

			b.subi(bgradient);
		}

	}

	public void modifyParameter(double cost){
		if(cost<this.preCost){
			this.currentEta*=1.05;
			this.currentMoment=moment;
		}else if(cost<1.04*this.preCost){
			this.currentEta*=0.7;
			this.currentMoment*=0.7;
		}else{
			this.currentEta=eta;
			this.currentMoment=1-moment;
		}
		this.preCost=cost;
	}
	public void init(Net net) {
		this.net =  net;
		BackwardResult bResult = new BackwardResult();

		for (DoubleMatrix weight : net.getWeights()) {
			bResult.gradients.add(DoubleMatrix.zeros(weight.rows,
					weight.columns));
		}
		for (DoubleMatrix b : net.getBs()) {
			bResult.biasGradients.add(DoubleMatrix.zeros(b.rows, b.columns));
		}
		preTrainResult=new TrainResult(null,bResult);
	}

}

现在,一个简单的神经网路从生成到训练已经简单实现完毕。

时间: 2024-10-12 15:52:34

用java写bp神经网络(二)的相关文章

用java写bp神经网络(四)

接上篇. 在(一)和(二)中,程序的体系是Net,Propagation,Trainer,Learner,DataProvider.这篇重构这个体系. Net 首先是Net,在上篇重新定义了激活函数和误差函数后,内容大致是这样的: List<DoubleMatrix> weights = new ArrayList<DoubleMatrix>(); List<DoubleMatrix> bs = new ArrayList<>(); List<Acti

【机器学习】BP神经网络实现手写数字识别

最近用python写了一个实现手写数字识别的BP神经网络,BP的推导到处都是,但是一动手才知道,会理论推导跟实现它是两回事.关于BP神经网络的实现网上有一些代码,可惜或多或少都有各种问题,在下手写了一份,连带着一些关于性能的分析也写在下面,希望对大家有所帮助. 本博文不含理论推导,如对BP的理论推导感兴趣百度即可,或参考<模式识别>. 一.数据库 程序使用的数据库是mnist手写数字数据库,这个数据库我有两个版本,一个是别人做好的.mat格式,训练数据有60000条,每条是一个784维的向量,

BP神经网络—java实现

神经网络的结构 神经网络的网络结构由输入层,隐含层,输出层组成.隐含层的个数+输出层的个数=神经网络的层数,也就是说神经网络的层数不包括输入层.下面是一个三层的神经网络,包含了两层隐含层,一个输出层.其中第一层隐含层的节点数为3,第二层的节点数为2,输出层的节点数为1:输入层为样本的两个特征X1,X2. 图1 三层神经网络 在神经网络中每一个节点的都与上一层的所有节点相连,称为全连接.神经网络的上一层输出的数据是下一层的输入数据.在图中的神经网络中,原始的输入数据,通过第一层隐含层的计算得出的输

BP神经网络识别手写数字项目解析及代码

这两天在学习人工神经网络,用传统神经网络结构做了一个识别手写数字的小项目作为练手.点滴收获与思考,想跟大家分享一下,欢迎指教,共同进步. 平常说的BP神经网络指传统的人工神经网络,相比于卷积神经网络(CNN)来说要简单些. 人工神经网络具有复杂模式和进行联想.推理记忆的功能, 它是解决某些传统方法所无法解决的问题的有力工具.目前, 它日益受到重视, 同时其他学科的发展, 为其提供了更大的机会.1986 年, Romelhart 和Mcclelland提出了误差反向传播算法(Error Back

JAVA之旅(二十四)——I/O流,字符流,FileWriter,IOException,文件续写,FileReader,小练习

JAVA之旅(二十四)--I/O流,字符流,FileWriter,IOException,文件续写,FileReader,小练习 JAVA之旅林林总总也是写了二十多篇了,我们今天终于是接触到了I/O了.如果你初学,不懂IO流,你可以从前往后慢慢看,但是你工作了一段时间你会发现,流的使用场景以及技术点是非常的强硬的,我们势必要掌握这个知识点,如果你觉得翻阅API比较鼓噪,看视频得不到精髓,看书看不到要点,你就跟随我的JAVA之旅,一起去探索吧! 一.I/O概述 I/O全名:Input Output

基于BP神经网络的手写数字识别

一.BP神经网络原理及结构 本部分先介绍神经网络基本单元神经元的结构和作用,再主要介绍BP神经网络的结构和原理. 1.神经元 神经元作为神经网络的基本单元,对于外界输入具有简单的反应能力,在数学上表征为简单的函数映射.如下图是一个神经元的基本结构,  神经元结构 图中是神经元的输入,是神经元输入的权重,是神经元的激活函数,y是神经元的输出,其函数映射关系为 激活函数来描述层与层输出之间的关系,从而模拟各层神经元之间的交互反应.激活函数必须满足处处可导的条件.常用的神经元函数有四种,分别是线性函数

数据挖掘系列(9)——BP神经网络算法与实践

神经网络曾经很火,有过一段低迷期,现在因为深度学习的原因继续火起来了.神经网络有很多种:前向传输网络.反向传输网络.递归神经网络.卷积神经网络等.本文介绍基本的反向传输神经网络(Backpropagation 简称BP),主要讲述算法的基本流程和自己在训练BP神经网络的一些经验. BP神经网络的结构 神经网络就是模拟人的大脑的神经单元的工作方式,但进行了很大的简化,神经网络由很多神经网络层构成,而每一层又由许多单元组成,第一层叫输入层,最后一层叫输出层,中间的各层叫隐藏层,在BP神经网络中,只有

java版本的神经网络——开源框架JOONE实践

由于实验室事情缘故,需要将Python写的神经网络转成Java版本的,但是python中的numpy等啥包也不知道在Java里面对应的是什么工具,所以索性直接寻找一个现成可用的Java神经网络框架,于是就找到了JOONE,JOONE是一个神经网络的开源框架,使用的是BP算法进行迭代计算参数,使用起来比较方便也比较实用,下面介绍一下JOONE的一些使用方法. JOONE需要使用一些外部的依赖包,这在官方网站上有,也可以在这里下载.将所需的包引入工程之后,就可以进行编码实现了. 首先看下完整的程序,

通俗讲解BP神经网络

BP(backward propogation)神经网络是广泛使用的一种神经网络.要我说,神经网络就是一种高端的插值技术.相应比较好的实现教程有: Matlab工具箱版本(使用简便,但是不适用于理解原理):漫谈ANN(2):BP神经网络: Matlab原理实现(根据原理实现的版本,未使用神经网络工具箱):简单易学的机器学习算法--神经网络之BP神经网络: C++原理实现(根据原理实现):BP神经网络原理及C++实战 三篇文章,第2.3篇适用于理解原理.详细的数学推导已经在里面了,就不赘述了.下面