今天笔者要介绍的就是如何利用java去实现手写数字识别。本文大部分内容都借鉴自其它博主,在此表示感谢,不过出于笔者喜欢记录自己做过的事情,并且写一点自己的个人体会。所以,希望大佬们不要见怪,再次感谢。
本文旨在帮助初次接触deeplearn4j的java爱好者,欢迎评论区留言交流。
温馨提示:为了避免不必要的麻烦,请先更新您的JDK和Eclipse至64位。
与其他深度学习框架相比,Deeplearning4j具有以下优点。
初学者可以从官网上下载DL4J的示例代码。
示例项目由maven构建,包含多个子项目。具体的项目目录如下图左边所示,这里推荐一个非常方便的github代码阅读工具。
这里,我们需要注意的是,dl4j-examples、rl4j-examples等示例项目都对应着同一个父项目。其实父项目什么内容也没有,只是包含一个pom.xml文件,里面有所有子项目需要用到的一些内容。例如,当您打开dl4j-examples时,会在其pox.xml文件中看到一个parent节点,其实就是指向父项目的pom.xml文件。这个节点的含义大家如果不清楚,可以查一下maven中parent的含义。
当我们需要建立一个自己的项目时,可以利用官方示例代码中已经提供的示例。注意到,有一个叫做 standalone-sample-project的子项目,我们可以直接复制pom.xml中的内容到自己的maven项目中,创建一个自己的maven项目。
详细的内容如下:
4.0.0
org.deeplearning4j
deeplearning4j-examples
1.0.0-beta4
1.0.0-beta4
1.0.0-beta4
1.2.3
1.8
2.4.3
org.deeplearning4j
deeplearning4j-core
${dl4j.version}
org.nd4j
nd4j-native
${nd4j.version}
ch.qos.logback
logback-classic
${logback.version}
org.apache.maven.plugins
maven-compiler-plugin
3.5.1
${java.version}
org.apache.maven.plugins
maven-shade-plugin
${maven-shade-plugin.version}
true
bin
true
*:*
org/datanucleus/**
META-INF/*.SF
META-INF/*.DSA
META-INF/*.RSA
package
shade
reference.conf
如果没有额外的需求,您只需要修改一下:Group-ID 和 artifact ID
好了,有关官方的examples的讲解就这些了,如果在看的过程中有疑问,欢迎大家评论留言,相互交流。
MNIST数据集包含一个有6万个样例的训练集和一个有1万个样例的测试集。训练集用于让算法学习如何准确地预测出图像的整数标签,而测试集则用于检查已训练网络的预测有多准确。
其实这个数据集没有什么可以说的,因为为了方便学习,deeplearn4j的官方已经处理好了数据,根本不需要我们自己去处理,其中有一个 MnistDataSetIterator 已经帮你下载好了数据集,同时处理好了数据集,降低了入门者的学习难度。
官方的实例代码处理MINIST数据集有点简单,笔者找到了一位大佬的博客,大佬没有使用BP神经网络做识别,而是使用卷积神经网络训练,效果更好。卷积神经网络和BP神经网络这里不做详细介绍,相信大家既然看到这里就应该有一定的理论基础。这里笔者借鉴了一位大佬的代码,请移步使用Dl4j训练的一个手写数字识别软件欣赏。真的很棒,对不对?下面只给出训练过程代码。
package cn.rocket;
import java.io.File;
import java.io.IOException;
import org.deeplearning4j.datasets.iterator.impl.MnistDataSetIterator;
import org.deeplearning4j.eval.Evaluation;
import org.deeplearning4j.nn.api.OptimizationAlgorithm;
import org.deeplearning4j.nn.conf.BackpropType;
import org.deeplearning4j.nn.conf.MultiLayerConfiguration;
import org.deeplearning4j.nn.conf.NeuralNetConfiguration;
import org.deeplearning4j.nn.conf.inputs.InputType;
import org.deeplearning4j.nn.conf.layers.ConvolutionLayer;
import org.deeplearning4j.nn.conf.layers.DenseLayer;
import org.deeplearning4j.nn.conf.layers.OutputLayer;
import org.deeplearning4j.nn.conf.layers.SubsamplingLayer;
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.deeplearning4j.nn.weights.WeightInit;
import org.deeplearning4j.optimize.listeners.ScoreIterationListener;
import org.deeplearning4j.util.ModelSerializer;
import org.nd4j.linalg.activations.Activation;
import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
import org.nd4j.linalg.learning.config.Nesterovs;
import org.nd4j.linalg.lossfunctions.LossFunctions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TrainCNN {
private static Logger log = LoggerFactory.getLogger(TrainCNN.class);
public static void main(String[] args) throws IOException {
// 图片的尺寸
final int numRows = 28;
final int numColumns = 28;
final int channels = 1;
int outputNum = 10; // 输出层的神经元个数
int batchSize = 128; // 每个批次的大小
int rngSeed = 123; // 随机数
int numEpochs = 1; // 训练的次数
// 划分训练集和测试集
DataSetIterator mnistTrain = new MnistDataSetIterator(batchSize, true, rngSeed);
DataSetIterator mnistTest = new MnistDataSetIterator(batchSize, false, rngSeed);
log.info("创建模型....");
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder().seed(rngSeed).l2(0.0005)
.weightInit(WeightInit.XAVIER).optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)
.updater(new Nesterovs(0.006, 0.9)).list()
.layer(0,
new ConvolutionLayer.Builder(5, 5).nIn(channels).stride(1, 1).nOut(20)
.activation(Activation.IDENTITY).build())
.layer(1,
new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX).kernelSize(2, 2).stride(2, 2)
.build())
.layer(2,
new ConvolutionLayer.Builder(5, 5).stride(1, 1).nOut(50).activation(Activation.IDENTITY)
.build())
.layer(3,
new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX).kernelSize(2, 2).stride(2, 2)
.build())
.layer(4, new DenseLayer.Builder().activation(Activation.RELU).nOut(500).build())
.layer(5,
new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD).nOut(outputNum)
.activation(Activation.SOFTMAX).build())
.setInputType(InputType.convolutionalFlat(numRows, numColumns, channels))
.backpropType(BackpropType.Standard).build();
MultiLayerNetwork model = new MultiLayerNetwork(conf);
model.init();
// 设置输出
model.setListeners(new ScoreIterationListener(1));
log.info("训练模型....");
model.fit(mnistTrain, numEpochs);
log.info("测试模型....");
Evaluation eval = model.evaluate(mnistTest);
log.info(eval.stats());
// 保存训练好的模型
File locationToSave = new File("模型.zip");
ModelSerializer.writeModel(model, locationToSave, false);
}
}
笔者项目的github地址:https://github.com/jack13163/MNISTBYDL4J
那么好的东西,所以笔者不禁想将大佬的东西打包为exe可执行文件。
将java项目打包成exe可执行文件的第一步是生成一个可执行的jar文件,这个jar文件中应该包含有本项目用到的所有的jar包及其依赖。既然是maven项目,那么自然最好使用maven工具进行打包了。
编译插件,用来重新编译项目中的java文件,生成class文件。
org.apache.maven.plugins
maven-compiler-plugin
3.5.1
${java.version}
依赖处理插件,能够将项目所需要的jar包全部复制到target目录下的指定目录下(这里我们指定的是lib目录,如果lib目录不存在,那么会自动创建lib目录)。
org.apache.maven.plugins
maven-dependency-plugin
copy
compile
copy-dependencies
${project.build.directory}/lib
jar打包插件,通过使用eclipse的maven插件,执行maven->build...->在target中输入:"install" 的方式调用,或者直接右键项目->maven->install。
org.apache.maven.plugins
maven-jar-plugin
true
lib/
cn.rocket.MainFrame
assembly打包插件,通过使用eclipse的maven插件,执行maven->build...->在target中输入:"assembly:assembly" 的方式调用。
maven-assembly-plugin
cn.rocket.MainFrame
true
lib/
jar-with-dependencies
shade打包插件,通过使用eclipse的maven插件,执行maven->build...->在target中输入:"package" 的方式调用。
org.apache.maven.plugins
maven-shade-plugin
${maven-shade-plugin.version}
true
bin
false
*:*
org/datanucleus/**
META-INF/*.SF
META-INF/*.DSA
META-INF/*.RSA
package
shade
cn.rocket.MainFrame
reference.conf
最终,可以将生成的jar文件和模型文件放在同一目录下,通过运行:java -jar XXX.jar的方式运行,若出现报错,也别着急,记得留言交流,哈哈。
其实这个并不能将jre打包进exe中,但是可以实现在没有配置java环境的系统下运行,最终只需要下面的三个文件,就可以实现各个平台上的运行,其中minist.exe是根据jar文件生成的exe可执行文件,jre是对应的java环境,模型是这个项目之前预训练好的网络模型。
打包主要用到了叫做exe4j的软件,关于打包的相关教程,请移步至:使用exe4j打包exe。用exe4j打包的会有下面的内容,确定就行。
下面是识别效果,是不是效果不错,哈哈。训练的次数太少了,只训练了一次,大家可以找到代码调一下,重新训练一下。
最后,再对原来做这个程序的大佬表示感谢。