如何在Android应用程序中使用Deeplearning4J


作者:Ashraff Hathibelagal • 2017年1月7日

一般而言,配有多个GPU的高性能计算机最适合承担训练神经网络的任务。那么,普通的Android手机或平板电脑是否能胜任这项工作呢?这当然是可行的。但是考虑到Android设备的典型配置,运行速度可能会相当缓慢。如果您对此并不在意,请继续阅读。

在本教程中,我将向您介绍如何用Deeplearning4J这一热门的Java深度学习库来在Android设备上创建和训练神经网络。



必要条件

为获得最佳结果,您的系统需具备以下条件:

  • 运行21或更高级别API的Android设备或模拟器,至少有大约200 MB的内部存储空间。强烈建议您先使用模拟器,一旦内存或存储空间不足,您可以对模拟器进行快速调整。
  • Android Studio 2.2或更新版本


配置Android Studio项目

为了能在项目中使用Deeplearning4J,请将以下compile依赖项添加至您的应用模块的build.gradle文件中:


compile 'org.deeplearning4j:deeplearning4j-core:0.7.2'
compile 'org.nd4j:nd4j-native:0.7.2'
compile 'org.nd4j:nd4j-native:0.7.2:android-x86'
compile 'org.nd4j:nd4j-native:0.7.2:android-arm'

如您所见,DL4J依赖于ND4J(全称为“面向Java的N维数组”)这一可以快速处理N维数组的运算库。ND4J内部依赖一个称为JavaCPP的库,其中包含特定平台的本机代码。因此,您必须加载一个与Android设备的基础系统架构相匹配的ND4J版本。我用的是一台x86设备,所以我的平台是android-x86

DL4J和ND4J有多个依赖项文件的名字相同。为了避免构建错误,请将以下exclude参数添加至您的packagingOptions


packagingOptions {
    exclude 'META-INF/DEPENDENCIES'
    exclude 'META-INF/DEPENDENCIES.txt'
    exclude 'META-INF/LICENSE'
    exclude 'META-INF/LICENSE.txt'
    exclude 'META-INF/license.txt'
    exclude 'META-INF/NOTICE'
    exclude 'META-INF/NOTICE.txt'
    exclude 'META-INF/notice.txt'
    exclude 'META-INF/INDEX.LIST'
}

此外,编译完成后的代码中的方法数量将远远超过65,536。应对方法是在defaultConfig中添加以下选项:


multiDexEnabled true

然后请点击Sync Now,更新项目。



启动一项异步任务

训练神经网络需要消耗大量CPU资源,因此最好不要在应用程序的UI线程中运行。我不太确定DL4J是否默认以异步方式训练网络。保险起见,我会先用AsyncTask类来生成一个独立的线程。


AsyncTask.execute(new Runnable() {
    @Override
    public void run() {
        createAndUseNetwork();
    }
});

createAndUseNetwork()方法还不存在,需要自行创建。


private void createAndUseNetwork() {

}


创建神经网络

DL4J的API非常直观易用。现在让我们用它来创建一个具有隐藏层的简单的多层感知器。网络将接受两项输入值,产生一项输出值。我们用DenseLayer(稠密层)和OutputLayer(输出层)两个类来创建网络的层。请将以下代码添加至您前一步创建的createAndUseNetwork()方法中:


DenseLayer inputLayer = new DenseLayer.Builder()
        .nIn(2)
        .nOut(3)
        .name("Input")
        .build();

DenseLayer hiddenLayer = new DenseLayer.Builder()
        .nIn(3)
        .nOut(2)
        .name("Hidden")
        .build();

OutputLayer outputLayer = new OutputLayer.Builder()
        .nIn(2)
        .nOut(1)
        .name("Output")
        .build();

现在层已经准备就绪,可以创建一个NeuralNetConfiguration.Builder对象来配置我们的神经网络。


NeuralNetConfiguration.Builder nncBuilder = new NeuralNetConfiguration.Builder();
nncBuilder.iterations(10000);
nncBuilder.learningRate(0.01);

在上述代码中,我设置了两项重要参数的值:学习速率和迭代次数。您可以自行改变这些值。

现在我们必须创建一个NeuralNetConfiguration.ListBuilder对象来将层连接起来,为其明确指令。


NeuralNetConfiguration.ListBuilder listBuilder = nncBuilder.list();
listBuilder.layer(0, inputLayer);
listBuilder.layer(1, hiddenLayer);
listBuilder.layer(2, outputLayer);

然后用以下代码启用反向传播:


listBuilder.backprop(true);

至此,我们的神经网络就可以作为MultiLayerNetwork类的一个实例生成并初始化了。


MultiLayerNetwork myNetwork = new MultiLayerNetwork(listBuilder.build());
myNetwork.init();


创建训练数据

为了创建训练数据,我们要用到ND4J提供的INDArray类。训练数据的形式如下:


输入      预期输出
------      ----------------
0,0         0
0,1         1
1,0         1
1,1         0

您可能已经猜到,我们的神经网络的运作模式将会和异或门一样。训练数据包含四个样例,必须在代码中提及。


final int NUM_SAMPLES = 4;

接下来为输入和预期输出创建两个INDArray对象,并将其初始化为零。


INDArray trainingInputs = Nd4j.zeros(NUM_SAMPLES, inputLayer.getNIn());
INDArray trainingOutputs = Nd4j.zeros(NUM_SAMPLES, outputLayer.getNOut());

请注意,输入数组中的列数应等于输入层中的神经元数量。与此类似,输出数组中的列数应等于输出层中的神经元数量。

用训练数据填充这些数组很容易。只需使用putScalar()方法即可:


// 如输入0,0则显示0
trainingInputs.putScalar(new int[]{0,0}, 0);
trainingInputs.putScalar(new int[]{0,1}, 0);
trainingOutputs.putScalar(new int[]{0,0}, 0);

// 如输入0,1则显示1
trainingInputs.putScalar(new int[]{1,0}, 0);
trainingInputs.putScalar(new int[]{1,1}, 1);
trainingOutputs.putScalar(new int[]{1,0}, 1);

// 如输入1,0则显示1
trainingInputs.putScalar(new int[]{2,0}, 1);
trainingInputs.putScalar(new int[]{2,1}, 0);
trainingOutputs.putScalar(new int[]{2,0}, 1);

// 如输入1,1则显示0
trainingInputs.putScalar(new int[]{3,0}, 1);
trainingInputs.putScalar(new int[]{3,1}, 1);
trainingOutputs.putScalar(new int[]{3,0}, 0);

我们不会直接使用INDArray对象,而是将其转换为一个DataSet。


DataSet myData = new DataSet(trainingInputs, trainingOutputs);

至此,我们可以调用fit()方法,将数据集输入神经网络,开始训练。


myNetwork.fit(myData);

以上就是全部的流程。您的神经网络已经准备就绪,可以投入使用了。



总结

本教程已向您说明,在Android Studio项目中用Deeplearning4J学习库来创建和训练神经网络是非常容易的。但是我要提醒您,在某些情况下,用电池驱动的低性能设备来训练神经网络可能并不是个好主意。

本文最初由Ashraff Hathibelagal发表于Progur

与我们在Gitter聊天