Cloud ML オンライン予測のリクエスト容量制限
Google Cloud Machine Learningのオンライン予測はJSONを入力として予測を行います。しかしその容量は1.5MBまでに制限されています。
例えば400×400のカラー画像を入力するMobileNetを使用した学習モデルに対し、
import tensorflow as tf
model = tf.keras.applications.MobileNet(
input_shape=(400,400,3),
alpha=0.5,weights=None, classes=101)
予測リクエストを作成すると、
from googleapiclient import discovery
import cv2
service = discovery.build('ml','v1')
name = "projects/%s/models/%s/versions/%s" %
("your_project_id", "your_model_name","version")
path = "input.jpg" # 400×400カラー
x = cv2.imread(path,cv2.IMREAD_COLOR)
obj = {'instances' : [{'input' : x.tolist()}]}
response = service.projects().predict(name=name,body=obj).execute()
このようなエラーが発生します。
"Request payload size exceeds the limit: 1572864 bytes."
入力が生のRGB形式だとPOSTしているJSONがこのようになり、400×400カラーだと2.3MBになってしまいます。
{"instances": [{"input": [[[3, 14, 41], [3, 14, 41], [4, 15, 42], ...
PNGデコードをモデルに組み込む
この問題を回避するためにPNGデコードをモデルに組み込みます。
import tensorflow as tf
def decode(image_str_tensor):
"画像1枚分のPNGデータをデコードする関数"
image_str_tensor = tf.reshape(image_str_tensor,[])
image = tf.image.decode_png(image_str_tensor,channels=3)
x = tf.cast(image,tf.float32)
return x
def decode_layer(x):
"PNGデータをデコードするラムダレイヤーの関数"
x = tf.map_fn(decode, x,dtype=tf.float32)
return x
# PNGデータをデコードするモデル
decode_model = tf.keras.models.Sequential()
decode_model.add(tf.keras.layers.Lambda(decode_layer,
input_shape=[None],dtype=tf.string))
# 分類するモデル
mobilenet = tf.keras.applications.MobileNet(
input_shape=(400,400,3),
alpha=0.5,weights=None, classes=101)
# 2つをつなぐ
model = tf.keras.models.Sequential([decode_model,mobilenet])
tf.image.decode_png関数は画像1枚分のデコードしか対応していないため、tf.map_fn関数でバッチを1枚ずつの処理に分解しています。モデルの入力はstring型の0階テンソルですが、Kerasの仕様で0階テンソル入力を設定できないので、1階テンソル入力のモデルとして、ラムダレイヤーの中で0階テンソルに変換してからtf.image.decode_png関数に渡してます。
PNGデータをデコードするモデルとMobileNetで分類するモデルをtf.keras.models.Sequentialクラスのコンストラクタにリストで渡すことで2つを繋いだモデルを作成することができます。
PNG入力のモデルをデプロイしましたら、PNGデータをPOSTします。
from googleapiclient import discovery
import cv2
import base64
service = discovery.build('ml','v1')
name = "projects/%s/models/%s/versions/%s" %
("your_project_id", "your_model_name","version")
path = "input.jpg"
x = cv2.imread(path,cv2.IMREAD_COLOR)
ret,x = cv2.imencode(".png",x)
x = x.tostring()
obj = {'instances' : [{'input' : {"b64": base64.b64encode(x).decode('utf-8')}}]}
response = service.projects().predict(name=name,body=obj).execute()
入力がBase64エンコードされたPNGデータになりましたので、JSONのサイズは300KBぐらいに減り、1.5MB制限を回避できました。
全体ソースコード
この考えで作成したFood-101データセットによる分類をGoogle Cloud Machine Learningで実行するプロジェクトはこちらになります。