深度学习自编码的Java实现

深度学习自编码器即Java实现

​ 自编码(Auto-Encode)是一种无监督学习,不给定标签向量,它可以借助神经网络来实现,将神经网络的输入和输出进行对比来不断地重构误差,修正神经网络中各层节点的权值和偏量,使得网络的输出不断地逼近输入,理想状态下可以使得输出等于输入,而神经网络隐含层中的某一层(例如隐含层最中间的那一层)的输出值可以作为已编码数据进行使用。

​ 自编码器的训练过程是一种对原数据特征的提取,通过不断地训练数据从而得到蕴含原数据主要特征的目标数据,目标数据在维度上是低于原数据的。

自编码神经网络的实现方式一

​ 建立多层神经网络时,设定多层隐含层的最中间那一层的节点数为目标维度,然后将原数据输入神经网络进行多次迭代训练得到模型。不过,在原数据维度较高而数据量较少时,这种方式得到自编码器模型通常效率较低,模型的精度也不高。

package bpAutoEncode.single;

import java.util.Arrays;

import bpAutoEncode.base.IbpBase;
import tool.Function;

//神经网络基本实现:sigmoid+平方方差
//本程序在出现三个迭代变量时,通常i代表层数,j代表某层的节点,k代表下一层的节点
public class BPEncode implements IbpBase{
	private double[][] layer;//输出值
	private double[][][] weight;//权值,【n】【a】【b】,n<总层数-1
	private double[][] error;//误差
	private double rate;//学习系数
	
	public BPEncode(int features,int feaAfterEncode,int layerNum,double r) {
		if(layerNum%2==0) ++layerNum;
		int[] numberOfLayer=new int[layerNum];
		numberOfLayer[0]=numberOfLayer[layerNum-1]=features;
		numberOfLayer[layerNum/2]=feaAfterEncode;
		int step=(features-feaAfterEncode)/(layerNum/2);
		for (int i = 1; i < layerNum/2; i++) {
			numberOfLayer[i]=numberOfLayer[i-1]-step;
			numberOfLayer[layerNum-1-i]=numberOfLayer[layerNum-i]-step;
		}
		System.out.println(Arrays.toString(numberOfLayer));
		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 p) {
		int xn=0;
		double tmp;
		do {
			for(int i=0;i<inp.length;++i) {
				computeOut(inp[i]);
				backPropagation(inp[i]);
			}
			tmp=reportModel(inp, p);
			System.out.println("迭代训练了"+(++xn)+"次.当前模型准确率为:"+tmp);
			if(xn%10000==0) p+=0.01;
		} while (tmp<1.0-p);
	}
	
	public void computeEncodeData(double[] inp,double[] result) {
		computeOut(inp);
		for(int i=0;i<result.length;++i) result[i]=layer[layer.length/2][i];
	}
	
	public void computeEncodeDataSet(double[][] inp,double[][] dataSet) {
		for(int i=0;i<inp.length;++i)
			computeEncodeData(inp[i], dataSet[i]);
	}
	public double reportModel(double[][] inp,double p) {
		int count=0;
		for(int i=0;i<inp.length;++i) {
			double[] result=computeOut(inp[i]);
			//System.out.println(Arrays.toString(inp[i])+"\t"+Arrays.toString(result));
			//System.out.println(Arrays.toString(result));
			if(compare(result, inp[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+=Math.pow(a1[i]-a2[i],2.0);
		err*=0.5;
		if(err<p) return true;
		else return false;
	}
}

自编码神经网络的实现方式二

​ 该方式采用逐层训练的方式,以多个神经网络模型分别训练自编码神经网络的每一层,然后组合成自编码神经网络模型。即,以【方式一】训练出多个模型,但每个模型“降维”较少,然后组合成一个模型。

package bpAutoEncode.multi;

import java.util.ArrayList;
import bpAutoEncode.base.IbpBase;
import tool.Function;

//神经网络基本实现:sigmoid+平方方差
//本程序在出现三个迭代变量时,通常i代表层数,j代表某层的节点,k代表下一层的节点
public class BPEncodeMulti implements IbpBase{
	private double[][] layer;//输出值
	private double[][][] weight;//权值,【n】【a】【b】,n<总层数-1
	private double[][] error;//误差
	private double rate;//学习系数
	
	public BPEncodeMulti(int features,int feaAfterEncode,double r) {
		int layerNum=3;
		int[] numberOfLayer=new int[layerNum];
		numberOfLayer[0]=numberOfLayer[layerNum-1]=features;
		numberOfLayer[layerNum/2]=feaAfterEncode;
		init(numberOfLayer, r);
	}
	
	public BPEncodeMulti(ArrayList<BPEncodeMulti> models,double r) {
		int[] numberOfLayer=new int[models.size()*2+1];
		numberOfLayer[models.size()]=models.get(models.size()-1).getMinNodeNum();
		int n=numberOfLayer.length;//模型的层数
		rate=r;
		layer=new double[n][];
		weight=new double[n-1][][];
		error=new double[n][];
		for(int i=0;i<models.size();++i) {
			numberOfLayer[i]=numberOfLayer[n-1-i]=models.get(i).getMaxNodeNum();
			weight[i]=models.get(i).getZoomOutWeight();
			weight[weight.length-1-i]=models.get(i).getZoomInWeight();
		}
		for(int i=0;i<n;++i) {
			layer[i]=new double[numberOfLayer[i]];
			error[i]=new double[numberOfLayer[i]];
		}
	}
	
	public double[][] getZoomInWeight() {
		return weight[1];
	}
	
	public double[][] getZoomOutWeight() {
		return weight[0];
	}
	
	public int getMinNodeNum() {
		return layer[1].length;
	}
	
	public int getMaxNodeNum() {
		return layer[0].length;
	}
	
	@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];
	}

	public double[][] computeOut(double[][] inp) {
		double[][] result=new double[inp.length][];
		for(int i=0;i<inp.length;++i) result[i]=computeOut(inp[i]);
		return result;
	}
	
	@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 p) {
		int xn=0;
		double tmp;
		while(true) {
			for(int i=0;i<inp.length;++i) {
				computeOut(inp[i]);
				backPropagation(inp[i]);
			}
			tmp=reportModel(inp, p);
			++xn;
			if(tmp>1.0-p) return;
			if(xn%10000==0) p+=0.01;
		}
	}
	
	public static ArrayList<BPEncodeMulti> getTrainModel(double[][] inp,double rate,double p) {
		ArrayList<BPEncodeMulti> result=new ArrayList<BPEncodeMulti>();
		int max=inp[0].length;
		int min=(int) Math.sqrt(max);
		min+=(max-min)%4;
		int n=(max-min)/4;
		System.out.println(n);
		double[][] in=inp;
		for(int i=0;i<n;++i) {
			BPEncodeMulti bpEncodeMulti=new BPEncodeMulti(max-4*i, max-4*(i+1), rate);
			bpEncodeMulti.trainModel(in, p);
			System.out.print(bpEncodeMulti.reportModel(in, p));
			double[][] tmp=new double[in.length][max-4*i-4];
			bpEncodeMulti.computeEncodeDataSet(in, tmp);
			in=tmp;
			result.add(bpEncodeMulti);
			System.out.println("\t第"+(i+1)+"个子模型*.");
		}
		return result;
	}
	
	public static double repModel(ArrayList<BPEncodeMulti> model,double[][] inp,double p) {
		System.out.println(model.size());
		int count=0;
		for(double[] in : inp) {
			double[] a=in;
			for(int i=0;i<model.size();++i) {
				double[] tmp=new double[model.get(i).getMinNodeNum()];
				model.get(i).computeEncodeData(a, tmp);
				a=tmp;
			}
			for(int i=model.size()-1;i>=0;--i) 
				a=model.get(i).computeDecodeData(a);
			if(compare(a, in, p)) ++count;
		}
		return count/(double)inp.length;
	}
	
	public void computeEncodeData(double[] inp,double[] result) {
		computeOut(inp);
		for(int i=0;i<result.length;++i) result[i]=layer[layer.length/2][i];
	}
	
	public void computeEncodeDataSet(double[][] inp,double[][] dataSet) {
		for(int i=0;i<inp.length;++i)
			computeEncodeData(inp[i], dataSet[i]);
	}
	
	public double[] computeDecodeData(double[] inp) {
		for(int i=layer.length/2+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==2?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];
	}
	
	public double[][] computeDecodeDataSet(double[][] inp) {
		double[][] result=new double[inp.length][];
		for(int i=0;i<result.length;++i) result[i]=computeDecodeData(inp[i]);
		return result;
	}
	
	public double reportModel(double[][] inp,double p) {
		int count=0;
		for(int i=0;i<inp.length;++i) {
			double[] result=computeOut(inp[i]);
			//System.out.println(Arrays.toString(inp[i])+"\t"+Arrays.toString(result));
			//System.out.println(Arrays.toString(result));
			if(compare(result, inp[i], p)) ++count;
		}
		return count/(double)inp.length;
	}
	
	public static boolean compare(double[] a1,double[] a2,double p) {//平方误差小于p则算正确
		double err=0.0;
		for(int i=0;i<a1.length;++i)
			err+=Math.pow(a1[i]-a2[i],2.0);
		err/=a1.length;
		if(err<p) return true;
		else return false;
	}
}

参考链接:
深度神经网络
深度学习的自编码

发布了13 篇原创文章 · 获赞 0 · 访问量 1409

猜你喜欢

转载自blog.csdn.net/qq_34262612/article/details/104374315
今日推荐