Nic*_*reg 9 gcloud tensorflow-serving google-cloud-ml tensorflow-estimator
谷歌云文档(请参阅预测输入中的二进制数据)指出:
必须将编码的字符串格式化为JSON对象,并使用名为b64的单个键.以下Python示例使用base64库对原始JPEG数据的缓冲区进行编码以生成实例:
Run Code Online (Sandbox Code Playgroud){"image_bytes":{"b64": base64.b64encode(jpeg_data)}}在TensorFlow模型代码中,您必须为输入和输出张量命名别名,以便它们以'_bytes'结尾.
我想了解更多有关此过程如何在Google云端运行的信息.
ml-engine是否会自动将"b64"字符串之后的任何内容解码为字节数据?
当请求具有此嵌套结构时,它是否仅将"b64"部分传递给服务输入函数并删除"image_bytes"键?
每个请求是单独传递给服务输入函数还是已经批处理?
我们是否在服务输入函数返回的ServingInputReceiver中定义输入输出别名?
我发现无法创建服务输入函数,该函数使用此嵌套结构来定义要素占位符.我只在我的中使用"b64"而且我不确定gcloud ml-engine在接收请求时会做什么.
另外,当在本地使用预测时gcloud ml-engine local predict,发送具有嵌套结构的请求失败,(意外的密钥image_bytes,因为它没有在服务输入函数中定义).但是在预测使用时gcloud ml-engine predict,即使服务输入函数不包含对"image_bytes"的引用,使用嵌套结构发送请求也能正常工作.当忽略"image_bytes"并传入"b64"时,gcloud预测也有效.
服务输入功能的示例
def serving_input_fn():
feature_placeholders = {'b64': tf.placeholder(dtype=tf.string,
shape=[None],
name='source')}
single_image = tf.decode_raw(feature_placeholders['b64'], tf.float32)
inputs = {'image': single_image}
return tf.estimator.export.ServingInputReceiver(inputs, feature_placeholders)
Run Code Online (Sandbox Code Playgroud)
我使用图像给出了示例,但我假设同样适用于作为字节和base64编码发送的所有类型的数据.
有很多stackoverflow问题,其中包含对包含"_bytes"信息片段的需求的引用,但我觉得如果有人可以详细解释一下这些内容会有什么用,那么我就不会如此受欢迎格式化请求时错过.
Stackoverflow关于此主题的问题
rha*_*l80 15
为了帮助澄清您的一些问题,请允许我从预测请求的基本解剖开始:
{"instances": [<instance>, <instance>, ...]}
Run Code Online (Sandbox Code Playgroud)
instanceJSON对象在哪里(dict/map,我将在下文中使用Python术语"dict"),属性/键是输入的名称,其值包含该输入的数据.
云服务的作用(并gcloud ml-engine local predict使用与服务相同的底层库)是它采用dicts列表(可以被认为是数据行),然后将其转换为列表的字典(可以被认为是包含批量实例的柱状数据,其密钥与原始数据中的密钥相同.例如,
{"instances": [{"x": 1, "y": "a"}, {"x": 3, "y": "b"}, {"x": 5, "y": "c"}]}
Run Code Online (Sandbox Code Playgroud)
成为(内部)
{"x": [1, 3, 5], "y": ["a", "b", "c"]}
Run Code Online (Sandbox Code Playgroud)
此dict中的键(因此,在原始请求中的实例中)必须对应于传递给dict的dict的键ServingInputFnReceiver.从该示例中应该显而易见的是,服务"批量化"所有数据,这意味着所有实例作为单个批次被馈送到图中.这就是为什么输入形状的外部维度必须是None- 它是批量维度,并且在请求之前不知道(因为每个请求可能具有不同数量的实例).导出图形以接受上述请求时,您可以定义如下函数:
def serving_input_fn():
inputs = {'x': tf.placeholder(dtype=tf.int32, shape=[None]),
'y': tf.placeholder(dtype=tf.string, shape=[None]}
return tf.estimator.export.ServingInputReceiver(inputs, inputs)
Run Code Online (Sandbox Code Playgroud)
由于JSON不(直接)支持二进制数据,并且由于TensorFlow无法区分"字符串"和"字节",因此我们需要专门处理二进制数据.首先,我们需要以"_bytes"结尾所述输入的名称,以帮助区分文本字符串和字节字符串.使用上面的示例,假设y包含二进制数据而不是文本.我们将声明以下内容:
def serving_input_fn():
inputs = {'x': tf.placeholder(dtype=tf.int32, shape=[None]),
'y_bytes': tf.placeholder(dtype=tf.string, shape=[None]}
return tf.estimator.export.ServingInputReceiver(inputs, inputs)
Run Code Online (Sandbox Code Playgroud)
请注意,唯一改变的是使用y_bytes而不是y输入的名称.
接下来,我们需要实际base64编码数据; 在字符串可以接受的任何地方,我们可以使用像这样的对象:{"b64":""}.调整运行示例,请求可能如下所示:
{
"instances": [
{"x": 1, "y_bytes": {"b64": "YQ=="}},
{"x": 3, "y_bytes": {"b64": "Yg=="}},
{"x": 5, "y_bytes": {"b64": "Yw=="}}
]
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,服务完全按照以前的方式执行,但添加了一个步骤:在发送到TensorFlow之前,它会自动base64对字符串进行解码(并用字节"替换"{"b64":...}对象).所以TensorFlow实际上最终得到了一个像以前一样的dict:
{"x": [1, 3, 5], "y_bytes": ["a", "b", "c"]}
Run Code Online (Sandbox Code Playgroud)
(请注意,输入的名称未更改.)
当然,base64文本数据有点毫无意义; 你通常会这样做,例如,对于不能通过JSON以任何其他方式发送的图像数据,但我希望上面的例子足以说明这一点.
还有另一个要点:服务支持一种速记.如果您的TensorFlow模型只有一个输入,则无需在实例列表中的每个对象中不断重复该输入的名称.为了说明,想象一下只导出一个模型x:
def serving_input_fn():
inputs = {'x': tf.placeholder(dtype=tf.int32, shape=[None])}
return tf.estimator.export.ServingInputReceiver(inputs, inputs)
Run Code Online (Sandbox Code Playgroud)
"长形式"请求看起来像这样:
{"instances": [{"x": 1}, {"x": 3}, {"x": 5}]}
Run Code Online (Sandbox Code Playgroud)
相反,您可以用速记发送请求,如下所示:
{"instances": [1, 3, 5]}
Run Code Online (Sandbox Code Playgroud)
请注意,这甚至适用于base64编码数据.因此,例如,如果x我们只是导出y_bytes,而不是仅导出,我们可以简化以下请求:
{
"instances": [
{"y_bytes": {"b64": "YQ=="}},
{"y_bytes": {"b64": "Yg=="}},
{"y_bytes": {"b64": "Yw=="}}
]
}
Run Code Online (Sandbox Code Playgroud)
至:
{
"instances": [
{"b64": "YQ=="},
{"b64": "Yg=="},
{"b64": "Yw=="}
]
}
Run Code Online (Sandbox Code Playgroud)
在许多情况下,这只是一个小小的胜利,但它肯定有助于可读性,例如,当输入包含CSV数据时.
因此,将它完全适应您的特定场景,这是您的服务功能应该是什么样子:
def serving_input_fn():
feature_placeholders = {
'image_bytes': tf.placeholder(dtype=tf.string, shape=[None], name='source')}
single_image = tf.decode_raw(feature_placeholders['image_bytes'], tf.float32)
return tf.estimator.export.ServingInputReceiver(feature_placeholders, feature_placeholders)
Run Code Online (Sandbox Code Playgroud)
与您当前的代码有显着差异:
b64,但image_bytes(可以是任何结尾_bytes)feature_placeholders用作两个参数
ServingInputReceiver示例请求可能如下所示:
{
"instances": [
{"image_bytes": {"b64": "YQ=="}},
{"image_bytes": {"b64": "Yg=="}},
{"image_bytes": {"b64": "Yw=="}}
]
}
Run Code Online (Sandbox Code Playgroud)
或者,可选地,简而言之:
{
"instances": [
{"b64": "YQ=="},
{"b64": "Yg=="},
{"b64": "Yw=="}
]
}
Run Code Online (Sandbox Code Playgroud)
最后一个最后一点.gcloud ml-engine local predict并gcloud ml-engine predict根据传入的文件内容构造请求.请注意,文件的内容当前不是完整的有效请求,而是--json-instances文件的每一行都成为实例列表中的一个条目,这一点非常重要..特别是在你的情况下,文件看起来像(换行符在这里有意义):
{"image_bytes": {"b64": "YQ=="}}
{"image_bytes": {"b64": "Yg=="}}
{"image_bytes": {"b64": "Yw=="}}
Run Code Online (Sandbox Code Playgroud)
或等效的简写.gcloud将采用每一行并构建上面显示的实际请求.
| 归档时间: |
|
| 查看次数: |
943 次 |
| 最近记录: |