web-dev-qa-db-ja.com

base64でエンコードされた画像をTensorflow予測に渡す方法は?

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でトレーニングする必要がありますか?

7
user3567174

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..."}]}
2
rhaertel80

画像をモデルに送信する効率的な方法が必要な場合(必ずしも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 を参照してください。評価入力関数は次のようになります。

2
Lak

@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のバッチに追加します。

1
Milad Shahidi