[专知-Java Deeplearning4j 深度学习教程 05] 无监督特征提取神器—AutoEncoder:图文-代码

【导读】主题链路知识是我们专知的核心功能之一,为用户提供AI领域系统性的知识学习服务,一站式学习人工智能的知识,包含人工智能( 机器学习、自然语言处理、计算机视觉等)、大数据、编程语言、系统架构。使用请访问专知 进行主题搜索查看 - 桌面电脑访问http://www.zhuanzhi.ai, 手机端访问http://www.zhuanzhi.ai 或关注微信公众号后台回复" 专知"进入专知,搜索主题查看。继Pytorch教程后,我们推出面向Java程序员的深度学习教程DeepLearning4J。Deeplearning4j的案例和资料很少,官方的doc文件也非常简陋,基本上所有的类和函数的都没有解释。为此,我们推出来自中科院自动化所专知小组博士生Hujun与Sanglei创作的-分布式Java开源深度学习框架Deeplearning4j学习教程包括以下:

特征提取

对很多机器学习/数据挖掘任务来说,选取或设计优质的的特征比设计一个好的分类器显得更为重要,然而优质特征的设计往往需要耗费大量的时间。深度学习包含了许多优质的无监督的特征自动提取算法,可以自动化地从原始特征(例如图像像素向量、文本词频向量等)中提取优质的特征,大大地节约了特征设计的成本,收到工业界的青睐。本文介绍一种无监督学习特征的模型——AutoEncoder,并提供DL4J实现AutoEncoder的代码。

特征提取示例

Iris是一个经典的数据集,数据由150个样本组成,包含3个类别的样本(3种标签),每个样本由4个特征和1个标签组成。例如数据的前几行如下所示,数据的前4列分别表示样本的4个特征,最后一列Iris-setosa是样本的标签,即样本的所属类别,是分类器需要预测的标签。

5.1,3.5,1.4,0.2,Iris-setosa

4.9,3.0,1.4,0.2,Iris-setosa

4.7,3.2,1.3,0.2,Iris-setosa

4.6,3.1,1.5,0.2,Iris-setosa

5.0,3.6,1.4,0.2,Iris-setosa

5.4,3.9,1.7,0.4,Iris-setosa

Iris数据集的下载地址为https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data。可以看出,很难人工设计一个区分这三种花。将Iris数据集可视化之后如下图所示,每个小图表示从某2个维度(一个维度对应一种特征)去观察Iris得到的结果,可以看出该数据集在一些小图中是线性可分的(任选两类样本,可以用一条直线去大致分隔这两类样本)。

但依赖可视化的方法是不可行的,Iris数据集只有4个特征和150个样本,如果换成MNIST数据集,则有784个特征,会产生613872张小图。另外,大部分情况下不是选取2个特征进行线性组合就可以获得优质特征的,而是需要多个特征做复杂的数学运算才可以得到优质的特征,因此,需要其他的方法来解决设计优质特征的问题。

图片

PCA(主成分分析)是一种传统的学习特征的方法。PCA可以将Iris数据集的4个特征变换为2个新的特征,2个新特征的可视化如下图所示,可以看到,学到的特征是线性可分的,可直接用于分类。下面,我们将介绍深度学习中一个简单粗暴的特征学习神器——AutoEncoder。

图片

AutoEncoder

AutoEncoder其实就是一个3层神经网络,由1个输入层、1个隐藏层和1个输出层构成(如下图所示)。AutoEncoder用网络的输入数据作为Label,即希望网络的输出层输出和输入层一样的东西。如果将整个网络看作一个函数,这个函数为hW,b(x)≈x,其中x表示网络的输入(例如对于Iris数据集来说,x是一个4维向量,代表某个样本的4个特征)。AutoEncoder训练成功后,输入一个样本的特征,隐藏层的激活值即为学习到的该样本的新特征。

图片

直观地理解为什么AutoEncoder为什么可以学习到特征,数据从AutoEncoder的输入层到输出层会经过两次变换,第一次将输入数据变换为隐藏层的激活值,第二次将隐藏层的激活值变换为输出层(即还原为输入),如果可以成功地还原输入数据,则说明隐藏层的激活值包含了输入层所有的信息(严格地说,是隐藏层的激活值加上AutoEncoder的网络参数包含了输入层的所有信息,但网络参数是所有样本共享的,因此在网络参数固定的情况下,可以认为隐藏层的激活值等价于其对应的输入数据)。

下面给出Deeplearning4j实现AutoEncoder的代码,有几个需要注意的地方:


import org.deeplearning4j.datasets.iterator.impl.IrisDataSetIterator;  
import org.deeplearning4j.nn.api.OptimizationAlgorithm;  
import org.deeplearning4j.nn.conf.MultiLayerConfiguration;  
import org.deeplearning4j.nn.conf.NeuralNetConfiguration;  
import org.deeplearning4j.nn.conf.layers.DenseLayer;  
import org.deeplearning4j.nn.conf.layers.OutputLayer;  
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;  
import org.deeplearning4j.optimize.listeners.ScoreIterationListener;  
import org.math.plot.Plot2DPanel;  
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.dataset.api.preprocessor.DataNormalization;  
import org.nd4j.linalg.dataset.api.preprocessor.NormalizerMinMaxScaler;  
import org.nd4j.linalg.lossfunctions.LossFunctions.LossFunction;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  

import javax.swing.\*;  
import java.awt.\*;  
import java.util.List;  


\***  

 本教程由专知提供:http://www.zhuanzhi.ai/*  

本教程演示如何用Deeplearning4j构建AutoEncoder*  
除了DL4J所需的基础库,还需要导入JMathPlot的Maven依赖:*  
   https://mvnrepository.com/artifact/com.github.yannrichet/JMathPlot*  

本教程用DL4J在Iris数据集上学习AutoEncoder*  
将Iris数据集的4维的原始特征变换为2维的优质特征*  
最后用JMathPlot绘制学习到的特征*  
*\**  
*\* \@author hu*  
***/

public class AutoEncoderExample {  

   private static Logger *log* =
LoggerFactory.*getLogger*(AutoEncoderExample.class);  

   public static void main(String[] args) throws Exception {  
       int inputDim = 4; // 输入数据维度,即原始特征数量  
       final int hiddenDim = 2; //隐藏层维度,即学习到的特征的维度  

       int batchSize = 150; // 这里用整个数据集的大小作为batchSize  
       int rngSeed = 123; // 随机种子,保证每次运行程序获得同样的结果  
       int numEpochs = 1000; // epoch数量,扫描一遍数据集为一个epoch  

       //用DL4J自带的Iris数据集  
       DataSetIterator irisDataSet = new IrisDataSetIterator(batchSize, 150);  
       //将Iris数据集归一化到-1和1之间  
       //本示例用tanh激活输出层,所以用-1到1  
       DataNormalization norm = new NormalizerMinMaxScaler(-1,1);  
       norm.fit(irisDataSet);  
       irisDataSet.setPreProcessor(norm);  


       *log*.info("Build model....");  
       MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()  
               .seed(rngSeed) //设置随机种子,保证每次运行程序获得同样的结果  

 .optimizationAlgo(OptimizationAlgorithm.*STOCHASTIC_GRADIENT_DESCENT*)  
               .iterations(1)  
               .learningRate(3e-2) //学习速率  
               .regularization(true).l2(1e-4)  
               .list()  
               //构建Encoder  
               .layer(0, new DenseLayer.Builder()  
                       .nIn(inputDim)  
                       .nOut(hiddenDim)  
                       .activation(Activation.*TANH*)  
                       .build())  
               //构建Decoder  
               .layer(1, new OutputLayer.Builder(LossFunction.*MSE*)  
                       .nIn(hiddenDim)  
                       .nOut(inputDim)  
                       .activation(Activation.*TANH*)  
                       .build())  
               .pretrain(false).backprop(true)  
               .build();  

       MultiLayerNetwork model = new MultiLayerNetwork(conf);  
       model.init();  
       //每50个Iteration就print一次score  
       model.setListeners(new ScoreIterationListener(50));  

       *log*.info("Train model....");  
       //训练  
       for( int i=0; i\<numEpochs; i++ ){  
           irisDataSet.reset();  
           while(irisDataSet.hasNext()){  
               INDArray inputs = irisDataSet.next().getFeatures();  
               //将网络的输入和Label都设置为样本原始特征  
               model.fit(inputs, inputs);  
           }  
       }  

       *log*.info("Plot learned features....");  
       irisDataSet.reset();  
       //取一个batch的数据,这里batchSize为数据集大小  
       //因此这里会取出所有的数据  
       DataSet plotDataSet = irisDataSet.next();  
       //获取原始特征  
       INDArray inputs = plotDataSet.getFeatures();  
       //前向传播到第0层(即隐藏层)  

 //返回的是一个数组,数组包含前向传播到指定层所经过的所有层的激活值(包括指定层)  
       List\<INDArray\> activationList =
model.feedForwardToLayer(0,inputs,false);  
       //取出数组中的最后一层激活值(也就是隐藏层的激活值)  
       INDArray features = activationList.get(activationList.size() - 1);  
       //取出原始数据的Label,原始数据的Label用one
hot格式,因此需要用argMax(1)将其转换为普通数值Label  
       INDArray labels = plotDataSet.getLabels().argMax(1);  

       //使用JMathPlot绘制特征2D图  
       //用2个坐标轴表示学习到的特征的2个维度  
       //用颜色表示样本的类别  
       Plot2DPanel plot = new Plot2DPanel();  
       Color[] colors = new Color[]{Color.*red*, Color.*green*, Color.*blue*};  
       for(int i = 0;i\<features.shape()[0];i++){  
           Color color = colors[labels.getInt(i)];  
           double x = features.getDouble(i,0);  
           double y = features.getDouble(i,1);  
           plot.addScatterPlot("iris", color,new double[]{x},new double[]{y});  
       }  
       //将JMathPlot嵌套在JFrame里展示  
       JFrame frame = new JFrame("a plot panel");  
       frame.setBounds(200,200,800,800);  
       frame.setContentPane(plot);  
       frame.setVisible(true);  

   }  

}

运行结果:

图片

请继续关注“DeepLearning4j”教程。完整系列搜索查看,请PC登录http://www.zhuanzhi.ai, 搜索“DeepLearning4j”即可得。


对DeepLearning4j教程感兴趣的同学,欢迎进入我们的专知DeepLearning4j主题群一起交流、学习、讨论,扫一扫如下群二维码即可进入(先加微信小助手weixinhao: Rancho_Fang,注明Deeplearning4j)。

展开全文
相关主题
Top
微信扫码咨询专知VIP会员