将浮点数组发送到部署在 Google Cloud ML-Engine 上的 Tensorflow 模型

Send Float Array to Tensorflow Model Deployed on Google Cloud ML-Engine

我创建了一个虚拟模型,returns 数组输入到它并将它部署在 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];
}

cloud 期望预测的形式 google 是 (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);

然后我调用这个发送请求和returns结果的函数

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 次方左右)。

在模型端,处理请求的模型的服务输入函数(使用自定义张量流估计器创建)是

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 字符串传递,因为 google 云应该根据 "b64" 键自动解码 base64 字符串,并且在从Python.

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

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

Primitive values are translated to (or from) sequences of bytes according to the buffer's current byte order, which may be retrieved and modified via the order methods. Specific byte orders are represented by instances of the ByteOrder class. The initial order of a byte buffer is always BIG_ENDIAN.

但是 TensorFlow 的 decode_raw 默认为小端

little_endian: An optional bool. Defaults to True. Whether the input bytes are in little-endian order. Ignored for out_type values that are stored in a single byte like 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
    )

(我对函数做了一些其他更改,如单独 中所述)