LoginSignup
0
2

More than 5 years have passed since last update.

Kerasで作成した学習モデルにPNGデコードを組み込みGoogle Cloud MLオンライン予測の1.5MB制限を回避する

Last updated at Posted at 2019-01-02

Cloud ML オンライン予測のリクエスト容量制限

Google Cloud Machine Learningのオンライン予測はJSONを入力として予測を行います。しかしその容量は1.5MBまでに制限されています。

例えば400×400のカラー画像を入力するMobileNetを使用した学習モデルに対し、

model.py
import tensorflow as tf
model = tf.keras.applications.MobileNet(
    input_shape=(400,400,3),
    alpha=0.5,weights=None, classes=101)

予測リクエストを作成すると、

request.py
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デコードをモデルに組み込みます。

model.py
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します。

request.py
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で実行するプロジェクトはこちらになります。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2