このガイドでは、ニューラルネットワークモデルをトレーニングして、スニーカーやシャツなどの衣類の画像を分類し、トレーニングしたモデルを保存してから、 TensorFlowServingで提供します。 TensorFlowでのモデリングとトレーニングではなく、TensorFlowサービングに焦点が当てられているため、モデリングとトレーニングに焦点を当てた完全な例については、 基本的な分類の例を参照してください。
このガイドでは、高レベルAPIであるtf.kerasを使用して、 TensorFlowでモデルを構築およびトレーニングします。
import sys
# Confirm that we're using Python 3
assert sys.version_info.major is 3, 'Oops, not running Python 3. Use Runtime > Change runtime type'
# TensorFlow and tf.keras
print("Installing dependencies for Colab environment")
!pip install -Uq grpcio==1.26.0
import tensorflow as tf
from tensorflow import keras
# Helper libraries
import numpy as np
import matplotlib.pyplot as plt
import os
import subprocess
print('TensorFlow version: {}'.format(tf.__version__))
モデルを作成する
FashionMNISTデータセットをインポートする
このガイドでは、10のカテゴリに70,000のグレースケール画像を含むFashionMNISTデータセットを使用します。画像は、次のように、低解像度(28 x 28ピクセル)で個々の衣料品を示しています。
![]() |
図1.ファッション-MNISTサンプル(Zalando、MITライセンスによる)。 |
ファッションMNISTは、従来のMNISTデータセットのドロップイン代替品として意図されています。多くの場合、コンピュータービジョンの機械学習プログラムの「Hello、World」として使用されます。データをインポートしてロードするだけで、TensorFlowから直接FashionMNISTにアクセスできます。
fashion_mnist = keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
# scale the values to 0.0 to 1.0
train_images = train_images / 255.0
test_images = test_images / 255.0
# reshape for feeding into the model
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1)
test_images = test_images.reshape(test_images.shape[0], 28, 28, 1)
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
print('\ntrain_images.shape: {}, of {}'.format(train_images.shape, train_images.dtype))
print('test_images.shape: {}, of {}'.format(test_images.shape, test_images.dtype))
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz 32768/29515 [=================================] - 0s 0us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz 26427392/26421880 [==============================] - 0s 0us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz 8192/5148 [===============================================] - 0s 0us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz 4423680/4422102 [==============================] - 0s 0us/step train_images.shape: (60000, 28, 28, 1), of float64 test_images.shape: (10000, 28, 28, 1), of float64
モデルのトレーニングと評価
モデリングの部分に焦点を当てていないので、可能な限り単純なCNNを使用しましょう。
model = keras.Sequential([
keras.layers.Conv2D(input_shape=(28,28,1), filters=8, kernel_size=3,
strides=2, activation='relu', name='Conv1'),
keras.layers.Flatten(),
keras.layers.Dense(10, activation=tf.nn.softmax, name='Softmax')
])
model.summary()
testing = False
epochs = 5
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=epochs)
test_loss, test_acc = model.evaluate(test_images, test_labels)
print('\nTest accuracy: {}'.format(test_acc))
Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= Conv1 (Conv2D) (None, 13, 13, 8) 80 _________________________________________________________________ flatten (Flatten) (None, 1352) 0 _________________________________________________________________ Softmax (Dense) (None, 10) 13530 ================================================================= Total params: 13,610 Trainable params: 13,610 Non-trainable params: 0 _________________________________________________________________ Train on 60000 samples Epoch 1/5 60000/60000 [==============================] - 11s 185us/sample - loss: 0.5466 - accuracy: 0.8087 Epoch 2/5 60000/60000 [==============================] - 5s 79us/sample - loss: 0.4032 - accuracy: 0.8580 Epoch 3/5 60000/60000 [==============================] - 5s 76us/sample - loss: 0.3613 - accuracy: 0.8712 Epoch 4/5 60000/60000 [==============================] - 5s 75us/sample - loss: 0.3406 - accuracy: 0.8797 Epoch 5/5 60000/60000 [==============================] - 4s 75us/sample - loss: 0.3247 - accuracy: 0.8848 10000/10000 [==============================] - 1s 73us/sample - loss: 0.3510 - accuracy: 0.8747 Test accuracy: 0.8747000098228455
モデルを保存します
トレーニング済みモデルをTensorFlowServingに読み込むには、最初にSavedModel形式で保存する必要があります。これにより、明確に定義されたディレクトリ階層にprotobufファイルが作成され、バージョン番号が含まれます。 TensorFlow Servingを使用すると、推論リクエストを行うときに使用するモデルのバージョン、つまり「サーブ可能」を選択できます。各バージョンは、指定されたパスの下の異なるサブディレクトリにエクスポートされます。
# Fetch the Keras session and save the model
# The signature definition is defined by the input and output tensors,
# and stored with the default serving key
import tempfile
MODEL_DIR = tempfile.gettempdir()
version = 1
export_path = os.path.join(MODEL_DIR, str(version))
print('export_path = {}\n'.format(export_path))
tf.keras.models.save_model(
model,
export_path,
overwrite=True,
include_optimizer=True,
save_format=None,
signatures=None,
options=None
)
print('\nSaved model:')
!ls -l {export_path}
export_path = /tmp/1 WARNING:tensorflow:From /tensorflow-2.1.0/python3.6/tensorflow_core/python/ops/resource_variable_ops.py:1786: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version. Instructions for updating: If using Keras pass *_constraint arguments to layers. INFO:tensorflow:Assets written to: /tmp/1/assets Saved model: total 84 drwxr-xr-x 2 root root 4096 Jan 7 23:15 assets -rw-r--r-- 1 root root 74086 Jan 7 23:15 saved_model.pb drwxr-xr-x 2 root root 4096 Jan 7 23:15 variables
保存したモデルを調べます
コマンドラインユーティリティsaved_model_cli
を使用して、 SavedModelのMetaGraphDefs (モデル)とSignatureDefs (呼び出すことができるメソッド)を確認します。 TensorFlowガイドのSavedModelCLIに関するこの説明を参照してください。
saved_model_cli show --dir {export_path} --all
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs: signature_def['__saved_model_init_op']: The given SavedModel SignatureDef contains the following input(s): The given SavedModel SignatureDef contains the following output(s): outputs['__saved_model_init_op'] tensor_info: dtype: DT_INVALID shape: unknown_rank name: NoOp Method name is: signature_def['serving_default']: The given SavedModel SignatureDef contains the following input(s): inputs['Conv1_input'] tensor_info: dtype: DT_FLOAT shape: (-1, 28, 28, 1) name: serving_default_Conv1_input:0 The given SavedModel SignatureDef contains the following output(s): outputs['Softmax'] tensor_info: dtype: DT_FLOAT shape: (-1, 10) name: StatefulPartitionedCall:0 Method name is: tensorflow/serving/predict WARNING:tensorflow:From /tensorflow-2.1.0/python3.6/tensorflow_core/python/ops/resource_variable_ops.py:1786: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version. Instructions for updating: If using Keras pass *_constraint arguments to layers. Defined Functions: Function Name: '__call__' Option #1 Callable with: Argument #1 Conv1_input: TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name='Conv1_input') Argument #2 DType: bool Value: True Argument #3 DType: NoneType Value: None Option #2 Callable with: Argument #1 inputs: TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name='inputs') Argument #2 DType: bool Value: True Argument #3 DType: NoneType Value: None Option #3 Callable with: Argument #1 inputs: TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name='inputs') Argument #2 DType: bool Value: False Argument #3 DType: NoneType Value: None Option #4 Callable with: Argument #1 Conv1_input: TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name='Conv1_input') Argument #2 DType: bool Value: False Argument #3 DType: NoneType Value: None Function Name: '_default_save_signature' Option #1 Callable with: Argument #1 Conv1_input: TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name='Conv1_input') Function Name: 'call_and_return_all_conditional_losses' Option #1 Callable with: Argument #1 inputs: TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name='inputs') Argument #2 DType: bool Value: True Argument #3 DType: NoneType Value: None Option #2 Callable with: Argument #1 inputs: TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name='inputs') Argument #2 DType: bool Value: False Argument #3 DType: NoneType Value: None Option #3 Callable with: Argument #1 Conv1_input: TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name='Conv1_input') Argument #2 DType: bool Value: False Argument #3 DType: NoneType Value: None Option #4 Callable with: Argument #1 Conv1_input: TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name='Conv1_input') Argument #2 DType: bool Value: True Argument #3 DType: NoneType Value: None
それは私たちのモデルについて多くを教えてくれます!この場合、モデルをトレーニングしたばかりなので、入力と出力はすでにわかっていますが、知らなかった場合、これは重要な情報になります。たとえば、これがグレースケール画像データであるという事実のように、すべてを教えてくれるわけではありませんが、それは素晴らしいスタートです。
TensorFlowServingを使用してモデルをサーブします
TensorFlowServingディストリビューションURIをパッケージソースとして追加します。
このColabはDebian環境で実行されるため、 Aptitudeを使用してTensorFlowServingをインストールする準備をしています。 Aptitudeが認識しているパッケージのリストにtensorflow-model-server
パッケージを追加します。 rootとして実行していることに注意してください。
# This is the same as you would do from your command line, but without the [arch=amd64], and no sudo
# You would instead do:
# echo "deb [arch=amd64] http://storage.googleapis.com/tensorflow-serving-apt stable tensorflow-model-server tensorflow-model-server-universal" | sudo tee /etc/apt/sources.list.d/tensorflow-serving.list && \
# curl https://storage.googleapis.com/tensorflow-serving-apt/tensorflow-serving.release.pub.gpg | sudo apt-key add -
!echo "deb http://storage.googleapis.com/tensorflow-serving-apt stable tensorflow-model-server tensorflow-model-server-universal" | tee /etc/apt/sources.list.d/tensorflow-serving.list && \
curl https://storage.googleapis.com/tensorflow-serving-apt/tensorflow-serving.release.pub.gpg | apt-key add -
!apt update
deb http://storage.googleapis.com/tensorflow-serving-apt stable tensorflow-model-server tensorflow-model-server-universal % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2943 100 2943 0 0 11496 0 --:--:-- --:--:-- --:--:-- 11496 OK Get:1 http://storage.googleapis.com/tensorflow-serving-apt stable InRelease [3,012 B] Get:2 https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/ InRelease [3,626 B] Ign:3 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 InRelease Ign:4 https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 InRelease Hit:5 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 Release Get:6 https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 Release [564 B] Get:7 https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 Release.gpg [833 B] Hit:8 http://ppa.launchpad.net/graphics-drivers/ppa/ubuntu bionic InRelease Hit:9 http://archive.ubuntu.com/ubuntu bionic InRelease Get:10 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB] Get:11 http://storage.googleapis.com/tensorflow-serving-apt stable/tensorflow-model-server amd64 Packages [354 B] Get:12 https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/ Packages [81.6 kB] Get:13 http://archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB] Get:14 http://storage.googleapis.com/tensorflow-serving-apt stable/tensorflow-model-server-universal amd64 Packages [364 B] Get:15 http://ppa.launchpad.net/marutter/c2d4u3.5/ubuntu bionic InRelease [15.4 kB] Get:17 https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 Packages [30.4 kB] Get:18 http://archive.ubuntu.com/ubuntu bionic-backports InRelease [74.6 kB] Get:19 http://ppa.launchpad.net/marutter/c2d4u3.5/ubuntu bionic/main Sources [1,749 kB] Get:20 http://security.ubuntu.com/ubuntu bionic-security/universe amd64 Packages [796 kB] Get:21 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 Packages [1,073 kB] Get:22 http://security.ubuntu.com/ubuntu bionic-security/main amd64 Packages [776 kB] Get:23 http://security.ubuntu.com/ubuntu bionic-security/restricted amd64 Packages [21.3 kB] Get:24 http://archive.ubuntu.com/ubuntu bionic-updates/multiverse amd64 Packages [10.8 kB] Get:25 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 Packages [1,324 kB] Get:26 http://archive.ubuntu.com/ubuntu bionic-updates/restricted amd64 Packages [35.5 kB] Get:27 http://ppa.launchpad.net/marutter/c2d4u3.5/ubuntu bionic/main amd64 Packages [844 kB] Fetched 7,019 kB in 4s (1,913 kB/s) Reading package lists... Done Building dependency tree Reading state information... Done 21 packages can be upgraded. Run 'apt list --upgradable' to see them.
TensorFlowServingをインストールします
必要なのはこれだけです-1つのコマンドライン!
apt-get install tensorflow-model-server
Reading package lists... Done Building dependency tree Reading state information... Done The following package was automatically installed and is no longer required: libnvidia-common-430 Use 'apt autoremove' to remove it. The following NEW packages will be installed: tensorflow-model-server 0 upgraded, 1 newly installed, 0 to remove and 21 not upgraded. Need to get 140 MB of archives. After this operation, 0 B of additional disk space will be used. Get:1 http://storage.googleapis.com/tensorflow-serving-apt stable/tensorflow-model-server amd64 tensorflow-model-server all 2.0.0 [140 MB] Fetched 140 MB in 2s (78.8 MB/s) Selecting previously unselected package tensorflow-model-server. (Reading database ... 145674 files and directories currently installed.) Preparing to unpack .../tensorflow-model-server_2.0.0_all.deb ... Unpacking tensorflow-model-server (2.0.0) ... Setting up tensorflow-model-server (2.0.0) ...
TensorFlowサービングの実行を開始します
ここからTensorFlowServingの実行を開始し、モデルをロードします。ロード後、RESTを使用して推論リクエストを開始できます。重要なパラメータがいくつかあります。
-
rest_api_port
:RESTリクエストに使用するポート。 -
model_name
:これをRESTリクエストのURLで使用します。何でもかまいません。 -
model_base_path
:これはモデルを保存したディレクトリへのパスです。
os.environ["MODEL_DIR"] = MODEL_DIR
nohup tensorflow_model_server \
--rest_api_port=8501 \
--model_name=fashion_model \
--model_base_path="${MODEL_DIR}" >server.log 2>&1
Starting job # 0 in a separate thread.
tail server.log
[warn] getaddrinfo: address family for nodename not supported [evhttp_server.cc : 238] NET_LOG: Entering the event loop ...
TensorFlowServingでモデルにリクエストを送信します
まず、テストデータからランダムな例を見てみましょう。
def show(idx, title):
plt.figure()
plt.imshow(test_images[idx].reshape(28,28))
plt.axis('off')
plt.title('\n\n{}'.format(title), fontdict={'size': 16})
import random
rando = random.randint(0,len(test_images)-1)
show(rando, 'An Example Image: {}'.format(class_names[test_labels[rando]]))
わかりました、それは面白そうです。あなたがそれを認識するのはどれくらい難しいですか?次に、3つの推論リクエストのバッチ用のJSONオブジェクトを作成し、モデルが物事をどの程度認識しているかを確認しましょう。
import json
data = json.dumps({"signature_name": "serving_default", "instances": test_images[0:3].tolist()})
print('Data: {} ... {}'.format(data[:50], data[len(data)-52:]))
Data: {"signature_name": "serving_default", "instances": ... [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]]]]}
RESTリクエストを行う
サーバブルの最新バージョン
予測リクエストをPOSTとしてサーバーのRESTエンドポイントに送信し、3つの例を渡します。特定のバージョンを指定せずに、サーバーにservableの最新バージョンを提供するように依頼します。
!pip install -q requests
import requests
headers = {"content-type": "application/json"}
json_response = requests.post('http://localhost:8501/v1/models/fashion_model:predict', data=data, headers=headers)
predictions = json.loads(json_response.text)['predictions']
show(0, 'The model thought this was a {} (class {}), and it was actually a {} (class {})'.format(
class_names[np.argmax(predictions[0])], np.argmax(predictions[0]), class_names[test_labels[0]], test_labels[0]))
使用可能なものの特定のバージョン
それでは、サーバブルの特定のバージョンを指定しましょう。 1つしかないので、バージョン1を選択しましょう。3つの結果すべても見ていきます。
headers = {"content-type": "application/json"}
json_response = requests.post('http://localhost:8501/v1/models/fashion_model/versions/1:predict', data=data, headers=headers)
predictions = json.loads(json_response.text)['predictions']
for i in range(0,3):
show(i, 'The model thought this was a {} (class {}), and it was actually a {} (class {})'.format(
class_names[np.argmax(predictions[i])], np.argmax(predictions[i]), class_names[test_labels[i]], test_labels[i]))