全文共10735字,预计学习时长22分钟或更长
有一款生命周期管理工具(也称云服务)叫做Valohai,它有着友好的用户界面和简洁的布局设计。
许多有关Valohai的案例和文档都是基于Python和R及其相应的框架和数据库的,但是基于Java或JVM的却很少,所以本文打算借此机会一探究竟。
需要什么以及怎么做?
对任何一类机器学习或深度学习项目(想法)来说,代码和基础架构这两样东西(对于高级项目来说)必不可少,有代码就可以打造模型;有基础架构,生命周期就有遵循的轨迹。
当然了,该过程的前中后期也是需要一些步骤和工具的。那么为了一切从简,我们暂且说需要的是代码和基础架构。
代码
针对代码,笔者选择了一个使用DL4J改进的例子。该MNist项目的训练集含6万张镜像,测试集含1万张手写数字的镜像。在DL4J库中这些数据集都是可用的(就像Keras储存的大量数据集)。大家可以通过DL4J Cheatsheet在DatasetIterators上找一下MnistDataSetIterator,了解更多有关该数据集的详细信息。
在开始之前,先看一下会用到的源代码,主Java类称为org.deeplearning4j.feedforward.mnist.MLPMnistSingleLayerRunner。
基础架构
我们已经决定把Valohai当作基础架构来试行Java(训练和评估模型)。Valohai会辩识git repositories,然后直接导入,允许代码、无关平台或无关语言的操作。(在后面我们会看到这些是如何进行的)同时这也意味着,假如你是GitOps或Infrastructure-As-Code的铁杆粉丝,你会被Valohai的工作流惊到的。
此时只需注册一个Valohai账户,可以是免费注册的,登录后就能浏览一些有关各种构形的实例。其实免费账户就可以完全满足我们的需求。
Java和Valohai的深度学习之旅
将必备的部件和运行依赖项捆绑至Docker image中,并用其构建Java应用程序、训练模型。Valohai上的评估借助一个简单的valohai.yaml文件即可,可以在项目储存库的根文件夹中找到该文件。
Java深度学习:DL4J
这一块处理起来很简单,无需过多操作,建立一个jar文件再将数据集下载到Docker container中就行了。之前我们已经建立了Docker image,这里面涵括了构建应用程序需要的所有依赖项。该镜像已存到Docker Hub(库)(https://hub.docker.com/)中,搜索dl4j-mnist-single-layer(https://hub.docker.com/r/neomatrix369/dl4j-mnist-single-layer) 可以找到它(到时候会采用YAML文件中的特殊标记命名)。GraalVM 19.1.1(https://github.com/oracle/graal)会在此项目的Java构建和运行时发挥作用,而且它是会嵌入到Docker image中的(参见Docker文件:https://github.com/valohai/mlpmnist-dl4j-example/blob/master/Dockerfile,以查看Docker image的定义)。
编制
命令行出现调用uber jar时,进入MLPMnistSingleLayerRunner 类,然后根据传入的参数导向预期操作:
public static void main(String[] args) throws Exception {
MLPMnistSingleLayerRunner mlpMnistRunner = new MLPMnistSingleLayerRunner();
JCommander.newBuilder()
.addObject(mlpMnistRunner)
.build()
.parse(args);
mlpMnistRunner.execute();
}
传入uber jar的参数就是由该类接收的,采用的是execute() 法。
我们可以通过传递给Java应用(uber jar)的--action train参数创建模型,再用传递的--action evaluate参数评估模型。
上述Java应用的核心操作会在下文的两个Java类中有所体现。
训练模型
可以通过下述方法从命令行调用:
./runMLPMnist.sh --action train --output-dir ${VH_OUTPUTS_DIR}
or
java -Djava.library.path=""
-jar target/MLPMnist-1.0.0-bin.jar
--action train --output-dir ${VH_OUTPUTS_DIR}
模型会创建到由--output-dir(执行初始传入的参数)指定的文件夹中,命名为mlpmnist-single-layer.pb。对于Valohai,则是创建到${VH_OUTPUTS_DIR}中,这也正是我们所要做的(可参见valohai.yaml 文件:https://github.com/valohai/mlpmnist-dl4j-example/blob/master/valohai.yaml)。
想要查看源代码,请看类MLPMNistSingleLayerTrain.java:https://github.com/valohai/mlpmnist-dl4j-example/blob/master/src/main/java/org/deeplearning4j/feedforward/mnist/MLPMnistSingleLayerTrain.java。
评估模型
可以通过下述方法从命令行调用:
./runMLPMnist.sh --action evaluate --input-dir ${VH_INPUTS_DIR}/model
or
java -Djava.library.path=""
-jar target/MLPMnist-1.0.0-bin.jar
--action evaluate --input-dir ${VH_INPUTS_DIR}/model
此处最好是名为mlpmnist-single-layer.pb 的模型(训练步骤中创建)可以在--input-dir (应用程序调用时传入)指定的文件夹中显示。
想要查看源代码,请看MLPMNistSingleLayerEvaluate.java:https://github.com/valohai/mlpmnist-dl4j-example/blob/master/src/main/java/org/deeplearning4j/feedforward/mnist/MLPMnistSingleLayerEvaluate.java
笔者希望上述简短阐述可以让大家清楚地了解到Java应用大致是如何进行训练和评估的。
需要的就是这些,不过大家想尝试其他的代码(https://github.com/valohai/mlpmnist-dl4j-example/blob/master/Dockerfile)也是可以的(以及README.md和bash脚本),那就来满足你的好奇心,搞清楚这些东西都是怎么操作的吧!
Valohai
在Valohai上,我们可以自由组合运行环境、代码以及数据集,正如下文所示的YAML文件结构。这样一来,不同部件在升级时无需依赖另一方就可畅通无阻,Docker container中也就只有生成和运行组件。
执行期间,在Docker container中生成uber jar,再将其上传到某些内部或外部存储中,然后在另一个执行步骤中下载储存内(也可以是另一个位置)的uber jar和数据集以便训练模型。这样两个执行步骤就会解耦;我们可以一次建立一个jar文件,然后在同一文件中运行数百个训练步骤。生成和运行环境按理说不会变化得很频繁,所以可以缓存下来,而且执行期间代码、数据集和模型资源都可随时调用。
valohai.yaml
Java项目与Valohai服务硬件资源集成的核心在于确定 valohai.yaml 文件中的执行步骤,该文件位于项目文件夹的根目录中。valohai.yaml 大概是这个样子:
---
- step:
name: Build-dl4j-mnist-single-layer-java-app
image: neomatrix369/dl4j-mnist-single-layer:v0.5
command:
- cd ${VH_REPOSITORY_DIR}
- ./buildUberJar.sh
- echo "~~~ Copying the build jar file into ${VH_OUTPUTS_DIR}"
- cp target/MLPMnist-1.0.0-bin.jar ${VH_OUTPUTS_DIR}/MLPMnist-1.0.0.jar
- ls -lash ${VH_OUTPUTS_DIR}
environment: aws-eu-west-1-g2-2xlarge
- step:
name: Run-dl4j-mnist-single-layer-train-model
image: neomatrix369/dl4j-mnist-single-layer:v0.5
command:
- echo "~~~ Unpack the MNist dataset into ${HOME} folder"
- tar xvzf ${VH_INPUTS_DIR}/dataset/mlp-mnist-dataset.tgz -C ${HOME}
- cd ${VH_REPOSITORY_DIR}
- echo "~~~ Copying the build jar file from ${VH_INPUTS_DIR} to current location"
- cp ${VH_INPUTS_DIR}/dl4j-java-app/MLPMnist-1.0.0.jar .
- echo "~~~ Run the DL4J app to train model based on the the MNist dataset"
- ./runMLPMnist.sh {parameters}
inputs:
- name: dl4j-java-app
description: DL4J Java app file (jar) generated in the previous step 'Build-dl4j-mnist-single-layer-java-app'
- name: dataset
default: https://github.com/neomatrix369/awesome-ai-ml-dl/releases/download/mnist-dataset-v0.1/mlp-mnist-dataset.tgz
description: MNist dataset needed to train the model
parameters:
- name: --action
pass-as: '--action {v}'
type: string
default: train
description: Action to perform i.e. train or evaluate
- name: --output-dir
pass-as: '--output-dir {v}'
type: string
default: /valohai/outputs/
description: Output directory where the model will be created, best to pick the Valohai output directory
environment: aws-eu-west-1-g2-2xlarge
- step:
name: Run-dl4j-mnist-single-layer-evaluate-model
image: neomatrix369/dl4j-mnist-single-layer:v0.5
command:
- cd ${VH_REPOSITORY_DIR}
- echo "~~~ Copying the build jar file from ${VH_INPUTS_DIR} to current location"
- cp ${VH_INPUTS_DIR}/dl4j-java-app/MLPMnist-1.0.0.jar .
- echo "~~~ Run the DL4J app to evaluate the trained MNist model"
- ./runMLPMnist.sh {parameters}
inputs:
- name: dl4j-java-app
description: DL4J Java app file (jar) generated in the previous step 'Build-dl4j-mnist-single-layer-java-app'
- name: model
description: Model file generated in the previous step 'Run-dl4j-mnist-single-layer-train-model'
parameters:
- name: --action
pass-as: '--action {v}'
type: string
default: evaluate
description: Action to perform i.e. train or evaluate
- name: --input-dir
pass-as: '--input-dir {v}'
type: string
default: /valohai/inputs/model
description: Input directory where the model created by the previous step can be found created
environment: aws-eu-west-1-g2-2xlarge
如何构建dl4j的mnist单层java应用程序
由YAML文件可看出,这一步首先调用了Docker image,然后运行生成脚本来构建uber jar。Docker image中含有生成环境依赖项的设置(即GraalVM JDK、Maven等),从而实现Java应用的构建。不需要指定任何的输入内容或参数,因为这本身就是生成步骤。一旦生成结束,我们就会将名为 MLPMnist-1.0.0-bin.jar(原名称)的uber jar复制到/valohai/outputs文件夹内(表示为${VH_OUTPUTS_DIR})。此文件夹中的所有内容都会自动保存在项目存储中,比如AWS S3 bucket。最后,设定为AWS环境下运行。
注:Valohai免费用户没有Docker container的内部访问权限(默认情况下是这样),可以访问帮助以启用此选项(笔者就是这么办的),不然的话就无法在生成过程中下载Maven和其他依赖项了。
如何运行dl4j的mnist单层训练模型
这一部分与先前步骤的差不多,无非就是指定了两个输入内容,一个针对uber jar(MLPMnist-1.0.0.jar),一个针对数据集(解压缩到${HOME}/.deeplearning4j文件夹中)。该过程中将传递两个参数,--action train 和--output-dir /valohai/outputs,此间创建的模型会存到 /valohai/outputs/model文件夹中(表示为 ${VH_OUTPUTS_DIR}/model)。
注:在Valohai Web UI的执行选项卡中,除了可以通过datum:// or http:// URLs选择先前执行步骤中的输入内容,也可利用执行数字(即#1或#2)。在键盘上敲文件名的几个字母也可以助力全面搜索。
如何运行dl4j的mnist单层评估模型
这个步骤还是跟之前相似,不同之处在于会传递两个参数--action evaluate 和 --input-dir /valohai/inputs/model。此外,我们也会再一次指定两项输入内容:dl4j-java-app和model ,它们在YAML文件中定义而且都没有默认设置。这样一来操作者就可以选择想要评估的uber jar和模型——二者是在web界面运行dl4j的mnist单层评估模型时创建的。
笔者希望这可以解释上述定义文件的步骤,但如果你想进一步寻求帮助,别犹豫了,请查看这里的文件(https://docs.valohai.com/index.html)和教程(https://docs.valohai.com/tutorials/index.html)。
Valohai网页界面
有了账户,就可以登录继续创建项目,命名为mlpmnist-single-layer ,链接至git储存库 https://github.com/valohai/mlpmnist-dl4j-example,保存项目。
现在可以执行一下看看是怎么个操作法!
构建DL4J Java应用程序
点击网页界面的执行选项卡,再复制现有执行或者使用[创建执行]按钮创建新执行。所有的默认选项都会涉及到。选择构建dl4j的mnist单层java应用程序。
至于说环境,笔者会选择AWS eu-west-1 g2.2xlarge,然后再在网页最下方点击[创建执行]来看看执行的启动。
训练模型
点击网页界面的执行选项卡,重复上一步操作,然后选择运行dlj4的mnist单层训练模型。大家需要选择的是之前步骤中生成的Java应用程序(就是filed类中的jar文件)。而数据集已经提前通过valohai.yaml文件预填充过了:
点击[创建执行]启动这一项。
在log console中可以看到模型概要的变化:
[]
11:17:05 =========================================================================
11:17:05 LayerName (LayerType) nIn,nOut TotalParams ParamsShape
11:17:05 =========================================================================
11:17:05 layer0 (DenseLayer) 784,1000 785000 W:{784,1000}, b:{1,1000}
11:17:05 layer1 (OutputLayer) 1000,10 10010 W:{1000,10}, b:{1,10}
11:17:05 -------------------------------------------------------------------------
11:17:05 Total Parameters: 795010
11:17:05 Trainable Parameters: 795010
11:17:05 Frozen Parameters: 0
11:17:05 =========================================================================
[]
执行期间以及结束时,可以在执行主选项卡中的输出子选项卡下找到已创建的模型:
你也许已经注意到输出选项卡中有几个artifacts文件。那是因为我们在每一回epoch的最后一步都保存了一个checkpoint!从执行记录里能看到它们:
[]
11:17:14 o.d.o.l.CheckpointListener - Model checkpoint saved: epoch 0, iteration 469, path: /valohai/outputs/checkpoint_0_MultiLayerNetwork.zip
[]
Checkpoint压缩包内有每一节点处模型训练的状态,可在下面三个文件中查询:
configuration.json
coefficients.bin
updaterState.bin
训练模型>元数据
大家也许在执行记录中注意到这些符号了:
[]
11:17:05 {"epoch": 0, "iteration": 0, "score (loss function)": 2.410047}
11:17:07 {"epoch": 0, "iteration": 100, "score (loss function)": 0.613774}
11:17:09 {"epoch": 0, "iteration": 200, "score (loss function)": 0.528494}
11:17:11 {"epoch": 0, "iteration": 300, "score (loss function)": 0.400291}
11:17:13 {"epoch": 0, "iteration": 400, "score (loss function)": 0.357800}
11:17:14 o.d.o.l.CheckpointListener - Model checkpoint saved: epoch 0, iteration 469, path: /valohai/outputs/checkpoint_0_MultiLayerNetwork.zip
[]
这些符号会触发Valohai挑选这些值(JSON格式)以形成执行指标,这些指标在执行中及执行后都可以在执行主选项卡中的元数据子选项下找到:
通过将listener类(被称为Valohai元数据生成器)链接到模型中来完成这一步的操作。这样一来,训练中每一次迭代结束之时,都可以集中于listener类上,打印出元计数、迭代计数和分数(丢失的功能值)。下面是这个类的代码片段:
public void iterationDone(Model model, int iteration, int epoch) {
if (printIterations <= 0)
printIterations = 1;
if (iteration % printIterations == 0) {
double score = model.score();
System.out.println(String.format(
"{"epoch": %d, "iteration": %d, "score (loss function)": %f}