【问题标题】:Send Float Array to Tensorflow Model Deployed on Google Cloud ML-Engine将浮点数组发送到部署在 Google Cloud ML-Engine 上的 TensorFlow 模型
【发布时间】:2018-03-08 09:47:30
【问题描述】:

我创建了一个虚拟模型,该模型将数组输入返回给它并将其部署在 google-cloud ML-engine 上,以便我可以检查它如何解码请求中发送的音频。我无法以正确解码的方式将存储在浮点数组中的音频从 android 应用程序发送到模型。虽然从 Python 发送请求时我没有问题。

我提出如下要求: 音频被录制到

short[] inputBuffer = new short[RECORDING_LENGTH];

转换为浮点数组

float[] floatInputBuffer = new float[RECORDING_LENGTH];
for (int i = 0; i < RECORDING_LENGTH; ++i) {
    floatInputBuffer[i] = (float) inputBuffer[i];
}

谷歌云期望的预测形式是(see data encoding section)

{"instances": [{"b64": "X5ad6u"}, {"b64": "IA9j4nx"}]}

所以我将音频放入模仿此的地图中。

  public static String convertToBase64Bytes(float[] audio) {
    ByteBuffer byteBuffer = ByteBuffer.allocate(4 * audio.length);
    for (int i = 0; i < audio.length; i++) {
      float amplitude = audio[i];
      byteBuffer.putFloat(amplitude);
    }
    byte[] data = byteBuffer.array();
    String rtn = Base64.encodeToString(data, Base64.DEFAULT);
    return rtn;
  }

  String audioByteString = convertToBase64Bytes(floatInputBuffer);
  final ArrayList<HashMap<String, String>> requestList = new ArrayList<>();
  HashMap<String, String> singleRequest = new HashMap<>();
  singleRequest.put("b64", audioByteString);
  requestList.add(singleRequest);
  HashMap<String, ArrayList<HashMap<String, String>>> jsonRequest = new HashMap<>();
  jsonRequest.put("instances", requestList);

然后我调用这个发送请求并返回结果的函数

public String sendRequest(HashMap<String, ArrayList<HashMap<String, String>>> jsonRequest) throws Exception {
    HttpContent content = new JsonHttpContent(new JacksonFactory(), jsonRequest);
    HttpRequest request = requestFactory.buildRequest(method.getHttpMethod(), url, content);
    return request.execute().parseAsString();
}

检查模型的输出。数组的形状是正确的,但浮点值不正确。它们通常几乎为零(e 的 -26 次方左右)。

在模型端,处理请求的模型的服务输入函数(使用自定义 tensorflow Estimator 创建)是

def serving_input_fn():
    feature_placeholders = {'b64': tf.placeholder(dtype=tf.string,
                                                  shape=[None],
                                                  name='source')}
    audio_samples = tf.decode_raw(feature_placeholders['b64'], tf.float32)
    inputs = {'inarray': audio_samples}
    return tf.estimator.export.ServingInputReceiver(inputs, feature_placeholders)

我认为我错误地将编码的浮点数组作为 base64 字符串传递,因为由于“b64”键,谷歌云应该自动解码 base64 字符串,这在从 Python 发送请求时可以正常工作。

有谁知道如何将浮点数组从 android 发送到谷歌云上的模型,以便正确解码?

【问题讨论】:

    标签: java android arrays tensorflow google-cloud-ml


    【解决方案1】:

    这似乎是 BytesOrder/endian-ness 问题。来自ByteBuffer javadocs

    原始值根据缓冲区的当前字节顺序转换为(或从)字节序列,可以通过 order 方法检索和修改。特定的字节顺序由 ByteOrder 类的实例表示。字节缓冲区的初始顺序始终为 BIG_ENDIAN

    但 TensorFlow 的 decode_raw 默认为小端序

    little_endian:可选布尔值。 默认为真。输入字节是否为 little-endian 顺序。忽略存储在单个字节中的 out_type 值,如 uint8。

    解决方案是覆盖一个或另一个默认值。由于 ARM 处理器本身就是大端,也许在你的 Android 代码中坚持使用 BigEndian,并修改你的 TF 代码:

    def serving_input_fn():
        feature_placeholders = {
            'audio_bytes': tf.placeholder(
                dtype=tf.string,
                shape=[None],
                name='source'
             )
        }
        audio_samples = tf.decode_raw(
            feature_placeholders['audio_bytes'],
            tf.float32,
            little_endian=False
        )
        return tf.estimator.export.ServingInputReceiver(
            feature_placeholders,
            feature_placeholders
        )
    

    (我对该函数进行了一些其他更改,如单独的 SO post 中所述)

    【讨论】:

    • 有效!我不知道字节序是一回事,非常感谢。虽然我不明白为什么将 feature_placeholders 两次分配给 ServingInputReceiver 而不是 audio_samples,但我在另一篇文章中要求对此进行澄清,因此也无需在此解释。
    猜你喜欢
    • 2018-10-03
    • 2018-03-24
    • 1970-01-01
    • 2017-04-17
    • 2018-12-24
    • 2018-07-16
    • 1970-01-01
    • 2018-01-30
    • 2019-05-01
    相关资源
    最近更新 更多