专栏首页ATYUN订阅号使用Java部署训练好的Keras深度学习模型

使用Java部署训练好的Keras深度学习模型

编译:yxy

出品:ATYUN订阅号

Keras库为深度学习提供了一个相对简单的接口,使神经网络可以被大众使用。然而,我们面临的挑战之一是将Keras的探索模型转化为产品模型。Keras是用Python编写的,直到最近,这个语言之外的支持还很有限。虽然Flask,PySpark和Cloud ML等工具可以直接在Python中产品化模型,但我通常更喜欢使用Java来部署模型。

像ONNX这样的项目正朝着深度学习的标准化方向发展,但支持这些格式的运行时仍然有限。常用的方法是将Keras模型转换为TensorFlow图,然后在其他支持TensorFlow的运行时中使用这些图。我最近发现了Deeplearning4J(DL4J)项目,该项目本身支持Keras模型,使得在Java中进行深度学习很容易上手并运行。

我一直在探索深度学习的一个用例是使用Python训练Keras模型,然后使用Java产生模型。这对于需要直接在客户端进行深度学习的情况很有用,例如应用模型的Android设备,或者你希望利用使用Java编写的现有生产系统。使用keras的DL4J介绍可以访问下方链接。

链接:https://deeplearning4j.org/docs/latest/keras-import-overview

本文概述了在Python中训练Keras模型,并使用Java进行部署。我使用Jetty提供实时预测,使用Google的DataFlow构建批预测系统。运行这些示例所需的完整代码和数据可在GitHub上获得。

GitHub:https://github.com/bgweber/DeployKeras/tree/master

模型训练

第一步是使用Python中的Keras库训练模型。一旦你有一个可以部署的模型,你可以将它保存为h5格式并在Python和Java应用程序中使用它。在本教程中,我们使用我过去训练的模型(“预测哪些玩家可能购买新游戏”,模型用了Flask)进行预测。

模型的输入是十个二进制特征(G1,G2,…,G10),用于描述玩家已经购买的游戏,标签是一个单独的变量,用于描述用户是否购买了游戏,不包含在输入中。训练过程的主要步骤如下所示:

import keras
from kerasimport models, layers
# Define the model structure
model= models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10,)))
...
model.add(layers.Dense(1, activation='sigmoid'))
# Compile and fit the model
model.compile(optimizer='rmsprop',loss='binary_crossentropy',
              metrics=[auc])
history= model.fit(x, y, epochs=100, batch_size=100,
                    validation_split= .2, verbose=0)
# Save the model in h5 format
model.save("games.h5")

此过程的输出是一个h5文件,它表示我们可以在Python和Java应用程序中部署的训练模型。在本文中,我将展示如何在Java中构建批量和实时预测。

Java安装程序

要使用Java部署Keras模型,我们将使用Deeplearing4j库。它提供了Java深度学习的功能,可以加载和利用Keras训练的模型。我们还将使用Dataflow进行批预测,使用Jetty进行实时预测。以下是我在这个项目中使用的库:

  • Deeplearning4j:为Java提供深度神经网络功能。
  • ND4J:为Java提供张量操作。
  • Jetty:用于设置Web端点。
  • Cloud DataFlow:在GCP上为批量预测提供自动扩展。

我使用如下所示的pom.xml将它们导入到我的项目中。对于DL4J,使用Keras时需要core和modelimport库。

<dependencies>
  <dependency>     
    <groupId>org.deeplearning4j</groupId>     
    <artifactId>deeplearning4j-core</artifactId>
    <version>1.0.0-beta2</version>   
  </dependency>        
  <dependency>     
    <groupId>org.deeplearning4j</groupId>     
    <artifactId>deeplearning4j-modelimport</artifactId>     
    <version>1.0.0-beta2</version>   
  </dependency>                      
  <dependency>     
    <groupId>org.nd4j</groupId>     
    <artifactId>nd4j-native-platform</artifactId>
    <version>1.0.0-beta2</version>   
  </dependency>
  <dependency>     
    <groupId>org.eclipse.jetty</groupId>     
    <artifactId>jetty-server</artifactId>     
    <version>9.4.9.v20180320</version>  
  </dependency>  <dependency>     
    <groupId>com.google.cloud.dataflow</groupId>     
    <artifactId>google-cloud-dataflow-java-sdk-all</artifactId> 
    <version>2.2.0</version>    
  </dependency>
</dependencies>

我在Eclipse中设置了我的项目,一旦我正确配置了pom文件,就不需要额外的设置了。

使用DL4J进行Keras预测

现在我们已经设置了库,我们可以开始使用Keras模型进行预测。我编写了下面的脚本来检验加载Keras模型并对样本数据集进行预测。第一步是从h5文件加载模型。接下来,我定义长度为10的1D张量并生成随机二进制值。最后一步是调用模型上的输出方法以生成预测。由于我的模型有一个输出节点,我使用getDouble(0)返回模型的输出。

// imports
import org.deeplearning4j.nn.modelimport.keras.KerasModelImport;
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.factory.Nd4j;
import org.nd4j.linalg.io.ClassPathResource;
// load the model
String simpleMlp =new ClassPathResource(
                          "games.h5").getFile().getPath();
MultiLayerNetwork model = KerasModelImport.
                    importKerasSequentialModelAndWeights(simpleMlp);
// make a random sample
int inputs =10;
INDArray features = Nd4j.zeros(inputs);
for (int i=0; i<inputs; i++)
    features.putScalar(new int[] {i}, Math.random() <0.5 ?0 :1);
// get the prediction
double prediction = model.output(features).getDouble(0);

使用DL4J时熟悉的关键概念之一是张量。Java没有用于高效张量选项的内置库,所以要用NDJ4。它提供了N维数组,它提供了在Java中实现深度学习后端的n维数组。要在张量对象中设置一个值,需要向张量传递一个提供n维索引的整数数组,以及要设置的值。由于我使用的是1维张量,因此数组长度为1。

模型对象提供predict 和output方法。predict方法返回类的预测(0或1),而output方法返回连续标签,类似于scikit-learn中的predict_proba。

实时预测

现在我们已经在Java中运行了Keras模型,我们可以开始提供模型预测。我们将采用的第一种方法是使用Jetty在Web上设置端点以提供模型预测。

Jetty设置完整代码:https://github.com/bgweber/DeployKeras/blob/master/JettyDL4J.java

模型端点作为单个类实现,用于加载Keras模型并提供预测。它实现了Jetty的AbstractHandler接口以提供模型结果。以下代码展示了如何将Jetty服务设置为在端口8080上运行,并实例化JettyDL4J类,该类在构造函数中加载Keras模型。

// Setting up the web endpoint
Server server =new Server(8080);
server.setHandler(new JettyDL4J());
server.start();
server.join();
// Load the Keras model
public JettyDL4J()throws Exception { 
    String p=new ClassPathResource("games.h5").getFile().getPath();
    model=KerasModelImport.importKerasSequentialModelAndWeights(p);
}

用于管理Web请求的程序如下面的代码片段所示。传入的参数(G1,G2,…,G10)被转换为1维张量对象并传递给Keras模型的输出方法。然后将请求标记为已处理,并将预测作为字符串返回。

// Entry point for the model prediction request
public void handle(String target,Request baseRequest,
    HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException {
  // create a dataset from the input parameters
  INDArray features = Nd4j.zeros(inputs);
  for (int i=0; i<inputs; i++)
      features.putScalar(new int[] {i},  Double.parseDouble(
                          baseRequest.getParameter("G" + (i +1))));
  // output the estimate
  double prediction = model.output(features).getDouble(0);
  response.setStatus(HttpServletResponse.SC_OK);
  response.getWriter().println("Prediction: " + prediction);
  baseRequest.setHandled(true);
}

运行该类时,它会在端口8080上设置一个端点。您可以通过将浏览器指向以下URL来调用模型服务:

// Request
http://localhost:8080/?G1=1&G2=0&G3=1&G4=1&G5=0&G6=1&G7=0&G8=1&G9=1&G10=1
// Result
Prediction:0.735433042049408

结果是一个Keras模型,你现在可以实时调用它以从深度学习模型中获取预测。对于生产系统,你需要在Jetty端点前设置服务,而不是直接在Web上公开端点。

批量预测

Keras模型的另一个用例是批量预测,你可能需要为数百万条记录应用估算值。可以使用Keras模型直接在Python中事先这一点,但此方法的可扩展性受到限制。我将展示如何使用Google的DataFlow将预测应用于使用完全托管管道的海量数据集。

使用DataFlow,你可以指定要对数据集执行的操作的图,其中源和目标数据集可以是关系数据库,消息传递服务,应用程序数据库和其他服务。这些图可以作为批处理操作执行,其中基础架构启动并处理大型数据集然后关闭,或者以流模式运行,维持基础架构并且请求到达时处理。在这两种情况下,该服务都将自动调整以满足需求。它完全可以管理,非常适合可以独立执行的大型计算。

用于批量深度学习的DataFlow DAG

我的DataFlow流程中操作DAG如上所示。第一步是为模型创建数据集以进行评分。在这个例子中,我从我的样本CSV总加载值,而在实践中我通常使用BigQuery作为源和同步的模型预测。下一步是转换,它将TableRow对象作为输入,将行转换为1维张量,将模型应用于每个张量,并创建具有预测值的新输出TableRow。

DAG的完整代码:https://github.com/bgweber/DeployKeras/blob/master/DataFlowDL4J.java

此管道中的关键步骤是Keras Predict转换(如下面的代码片段所示)。转换对一组对象进行操作然后返回一组对象。在转换器中,你可以定义诸如Keras模型之类的对象,这些对象在转换器中定义的每个流程元素步骤被共享。结果是模型为每个转换器加载一次,而不是为每个需要预测的记录加载一次。

// Apply the transform to the pipeline
.apply("Keras Predict",new PTransform<PCollection<TableRow>,
                                       PCollection<TableRow>>() {      
  // Load the model in the transformer
  public PCollection<TableRow> expand(PCollection<TableRow> input) {                       
    final int inputs =10;
    final MultiLayerNetwork model;      
    try {       
     String p= newClassPathResource("games.h5").getFile().getPath();
     model=KerasModelImport.importKerasSequentialModelAndWeights(p);
  }            
  catch (Exception e) {
     throw new RuntimeException(e);
  }
  // create a DoFn for applying the Keras model to instances 
  return input.apply("Pred",ParDo.of(new DoFn<TableRow,TableRow>(){
    @ProcessElement
    public void processElement(ProcessContext c)throws Exception {
       ... // Apply the Keras model
    }}));        
}})

流程元素方法的代码如下所示。它读取输入记录,从表格行创建张量,应用模型,然后保存记录。输出行包含预测值和实际值。

// get the record to score
  TableRow row = c.element();     
  // create the feature vector                  
  INDArray features = Nd4j.zeros(inputs);                
  for (int i=0; i<inputs; i++)                  
    features.putScalar(new int[] {i},
          Double.parseDouble(row.get("G" + (i+1)).toString()));                                             
  // get the prediction                 
  double estimate = model.output(features).getDouble(0);             
  // save the result                 
  TableRow prediction =new TableRow();                   
  prediction.set("actual", row.get("actual"));                 
  prediction.set("predicted", estimate);                       
  c.output(prediction);

在本教程中排除了CSV加载和BigQuery编写代码块,因为你可能正在使用不同的端点。如果想尝试运行DAG,可以在GitHub上找到代码和CSV 。要将结果保存到BigQuery,需要设置tempLocation程序参数,如下所示:

--tempLocation=gs://your-gs-bucket/temp-dataflow-location

运行DAG后,将在BigQuery中创建一个新表,其中包含数据集的实际值和预测值。下图显示了来自Keras模型应用程序的示例数据点。

BigQuery中的预测结果

将DataFlow与DL4J一起使用的结果是,你可以使用自动扩展基础架构为批量预测评分数百万条记录。

结论

随着深度学习越来越受欢迎,越来越多的语言和环境支持这些模型。随着库开始标准化模型格式,让使用单独的语言进行模型训练和模型部署成为可能。这篇文章展示了,用Python中Keras库训练的神经网络可以使用Java中的DL4J库进行批量和实时的预测

本文分享自微信公众号 - ATYUN订阅号(atyun_com),作者:yxy

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-08-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 不用多进程的Python十倍速并行技巧(下)

    上一篇我们学习了三种不易用Python多处理表示的工作负载基准测试的其中两种,并比较了Ray、Python多处理和串行Python代码。今天这一篇我们来聊聊第三...

    AiTechYun
  • 使用Keras集成卷积神经网络的入门级教程

    在统计学和机器学习中,组合使用多种学习算法往往比单独的任何的学习算法更能获得好的预测性能。与统计力学中的统计集成不同(通常是无穷大),机器学习的集成由具体的有限...

    AiTechYun
  • 语言生成实战:自己训练能讲“人话”的神经网络(下)

    在昨天的学习当中,我们了解了培养一个会说话的语言生成模型所需要的如何创建数据集这一模块,今天我们继续学习构建语言生成模型。

    AiTechYun
  • 【Python环境】基于 Python 和 Scikit-Learn 的机器学习介绍

    你好,%用户名%! 我叫Alex,我在机器学习和网络图分析(主要是理论)有所涉猎。我同时在为一家俄罗斯移动运营商开发大数据产品。这是我第一次在网上写文章,不喜勿...

    陆勤_数据人网
  • 三值网络--Trained Ternary Quantization

    Trained Ternary Quantization ICLR 2017 https://github.com/TropComplique/traine...

    用户1148525
  • NSURLSession 所有的都在这里(二)

    Mr.RisingSun
  • GCAC05 补充2.5 onion routing

    洋葱路由器是一个类似于P2P原理的代理服务器,所有安装了洋葱路由的用户既是代理服务器的使用者也是代理服务器的提供者,洋葱路由器是由志愿者,花费自己的带宽建立起来...

    安包
  • 研发:Idea工具因为版本工具设置问题,导致全是红色

    I have the current status of my files in the folders coloured red/brown. I tried...

    heidsoft
  • 教程 | 如何利用TensorFlow.js部署简单的AI版「你画我猜」图像识别应用

    我们将使用卷积神经网络(CNN)来识别不同类型的手绘图像。这个卷积神经网络将在 Quick Draw 数据集(https://github.com/google...

    机器之心
  • 快速开启你的第一个项目:TensorFlow项目架构模板

    机器之心

扫码关注云+社区

领取腾讯云代金券