用java写bp神经网络(四)

接上篇。

在(一)和(二)中,程序的体系是Net,Propagation,Trainer,Learner,DataProvider。这篇重构这个体系。

Net

首先是Net,在上篇重新定义了激活函数和误差函数后,内容大致是这样的:

List<DoubleMatrix> weights = new ArrayList<DoubleMatrix>();
	List<DoubleMatrix> bs = new ArrayList<>();
	List<ActivationFunction> activations = new ArrayList<>();
	CostFunction costFunc;
	CostFunction accuracyFunc;
	int[] nodesNum;
	int layersNum;

public CompactDoubleMatrix getCompact(){
		return new CompactDoubleMatrix(this.weights,this.bs);
	}

函数getCompact()生成对应的超矩阵。

DataProvider

DataProvider是数据的提供者。

public interface DataProvider {
    DoubleMatrix getInput();
    DoubleMatrix getTarget();
}

如果输入为向量,还包含一个向量字典。

public interface DictDataProvider extends DataProvider {
	public DoubleMatrix getIndexs();
	public DoubleMatrix getDict();
}

每一列为一个样本。getIndexs()返回输入向量在字典中的索引。

我写了一个有用的类BatchDataProviderFactory来对样本进行批量分割,分割成minibatch。

int batchSize;
	int dataLen;
	DataProvider originalProvider;
	List<Integer> endPositions;
	List<DataProvider> providers;

	public BatchDataProviderFactory(int batchSize, DataProvider originalProvider) {
		super();
		this.batchSize = batchSize;
		this.originalProvider = originalProvider;
		this.dataLen = this.originalProvider.getTarget().columns;
		this.initEndPositions();
		this.initProviders();
	}

	public BatchDataProviderFactory(DataProvider originalProvider) {
		this(4, originalProvider);
	}

	public List<DataProvider> getProviders() {
		return providers;
	}

batchSize指明要分多少批,getProviders返回生成的minibatch,被分的原始数据为originalProvider。

Propagation

Propagation负责对神经网络的正向传播过程和反向传播过程。接口定义如下:

public interface Propagation {
	public PropagationResult propagate(Net net,DataProvider provider);
}

传播函数propagate用指定数据对指定网络进行传播操作,返回执行结果。

BasePropagation实现了该接口,实现了简单的反向传播:

public class BasePropagation implements Propagation{

	// 多个样本。
	protected ForwardResult forward(Net net,DoubleMatrix input) {

		ForwardResult result = new ForwardResult();
		result.input = input;
		DoubleMatrix currentResult = input;
		int index = -1;
		for (DoubleMatrix weight : net.weights) {
			index++;
			DoubleMatrix b = net.bs.get(index);
			final ActivationFunction activation = net.activations
					.get(index);
			currentResult = weight.mmul(currentResult).addColumnVector(b);
			result.netResult.add(currentResult);

			// 乘以导数
			DoubleMatrix derivative = activation.derivativeAt(currentResult);
			result.derivativeResult.add(derivative);

			currentResult = activation.valueAt(currentResult);
			result.finalResult.add(currentResult);

		}

		result.netResult=null;// 不再需要。

		return result;
	}

    // 多个样本梯度平均值。
	protected BackwardResult backward(Net net,DoubleMatrix target,
			ForwardResult forwardResult) {
		BackwardResult result = new BackwardResult();

		DoubleMatrix output = forwardResult.getOutput();
		DoubleMatrix outputDerivative = forwardResult.getOutputDerivative();

		result.cost = net.costFunc.valueAt(output, target);
		DoubleMatrix outputDelta = net.costFunc.derivativeAt(output, target).muli(outputDerivative);
		if (net.accuracyFunc != null) {
			result.accuracy=net.accuracyFunc.valueAt(output, target);
		}

		result.deltas.add(outputDelta);
		for (int i = net.layersNum - 1; i >= 0; i--) {
			DoubleMatrix pdelta = result.deltas.get(result.deltas.size() - 1);

			// 梯度计算,取所有样本平均
			DoubleMatrix layerInput = i == 0 ? forwardResult.input
					: forwardResult.finalResult.get(i - 1);
			DoubleMatrix gradient = pdelta.mmul(layerInput.transpose()).div(
					target.columns);
			result.gradients.add(gradient);
			// 偏置梯度
			result.biasGradients.add(pdelta.rowMeans());

			// 计算前一层delta,若i=0,delta为输入层误差,即input调整梯度,不作平均处理。
			DoubleMatrix delta = net.weights.get(i).transpose().mmul(pdelta);
			if (i > 0)
				delta = delta.muli(forwardResult.derivativeResult.get(i - 1));
			result.deltas.add(delta);
		}
		Collections.reverse(result.gradients);
		Collections.reverse(result.biasGradients);

		//其它的delta都不需要。
		DoubleMatrix inputDeltas=result.deltas.get(result.deltas.size()-1);
		result.deltas.clear();
		result.deltas.add(inputDeltas);

		return result;
	}

	@Override
	public PropagationResult propagate(Net net, DataProvider provider) {
		ForwardResult forwardResult=this.forward(net, provider.getInput());
		BackwardResult backwardResult=this.backward(net, provider.getTarget(), forwardResult);
		PropagationResult result=new PropagationResult(backwardResult);
		result.output=forwardResult.getOutput();
		return result;
	}

我们定义的PropagationResult略为:

public class PropagationResult{
		DoubleMatrix output;// 输出结果矩阵:outputLen*sampleLength
		DoubleMatrix cost;// 误差矩阵:1*sampleLength
		DoubleMatrix accuracy;// 准确度矩阵:1*sampleLength
		private List<DoubleMatrix> gradients;// 权重梯度矩阵
		private List<DoubleMatrix> biasGradients;// 偏置梯度矩阵
		DoubleMatrix inputDeltas;//输入层delta矩阵:inputLen*sampleLength

		public CompactDoubleMatrix getCompact(){
			return new CompactDoubleMatrix(gradients,biasGradients);
		}

	}

另一个实现了该接口的类为MiniBatchPropagation。他在内部用并行方式对样本进行传播,然后对每个minipatch结果进行综合,内部用到了BatchDataProviderFactory类和BasePropagation类。

Trainer

Trainer接口定义为:

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

简单的实现类为:

public class CommonTrainer implements Trainer {
	int ecophs;
	Learner learner;
	Propagation propagation;
	List<Double> costs = new ArrayList<>();
	List<Double> accuracys = new ArrayList<>();
	public void trainOne(Net net, DataProvider provider) {
		PropagationResult propResult = this.propagation
				.propagate(net, provider);
		learner.learn(net, propResult, provider);

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

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

	}
}

简单的迭代echops此,没有智能停止功能,每次迭代用Learner调节权重。

Learner

Learner根据每次传播结果对网络权重进行调整,接口定义如下:

public interface Learner<N extends Net,P extends DataProvider> {
    public void learn(N net,PropagationResult propResult,P provider);
}

一个简单的根据动量因子-自适应学习率进行调整的实现类为:

public class MomentAdaptLearner<N extends Net, P extends DataProvider>
		implements Learner<N, P> {
	double moment = 0.7;
	double lmd = 1.05;
	double preCost = 0;
	double eta = 0.01;
	double currentEta = eta;
	double currentMoment = moment;
	CompactDoubleMatrix preGradient;

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

	public MomentAdaptLearner() {

	}

	@Override
	public void learn(N net, PropagationResult propResult, P provider) {
		if (this.preGradient == null)
			init(net, propResult, provider);

		double cost = propResult.getMeanCost();
		this.modifyParameter(cost);
		System.out.println("current eta:" + this.currentEta);
		System.out.println("current moment:" + this.currentMoment);
		this.updateGradient(net, propResult, provider);

	}

	public void updateGradient(N net, PropagationResult propResult, P provider) {
		CompactDoubleMatrix netCompact = this.getNetCompact(net, propResult,
				provider);
		CompactDoubleMatrix gradCompact = this.getGradientCompact(net,
				propResult, provider);
		gradCompact = gradCompact.mul(currentEta * (1 - currentMoment)).addi(
				preGradient.mul(currentMoment));
		netCompact.subi(gradCompact);
		this.preGradient = gradCompact;
	}

	public CompactDoubleMatrix getNetCompact(N net,
			PropagationResult propResult, P provider) {
		return net.getCompact();
	}

	public CompactDoubleMatrix getGradientCompact(N net,
			PropagationResult propResult, P provider) {
		return propResult.getCompact();
	}

	public void modifyParameter(double cost) {

		if (this.currentEta > 10) {
			this.currentEta = 10;
		} else if (this.currentEta < 0.0001) {
			this.currentEta = 0.0001;
		} else 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 = 0.1;
		}
		this.preCost = cost;
	}

	public void init(Net net, PropagationResult propResult, P provider) {
		PropagationResult pResult = new PropagationResult(net);
		preGradient = pResult.getCompact().dup();
	}

}

在上面的代码中,我们可以看到CompactDoubleMatrix类对权重自变量的封装,使代码更加简洁,它在此表现出来的就是一个超矩阵,超向量,完全忽略了内部的结构。

同时,其子类实现了同步更新字典的功能,代码也很简洁,只是简单的把需要调整的矩阵append到超矩阵中去即可,在父类中会统一对其进行调整:

public class DictMomentLearner extends
		MomentAdaptLearner<Net, DictDataProvider> {

	public DictMomentLearner(double moment, double eta) {
		super(moment, eta);
	}

	public DictMomentLearner() {
		super();
	}

	@Override
	public CompactDoubleMatrix getNetCompact(Net net,
			PropagationResult propResult, DictDataProvider provider) {
		CompactDoubleMatrix result = super.getNetCompact(net, propResult,
				provider);
		result.append(provider.getDict());
		return result;
	}

	@Override
	public CompactDoubleMatrix getGradientCompact(Net net,
			PropagationResult propResult, DictDataProvider provider) {
		CompactDoubleMatrix result = super.getGradientCompact(net, propResult,
				provider);
		result.append(DictUtil.getDictGradient(provider, propResult));
		return result;
	}

	@Override
	public void init(Net net, PropagationResult propResult,
			DictDataProvider provider) {
		DoubleMatrix preDictGradient = DoubleMatrix.zeros(
				provider.getDict().rows, provider.getDict().columns);
		super.init(net, propResult, provider);
		this.preGradient.append(preDictGradient);
	}
}
时间: 2024-10-08 08:15:46

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

用java写bp神经网络(二)

接上篇. Net和Propagation具备后,我们就可以训练了.训练师要做的事情就是,怎么把一大批样本分成小批训练,然后把小批的结果合并成完整的结果(批量/增量):什么时候调用学习师根据训练的结果进行学习,然后改进网络的权重和状态:什么时候决定训练结束. 那么这两位老师儿长的什么样子,又是怎么做到的呢? public interface Trainer { public void train(Net net,DataProvider provider); } public interface

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

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

BP神经网络—java实现

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

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

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

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

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

通俗讲解BP神经网络

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

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

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

BP神经网络的数学原理及其算法实现

标签: 分类器神经网络 出处http://blog.csdn.net/zhongkejingwang/article/details/44514073 上一篇文章介绍了KNN分类器,当时说了其分类效果不是很出色但是比较稳定,本文后面将利用BP网络同样对Iris数据进行分类. 什么是BP网络 BP神经网络,BP即Back Propagation的缩写,也就是反向传播的意思,顾名思义,将什么反向传播?文中将会解答.不仅如此,关于隐层的含义文中也会给出个人的理解.最后会用Java实现的BP分类器作为其

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

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