【问题标题】:Loading sklearn model in Java. Model created with DNNClassifier in python在 Java 中加载 sklearn 模型。在 python 中使用 DNNClassifier 创建的模型
【发布时间】:2017-09-21 18:37:48
【问题描述】:

目标是在 Java 中使用tensorflow.contrib.learn.learn.DNNClassifier 打开一个在 python 中创建/训练的模型。

目前的主要问题是知道“张量”的名称,以便在 java 中的 session runner 方法中给出。

我在 python 中有这个测试代码:

    from __future__ import division, print_function, absolute_import
import tensorflow as tf
import pandas as pd
import tensorflow.contrib.learn as learn
import numpy as np
from sklearn import metrics
from sklearn.cross_validation import train_test_split
from tensorflow.contrib import layers
from tensorflow.contrib.learn.python.learn.utils import input_fn_utils
from tensorflow.python.ops import array_ops
from tensorflow.python.framework import dtypes
from tensorflow.python.util.compat import as_text

print(tf.VERSION)

df = pd.read_csv('../NNNormalizeData-out.csv')

inputs = []
target = []

y=0;    
for x in df.columns:
    if y != 35 :
        #print("added %d" %y)
        inputs.append(x)
    else :
        target.append(x)
    y+=1

total_inputs,total_output = df.as_matrix(inputs).astype(np.float32),df.as_matrix([target]).astype(np.int32)

train_inputs, test_inputs, train_output, test_output = train_test_split(total_inputs, total_output, test_size=0.2, random_state=42)

feature_columns = [tf.contrib.layers.real_valued_column("", dimension=train_inputs.shape[1],dtype=tf.float32)]
#target_column = [tf.contrib.layers.real_valued_column("output", dimension=train_output.shape[1])]

classifier = learn.DNNClassifier(hidden_units=[10, 20, 5], n_classes=5
                                 ,feature_columns=feature_columns)

classifier.fit(train_inputs, train_output, steps=100)

#Save Model into saved_model.pbtxt file (possible to Load in Java)
tfrecord_serving_input_fn = tf.contrib.learn.build_parsing_serving_input_fn(layers.create_feature_spec_for_parsing(feature_columns))  
classifier.export_savedmodel(export_dir_base="test", serving_input_fn = tfrecord_serving_input_fn,as_text=True)


# Measure accuracy
pred = list(classifier.predict(test_inputs, as_iterable=True))
score = metrics.accuracy_score(test_output, pred)
print("Final score: {}".format(score))

# test individual samples 
sample_1 = np.array( [[0.37671986791414125,0.28395908337619136,-0.0966095873607713,-1.0,0.06891621389763203,-0.09716678086712205,0.726029084013637,4.984689881073479E-4,-0.30296253267499107,-0.16192917054985334,0.04820256230479658,0.4951319883569152,0.5269983894210499,-0.2560313828048315,-0.3710980821053321,-0.4845867212612598,-0.8647234314469595,-0.6491591208322198,-1.0,-0.5004549422844073,-0.9880910165770813,0.5540293108747256,0.5625990251930839,0.7420121698556554,0.5445551415657979,0.4644276850235627,0.7316976292340245,0.636690006814346,0.16486621649984112,-0.0466018967678159,0.5261100063227044,0.6256168612312738,-0.544295484930702,0.379125782517193,0.6959368575211544]], dtype=float)
sample_2 = np.array( [[1.0,0.7982741870963959,1.0,-0.46270838239235024,0.040320274521029376,0.443451913224413,-1.0,1.0,1.0,-1.0,0.36689718911339564,-0.13577379160035796,-0.5162916256414466,-0.03373651520104648,1.0,1.0,1.0,1.0,0.786999801054777,-0.43856035121103853,-0.8199093927945158,1.0,-1.0,-1.0,-0.1134921695894473,-1.0,0.6420892436196663,0.7871737734493178,1.0,0.6501788845358409,1.0,1.0,1.0,-0.17586627413625022,0.8817194210401085]], dtype=float)

pred = list(classifier.predict(sample_2, as_iterable=True))
print("Prediction for sample_1 is:{} ".format(pred))

pred = list(classifier.predict_proba(sample_2, as_iterable=True))
print("Prediction for sample_2 is:{} ".format(pred))

创建了一个 model_saved.pbtxt 文件。

我尝试使用以下代码在 Java 中加载此模型:

    public class HelloTF {
    public static void main(String[] args) throws Exception {
        SavedModelBundle bundle=SavedModelBundle.load("/java/workspace/APIJavaSampleCode/tfModels/dnn/ModelSave","serve");
        Session s = bundle.session();

        double[] inputDouble = {1.0,0.7982741870963959,1.0,-0.46270838239235024,0.040320274521029376,0.443451913224413,-1.0,1.0,1.0,-1.0,0.36689718911339564,-0.13577379160035796,-0.5162916256414466,-0.03373651520104648,1.0,1.0,1.0,1.0,0.786999801054777,-0.43856035121103853,-0.8199093927945158,1.0,-1.0,-1.0,-0.1134921695894473,-1.0,0.6420892436196663,0.7871737734493178,1.0,0.6501788845358409,1.0,1.0,1.0,-0.17586627413625022,0.8817194210401085};
        float [] inputfloat=new float[inputDouble.length];
        for(int i=0;i<inputfloat.length;i++)
        {
            inputfloat[i]=(float)inputDouble[i];
        }
        Tensor inputTensor = Tensor.create(new long[] {35}, FloatBuffer.wrap(inputfloat) );

        Tensor result = s.runner()
                .feed("input_example_tensor", inputTensor)
                .fetch("dnn/multi_class_head/predictions/probabilities")
                .run().get(0);


         float[] m = new float[5];
         float[] vector = result.copyTo(m);
         float maxVal = 0;
         int inc = 0;
         int predict = -1;
         for(float val : vector) 
         {
             System.out.println(val+"  ");
             if(val > maxVal) {
                 predict = inc;
                 maxVal = val;
             }
             inc++;
         }
         System.out.println(predict);



    }
} 

我在 .run().get(0); 上遇到错误;行:

Exception in thread "main" org.tensorflow.TensorFlowException: Output 0 of type float does not match declared output type string for node _recv_input_example_tensor_0 = _Recv[_output_shapes=[[-1]], client_terminated=true, recv_device="/job:localhost/replica:0/task:0/cpu:0", send_device="/job:localhost/replica:0/task:0/cpu:0", send_device_incarnation=3663984897684684554, tensor_name="input_example_tensor:0", tensor_type=DT_STRING, _device="/job:localhost/replica:0/task:0/cpu:0"]()
    at org.tensorflow.Session.run(Native Method)
    at org.tensorflow.Session.access$100(Session.java:48)
    at org.tensorflow.Session$Runner.runHelper(Session.java:285)
    at org.tensorflow.Session$Runner.run(Session.java:235)
    at tensorflow.HelloTF.main(HelloTF.java:35)

【问题讨论】:

  • 那么,您无法让 Saver 在您的 Python 代码中创建可识别的模型吗?我从 Saver 生成 .data 和 .meta 文件,而不是您提到的 .pbtxt 。
  • 我已经解决了问题...回答 2。我还能够加载(在 Java 中)保存在 .pbtxt 中的 keras 模型。诀窍是使用 tensorboard 查看图形以提取输入和输出张量的正确名称。 ...

标签: java tensorflow tensorflow-serving sklearn-pandas


【解决方案1】:

好的,我终于解决了:主要问题是在 java 中使用的输入名称是“dnn/input_from_feature_columns/input_from_feature_columns/concat”而不是“input_example_tensor”。

我使用图形导航发现了这一点:tensorboard --logdir=D:\python\Workspace\Autoencoder\src\dnn\ModelSave

这里是java代码:

public class HelloTF {
public static void main(String[] args) throws Exception {
    SavedModelBundle bundle=SavedModelBundle.load("/java/workspace/APIJavaSampleCode/tfModels/dnn/ModelSave","serve");
    Session s = bundle.session();

    double[] inputDouble = {1.0,0.7982741870963959,1.0,-0.46270838239235024,0.040320274521029376,0.443451913224413,-1.0,1.0,1.0,-1.0,0.36689718911339564,-0.13577379160035796,-0.5162916256414466,-0.03373651520104648,1.0,1.0,1.0,1.0,0.786999801054777,-0.43856035121103853,-0.8199093927945158,1.0,-1.0,-1.0,-0.1134921695894473,-1.0,0.6420892436196663,0.7871737734493178,1.0,0.6501788845358409,1.0,1.0,1.0,-0.17586627413625022,0.8817194210401085};
    float [] inputfloat=new float[inputDouble.length];
    for(int i=0;i<inputfloat.length;i++)
    {
        inputfloat[i]=(float)inputDouble[i];
    }
FloatBuffer.wrap(inputfloat) );
    float[][] data= new float[1][35];
    data[0]=inputfloat;
    Tensor inputTensor=Tensor.create(data);


    Tensor result = s.runner()
            .feed("dnn/input_from_feature_columns/input_from_feature_columns/concat", inputTensor)
            //.feed("input_example_tensor", inputTensor)
            //.fetch("tensorflow/serving/classify")
            .fetch("dnn/multi_class_head/predictions/probabilities")
            //.fetch("dnn/zero_fraction_3/Cast")
            .run().get(0);


     float[][] m = new float[1][5];
     float[][] vector = result.copyTo(m);
     float maxVal = 0;
     int inc = 0;
     int predict = -1;
     for(float val : vector[0]) 
     {
         System.out.println(val+"  ");
         if(val > maxVal) {
             predict = inc;
             maxVal = val;
         }
         inc++;
     }
     System.out.println(predict);



}

}

我已经测试了输出:

植物侧:

Prediction for sample_2 is:[3] 
Prediction for sample_2 is:[array([ 0.17157166,  0.24475774,  0.16158019,  0.24648622,  0.17560424], dtype=float32)] 

Java 端:

0.17157166  
0.24475774  
0.16158019   
0.24648622  
0.17560424  
3

【讨论】:

  • 顺便说一下,NNNormalizeData-out.csv 是一个 35 个输入和 1 个输出,有 5 个类别“0”、“1”、“2”、“3”、“4”。它是关于金融时间序列的,0 表示强势下跌,1- 弱势下跌 - 2 中性,3 和 4 表示上涨......现在我可以专注于模型本身:-)
【解决方案2】:

错误消息提供了一个线索:模型中名为 "input_example_tensor" 的张量预期具有 string 内容,而您提供了 float 值。

从张量的名称和你的代码来看,我猜你输入的张量是defined in input_fn_utils.py。这个张量被传递给tf.parse_example() op,它需要一个tf.train.Example 协议缓冲区的向量,序列化为字符串。

【讨论】:

  • 您可以在python中看到用于创建模型的代码,输入定义如下:feature_columns = [tf.contrib.layers.real_valued_column("", dimension=train_inputs.shape[1], dtype=tf.float32)]
  • 那我该怎么办?发送一个浮点数用逗号分隔的字符串 ?
  • 如果您确实在使用@mrry 指出的内容,那么正如他所说,它需要一个序列化的tf.train.Example 协议缓冲区。如果您使用的是 Maven,那么您可以使用 org.tensorflow:proto 包来获取所有协议缓冲区并构造和序列化示例(参见 javadoc
  • 我只是下载 tensorflow jar 和 dll 没有 maven ...(不知道如何处理该链接)我会寻找 org.tensorflow:proto 的 .jar 很难相信没有人尝试在 java 中运行使用 DNNClassifier 创建的模型......互联网上没有任何内容。刚发现有人问。我被这个问题困扰了两个星期,许多其他问题已经被克服(比如使用 DNNClassifier 获取 saved_model.pbtxt ......我认为这将是一个有很多访问者的页面:-))
  • Java 支持非常新,我们仍在为其添加功能!不幸的是,JAR 不包含创建Example 原型所需的代码。最简单的选择可能是下载 example.proto 源并在其上运行 Protobuf 编译器以生成必要的 Java 类。
【解决方案3】:

在 Tensorflow 1.1 上没有feed("input_example_tensor", inputTensor) 时出现错误。

但我发现example.proto 可以作为“input_example_tensor”输入,尽管花了很多时间来弄清楚如何为序列化协议缓冲区创建字符串张量。

这就是我创建inputTensor的方式。

org.tensorflow.example.Example.Builder example = org.tensorflow.example.Example.newBuilder();   
/* set some features to example... */

Tensor exampleTensor = Tensor.create(example.build().toByteArray());
// Here, the shape of exampleTensor is not specified yet.

// Set the shape to feed this as "input_example_tensor"
Graph g = bundle.graph(); 
Output examplePlaceholder =
                  g.opBuilder("Placeholder", "example")
                  .setAttr("dtype", exampleTensor.dataType())                        
                      .build().output(0);
Tensor shapeTensor = Tensor.create(new long[]{1}, IntBuffer.wrap(new int[]{1}));                      
Output shapeConst = g.opBuilder("Const", "shape")
                      .setAttr("dtype", shapeTensor.dataType())
                      .setAttr("value", shapeTensor)
                      .build().output(0);
Output shaped = g.opBuilder("Reshape", "output").addInput(examplePlaceholder).addInput(shapeConst).build().output(0);


Tensor inputTensor = s.runner().feed(examplePlaceholder, exampleTensor).fetch(shaped).run().get(0);                   
// Now, inputTensor has shape of [1] and ready to feed.     

【讨论】:

    【解决方案4】:

    您在 .feed() 和 .fetch() 中的参数应该与您的输入和输出数据类型匹配。

    您可以查看已保存的model.pbtxt 文件。有关于您的参数及其输入/输出类型的详细信息。

    例如,

    我的java代码

    Tensor result = s.runner()
            .feed("ParseExample/ParseExample", inputTensor)
            .fetch("dnn/binary_logistic_head/predictions/probabilities")
            .run().get(0);
    

    我的 savedModel.pbtxt(其中一部分)

    node {
      name: "ParseExample/ParseExample"
      op: "ParseExample"
      input: "input_example_tensor"
      input: "ParseExample/ParseExample/names"
      input: "ParseExample/ParseExample/dense_keys_0"
      input: "ParseExample/Const"
      attr {
        key: "Ndense"
        value {
          i: 1
        }
      }
      attr {
        key: "Nsparse"
        value {
          i: 0
        }
      }
      attr {
        key: "Tdense"
        value {
          list {
            type: DT_FLOAT
          }
        }
      }
      attr {
        key: "_output_shapes"
        value {
          list {
            shape {
              dim {
                size: -1
              }
              dim {
                size: 2
              }
            }
          }
        }
      }
      attr {
        key: "dense_shapes"
        value {
          list {
            shape {
              dim {
                size: 2
              }
            }
          }
        }
      }
      attr {
        key: "sparse_types"
        value {
          list {
          }
        }
      }
    }
      outputs {
        key: "scores"
        value {
          name: "dnn/binary_logistic_head/predictions/probabilities:0"
          dtype: DT_FLOAT
          tensor_shape {
            dim {
              size: -1
            }
            dim {
              size: 2
            }
          }
        }
      }
    

    它们都与我的数据类型 float 兼容。

    【讨论】:

    • 与下面的答案相同(带有完整代码)...如果您有更大且更复杂的模型,则很难在 .pbtxt 文件中找到名称。使用tensorboard和看图更容易...
    猜你喜欢
    • 1970-01-01
    • 2018-04-11
    • 1970-01-01
    • 2018-08-03
    • 2022-12-04
    • 2020-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多