java - 将 float 组发送到部署在 Google Cloud ML-Engine 上的 Tensorflow 模型

标签 java android arrays tensorflow google-cloud-ml

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

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

short[] inputBuffer = new short[RECORDING_LENGTH];

转换为 float 组

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"}]}

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

  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 估计器创建)是

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)

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

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

最佳答案

这似乎是 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
    )

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

关于java - 将 float 组发送到部署在 Google Cloud ML-Engine 上的 Tensorflow 模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49170040/

相关文章:

java - 如何将 JFace Treeviewer 中的数据选择解析到 RCP 中的另一个 View ?

java - 使用 Java 将分段函数转换为 CSV 文件

android - 如何使用不支持的 Ndef 写入/读取 NFC 卡?

javascript - 原生排序方法如何处理稀疏数组

c 编程 - 一位 parent 和 4 个 child 之间的双向沟通​​没有结果

javascript - 映射对象内的映射对象

java - 如何将 Swing JFrame 配置为 Spring bean?

java - 达到数字时换行

android - 需要将数据从一个 Activity 传递到另一个引用数据库

android - Dexguard 发布 apk 不显示 UI automator 中元素的资源 ID