Float32の3次元配列を渡すことで予測を実行できるgoogle-cloud-mlモデルがあります...
{ 'instances' [ { 'input' : '[ [ [ 0.0 ], [ 0.5 ], [ 0.8 ] ] ... ] ]' } ] }
ただし、これは画像を送信するための効率的な形式ではないため、base64でエンコードされたpngまたはjpegを渡したいと思います。 このドキュメント それを行うことについて話しますが、明確ではないのは、jsonオブジェクト全体がどのように見えるかです。 { 'b64' : 'x0welkja...' }
の代わりに'[ [ [ 0.0 ], [ 0.5 ], [ 0.8 ] ] ... ] ]'
を使用し、囲んでいる「インスタンス」と「入力」を同じままにしますか?または他の構造?または、テンソルフローモデルをbase64でトレーニングする必要がありますか?
TensorFlowモデルは、base64データでトレーニングする必要はありません。トレーニンググラフはそのままにしておきます。ただし、モデルをエクスポートするときは、pngまたはjpeg(または小さい場合はraw)データを受け入れることができるモデルをエクスポートする必要があります。次に、モデルをエクスポートするときに、必ず_bytes
で終わる出力の名前を使用する必要があります。これは、base64でエンコードされたデータを送信することをCloudMLEngineに通知します。すべてをまとめると、次のようになります。
from tensorflow.contrib.saved_model.python.saved_model import utils
# Shape of [None] means we can have a batch of images.
image = tf.placeholder(shape=[None], dtype=tf.string)
# Decode the image.
decoded = tf.image.decode_jpeg(image, channels=3)
# Do the rest of the processing.
scores = build_model(decoded)
# The input name needs to have "_bytes" suffix.
inputs = {'image_bytes': image}
outputs = {'scores': scores}
utils.simple_save(session, export_dir, inputs, outputs)
送信するリクエストは次のようになります。
{"instances": [{"b64": "x0welkja..."}]}
画像をモデルに送信する効率的な方法が必要な場合(必ずしもbase-64でエンコードする必要はありません)、画像をGoogle Cloud Storageにアップロードしてから、モデルにGCSを読み取らせることをお勧めします。このように、画像サイズに制限されず、GCS APIが提供するマルチパート、マルチスレッド、再開可能なアップロードなどを利用できます。
TensorFlowのtf.read_fileはGCSから直接オフになります。これを行うサービングinput_fnの例を次に示します。 CMLEへのリクエストでは、画像のURL(gs://bucket/some/path/to/image.jpg)が送信されます。
def read_and_preprocess(filename, augment=False):
# decode the image file starting from the filename
# end up with pixel values that are in the -1, 1 range
image_contents = tf.read_file(filename)
image = tf.image.decode_jpeg(image_contents, channels=NUM_CHANNELS)
image = tf.image.convert_image_dtype(image, dtype=tf.float32) # 0-1
image = tf.expand_dims(image, 0) # resize_bilinear needs batches
image = tf.image.resize_bilinear(image, [HEIGHT, WIDTH], align_corners=False)
#image = tf.image.per_image_whitening(image) # useful if mean not important
image = tf.subtract(image, 0.5)
image = tf.multiply(image, 2.0) # -1 to 1
return image
def serving_input_fn():
inputs = {'imageurl': tf.placeholder(tf.string, shape=())}
filename = tf.squeeze(inputs['imageurl']) # make it a scalar
image = read_and_preprocess(filename)
# make the outer dimension unknown (and not 1)
image = tf.placeholder_with_default(image, shape=[None, HEIGHT, WIDTH, NUM_CHANNELS])
features = {'image' : image}
return tf.estimator.export.ServingInputReceiver(features, inputs)
上記のrhaertel80の提案と同様に、トレーニングコードは実際の画像をトレーニングします。トレーニングの内容については、 https://github.com/GoogleCloudPlatform/training-data-analyst/blob/master/courses/machine_learning/deepdive/08_image/flowersmodel/trainer/task.py#L27 を参照してください。評価入力関数は次のようになります。
@Lakの回答(Lakに感謝)を使用して、1つのjsonファイルで複数のインスタンスのオンライン予測を取得しようとしましたが、次のエラーが発生し続けました(テストjsonに2つのインスタンスがあったため、形状[2])。
入力ファイル名テンソルはスカラーでなければなりませんが、形状は[2]でした。
問題は、MLエンジンがすべてのインスタンスをまとめてバッチ処理し、それらをサービングインパーレシーバー関数に渡すことですが、@ Lakのサンプルコードは、入力が単一のインスタンスであると想定しています(jsonに単一のインスタンスがある場合は実際に正常に機能します)。入力のバッチを処理できるようにコードを変更しました。私はそれが誰かを助けることを願っています:
def read_and_preprocess(filename):
image_contents = tf.read_file(filename)
image = tf.image.decode_image(image_contents, channels=NUM_CHANNELS)
image = tf.image.convert_image_dtype(image, dtype=tf.float32) # 0-1
return image
def serving_input_fn():
inputs = {'imageurl': tf.placeholder(tf.string, shape=(None))}
filename = inputs['imageurl']
image = tf.map_fn(read_and_preprocess, filename, dtype=tf.float32)
# make the outer dimension unknown (and not 1)
image = tf.placeholder_with_default(image, shape=[None, HEIGHT, WIDTH, NUM_CHANNELS])
features = {'image': image}
return tf.estimator.export.ServingInputReceiver(features, inputs)
主な変更点は、1)入力テンソルをsqueeze
しない(jsonにインスタンスが1つしかない特別な場合に問題が発生する)こと、および2)tf.map_fn
を使用してread_and_preprocess
関数を入力画像URLのバッチに追加します。