TensorFlow Serving ist ein flexibles, leistungsstarkes Modellierungssystem für maschinelles Lernen, das für Produktionsumgebungen entwickelt wurde. Mit TensorFlow Serving ist es einfach, mit TensorFlow erstellte Modelle zu hosten und APIs verfügbar zu machen.
Weitere Informationen finden Sie in der TensorFlow-Serving-Dokumentation (https://www.tensorflow.org/tfx/guide/serving).
Dieses Mal habe ich TensorFlow Serving auf AWS EC2 verwendet, um ein Deep-Learning-Modell von TensorFlow zu hosten. Am Ende des Artikels versuche ich auch Docker.
Geben Sie "Deep Learning AMI" in die AMI-Suchleiste ein, um nach dem AMI zu suchen, das Sie verwenden möchten. Dieses Mal habe ich "Deep Learning AMI (Ubuntu 18.04) Version 30.0 --ami-0b1b56cbf0f8fcea3" verwendet. Ich habe "p2.xlarge" als Instanztyp verwendet. Die Sicherheitsgruppe ist so eingestellt, dass ssh und http über die Entwicklungsumgebung verbunden werden können und alle anderen Einstellungen als Standardeinstellungen beibehalten werden.
Melden Sie sich bei EC2 an und erstellen Sie die Umgebung.
~$ ls
LICENSE README examples tools
Nvidia_Cloud_EULA.pdf anaconda3 src tutorials
Das Installationsverfahren wird auf der offiziellen Website vorgestellt.
Fügen Sie zunächst den TensorFlow Serving URI zu sources.list.d hinzu.
~$ 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 -
deb [arch=amd64] 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 18166 0 --:--:-- --:--:-- --:--:-- 18166
OK
Führen Sie die Installation durch.
~$ sudo apt-get update && apt-get install tensorflow-model-server
~$ tensorflow_model_server --version
TensorFlow ModelServer: 1.15.0-rc2+dev.sha.1ab7d59
TensorFlow Library: 1.15.2
Damit ist die Installation abgeschlossen.
Von hier aus erstellen wir ein Modell für die Bereitstellung. Bereiten Sie zunächst ein Arbeitsverzeichnis vor.
~$ mkdir tfexample
~$ cd tfexample
Starten Sie jupyter-lab und erstellen Sie das Modell.
~/tfexample$ jupyter-lab --no-browser --port=8888 --ip=0.0.0.0 --allow-root
...
http://127.0.0.1:8888/?token=b92a7ceefb20c7ab3e475474dbde66a771870de1d8f5bd70
...
Da es einen Ort gibt, an dem die URL in der Standardausgabe angezeigt wird, greifen Sie zu, indem Sie den Teil von 127.0.0.1
auf die IP-Adresse der Instanz umschreiben.
Wählen Sie nach dem Start von jupyer lab den Kernel conda_tensorflow2_py36 aus und öffnen Sie das Notizbuch. Benennen Sie es in "tfmodel.ipynb" um.
Dieses Mal werde ich ein Model mit Fashionmnist machen.
tfmodel.ipynb
import sys
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
import os
import tempfile
print('TensorFlow version: {}'.format(tf.__version__))
# TensorFlow version: 2.1.0
tfmodel.ipynb
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))
# train_images.shape: (60000, 28, 28, 1), of float64
# test_images.shape: (10000, 28, 28, 1), of float64
tfmodel.ipynb
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 [==============================] - 46s 770us/sample - loss: 0.5398 - accuracy: 0.8182
# Epoch 2/5
# 60000/60000 [==============================] - 5s 76us/sample - loss: 0.3849 - accuracy: 0.8643
# Epoch 3/5
# 60000/60000 [==============================] - 5s 76us/sample - loss: 0.3513 - accuracy: 0.8751
# Epoch 4/5
# 60000/60000 [==============================] - 5s 76us/sample - loss: 0.3329 - accuracy: 0.8820
# Epoch 5/5
# 60000/60000 [==============================] - 5s 76us/sample - loss: 0.3204 - accuracy: 0.8847
# 10000/10000 [==============================] - 1s 78us/sample - loss: 0.3475 - accuracy: 0.8779
# Test accuracy: 0.8779000043869019
tfmodel.ipynb
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 /home/ubuntu/anaconda3/envs/tensorflow2_p36/lib/python3.6/site-packages/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 ubuntu ubuntu 4096 Jul 17 10:49 assets
# -rw-rw-r-- 1 ubuntu ubuntu 74970 Jul 17 10:49 saved_model.pb
# drwxr-xr-x 2 ubuntu ubuntu 4096 Jul 17 10:49 variables
Das Speicherziel des Modells wurde vom Modul "tempfile" erstellt. Dieses Mal wird das Modell in / tmp / 1
gespeichert.
Öffnen Sie ein anderes Terminal, melden Sie sich bei der Instanz an und starten Sie den Server.
~$ export MODEL_DIR=/tmp
~$ tensorflow_model_server \
--rest_api_port=8501 \
--model_name=fashion_model \
--model_base_path="${MODEL_DIR}"
Es scheint, dass es eine Struktur geben sollte, die die Version unter "model_base_path" anzeigt und das Modell darunter gespeichert wird.
model_base_path/
├ 1/
│ ├ assets/
│ ├ variables/
│ └ saved_model.pb
├ 2/
│ ├ (unten weggelassen)
Ich werde eine Anfrage stellen und sie überprüfen. Gehen Sie zurück zu Ihrem Notizbuch und stellen Sie eine Anfrage.
tfmodel.ipynb
def show(idx, title):
plt.figure()
plt.imshow(test_images[idx].reshape(28,28), cmap = "gray")
plt.axis('off')
plt.title('\n\n{}'.format(title), fontdict={'size': 16})
tfmodel.ipynb
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]]]]}
tfmodel.ipynb
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]))
Senden Sie Daten im JSON-Format per POST. Die Daten werden für den Schlüssel "Instanzen" festgelegt, sie werden jedoch im Stapel vorhergesagt. Daher müssen Sie bei der Form vorsichtig sein.
Übrigens sind die Inhalte von "Vorhersagen" wie folgt.
predictions[0]
# [7.71279588e-07,
# 4.52205953e-08,
# 5.55571035e-07,
# 1.59779923e-08,
# 2.27421737e-07,
# 0.00600787532,
# 8.29056205e-07,
# 0.0466650613,
# 0.00145569211,
# 0.945868969]
Die Wahrscheinlichkeiten für jede Klasse werden in der Liste gespeichert. Dies ist die gleiche Ausgabe wie der folgende Code.
model.predict(test_images[0:3]).tolist()[0]
~$ docker --version
Docker version 19.03.11, build 42e35e61f3
~$ docker pull tensorflow/serving
~$ docker run -d -t --rm -p 8501:8501 -v "/tmp:/models/fashion_model" -e MODEL_NAME=fashion_model tensorflow/serving
Die Einstiegspunkte sind wie folgt. Der RESTful-API-Port ist 8501, der gRPC-Port ist 8500 und der model_base_path
ist $ {MODEL_BASE_PATH} / $ {MODEL_NAME}
.
tensorflow_model_server --port=8500 --rest_api_port=8501 \
--model_name=${MODEL_NAME} --model_base_path=${MODEL_BASE_PATH}/${MODEL_NAME}
Die Einstiegspunktdatei wird in / usr / bin / tf_serving_entrypoint.sh
gespeichert und enthält tatsächlich den folgenden Code:
#!/bin/bash
tensorflow_model_server --port=8500 --rest_api_port=8501 --model_name=${MODEL_NAME} --model_base_path=${MODEL_BASE_PATH}/${MODEL_NAME} "$@"
Wenn Sie Docker verwenden, müssen Sie daher lediglich den Modellspeicherpfad des Hosts in Dockers "model_base_path" einbinden.
Recommended Posts