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/

相关文章:

php - 从多维数组中删除重复值

java - Mybatis:如何将<include refid/>与mapper.xml中的String连接?

java - Button2 正在做 Button1 应该做的事情

android - 存储需要在我的移动应用程序中检索的数据

java - android上的数组加法

java - 如何用数组引用这里

java - 使用 java 根据 LinkedHashMap 中的键与另一个 LinkedHashMap 进行值比较

java - Grails 保存方法给出错误拒绝值

Java:新枚举是旧枚举的子集

android - Viewpager 初始 fragment 中的操作项未显示