java能用来深度学习吗_java web可以调用深度学习吗 - CSDN
  • 深度学习,人工智能今天已经成了IT界最流行的词,而tensorflow,phython又是研究深度学习神经网络的热门工具。tensorflow是google的出品,而phython又以简练的语法,独特的代码结构和语言特性为众多数据科学家和AI...

    1、Deeplearning4j

    深度学习,人工智能今天已经成了IT界最流行的词,而tensorflow,phython又是研究深度学习神经网络的热门工具。tensorflow是google的出品,而phython又以简练的语法,独特的代码结构和语言特性为众多数据科学家和AI工程师们所喜爱。但今天介绍的不是这两个炙手可热的东东,而是相对冷门,但对于国内大多数的工程师而言更友好,基于java的另一个深度学习框架deeplearning4j。截止本文编写时间为止,dl4j已经发布了1.0版本的beta版。

    本文这里不会花太多笔墨去介绍神经网络的原理,有关这方面的知识,网络上面很多,我就不在此献丑。对于我等工程师而言,最关心的不外乎是神经网络是什么,神经网络有什么用,如何构建神经网络这三个问题。

    啥是神经网络

    神经网络是一个模拟生物(更确切的说,就是人)大脑结构的数学模型。人的大脑由1000亿个神经元构成,神经元通过各自间的连接,形成一个网络,就是所谓的神经网络。人的大脑的功能,不言自喻。人工神经网络就是通过数学建模的办法模拟大脑神经网络结构,实现类似人脑的功能。而人脑,较之于电脑,最显著的特征就是有自主意识,能识别一些相同或相似的物体,并能对这些物体进行有效分类。例如我们看到一幅徐悲鸿的画,就马上能知道画中所画的是一种叫马的生物。同样,我们希望电脑也能达到这样的效果。但可惜的是,计算机无法形成意识,只能处理与数字相关的工作。因此,要想计算机也能正确识别出马,就必须通过数学建模和一套相应的算法,使计算机“知道”这是马。这里知道二字我打了个双引号,为什么?因为计算机的理解最后还是一堆数字,真正的解读还是人。后面我会透过一个具体的例子,来说明为什么我要这么说。

    3、神经网络的作用

    正如第二节所说,人工神经网络是模拟人的大脑结构。因此神经网络的处理能力及程度取决于人对自身大脑结构的理解。就目前而言,神经网络最常被应用在分类和模式识别上面。最著名的例子就是百度首席科学家吴达恩(Andrew Wu)用youtube上的视频训练神经网络,使神经网络准确识别出猫。这有什么用?汽车自动驾驶系统就必须用到这方面的能力。试想下,如果汽车自动驾驶系统连基本的红绿灯,行人,车辆都识别不出来,谁敢让计算机帮忙开车。

    但对于我等程序员吊丝,又不是搞自动驾驶,计算机这种连三岁小儿都不如的识别能力,有什么用?有用!神经网络有模式识别的能力,举个例子,某个购物网站通过数据统计,统计出每个注册用户的每天的订单数据情况,这些订单数据可能千差万别。数据之间也没有必然联系,如果要统计这些用户的类别,分析出他们的一些属性,该如何操作。人手操作肯定不行,像某宝网每天PB级的数据,那要请多少人来干这活。那计算机能干这活吗?能,关键是要把这些数据转换成计算机,更准确的说,神经网络,能识别的数据模型,然后,根据人们的一些直观认识,例如买化妆品的大多是小女生,买保健品的多是老大妈老大爷,诸如此类的所谓先验知识,来设定神经网络的分类目标。

    虽说通过不断学习,网络总能与人们的直观认识达成一致(至少是大部分一致),但这仅对于训练样本而言。我们训练网络的目的,是为了最终的预测(也称为泛化)。如果网络对于除训练样本之外的数据,识别出来的分类未能与人们直观的认识相一致,就会出现所谓的过拟合的问题。
    事实上,究竟人们直观认识上的东西是不是真能找到这样一个数学公式来近似模拟,不好说。因此,神经网络擅长于一些模识较为固定的领域,例如图像识别。因为大部分的图形,都可以找到其相应的解析函数。但如果遇到一些比较高级的概念时,例如某某是不是个骗子,某某是不是好人等等,神经网络可能就会懵B。因此神经网络不是万能的。

    2 神经网络的一些基本概念

    神经网络有多种,不同的神经网络有不同的用途。如正向前馈网络(BP网络),递归网络(RNN),卷积网(CNN)。但不管什么样的神经网络都离不开以下这几个概念。这里只粗浅的介绍其中一些概念(不一定全)及在dl4j中相对应的部分,其中一些复杂的数学推导请参看相关的参考文章,这里不作展开。

    神经元

    这是神经网络最基本的元素,神经元接收输入,并产生输出。

    最经典的结构如下图所示:

    神经元简化图

    神经元输入输出的数学模型大致可以简化成如下数学公式

    Y=f(\sum w_{_{i}}x_{_{i}} + b)

    其中xi是输入,wi是权值(),b是偏置,f(·)是激活函数(activation function,常用的有sigmod,RELU,SOFTMAX)。这个公式就是神经元实际执行的计算操作。在dl4j里面没有单独为神经元建模,因为框架实际上已经把网络模型转换为矩阵运算。

    网络模型

    可以看得出来,神经元的输出计算是一个简单的线性函数,所以一层的神经网络(称为感知器perceptor)只能处理线性可分的分类问题。为了打破感知器的局限,才有了BP网络等多层网络模型。理论证明,经过神经元之间的相互连接,形成一个更复杂的确网络,能解决更多的分类问题。

    神经元的输入可能是原始数据样品,也可能是上一层数据的输入,这取决于神经元所处的层,对于一些复杂的神经网络来说,神经元可能有多层分布,每一层的神经元接收上一层(如果存在)神经元的输出数据,产出下一层(如果存在)神经元的输入数据。输入层接收的是原始输入,输出层输出的是最终输出。

    传统的三层BPN的是长下面这副模样的:

    三层神经网络图
    三层BP网络

    如果把整个神经网络看作一个黑盒子,那输入输出的关系更可以进一步简化成Y=F(X)。理论上,通过选择不同的权值和激活函数,函数F(X)的输出能逼近任意连续函数。神经网络能实现分类的原理即在此。

    举例,2维平面直角坐标系(可推广到n维)上有一堆点(事先不知道其规律),如果能过这些点作一个曲线(推广到n维上就是超平面),那么这个曲线的解析函数即为神经网络所演化出来的F(X)。如果有n条曲线刚好穿过这些点,实际上就相当于把这些点分成了n类。如果网络自己学习到的这些曲线与人们直观认识上的分类相一致,我们就认为网络已经成功实现了分类。

    下面再来聊聊何谓“深度学习”。如果说一个人的思想有深度,大概你可以例举他说过的一些话来证明。那计算机的自主学习怎样才算作有深度?最简单的判断标准,就是用于学习的网络有多少层,有多深。

    在dl4j中,可以为每一层进行单独建模。建模的过程中,可以自由选择参数,参数包括输入输出的个数,偏置值,权值初始化方案,激活函数,损失计算函数等。

    训练样本

    训练样本就是你能给神经网络提供的数据,训练样本在dl4j里面分成两部分,分别为特征(features)和标签(labels),特征就是网络实际用于训练的数据,例如人的身高,体重等,标签顾名思义就是给样本打标签,在dl4j的一些demo里,就是一个某维为1,其余全为0的n维向量。如果按照身高,把人们分成三类,高个,中等身材,矮个。那么转换成dl4j的标签即为一个三维向量,如要把一个人标签为高个,那么他的标签向量即为(1.0,0,0)。

    为了保证学习质量,训练样本一般成批地进行输入,所以这里又有一个最小批量(miniBatch)的概念。为什么要设定最小批量,因为神经网络是一个复杂的计算模型,要考虑到一个性能与精度的问题,样本量太大,会导致性能下降,样本量太小,样本覆盖范围不够广,精度又会降低,因此设定一个最小批量值,有助于得到一个性能与精度之间的平衡。

    而训练的过程,就是不断向神经网络输入相同的数据样本,不断调整网络参数,并最终得到输出误差达到指定范围内的网络模型。

    误差与收敛

    如前文所述,我们其实是想找到一个近似函数来模拟点集在n维空间的超平面轨迹F(X)。但是神经网络找到的F(X),未必就是我们想要的东西。例如我们要求张量(即n维向量)T应当是属于某一个分类,从数学的角度来说,就是对于分类函数F'(X),这个张量会有预期的输出。如果网络训练的结果与预期输出不一致,就会出现误差。

    误差会通过所谓的损失函数(英文称loss function,或cost function,有好几种,常用的有log似然函数)来计算,再通过反向传播算法来修正网络参数(权重)来缩小误差。我们希望误差透过上述算法可以变得越来越小,用数学的语言来说,就是所谓的收敛。但收敛并不是越快越好,因为收敛得快,不一定代表后面预测得准,因此会有所谓的学习率,学习率就是用来控制收敛速度。

    上述概念,如偏置,激活函数,网络层数,神经元个数,损失函数,学习率等,在dl4j中都被称为神经网络的超参数(hyper parameter)。通过调节这些参数,能得到不同功能,不同分类特征的神经网络,因此称为超参数。

    3 实战

    介绍完一堆看似复杂的基本概念后,接下来不废话,马上进入实战。实战的内容并不是随便地拿别人的示例跑跑就完事。我的目标是自己出题,自己解决。因为别人的example都是预先设计好的,而且每个模型都只能针对特定的问题,不能照搬。只有自已找问题自己解决,这样才能验证自己的理解是不是正确,和发现这里面可能存在着的坑。

    先出题:现在把人们按经济情况和家庭状况分成五类,一类是穷吊丝(特征:没积蓄,收入低,无车无房无家室),第二类是中层白领(稍有积蓄,收入较高,可能有车有房有家室),第三类是高管(有一定存款,收入高,有车有楼有家室),第四类是有钱人(有大量存款,收入极高,有车有楼有家室),第五类是钻石王老五(还没结婚的有钱人)

    解题思路:

    1. 按照题目把样本数据抽取五个特征值组成一个五维的特征向量(存款,收入,是否有车,是否有房,是否有家室)
    2. 生成随机样本数据和对应的标签
    3. 构建神经网络,设置神经网络的超参数
    4. 把样本数据和标签打包,预处理(正则化),传给神经网络进行训练
    5. 生成一批新的样本数据来评价学习结果,如果结果较差,返回第2步,如果结果理想,则输出评价结果

    下面是dl4j打印出来的输出结果:

     

    ========================Evaluation Metrics========================
     # of classes:    5
     Accuracy:        0.9420
     Precision:       0.9519
     Recall:          0.9411
     F1 Score:        0.9381
    Precision, recall & F1: macro-averaged (equally weighted avg. of 5 classes)


    =========================Confusion Matrix=========================
       0   1   2   3   4
    ---------------------
     183   0   0   0   0 | 0 = 0
      58 139   0   0   0 | 1 = 1
       0   0 221   0   0 | 2 = 2
       0   0   0 193   0 | 3 = 3
       0   0   0   0 206 | 4 = 4

    Confusion matrix format: Actual (rowClass) predicted as (columnClass) N times
    ==================================================================

    这里我简单解释一下结果:

    Accuracy 精确率,正确识别数/总样本数

    Precision Recall & F1 : 各分类准确率的宏观平均值 ,(一直没弄懂Precision 和F1是怎么来的,只知道Recall是各个分类的准确率的算术平均值,例子中就是100% + 70% + 100 % + 100 % + 100%  / 500 % = 94%) 。

    对于我们来说关心一个总体准确率就够了,下面的混淆矩阵才是重点,很多时候超参调优就是看这鬼东西。

    混淆矩阵的格式为实际的分类(以行作为标记)被预测为某个分类(以列作为标次)的次数,例如矩阵第1行1列的数字为183,就是说实际上的1(第1行)分类被预测为1(第1列)分类的次数为183次,也就是1分类的样品被正确识别了183次。

    从结果矩阵的格式可以看出,对角线上的数据就是正常识别的次数,其余的皆为误识别。最理想的矩阵就是对角阵。

    以上就是我历经多次手动调整网络超差数所得到的结果,识别率达9成以上,虽然结果并不完美,还有部分白领被错误识别为屌丝,原因在于我将白领的有车有房的特征设置成随机的。随机数据本来就没啥规律,机器识别不出来我觉得也不奇怪。

    下面介绍一下我用到的超参数:BP网络;层数(3层,输入-隐层-输出);隐层神经元1295个(也就是能产生1295个输出);输入层、隐层的激活函数RELU,输出层的激活函数SOFTMAX;损失函数为负log似然函数;学习率0.5;各层偏置值(0.4,0.08,1);

    整个输出结果是我用了1000个样本数据(样本数据加入了一定的噪音)训练了150次得到的。

    看到这个结果,我也是挺激动,毕竟是苦战了三天三夜所训练出来的第一个效果不错的神经网络。

    为什么看似这么low 逼的题目居然要花这么久的时间。下面就开始要扒一扒用dl4j训练神经网络时踩过的一些大坑了。

    天坑一:超参数的选择

    这个可以说是天坑中的天坑。至本文编写之日止,学术界对于如何选择超参数,一直是个难题。反卷积网就是在这个背景下产生的。对于大部分的拿来主义者来说,最简单快捷的学习方法就是直接用别人的代码复制粘贴,然后应用到自己的业务场景上面去,但我可以很负责任地告诉你,对于训练神经网络来说,你还真不能拿别人现成的东西来用。

    正如我在本节开头所说的,神经网络模型并不具有普遍性。你要处理什么样的数据,要识别出什么东西,就需要一个特定的模型。更坑的是,影响神经网络精确率的因素,除了隐层数,隐层神经元个数外,还有学习率,偏置值,冲量值等一大堆。更让人吐血的是,这些数据的选择没有任何理论上的支持。正如一些文章里面说的,训练神经网络变成了一个抽奖过程。

    天坑二:数据的标准化(Normaliztion)

    神经网络更适合处理标准化的数据,何谓标准化数据,就是经过处理后,特征值在-1到1之间。数据标准化过程在dl4j提供的官方example里面一点都看不到,如果不去看官网,不仔细比对官方提供的训练集数据,光学着它的示例来写code,正如官方文档所说,性能会比较差。

    4、代码

    下面贴出实现上述例子所用到的代码。

    首先是主测试类:

    package org.aztec.dl_common;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    import java.util.List;
    import java.util.Random;
    
    import org.apache.commons.lang.ArrayUtils;
    import org.apache.commons.lang3.RandomUtils;
    import org.datavec.api.records.reader.RecordReader;
    import org.datavec.api.records.reader.impl.csv.CSVRecordReader;
    import org.datavec.api.split.FileSplit;
    import org.deeplearning4j.datasets.datavec.RecordReaderDataSetIterator;
    import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
    
    import com.google.common.collect.Lists;
    
    import junit.framework.TestCase;
    
    /**
     * BP网络测试类
     */
    public class BP_NetworkTest 
        extends TestCase{
    	
    	private static final Random random = new Random();
    	private static final int inputNum = 5;
    	private static final int labelNum = 5;
    	
    	public static void main(String[] args) {
    		//testTrain();
    		//testMultiSample();
    		//generateData();
    		train(new File("test/csv/test_classfy_1.csv"),new File("test/csv/test_classfy_2.csv"));
    		//testRead(new File("E:\\lm/git/dl4j/examples/dl4j-examples/dl4j-examples/src/main/resources/classification/linear_data_train.csv"));
    	}
    	
    	
    	/**
    	 * 生成数据
    	 */
    	private static void generateData() {
    		try {
    			//testMain(args);
    			File oldFile = new File("test/csv/test_classfy_1.csv");
    			oldFile.delete();
    			File newFile = new File("test/csv/test_classfy_2.csv");
    			newFile.delete();
    			generateCSVData(1000, oldFile);
    			generateCSVData(1000, newFile);
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * 读取csv文件数据,并训练网络
    	 * @param trainFile
    	 * @param testFile
    	 */
    	public static void train(File trainFile,File testFile) {
    		try {
    			int batchSize = 50;
    			RecordReader rr = new CSVRecordReader();
    			rr.initialize(new FileSplit(trainFile));
    
    	        DataSetIterator trainIter = new RecordReaderDataSetIterator(rr,batchSize,0,labelNum);
    			RecordReader rr2 = new CSVRecordReader();
    			rr2.initialize(new FileSplit(testFile));
    	        DataSetIterator testIter = new RecordReaderDataSetIterator(rr2,batchSize,0,labelNum);
    	        SimpleNetworkConfiguration snc = new SimpleNetworkConfiguration(inputNum, labelNum);
    	        SimpleBPNN bpnn = new SimpleBPNN();
    	        //1295 5x
    	        snc.setHiddenLayerNeuronNum(1295);
    	        snc.setLayNum(1);
    	        //snc.setBias(0.201);
    	        snc.setLearnRatio(0.5);
    	        snc.setBiasArray(new double[] {0.04,0.080,1});
    	        snc.setMomentum(0.001);
    	        snc.setNumEpochs(150);
    	        bpnn.buildNetwork(snc);
    	        bpnn.train(trainIter, snc.getNumEpochs(),false);
    	        bpnn.validate(testIter, snc.getOutputNum(),false);
    	        
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (InterruptedException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    	}
    	
    	/**
    	 * 生成csv文件数据
    	 * @param sampleSize
    	 * @param csvFile
    	 * @throws IOException
    	 */
    	private static void generateCSVData(int sampleSize,File csvFile) throws IOException {
    		if(!csvFile.exists()) {
    			csvFile.createNewFile();
    		}
    		RandomAccessFile raf = new RandomAccessFile(csvFile, "rw");
    		FileChannel fc = raf.getChannel();
    
    		double[] peopleData = new double[] {};
    		for(int i = 0;i < sampleSize;i++) {
    			int testNum = RandomUtils.nextInt() % labelNum;
    			String writeLine = new String("" + testNum);
    			switch(testNum) {
    			case 0 :
    				peopleData = generatePoorData();
    				break;
    			case 1 :
    				peopleData = generateWriteCollarData();
    				break;
    			case 2 :
    				peopleData = generateLeaderData();
    				//labels[0][i] = 2;
    				break;
    			case 3 :
    
    				peopleData = generateRichData();
    				//labels[0][i] = 3;
    				break;
    			case 4 :
    
    				peopleData = generateGoldenBachelorData();
    				//labels[0][i] = 4;
    				break;
    			}
    			for(int j = 0;j < peopleData.length;j++) {
    				writeLine += "," + peopleData[j];
    			}
    			writeLine += "\n";
    			byte[] lineBytes = writeLine.getBytes();
    			ByteBuffer bb = ByteBuffer.allocate(lineBytes.length);
    			bb.put(lineBytes);
    			bb.flip();
    			fc.write(bb);
    		}
    		fc.close();
    	}
    	
    	
    	/**
    	 * 生成样品数据
    	 * @param sampleSize
    	 * @return
    	 */
    	private static List<double[][]> generateSampleData(int sampleSize){
    		
    		List<double[][]> sampleAllDatas = Lists.newArrayList();
    		double[][] features = new double[sampleSize][];
    		double[][] labels = new double[sampleSize][];
    		//double[][] labels = new double[1][sampleSize];
    		for(int i = 0;i < sampleSize;i++) {
    			int testNum = RandomUtils.nextInt() % 5;
    			switch(testNum) {
    			case 0 :
    				features[i] = generatePoorData();
    				labels[i] = new double[]{1,0,0,0,0};
    				//labels[0][i] = 0;
    				break;
    			case 1 :
    
    				features[i] = generateWriteCollarData();
    				labels[i] = new double[]{0,1,0,0,0};
    				//labels[0][i] = 1;
    				break;
    			case 2 :
    
    				features[i] = generateLeaderData();
    				labels[i] = new double[]{0,0,1,0,0};
    				//labels[0][i] = 2;
    				break;
    			case 3 :
    
    				features[i] = generateRichData();
    				labels[i] = new double[]{0,0,0,1,0};
    				//labels[0][i] = 3;
    				break;
    			case 4 :
    
    				features[i] = generateGoldenBachelorData();
    				labels[i] = new double[]{0,0,0,0,1};
    				//labels[0][i] = 4;
    				break;
    			}
    			
    		}
    		sampleAllDatas.add(features);
    		sampleAllDatas.add(labels);
    		return sampleAllDatas;
    		
    	}
    	
    	/**
    	 * 生成随机噪声
    	 * @param base
    	 * @return
    	 */
    	private static double getRandomNoise(double base) {
    		double randomNum = random.nextDouble();
    		return randomNum;
    		//return base * randomNum;
    		//return 0;
    	}
    	
    	
    	/**
    	 * 生成吊丝数据
    	 * @param base
    	 * @return
    	 */
    	private static double[] generatePoorData() {
    		//为了简化数据模样,提高训练速度,钱被简化成了一个倍数的概念。当然使用真实的人民币单位也可以,只是性能会差一点而已经
    		double base = 1;
    		double deposit = base + getRandomNoise(10);
    		double income = base + getRandomNoise(10);
    		return new double[] {deposit,income,0d,0d,0d};
    		//return new double[] {deposit,income};
    	}
    	
    
    	/**
    	 * 生成白领数据
    	 * @return
    	 */
    	private static double[] generateWriteCollarData() {
    		double base = 3;
    		double deposit =  base + getRandomNoise(10);
    		double hasChild = 0;
    		double income =  base + getRandomNoise(10);
    		double hasHouse = random.nextDouble() > 0.5 ? 1 : 0;
    		double hasCar = random.nextDouble() > 0.5 ? 1 : 0;
    		return new double[] {deposit,income,hasHouse,hasCar,hasChild};
    
    	}
    	
    	/**
    	 * 生成高管数据
    	 * @return
    	 */
    	private static double[] generateLeaderData() {
    		double base = 9;
    		double deposit = base + getRandomNoise(10);
    		double income =  base + getRandomNoise(10) ;
    		return new double[] {deposit,income,1,1,1};
    	}
    	
    	/**
    	 * 生成有钱人数据
    	 * @return
    	 */
    	private static double[] generateRichData() {
    		double base = 100;
    		double deposit = base + getRandomNoise(10);
    		double income = base + getRandomNoise(10);
    		return new double[] {deposit,income,1,1,1};
    	}
    	
    	/**
    	 * 生成钻石王老五数据
    	 * @return
    	 */
    	private static double[] generateGoldenBachelorData() {
    		double base = 100;
    		double deposit = base + getRandomNoise(10);
    		double income = base + getRandomNoise(10);
    		return new double[] {deposit,income,1,1,0d};
    	}
    }
    

    在测试类中,出于程序员的强迫症发作,我对dl4j进行一些简单的封装。

    下面是这些封装类:

    package org.aztec.dl_common;
    
    import org.deeplearning4j.eval.Evaluation;
    import org.deeplearning4j.nn.api.OptimizationAlgorithm;
    import org.deeplearning4j.nn.conf.MultiLayerConfiguration;
    import org.deeplearning4j.nn.conf.NeuralNetConfiguration;
    import org.deeplearning4j.nn.conf.NeuralNetConfiguration.Builder;
    import org.deeplearning4j.nn.conf.NeuralNetConfiguration.ListBuilder;
    import org.deeplearning4j.nn.conf.layers.DenseLayer;
    import org.deeplearning4j.nn.conf.layers.OutputLayer;
    import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
    import org.deeplearning4j.nn.weights.WeightInit;
    import org.deeplearning4j.optimize.listeners.ScoreIterationListener;
    import org.nd4j.linalg.activations.Activation;
    import org.nd4j.linalg.api.ndarray.INDArray;
    import org.nd4j.linalg.dataset.DataSet;
    import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
    import org.nd4j.linalg.factory.Nd4j;
    import org.nd4j.linalg.learning.config.Nesterovs;
    import org.nd4j.linalg.lossfunctions.LossFunctions.LossFunction;
    
    public class SimpleBPNN {
    
        protected Builder networkBuilder;
        protected MultiLayerNetwork network;
    
    	public SimpleBPNN() {
    		// TODO Auto-generated constructor stub
    	}
    	
    	public void reset() {
    		
    	}
    	
    	public void buildNetwork(SimpleNetworkConfiguration networkConfig) {
    		networkBuilder = new NeuralNetConfiguration.Builder();
    		int layNum = networkConfig.getLayNum();
    		ListBuilder listBuilder = networkBuilder.seed(networkConfig.getRngSeed()) //include a random seed for reproducibility
                    // use stochastic gradient descent as an optimization algorithm
    				.optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)
    				.biasInit(networkConfig.getBias())
                    .updater(new Nesterovs(networkConfig.getLearnRatio(), networkConfig.getMomentum()))
                    //.activation(Activation.ELU)
                    .l2(networkConfig.getL2())
                    .list();
    		int fistLayOutput = networkConfig.getLayNum() > 0 ? networkConfig.getHiddenLayerNeuronNum() :
    				networkConfig.getOutputNum() ;
    		double[] biasArray = networkConfig.getBiasArray();
    		if(biasArray == null) {
    			biasArray = new double[layNum + 2];
    			for(int i = 0;i < biasArray.length;i++) {
    				biasArray[i] = networkConfig.getBias();
    			}
    		}
    		int layCursor = 0;
    		listBuilder = listBuilder.layer(0,new DenseLayer.Builder() //create the first, input layer with xavier initialization
    	                        .nIn(networkConfig.getInputNum())
    	                        .nOut(fistLayOutput)
    	                        .biasInit(biasArray[layCursor])
    	                        .activation(Activation.RELU)
    	                        .weightInit(WeightInit.XAVIER)
    	                        .build());
    		int lastOut = fistLayOutput;
    		layCursor ++;
    		for(int i = 0;i < layNum;i++) {
    			System.out.println("adding hidden layer!!");
    			int nIn = networkConfig.getHiddenLayerNeuronNum();
    			int nOut = networkConfig.getHiddenLayerNeuronNum();
    			if(i == layNum - 1) {
    				nOut = networkConfig.getOutputNum();
    			}
    			lastOut = nOut;
    			listBuilder = listBuilder.layer(i + 1,new DenseLayer.Builder()
                        .nIn(nIn)
                        .nOut(nOut)
                        .activation(Activation.RELU)
                        .weightInit(WeightInit.XAVIER)
                        .biasInit(biasArray[layCursor])
                        .build());
    			layCursor ++;
    		}
    		listBuilder = listBuilder.layer(layNum + 1,new OutputLayer.Builder(LossFunction.NEGATIVELOGLIKELIHOOD)
            		.activation(Activation.SOFTMAX)
    				.nIn(lastOut).nOut(networkConfig.getOutputNum())
    				.biasInit(biasArray[layCursor])
                    .build());
    		MultiLayerConfiguration  mlc = listBuilder.pretrain(false).backprop(true).build();
    		network = new MultiLayerNetwork(mlc);
    
    		network.init();
            //print the score with every 1 iteration
    		ScoreIterationListener sil = new ScoreIterationListener();
    		network.setListeners(sil);
    		/*
    */
    	}
    	
    	private DataSetIterator normalize(DataSetIterator trainningDatas) {
    
            return NormalizeUtils.transform(trainningDatas);
    	}
    	
    	private boolean hasNormalizeBefore(DataSetIterator trainningDatas) {
    		if(NormalizeAware.class.isAssignableFrom(trainningDatas.getClass())) {
    			return ((NormalizeAware) trainningDatas).isNormalized();
    		}
    		return false;
    	}
    	
    	public void train(DataSetIterator trainningDatas,int numEpochs,boolean normalized) {
    		
    		DataSetIterator normalizedDatas = trainningDatas;
    		if (!normalized) {
    			normalizedDatas = normalize(trainningDatas);
    		}
    		network.pretrain(normalizedDatas);
    		for (int i = 0; i < numEpochs; i++) {
    			network.fit(normalizedDatas);
    			// preprocess(trainningDatas);
    			//normalizedDatas.reset();
    		}
    	}
    	
    	public double[] predict(double[] features) {
    		if(features != null && features.length > 0) {
    			INDArray outArray = network.output(Nd4j.create(features));
    			return outArray.toDoubleVector();
    		}
    		return null;
    	}
    
    	public Evaluation validate(DataSetIterator dataSet,int outputNum,boolean normalized) {
    		DataSetIterator normalizedDatas = dataSet;
    		if (!normalized) {
    			normalizedDatas = normalize(dataSet);
    		}
    		Evaluation eval = new Evaluation(outputNum); //create an evaluation object with 10 possible classes
            if(normalizedDatas.resetSupported()) {
            	normalizedDatas.reset();
            }
    		while(normalizedDatas.hasNext()){
                DataSet next = normalizedDatas.next();
                INDArray output = network.output(next.getFeatures()); //get the networks prediction
    
                eval.eval(next.getLabels(), output); //check the prediction against the true class
            }
            System.out.println(eval.stats());
            return eval;
    	}
    }
    
    package org.aztec.dl_common;
    
    public class SimpleNetworkConfiguration {
    
    	protected int inputNum = 10;
        protected int outputNum = 10; // number of output classes
        protected int batchSize = 50; // batch size for each epoch
        protected int rngSeed = 123; // random number seed for reproducibility
        protected int numEpochs = 150; // number of epochs to perform
        protected int layNum = 1;
        protected double bias = 0.3;
        protected double[] biasArray;
        protected double learnRatio = 0.006;
        protected double momentum = 0.01;
        protected double l2 = 1e-4;
        protected double l1 = 1e-1;
        protected int hiddenLayerNeuronNum = 1000;
        
        public SimpleNetworkConfiguration(int inputNum,int outputNum) {
        	this.inputNum = inputNum;
        	this.outputNum = outputNum;
        }
    
    	public SimpleNetworkConfiguration(SimpleTensorIterator iterator) {
    		this.inputNum = iterator.inputColumns();
    		this.outputNum = iterator.totalOutcomes();
    		
    	}
    
    	public int getOutputNum() {
    		return outputNum;
    	}
    
    	public void setOutputNum(int outputNum) {
    		this.outputNum = outputNum;
    	}
    
    	public int getBatchSize() {
    		return batchSize;
    	}
    
    	public void setBatchSize(int batchSize) {
    		this.batchSize = batchSize;
    	}
    
    	public int getRngSeed() {
    		return rngSeed;
    	}
    
    	public void setRngSeed(int rngSeed) {
    		this.rngSeed = rngSeed;
    	}
    
    	public int getNumEpochs() {
    		return numEpochs;
    	}
    
    	public void setNumEpochs(int numEpochs) {
    		this.numEpochs = numEpochs;
    	}
    
    	public int getLayNum() {
    		return layNum;
    	}
    
    	public void setLayNum(int layNum) {
    		this.layNum = layNum;
    	}
    
    	public int getHiddenLayerNeuronNum() {
    		return hiddenLayerNeuronNum;
    	}
    
    	public void setHiddenLayerNeuronNum(int hiddenLayerNeuronNum) {
    		this.hiddenLayerNeuronNum = hiddenLayerNeuronNum;
    	}
    
    	public int getInputNum() {
    		return inputNum;
    	}
    
    	public void setInputNum(int inputNum) {
    		this.inputNum = inputNum;
    	}
    
    	public double getLearnRatio() {
    		return learnRatio;
    	}
    
    	public void setLearnRatio(double learnRatio) {
    		this.learnRatio = learnRatio;
    	}
    
    	public double getBias() {
    		return bias;
    	}
    
    	public void setBias(double bias) {
    		this.bias = bias;
    	}
    
    	public double getMomentum() {
    		return momentum;
    	}
    
    	public void setMomentum(double momentum) {
    		this.momentum = momentum;
    	}
    
    	public double getL2() {
    		return l2;
    	}
    
    	public void setL2(double l2) {
    		this.l2 = l2;
    	}
    
    	public double getL1() {
    		return l1;
    	}
    
    	public void setL1(double l1) {
    		this.l1 = l1;
    	}
    
    	public double[] getBiasArray() {
    		return biasArray;
    	}
    
    	public void setBiasArray(double[] biasArray) {
    		this.biasArray = biasArray;
    	}
    
    }
    

    还有一些工具类:

    package org.aztec.dl_common;
    
    import org.apache.commons.math3.stat.descriptive.moment.Mean;
    import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation;
    import org.nd4j.linalg.api.ndarray.INDArray;
    import org.nd4j.linalg.dataset.DataSet;
    import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
    import org.nd4j.linalg.dataset.api.preprocessor.NormalizerStandardize;
    import org.nd4j.linalg.factory.Nd4j;
    
    public class NormalizeUtils {
    
    	private NormalizeUtils() {
    	}
    	
    	public static INDArray getMean(INDArray features) {
    		int colNum = features.columns();
    		double[] means = new double[colNum];
    		for(int i = 0;i < colNum;i++) {
    			double[] colDatas = features.getColumn(i).toDoubleVector();
    			Mean mean = new Mean();
    			means[i] = mean.evaluate(colDatas);
    		}
    		return Nd4j.create(means);
    	}
    
    	public static INDArray getStandardDeviation(INDArray features) {
    		int colNum = features.columns();
    		double[] stds = new double[colNum];
    		for(int i = 0;i < colNum;i++) {
    			double[] colDatas = features.getColumn(i).toDoubleVector();
    			StandardDeviation std = new StandardDeviation();
    			stds[i] = std.evaluate(colDatas);
    		}
    		return Nd4j.create(stds);
    	}
    	
    	public static  DataSetIterator transform(DataSetIterator itr) {
    		NormalizerStandardize ns = new NormalizerStandardize();
    		//ns.fitLabel(true);
    		ns.fit(itr);
    		SimpleTensorIterator tensorIterator = new SimpleTensorIterator();
    		itr.reset();
    		while(itr.hasNext()) {
    			DataSet ds = itr.next();
    			ns.transform(ds);
    			tensorIterator.addDataSet(ds);
    		}
    		tensorIterator.reset();
    		return tensorIterator;
    	}
    }
    
    package org.aztec.dl_common;
    
    import java.util.Iterator;
    import java.util.List;
    
    import org.nd4j.linalg.api.ndarray.INDArray;
    import org.nd4j.linalg.dataset.DataSet;
    import org.nd4j.linalg.dataset.api.DataSetPreProcessor;
    import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
    import org.nd4j.linalg.dataset.api.preprocessor.NormalizerStandardize;
    import org.nd4j.linalg.factory.Nd4j;
    
    import com.google.common.collect.Lists;
    
    
    public class SimpleTensorIterator implements DataSetIterator,NormalizeAware {
    	
    	
    	protected List<DataSet> datas;
    	protected Iterator<DataSet> trainningDatas;
    	protected List<String> labels;
    	protected int inputNums;
    	protected DataSetPreProcessor preprocessor;
    	protected int batch;
    	protected boolean normalized = false;
    	
    	public SimpleTensorIterator() {
    		datas = Lists.newArrayList();
    		labels = Lists.newArrayList();
    		preprocessor = new NormalizerStandardize();
    	}
    	
    	public SimpleTensorIterator(NetworkInput input) {
    		init(Lists.newArrayList(input),1);
    	}
    
    	public SimpleTensorIterator(List<NetworkInput> inputs,int batch) {
    		init(inputs,batch);
    	}
    	
    	private void init(List<NetworkInput> inputs,int batch) {
    		List<DataSet> dataList = Lists.newArrayList();
    		double[][] features = new double[inputs.size()][];
    		double[][] labels = new double[inputs.size()][];
    		for(int i = 0;i < inputs.size();i++) {
    			NetworkInput input = inputs.get(i);
    			inputNums = input.getFeatures().length;
    			if(this.labels == null && input.getLabelNames() != null) {
    				this.labels = input.getLabelNames();
    			}
    			features[i] = inputs.get(i).getFeatures();
    			labels[i] = inputs.get(i).getLables();
    		}
    		dataList.add(getDataSet(features, labels));
    		/*for(NetworkInput input : inputs) {
    			inputNums = input.getFeatures().length;
    			if(labels == null && input.getLabelNames() != null) {
    				labels = input.getLabelNames();
    			}
    			dataList.add(getDataSet(input.getFeatures(), input.getLables()));
    		}*/
    		trainningDatas = dataList.iterator();
    		this.batch = batch;
    		datas = Lists.newArrayList();
    		datas.addAll(dataList);
    		preprocessor = new NormalizerStandardize();
    	}
    	
    	private DataSet getDataSet(double[] featureRawDatas,double[] lableRawDatas) {
    		
    		INDArray features = Nd4j.create(featureRawDatas);
    		INDArray lables = null;
    		if(lableRawDatas != null) {
    			lables = Nd4j.create(lableRawDatas);
    		}
    		return new DataSet(features, lables);
    	}
    	
    	private DataSet getDataSet(double[][] featureRawDatas,double[][] lableRawDatas) {
    		
    		INDArray features = Nd4j.create(featureRawDatas);
    		INDArray lables = null;
    		if(lableRawDatas != null && lableRawDatas.length > 0) {
    			lables = Nd4j.create(lableRawDatas);
    		}
    		return new DataSet(features, lables);
    	}
    
    	public boolean hasNext() {
    		// TODO Auto-generated method stub
    		return trainningDatas.hasNext();
    	}
    
    	public DataSet next() {
    		// TODO Auto-generated method stub
    		return trainningDatas.next();
    	}
    
    	public DataSet next(int num) {
    		// TODO Auto-generated method stubf
    		for(int i = 0;i < num;i++) {
    			 trainningDatas.next();
    		}
    		return trainningDatas.next();
    	}
    
    	public int inputColumns() {
    		// TODO Auto-generated method stub
    		return inputNums;
    	}
    
    	public int totalOutcomes() {
    		// TODO Auto-generated method stub
    		return labels.size();
    	}
    
    	public boolean resetSupported() {
    		// TODO Auto-generated method stub
    		return false;
    	}
    
    	public boolean asyncSupported() {
    		// TODO Auto-generated method stub
    		return false;
    	}
    
    	public void reset() {
    		// TODO Auto-generated method stub
    		trainningDatas = datas.iterator();
    	}
    
    	public int batch() {
    		// TODO Auto-generated method stub
    		return batch;
    	}
    
    	public void setPreProcessor(DataSetPreProcessor preProcessor) {
    		// TODO Auto-generated method stub
    		this.preprocessor = preProcessor;
    	}
    
    	public DataSetPreProcessor getPreProcessor() {
    		// TODO Auto-generated method stub
    		return preprocessor;
    	}
    
    	public List<String> getLabels() {
    		return labels;
    	}
    
    	public boolean isNormalized() {
    		return normalized;
    	}
    
    	public void setNormalized(boolean flag) {
    		// TODO Auto-generated method stub
    		normalized = flag;
    	}
    
    	public void addDataSet(DataSet set) {
    		datas.add(set);
    	}
    	
    }
    

     

    5 小结

    人工智能,深度学习虽然已经不是什么新鲜事了,但对于我等普通程序猿来说,其普及率并不算高,主要是由于深度学习涉及到很多艰涩的概念和数学原理,是一般程序员极少用到的。因此,本文仅算是一个科普文章,只从一些最基本的概念原理出发,并从这些基本原理开始,讨论神经网络的模型与调优,并应用到一些实际例子中,去体现神经网络的神奇。

    另外,鉴于国内还是java程序员占多数,大部分公司的软件架构都是java生态体系为主,因此介绍java系的深度学习框架应该更为国内程序员所接收。

    展开全文
  • 随着机器学习、深度学习为主要代表的人工智能技术的逐渐成熟,越来越多的 AI 产品得到了真正的落地。无论是以语音识别和自然语言处理为基础的个人助理软件,还是以人脸识别为基础的刷脸付费系统,这些都是 AI 技术在...

    作者:万宫玺

    随着机器学习、深度学习为主要代表的人工智能技术的逐渐成熟,越来越多的 AI 产品得到了真正的落地。无论是以语音识别和自然语言处理为基础的个人助理软件,还是以人脸识别为基础的刷脸付费系统,这些都是 AI 技术在现实生活中的实际应用。应当说 AI 正在走进千家万户,来到你我的身边。

    另一方面,从研发角度来讲,AI 产品的落地并不是一件容易的事情:

    • AI 技术数学理论要求高,数理统计、神经理论与脑科学、优化理论、矩阵论……
    • AI 硬件层面要求非常高,GPU、TPU、FPGA……

    如何基于目前的主流研究成果和硬件,对 AI 产品进行一站式的开发?这正是当前企业工程师面临的实际痛点。

    对此,多家企业及研究机构推出了自己的解决方案,如 Google 推出并开源了 TensorFlow,Facebook 主导 PyTorch 和 Caffe 2,Amazon 选择 MXNet 并打算投资围绕 MXNet 的系统,微软开发并大力推广 CNTK…

    这些都是以 Python 和 C/C++ 语言为主,而在目前企业项目中,无论是 Web 应用、Andriod 开发还是大数据应用,Java 语言都占有很大的比例。此外,我们必须看到,越来越多的从事传统 Java 应用开发的工程师尝试将 AI 技术融入到项目中,或者自身在尝试转型 AI 领域。因此如果有类似 TensorFlow、Caffe 这些 AI 解决方案而又同时基于 Java 的,那么无疑会为项目的推进及个人的发展带来很多便利。

    Deeplearning4j 正是这类解决方案中的佼佼者。

    Deeplearning4j 是什么

    Deeplearning4j 是由美国 AI 创业公司 Skymind 开源并维护的一个基于 Java/JVM 的深度学习框架。同时也是在 Apache Spark 平台上为数不多的,可以原生态支持分布式模型训练的框架之一。此外,Deeplearning4j 还支持多 CPU/GPU 集群,可以与高性能异构计算框架无缝衔接,从而进一步提升运算性能。在 2017 年下半年,Deeplearning4j 正式被 Eclipse 社区接收,同 Java EE 一道成为 Eclipse 社区的一员。

    图片

    为什么选择 Deeplearning4j

    1.基于 Java,专为企业应用而生

    Deeplearning4j 是基于 Java 的深度学习开源框架。从实际开发的角度上,它是面向 Layer 编程的神经网络开发框架,对很多常见的神经网络结构做了高度的封装,熟悉 Keras 的朋友可以认为 Deeplearning4j 是 Java 版本的 Keras。同时 Deeplearning4j 也完美兼容 Scala 和 Clojure。

    2.丰富的开源生态圈,遵循 Apache 2.0

    Deeplearning4j 也拥有自己的生态。在 Deeplearning4j 的相关开源项目中,就有专门为张量运算而开发的 ND4J 和数据处理的 DataVec。它们的作用相当于 Python 中的 NumPy 和 Pandas。当然,除了这些项目以外,Arbiter、RL4J 等项目也大大丰富了 Deeplearning4j 的生态圈。

    3.与 Hadoop 和 Spark 集成,支持分布式 CPU 和 GPU

    Deeplearning4j 是原生支持在 Apache Spark 上构建分布式深度学习解决方案的框架。由于在企业的实际应用场景中,大数据的统计和存储往往会依赖 Hive/HDFS 等存储介质。而算法模型的构建必须依赖庞大的数据,因此如果可以完成一站式的数据存储、数据提取和清洗、训练数据的构建、模型训练和调优的所有开发环节,无疑是非常理想的解决方案。Deeplearning4j 以数据并行化为理论基础构建了分布式神经网络建模的解决方案,为大数据 + 算法的开发提供了直接的支持。

    Deepleanring4j 支持多 CPU/GPU 集群的建模。就像在上文中提到的,GPU 等硬件的成熟大大加速了 AI 的发展。Deeplearning4j 通过 JavaCPP 技术调用 cuBLAS 来实现在 GPU 上的加速建模。对于 GPU 集群的支持则需要依赖 Spark。

    图片

    4.越来越受开发人员欢迎
    自 Deeplearning4j 从 2016 年左右开源以来,功能优化与新特性的丰富使得项目本身不断得到完善,在 GitHub 上的 Commiter 活跃度与 Star 数量也不断增加,使得该开源框架越来越得到国内外企业的关注。就目前 Skymind 官网提供的信息来看,就有数十家明星企业和研发机构在部署使用 Deeplearning4j,其中就不乏有 Oracle、Cisco、IBM、软银、亚马逊、阿里巴巴等知名企业。

    而随着 Deeplearning4j 在工业界的使用逐渐增多,更多的研发人员希望有一套教程可以用来辅助开发和作为参考,因此我在 GitChat 平台编写了这门《Deepleraning4j 快速入门》,希望可以帮助读者尽可能轻松与快速地掌握 Deeplearning4j 的使用。

    Deeplearning4j 的最新进展

    目前 Deeplearning4j 已经来到了 1.0.0-beta3 的阶段,马上也要发布正式的 1.0.0 版本。本课程我们主要围绕 0.8.0 和 1.0.0-alpha 展开(1.0.0-beta3 核心功能部分升级不大),这里罗列下从 0.7.0 版本到 1.0.0-alpha 版本主要新增的几个功能点:

    • Spark 2.x 的支持(>0.8.0)
    • 支持迁移学习(>0.8.0)
    • 内存优化策略 Workspace 的引入(>0.9.0)
    • 增加基于梯度共享(Gradients Sharing)策略的并行化训练方式(>0.9.0)
    • LSTM 结构增加 cuDNN 的支持(>0.9.0)
    • 自动微分机制的支持,并支持导入 TensorFlow 模型(>1.0.0-alpha)
    • YOLO9000 模型的支持(>1.0.0-aplpha)
    • CUDA 9.0 的支持(>1.0.0-aplpha)
    • Keras 2.x 模型导入的支持(>1.0.0-alpha)
    • 增加卷积、池化等操作的 3D 版本(>1.0.0-beta)

    除此之外,在已经提及的 Issue 上,已经考虑在 1.0.0 正式版本中增加对 YOLO v3、GAN、MobileNet、ShiftNet 等成果的支持,进一步丰富 Model Zoo 的直接支持范围,满足更多开发者的需求。

    你能从课程学到什么

    本课程主要面向:

    • 对 Deeplearning4j 入门感兴趣的初学者
    • 希望转型人工智能领域的 Java 工程师
    • 有科学计算背景的高校/企业工作人员

    我将结合 Deeplearning4j 的特性来介绍目前主流深度学习研究成果,通过大量的实际案例来讲解 Deeplearning4j 在结构化数据、自然语言处理、机器视觉领域的应用,具体内容涉及:

    • 多层感知机在分类和回归问题上的应用
    • 深度信念网络在数据压缩问题上的应用
    • 卷积神经网络在图像分类和目标检测问题上的应用
    • 循环神经网络在文本分类/文本生成/序列标注等问题上的应用
    • ……

    课程还将结合 Deeplearning4j 支持的特性,从本地单 CPU/多 CPU 建模开始介绍,循序渐进,逐步将单 GPU/多 GPU 并行以及 Spark 集群建模的详细内容介绍给大家,从而在宏观层面尽可能对 Deeplearning4j 的内容介绍得详尽且实用。

    由于 Deeplearning4j 生态圈的内容丰富,我将着重就最常用的 ND4J、DataVec、RL4J 做详细介绍。

    图片

    作者简介

    万宫玺,苏宁易购高级算法工程师。现任职于苏宁易购搜索研发中心,对机器学习/深度学习在自然语言处理、机器视觉等领域的应用开发有着丰富的经验,先后参与部门反作弊系统、智能问答机器人、Query 语义挖掘与分析系统等机器学习项目的开发。

    学习建议

    由于 AI 是一项跨多学科且对理论和工程开发都有着一定要求的技术,因此实际动手操作非常重要。只有自己动手做过一两个项目或者参加过一些算法比赛,研发人员才会对如何做好算法项目和产品有感性的认识,而不会仅仅停留在理论层面。踩坑虽然痛苦,但其实也是加深对理论理解的必需过程,在这个方面并没有太多捷径可走。

    在学习本课程前,希望读者有一定的 Java 工程基础,以及对机器学习/深度学习理论的简单了解。对优化理论、微分学、概率统计有一定的认识,对理解神经网络的基础理论(如 BP 算法)将大有裨益。

    最后需要说明的是,机器学习、深度学习领域发展迅速,我个人虽然专注机器学习领域的开发与应用多年,但是经验依然处于初窥门径的阶段,因此课程中难免存在错误和疏漏。希望大家在学习过程中发现任何问题及时帮忙指出,我会尽可能在第一时间修正。同时,我自己也希望和所有喜欢 AI 技术和本课程的朋友多些交流,不断完善和提高课程的质量。

    课程目录

    在这里插入图片描述

    识别二维码或点此试读

    在这里插入图片描述

    展开全文
  • Apache HTTPClient Tutorial 深度学习(二) 对于HTTP连接的管理 从一个主机到另一个主机之间建立连接的过程相当复杂并且涉及到两个端点之间的多个包交换,这可能相当耗时的开销。特别是对于小型HTTP消息,握手连接...
    Apache HTTPClient Tutorial 深度学习(二) 对于HTTP连接的管理


    2.1. Connection persistence(连接持久性)

            从一个主机到另一个主机之间建立连接的过程相当复杂并且涉及到两个端点之间的多个包交换,这可能相当耗时的开销。特别是对于小型HTTP消息,握手连接可能是很重要的如果可以实现一个重新使用开放连接来执行多个请求,那么就会有更高的数据吞吐量。
            HTTP/1.1 声明HTTP连接可以在每次默认的多个请求中被重用。HTTP / 1.0兼容的端点还可以使用一种机制来显式地表达它们的首选项连接到多个请求,并使用它。HTTP代理还可以保持空闲连接的存在在特定的时间段内,对于后续请求,需要连接到相同的目标主机。保持连接的能力通常被称为连接持。HttpClient完全支持连接的持久性。


    2.2. HTTP connection routing(HTTP连接路由)

            HttpClient 可以直接或通过路由建立到目标主机的连接可能涉及多个中间连接,也称为跃点。HttpClient 区别一条路线连接到一条平坦、隧道和分层的道路。
    多中间代理的使用与目标主机的隧道连接被称为代理链接。
            普通路由是通过连接到目标或第一个和唯一的代理来建立的。挖过的路线通过连接到第一个和隧道,通过连接到目标的一系列代理来建立。路线没有代理,就不能被隧道掘进。分层路由是通过在一个协议之上建立一个协议来建立的。现有的连接。协议只能通过隧道到达目标,或者直接连接到目标上。没有代理。


    2.2.1. Route computation(路由估算)

            RouteInfo接口表示关于目标主机的确定路由的信息一个或多个中间步骤或跃点。HttpRouteRouteInfo的具体实现,不能改变的(是不可变的)。HttpTracker是一个可变的RouteInfo实现在内部,HttpClient可以跟踪剩余的跳线到最终的路由目标。HttpTracker可以在成功执行下一跳到路由目标之后。HttpRouteDirector一个助手类,可用于计算路由中的下一个步骤。这个类是在内部使用的HttpClient
        HttpRoutePlanner是一个接口,它表示一个策略,用于计算给定目标的完整路由基于执行上下文。HttpClient附带两个默认的http自豪计划实现。SystemDefaultRoutePlanner基于java.net.ProxySelector。默认情况下,它会接收到JVM的代理设置,要么来自系统属性,要么来自运行应用程序的浏览器。DefaultProxyRoutePlanner实现不使用任何Java系统属性,也没有任何系统或浏览器代理设置。它总是通过相同的默认代理计算路由。

    2.2.2. Secure HTTP connections(安全的HTTP连接)

            如果在两个连接之间传输的信息,HTTP连接可以被认为是安全的端点不能被未经授权的第三方读取或篡改。SSL / TLS协议是用于确保HTTP传输安全性的最广泛使用的技术。然而,其他加密技术也可以被运用。通常,HTTP传输是通过ssl/tls加密的连接。

    2.3. HTTP connection managers(HTTP连接管理)

            HTTP连接是复杂的、有状态的、线程不安全的对象,需要正确地管理这些对象正确的函数。HTTP连接只能一次被一个执行线程使用。HttpClient使用一个特殊的实体来管理访问HTTP连接管理器的HTTP连接管理器和由HttpClientConnectionManager接口。HTTP连接的目的管理器将作为新的HTTP连接的工厂,管理持久性的生命周期连接和同步连接到持久连接,确保只有一个线程可以一次访问连接。内部HTTP连接管理器与实例一起工作ManagedHttpClientConnection作为代表一个真正的连接管理连接状态并控制执行的输入/输出操作。如果一个托管连接被释放或被显式关闭通过它的使用者,底层连接从代理中分离,并返回到经理。尽管服务使用者仍然持有代理实例的引用,但它已不再能够执行任何的输入/输出操作或改变真实连接的状态无意中。

    这是一个从连接管理器获取连接的例子:



    连接请求可以通过调用ConnectionRequest cancel()来提前终止。必要的。这将解开ConnectionRequest get()方法中阻塞的线程。

    2.3.2. Simple connection manager

        BasicHttpClientConnectionManager是一个简单的连接管理器,保持只有一个一次连接。尽管这个类是线程安全的,但它应该被一个执行线程使用只有。BasicHttpClientConnectionManager将为后续努力重用连接使用相同路径的请求。但是,它将关闭现有的连接并重新打开它给定路由,如果持久连接的路由与连接请求的路径不匹配。如果连接已经被分配了,然后是java.lang。IllegalStateException抛出。该连接管理器实现应该在EJB容器内使用。

    2.3.3. Pooling connection manager(连接池管理)

        PoolingHttpClientConnectionManager是一个更复杂的实现管理池客户端连接,并且能够从多个执行线程中服务连接请求。连接在每个路由基础上被汇集起来。对经理已经拥有的一条路线的请求
    池中可用的持久连接将通过从池中租赁连接来服务而不是建立一个全新的联系。
        PoolingHttpClientConnectionManager保持最大限度的连接每个路线基础和总计。在默认情况下,该实现将创建不超过2个并发连接每个给定的路由,总共不超过20个连接。对于许多实际应用程序来说,这些限制可能会被证明过于约束,特别是当他们使用HTTP作为他们的服务的传输协议时。

    这个例子展示了如何调整连接池参数:




    2.3.4. Connection manager shutdown(释放连接)

    当HttpClient实例不再需要并且即将退出时,关闭它是很重要的关闭它的连接管理器以确保管理器的所有连接都被关闭这些连接分配的系统资源被释放。



    2.4. Multithreaded request execution

            当配备一个池PoolingClientConnectionManager等连接管理器,HttpClient可以被用来同时执行多个请求,同时使用多个线程执行。
            根据其配置PoolingClientConnectionManager将分配连接。如果所有一个给定路由的连接已经被租借,一个连接的请求将被阻塞。直到连接被释放到池中。可以确保连接管理器不会通过设置http.connmanager.超时,在连接请求操作中无限期地阻塞。一个积极的价值。如果在给定的时间段内不能对连接请求进行服务就会抛出onnectionPoolTimeoutException。




    虽然HttpClient实例是线程安全的,并且可以在多个执行线程之间共享,强烈建议每个线程维护自己专用的HttpContext实例。



    2.5. Connection eviction policy

            经典阻塞输入/输出模型的主要缺点之一是网络套接字可以对其进行响应只有在一个输入/输出操作中阻塞时才会发生。当一个连接被释放到经理的时候,它可以被保存,但是它不能监控套接字的状态并对任何输入/输出事件作出反应。如果连接在服务器端关闭,则客户端连接无法检测到更改在连接状态(并在结束时关闭套接字)。
            HttpClient试图通过测试连接是否“过时”来缓解这个问题,这是不存在的因为它在服务器端关闭,在使用连接执行之前一个HTTP请求。过期的连接检查不是100%可靠的。唯一可行的解决方案对于空闲连接来说,每个套接字模型都不涉及一个线程,这是一个专用的监视器线程用来清除由于长时间不活动而被认为过期的连接。监控线程可以周期性地调用ClientConnectionManager关闭补偿连接()方法关闭所有过期连接,并将关闭连接从池中清除。它还可以可选地调用ClientConnectionManager近端()方法来关闭所有连接在给定的一段时间内,这些都是闲置的。




    2.6. Connection keep alive strategy

            HTTP规范没有指定持久连接可能存在多长时间,应该保持多长时间活着。一些HTTP服务器使用非标准的保持-活着的头来与客户端通信在几秒钟内,他们打算将连接保持在服务器端。HttpClient使如果有的话,可以使用这些信息。如果没有在响应中出现keep活着的头部,那么HttpClient就会出现假设连接可以无限地存活。但是,通常使用的许多HTTP服务器在特定的一段时间不活动之后,配置为减少持久连接,以便保存系统资源,经常不通知客户。如果默认策略是过于乐观的人可能会想要提供一种自定义的维持生命的策略。



    2.7. Connection socket factories

            HTTP连接在内部使用java.net.socket对象来处理数据的传输。过线。但是,它们依赖于ConnectionSocketFactory接口来创建、初始化和连接的套接字。这使HttpClient的用户能够提供特定于应用程序的套接字在运行时初始化代码。PlainConnectionSocketFactory是默认为创建和工厂初始化(加密)套接字。
            创建套接字的过程和将其连接到主机的过程是解耦的,所以这个套接字在连接操作中被阻塞时,可以关闭。



    2.7.1. Secure socket layering

        LayeredConnectionSocketFactory ConnectionSocketFactory接口的一个扩展。分层套接字工厂能够在现有的普通插座上创建套接字。套接字分层主要用于通过代理创建安全的套接字。HttpClient附带实现ssl/tls分层的SSLSocketFactory。请注意,HttpClient不使用任何定制加密功能。它完全依赖于标准的Java加密(JCE)和安全套接字(JSEE)扩展。

    2.7.2. Integration with connection manager

    自定义连接套接字工厂可以与特定的协议方案相关联,如HTTP或者HTTPS,然后用来创建一个自定义连接管理器。



    2.7.3. SSL/TLS customization

            HttpClient利用SSLConnectionSocketFactory创建SSL连接。SSLConnectionSocketFactory允许高度的定制。它可以举一个例子作为一个参数使用javax.ssl.sslcontext,并使用它创建自定义配置的SSL连接。



            定制SSLConnectionSocketFactory意味着一定程度的熟悉ssl/tls协议的概念,一个详细的解释,它超出了范围对于本文档。请参阅Java安全套接字扩展(JSSE)引用指南[http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html详细描述javax.net.ssl.sslcontext和相关工具。

    2.7.4. Hostname verification

            除了在SSL上执行的信任验证和客户端身份验证之外TLS协议级别,HttpClient可以选择性地验证目标主机名是否与此匹配存储在服务器x中的名称。当连接建立后,证书。这种验证可以为服务器信任材料的真实性提供额外的保证。javax.net.ssl.HostnameVerifier接口代表主机名验证的策略。HttpClient附带两javax.net.ssl.HostnameVerifier实现。重要的是:主机名验证不应与SSL信任验证混淆。

    • DefaultHostnameVerifier:HttpClient  使用的默认实现是符合RFC 2818。主机名必须匹配由该名称指定的任何其他名称证书,或者在没有其他名称的情况下,给出证书主题的最特定的CN。通配符可以在CN中出现,也可以在任何主项中出现。
    • NoopHostnameVerifier:  这个主机名验证器实际上是将主机名验证关闭。接受任何SSL会话是有效的,并匹配目标主机。

    在默认情况下,HttpClient使用DefaultHostnameVerifier实现。可以指定一个如果需要,不同的主机名验证器实现



            在版本4.4中,HttpClient使用Mozilla基金会友好维护的公共后缀列表确保SSL证书中的通配符不能被误用用于应用到多个域通用顶级域名。HttpClient将在发布时检索到的列表副本。最新修订版本的列表可以在https://publicsuffix.org/list/找到[https://publicsuffix.org/list/effective_tld_names.dat]。可以对列表进行本地复制并下载该列表,这是非常明智的。从它最初的位置开始一天不超过一次。



    可以使用空matcher来禁用对公共列表的验证。



    2.8. HttpClient proxy configuration

            尽管HttpClient知道复杂的路由方案和代理链接,但它只支持简单的直接或单跳的代理连接。
            要让HttpClient通过代理连接到目标主机,最简单的方法是设置缺省值代理参数:



    您还可以指示HttpClient使用标准的JRE代理选择器来获取代理信息:



    或者,您可以提供定制的RoutePlanner实现来完成一个完整的任务对HTTP路由计算过程的控制:



    About Me:



    展开全文
  • 1 引言一直在说深度学习框架,最近也在使用tensorflow进行了简单的实验,但是对其中关系的理解还是不够到位,他们里面究竟是怎样的一个运行机制呢?2 说明深度学习框架也就像Caffe、tensorflow这些是深度学习的工具...

    1 引言

    一直在说深度学习框架,最近也在使用tensorflow进行了简单的实验,但是对其中关系的理解还是不够到位,他们里面究竟是怎样的一个运行机制呢?

    2 说明

    深度学习框架也就像Caffe、tensorflow这些是深度学习的工具,简单来说就是库,编程时需要import caffe、import tensorflow。作一个简单的比喻,一套深度学习框架就是这个品牌的一套积木,各个组件就是某个模型或算法的一部分,你可以自己设计如何使用积木去堆砌符合你数据集的积木。好处是你不必重复造轮子,模型也就是积木,是给你的,你可以直接组装,但不同的组装方式,也就是不同的数据集则取决于你。

    3 应用优势

    深度学习框架的出现降低了入门的门槛,你不需要从复杂的神经网络开始编代码,你可以依据需要,使用已有的模型,模型的参数你自己训练得到,你也可以在已有模型的基础上增加自己的layer,或者是在顶端选择自己需要的分类器和优化算法(比如常用的梯度下降法)。当然也正因如此,没有什么框架是完美的,就像一套积木里可能没有你需要的那一种积木,所以不同的框架适用的领域不完全一致。 总的来说深度学习框架提供了一些列的深度学习的组件(对于通用的算法,里面会有实现),当需要使用新的算法的时候就需要用户自己去定义,然后调用深度学习框架的函数接口使用用户自定义的新算法.

    4 关于组件

    大部分深度学习框架都包含以下五个核心组件:
    1. 张量(Tensor)
    2. 基于张量的各种操作
    3. 计算图(Computation Graph)
    4. 自动微分(Automatic Differentiation)工具
    5. BLAS、cuBLAS、cuDNN等拓展包

    下面对这五大核心组件做一个简要的解释

    4.1张量(Tensor)

    张量是所有深度学习框架中最核心的组件,因为后续的所有运算和优化算法都是基于张量进行的。几何代数中定义的张量是基于向量和矩阵的推广,通俗一点理解的话,我们可以将标量视为零阶张量,矢量视为一阶张量,那么矩阵就是二阶张量。

    举例来说,我们可以将任意一张RGB彩色图片表示成一个三阶张量(三个维度分别是图片的高度、宽度和色彩数据)。如下图所示是一张普通的水果图片,按照RGB三原色表示,其可以拆分为三张红色、绿色和蓝色的灰度图片,如果将这种表示方法用张量的形式写出来,就是图中最下方的那张表格。
    这里写图片描述
    这里写图片描述
    这里写图片描述
    图中只显示了前5行、320列的数据,每个方格代表一个像素点,其中的数据[1.0, 1.0, 1.0]即为颜色。假设用[1.0, 0, 0]表示红色,[0, 1.0, 0]表示绿色,[0, 0, 1.0]表示蓝色,那么如图所示,前面5行的数据则全是白色。

    将这一定义进行扩展,我们也可以用四阶张量表示一个包含多张图片的数据集,其中的四个维度分别是:图片在数据集中的编号,图片高度、宽度,以及色彩数据。

    将各种各样的数据抽象成张量表示,然后再输入神经网络模型进行后续处理是一种非常必要且高效的策略。因为如果没有这一步骤,我们就需要根据各种不同类型的数据组织形式定义各种不同类型的数据操作,这会浪费大量的开发者精力。更关键的是,当数据处理完成后,我们还可以方便地将张量再转换回想要的格式。例如Python NumPy包中numpy.imread和numpy.imsave两个方法,分别用来将图片转换成张量对象(即代码中的Tensor对象),和将张量再转换成图片保存起来。

    4.2 基于张量的各种操作

    有了张量对象之后,下面一步就是一系列针对这一对象的数学运算和处理过程。
    这里写图片描述
    其实,整个神经网络都可以简单视为为了达到某种目的,针对输入张量进行的一系列操作过程。而所谓的“学习”就是不断纠正神经网络的实际输出结果和预期结果之间误差的过程。这里的一系列操作包含的范围很宽,可以是简单的矩阵乘法,也可以是卷积、池化和LSTM等稍复杂的运算。而且各框架支持的张量操作通常也不尽相同,详细情况可以查看其官方文档(如下为NumPy、Theano和TensorFlow的说明文档)。

    NumPy:http://www.scipy-lectures.org/intro/numpy/operations.html

    Theano:http://deeplearning.net/software/theano/library/tensor/basic.html

    TensorFlow:https://www.tensorflow.org/api_docs/python/math_ops/

    需要指出的是,大部分的张量操作都是基于类实现的(而且是抽象类),而并不是函数(这一点可能要归功于大部分的深度学习框架都是用面向对象的编程语言实现的)。这种实现思路一方面允许开发者将各种类似的操作汇总在一起,方便组织管理。另一方面也保证了整个代码的复用性、扩展性和对外接口的统一。总体上让整个框架更灵活和易于扩展,为将来的发展预留了空间。

    4.3 计算图(Computation Graph)

    有了张量和基于张量的各种操作之后,下一步就是将各种操作整合起来,输出我们需要的结果。

    但不幸的是,随着操作种类和数量的增多,管理起来就变得十分困难,各操作之间的关系变得比较难以理清,有可能引发各种意想不到的问题,包括多个操作之间应该并行还是顺次执行,如何协同各种不同的底层设备,以及如何避免各种类型的冗余操作等等。这些问题有可能拉低整个深度学习网络的运行效率或者引入不必要的Bug,而计算图正是为解决这一问题产生的。

    计算图首次被引入人工智能领域是在2009年的论文《Learning Deep Architectures for AI》。当时的图片如下所示,作者用不同的占位符(*,+,sin)构成操作结点,以字母x、a、b构成变量结点,再以有向线段将这些结点连接起来,组成一个表征运算逻辑关系的清晰明了的“图”型数据结构,这就是最初的计算图。
    这里写图片描述
    后来随着技术的不断演进,加上脚本语言和低级语言各自不同的特点(概括地说,脚本语言建模方便但执行缓慢,低级语言则正好相反),因此业界逐渐形成了这样的一种开发框架:前端用Python等脚本语言建模,后端用C++等低级语言执行(这里低级是就应用层而言),以此综合了两者的优点。可以看到,这种开发框架大大降低了传统框架做跨设备计算时的代码耦合度,也避免了每次后端变动都需要修改前端的维护开销。而这里,在前端和后端之间起到关键耦合作用的就是计算图。

    将计算图作为前后端之间的中间表示(Intermediate Representations)可以带来良好的交互性,开发者可以将Tensor对象作为数据结构,函数/方法作为操作类型,将特定的操作类型应用于特定的数据结构,从而定义出类似MATLAB的强大建模语言。

    需要注意的是,通常情况下开发者不会将用于中间表示得到的计算图直接用于模型构造,因为这样的计算图通常包含了大量的冗余求解目标,也没有提取共享变量,因而通常都会经过依赖性剪枝、符号融合、内存共享等方法对计算图进行优化。

    目前,各个框架对于计算图的实现机制和侧重点各不相同。例如Theano和MXNet都是以隐式处理的方式在编译中由表达式向计算图过渡。而Caffe则比较直接,可以创建一个Graph对象,然后以类似Graph.Operator(xxx)的方式显示调用。

    因为计算图的引入,开发者得以从宏观上俯瞰整个神经网络的内部结构,就好像编译器可以从整个代码的角度决定如何分配寄存器那样,计算图也可以从宏观上决定代码运行时的GPU内存分配,以及分布式环境中不同底层设备间的相互协作方式。除此之外,现在也有许多深度学习框架将计算图应用于模型调试,可以实时输出当前某一操作类型的文本描述。

    4.4 自动微分(Automatic Differentiation)工具

    计算图带来的另一个好处是让模型训练阶段的梯度计算变得模块化且更为便捷,也就是自动微分法。

    正如前面提到的,因为我们可以将神经网络视为由许多非线性过程组成的一个复杂的函数体,而计算图则以模块化的方式完整表征了这一函数体的内部逻辑关系,因此微分这一复杂函数体,即求取模型梯度的方法就变成了在计算图中简单地从输入到输出进行一次完整遍历的过程。与自动微分对应,业内更传统的做法是符号微分。

    符号微分即常见的求导分析。针对一些非线性过程(如修正线性单元ReLU)或者大规模的问题,使用符号微分法的成本往往非常高昂,有时甚至不可行(即不可微)。因此,以上述迭代式的自动微分法求解模型梯度已经被广泛采用。并且由于自动微分可以成功应对一些符号微分不适用的场景,目前许多计算图程序包(例如Computation Graph Toolkit)都已经预先实现了自动微分。

    另外,由于每个节点处的导数只能相对于其相邻节点计算,因此实现了自动微分的模块一般都可以直接加入任意的操作类中,当然也可以被上层的微分大模块直接调用。

    4.5 BLAS、cuBLAS、cuDNN等拓展包

    现在,通过上述所有模块,我们已经可以搭建一个全功能的深度学习框架:将待处理数据转换为张量,针对张量施加各种需要的操作,通过自动微分对模型展开训练,然后得到输出结果开始测试。这时还缺什么呢?答案是运算效率。

    由于此前的大部分实现都是基于高级语言的(如Java、Python、Lua等),而即使是执行最简单的操作,高级语言也会比低级语言消耗更多的CPU周期,更何况是结构复杂的深度神经网络,因此运算缓慢就成了高级语言的一个天然的缺陷。

    目前针对这一问题有两种解决方案。

    第一种方法是模拟传统的编译器。就好像传统编译器会把高级语言编译成特定平台的汇编语言实现高效运行一样,这种方法将高级语言转换为C语言,然后在C语言基础上编译、执行。为了实现这种转换,每一种张量操作的实现代码都会预先加入C语言的转换部分,然后由编译器在编译阶段将这些由C语言实现的张量操作综合在一起。目前pyCUDA和Cython等编译器都已经实现了这一功能。

    第二种方法就是前文提到的,利用脚本语言实现前端建模,用低级语言如C++实现后端运行,这意味着高级语言和低级语言之间的交互都发生在框架内部,因此每次的后端变动都不需要修改前端,也不需要完整编译(只需要通过修改编译参数进行部分编译),因此整体速度也就更快。

    除此之外,由于低级语言的最优化编程难度很高,而且大部分的基础操作其实也都有公开的最优解决方案,因此另一个显著的加速手段就是利用现成的扩展包。例如最初用Fortran实现的BLAS(基础线性代数子程序),就是一个非常优秀的基本矩阵(张量)运算库,此外还有英特尔的MKL(Math Kernel Library)等,开发者可以根据个人喜好灵活选择。
    这里写图片描述
    值得一提的是,一般的BLAS库只是针对普通的CPU场景进行了优化,但目前大部分的深度学习模型都已经开始采用并行GPU的运算模式,因此利用诸如NVIDIA推出的针对GPU优化的cuBLAScuDNN等更据针对性的库可能是更好的选择。

    运算速度对于深度学习框架来说至关重要,例如同样训练一个神经网络,不加速需要4天的时间,加速的话可能只要4小时。在快速发展的人工智能领域,特别是对那些成立不久的人工智能初创公司而言,这种差别可能就会决定谁是先驱者,而谁是追随者。

    展开全文
  • 第一式:java基础 第一招:java的四大特性:抽象,继承,封装,多态 抽象的概念: 这里我先补充一下对象的概念,在java中世界万物皆对象,对象就是客观存在的事物,比如一个人,一支笔。而抽象就是将对象的某些...
  • 深度学习的复杂性和难点主要体现在神经网络模型的数学推导、神经网络模型的优化,以及如何构建一个优秀的深度学习模型解决实际问题。 针对这些难点,特别推出了本达人课,希望通过课程内容的学习,大家可以扫清学习...
  • 深度学习书籍推荐: 深度学习(Deep Learning) by Ian Goodfellow and Yoshua Bengio and Aaron Courville 中文版下载地址:https://github.com/exacity/deeplearningbook-chinese ...
  • 前言 看过我之前的博客就知道,我曾经用java写了一个...由于这个原因,我打算使用c++和cuda重写一个深度学习框架。有了写CupDnn的经验,结合最近阅读darknet、caffe、tiny_dnn源码的心得,新写的[Grape](https://g...
  • 本周早些时候 Google 开源了 TensorFlow(GitHub),此举在深度学习领域影响巨大,因为 Google 在人工智能领域的研发成绩斐然,有着雄厚的人才储备,而且 Google 自己的 Gmail 和搜索引擎都在使用自行研发的深度学习...
  • 主持人:我们或多或少听到很多智能的东西,比如AlphaGo、智能驾驶汽车,这里面都蕴含着一个东西,就是这两年非常火热的机器学习的分支深度学习(Deep Learning)。提到Deep Learning的话,大家可能都知道今年Google...
  • 本文将告诉你学习Java需要达到的30个目标,学习过程中可能遇到的问题,及学习路线。希望能够对你的学习有所帮助。对比一下自己,你已经掌握了这30条中的多少条了呢? 路线 Java发展到现在,按应用来分主要分为三大...
  • deeplearning4j这是一个用Java实现的深度学习类库。 网址: https://deeplearning4j.org 问题和数据集Minist是一个每个学过机器学习的童鞋都熟悉的类库。这个数据集包含70,000个手写数字的图片。每张图片为28*28...
  • 深度学习研究的热潮持续高涨,各种开源深度学习框架也层出不穷,其中包括TensorFlow、Caffe、Keras、CNTK、Torch7、MXNet、Leaf、Theano、DeepLearning4、Lasagne、Neon,等等。然而TensorFlow却杀出重围,在关注度...
  • 本文介绍了包括 Python、Java、Haskell等在内的一系列编程语言的深度学习库。 Python Theano 是一种用于使用数列来定义和评估数学表达的 Python 库。它可以让 Python 中深度学习算法的编写更为简单。很多其他...
  • 导读:深度学习(Deep Learning)是机器学习中一种基于对数据进行表征学习的方法,深度学习的好处是用非监督式或半监督式的特征学习、分层特征提取高效算法来替代手工获取特征(feature)。作为当下最热门的话题,...
  • AlphaGo背后神秘的推动力就是TensorFlow——Google于2015年11月开源的机器学习及深度学习框架。 TensorFlow在2015年年底一出现就受到了极大的关注,在一个月内获得了GitHub上超过一万颗星的关注,目前在所有的机器...
  • 深度学习八大开源框架 导读:深度学习(Deep Learning)是机器学习中一种基于对数据进行表征学习的方法,深度学习的好处是用非监督式或半监督式的特征学习、分层特征提取高效算法来替代手工获取特征(feature)。...
  • 作者:肖仰华,复旦大学计算机科学技术学院,副教授,博士生导师,上海市互联网大数据工程技术中心副主任。主要研究方向为大数据管理与挖掘、知识库等。...随着深度学习对于大数据的红利消耗殆尽,深度学习模型效果...
  • 深度学习八大开源框架 导读:深度学习(Deep Learning)是机器学习中一种基于对数据进行表征学习的方法,深度学习的好处是用非监督式或半监督式的特征学习、分层特征提取高效算法来替代手工获取特征(feature)。...
  • 学习Java过程中,慕课网上的一些视频还有一些书籍对我帮助都很大。下面就我自己看过的一些书籍,来分享一些对我帮助比较大的书籍。 1《Head First Java.第二版》 可以说是我的Java启蒙书籍了,特别适合...
1 2 3 4 5 ... 20
收藏数 27,104
精华内容 10,841
关键字:

java能用来深度学习吗