Tham gia cộng đồng SIG TFX-Addons và giúp cải thiện TFX hơn nữa!
Trang này được dịch bởi Cloud Translation API.
Switch to English

Đào tạo và phục vụ mô hình TensorFlow với Phục vụ TensorFlow

Hướng dẫn này đào tạo mô hình mạng thần kinh để phân loại hình ảnh của quần áo, như giày thể thao và áo sơ mi , lưu mô hình đã đào tạo và sau đó phục vụ mô hình đó bằng TensorFlow Serving . Trọng tâm là Phục vụ TensorFlow, hơn là mô hình hóa và đào tạo trong TensorFlow, vì vậy để biết ví dụ hoàn chỉnh tập trung vào mô hình hóa và đào tạo, hãy xem ví dụ Phân loại cơ bản .

Hướng dẫn này sử dụng tf.keras , một API cấp cao để xây dựng và đào tạo các mô hình trong 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__))

Tạo mô hình của bạn

Nhập tập dữ liệu MNIST Thời trang

Hướng dẫn này sử dụng tập dữ liệu Fashion MNIST chứa 70.000 hình ảnh thang độ xám trong 10 danh mục. Các hình ảnh hiển thị từng mặt hàng quần áo ở độ phân giải thấp (28 x 28 pixel), như được thấy ở đây:

Thời trang MNIST sprite
Hình 1. Các mẫu thời trang-MNIST (của Zalando, Giấy phép MIT).

Fashion MNIST được thiết kế để thay thế cho tập dữ liệu MNIST cổ điển — thường được sử dụng làm chương trình máy học "Hello, World" cho thị giác máy tính. Bạn có thể truy cập MNIST Thời trang trực tiếp từ TensorFlow, chỉ cần nhập và tải dữ liệu.

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

Đào tạo và đánh giá mô hình của bạn

Hãy sử dụng CNN đơn giản nhất có thể, vì chúng tôi không tập trung vào phần mô hình hóa.

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, name='Dense')
])
model.summary()

testing = False
epochs = 5

model.compile(optimizer='adam', 
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=[keras.metrics.SparseCategoricalAccuracy()])
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         
_________________________________________________________________
Dense (Dense)                (None, 10)                13530     
=================================================================
Total params: 13,610
Trainable params: 13,610
Non-trainable params: 0
_________________________________________________________________
Epoch 1/5
1875/1875 [==============================] - 13s 2ms/step - loss: 0.7546 - sparse_categorical_accuracy: 0.7457
Epoch 2/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.4254 - sparse_categorical_accuracy: 0.8521
Epoch 3/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3812 - sparse_categorical_accuracy: 0.8668
Epoch 4/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3557 - sparse_categorical_accuracy: 0.8770
Epoch 5/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3415 - sparse_categorical_accuracy: 0.8795
313/313 [==============================] - 1s 2ms/step - loss: 0.3699 - sparse_categorical_accuracy: 0.8694

Test accuracy: 0.8694000244140625

Lưu mô hình của bạn

Để tải mô hình được đào tạo của chúng tôi vào TensorFlow Serving, trước tiên, chúng tôi cần lưu nó ở định dạng SavedModel . Thao tác này sẽ tạo tệp protobuf trong hệ thống phân cấp thư mục được xác định rõ và sẽ bao gồm số phiên bản. TensorFlow Serving cho phép chúng tôi chọn phiên bản của mô hình hoặc "có thể phục vụ" mà chúng tôi muốn sử dụng khi đưa ra các yêu cầu suy luận. Mỗi phiên bản sẽ được xuất sang một thư mục con khác nhau theo đường dẫn đã cho.

# 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

INFO:tensorflow:Assets written to: /tmp/1/assets

Saved model:
total 88
drwxr-xr-x 2 kbuilder kbuilder  4096 Mar  9 10:10 assets
-rw-rw-r-- 1 kbuilder kbuilder 78123 Mar  9 10:10 saved_model.pb
drwxr-xr-x 2 kbuilder kbuilder  4096 Mar  9 10:10 variables

Kiểm tra mô hình đã lưu của bạn

Chúng tôi sẽ sử dụng tiện ích dòng lệnh saved_model_cli để xem MetaGraphDefs (các mô hình) và SignatureDefs (các phương thức bạn có thể gọi) trong SavedModel của chúng tôi. Xem thảo luận này về CLI SavedModel trong Hướng dẫn TensorFlow.

saved_model_cli show --dir {export_path} --all
2021-03-09 10:10:12.685464: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0

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['Dense'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 10)
        name: StatefulPartitionedCall:0
  Method name is: tensorflow/serving/predict

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: False
        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
          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 #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

  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: False
        Argument #3
          DType: NoneType
          Value: None
    Option #2
      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 #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
          inputs: TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name='inputs')
        Argument #2
          DType: bool
          Value: True
        Argument #3
          DType: NoneType
          Value: None

Điều đó cho chúng tôi biết rất nhiều về mô hình của chúng tôi! Trong trường hợp này, chúng tôi chỉ đào tạo mô hình của mình, vì vậy chúng tôi đã biết các đầu vào và đầu ra, nhưng nếu chúng tôi không thực hiện thì đây sẽ là thông tin quan trọng. Nó không cho chúng ta biết tất cả mọi thứ, chẳng hạn như đây là dữ liệu hình ảnh thang độ xám, nhưng đó là một khởi đầu tuyệt vời.

Phục vụ mô hình của bạn với TensorFlow Serving

Thêm URI phân phối phục vụ TensorFlow làm nguồn gói:

Chúng tôi đang chuẩn bị cài đặt Cung cấp TensorFlow bằng Aptitude vì Colab này chạy trong môi trường Debian. Chúng tôi sẽ thêm tensorflow-model-server vào danh sách các gói mà Aptitude biết. Lưu ý rằng chúng tôi đang chạy dưới dạng root.

import sys
# We need sudo prefix if not on a Google Colab.
if 'google.colab' not in sys.modules:
  SUDO_IF_NEEDED = 'sudo'
else:
  SUDO_IF_NEEDED = ''
# 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" | {SUDO_IF_NEEDED} tee /etc/apt/sources.list.d/tensorflow-serving.list && \
curl https://storage.googleapis.com/tensorflow-serving-apt/tensorflow-serving.release.pub.gpg | {SUDO_IF_NEEDED} apt-key add -
!{SUDO_IF_NEEDED} 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  15822      0 --:--:-- --:--:-- --:--:-- 15822
OK
Hit:1 http://asia-east1.gce.archive.ubuntu.com/ubuntu bionic InRelease
Hit:2 http://asia-east1.gce.archive.ubuntu.com/ubuntu bionic-updates InRelease
Hit:3 http://asia-east1.gce.archive.ubuntu.com/ubuntu bionic-backports InRelease
Hit:4 http://packages.cloud.google.com/apt google-cloud-logging-wheezy InRelease
Get:5 https://packages.cloud.google.com/apt eip-cloud-bionic InRelease [5419 B]
Hit:6 https://nvidia.github.io/libnvidia-container/stable/ubuntu18.04/amd64  InRelease
Hit:7 https://nvidia.github.io/nvidia-container-runtime/ubuntu18.04/amd64  InRelease
Get:8 https://nvidia.github.io/nvidia-docker/ubuntu18.04/amd64  InRelease [1129 B]
Get:9 http://storage.googleapis.com/tensorflow-serving-apt stable InRelease [3012 B]
Hit:10 http://archive.canonical.com/ubuntu bionic InRelease
Hit:11 http://security.ubuntu.com/ubuntu bionic-security InRelease
Get:12 http://storage.googleapis.com/tensorflow-serving-apt stable/tensorflow-model-server amd64 Packages [340 B]
Get:13 http://storage.googleapis.com/tensorflow-serving-apt stable/tensorflow-model-server-universal amd64 Packages [348 B]
Fetched 10.2 kB in 1s (7051 B/s)



114 packages can be upgraded. Run 'apt list --upgradable' to see them.

Cài đặt phục vụ TensorFlow

Đây là tất cả những gì bạn cần - một dòng lệnh!

{SUDO_IF_NEEDED} apt-get install tensorflow-model-server
The following packages were automatically installed and are no longer required:
  adwaita-icon-theme ca-certificates-java dconf-gsettings-backend
  dconf-service default-jre default-jre-headless dkms fonts-dejavu-extra
  freeglut3 freeglut3-dev g++-6 glib-networking glib-networking-common
  glib-networking-services gsettings-desktop-schemas gtk-update-icon-cache
  hicolor-icon-theme humanity-icon-theme java-common libaccinj64-9.1
  libasound2 libasound2-data libasyncns0 libatk-bridge2.0-0
  libatk-wrapper-java libatk-wrapper-java-jni libatk1.0-0 libatk1.0-data
  libatspi2.0-0 libavahi-client3 libavahi-common-data libavahi-common3
  libcairo-gobject2 libcolord2 libcroco3 libcudart9.1 libcufft9.1 libcufftw9.1
  libcups2 libcurand9.1 libcusolver9.1 libcusparse9.1 libdconf1 libdrm-amdgpu1
  libdrm-dev libdrm-intel1 libdrm-nouveau2 libdrm-radeon1 libegl-mesa0 libegl1
  libegl1-mesa libepoxy0 libflac8 libfontenc1 libgbm1 libgdk-pixbuf2.0-0
  libgdk-pixbuf2.0-common libgif7 libgl1 libgl1-mesa-dev libgl1-mesa-dri
  libglapi-mesa libgles1 libgles2 libglu1-mesa libglu1-mesa-dev
  libglvnd-core-dev libglvnd-dev libglvnd0 libglx-mesa0 libglx0 libgtk-3-0
  libgtk-3-common libgtk2.0-0 libgtk2.0-common libice-dev libjansson4
  libjson-glib-1.0-0 libjson-glib-1.0-common liblcms2-2 libllvm9 libnppc9.1
  libnppial9.1 libnppicc9.1 libnppicom9.1 libnppidei9.1 libnppif9.1
  libnppig9.1 libnppim9.1 libnppist9.1 libnppisu9.1 libnppitc9.1 libnpps9.1
  libnvrtc9.1 libnvtoolsext1 libnvvm3 libogg0 libopengl0 libpciaccess0
  libpcsclite1 libproxy1v5 libpthread-stubs0-dev libpulse0 librest-0.7-0
  librsvg2-2 librsvg2-common libsensors4 libsm-dev libsndfile1
  libsoup-gnome2.4-1 libsoup2.4-1 libstdc++-6-dev libthrust-dev libvdpau-dev
  libvdpau1 libvorbis0a libvorbisenc2 libwayland-client0 libwayland-cursor0
  libwayland-egl1 libwayland-server0 libx11-dev libx11-xcb-dev libx11-xcb1
  libxau-dev libxcb-dri2-0 libxcb-dri2-0-dev libxcb-dri3-0 libxcb-dri3-dev
  libxcb-glx0 libxcb-glx0-dev libxcb-present-dev libxcb-present0 libxcb-randr0
  libxcb-randr0-dev libxcb-render0-dev libxcb-shape0 libxcb-shape0-dev
  libxcb-sync-dev libxcb-sync1 libxcb-xfixes0 libxcb-xfixes0-dev libxcb1-dev
  libxcomposite1 libxcursor1 libxdamage-dev libxdamage1 libxdmcp-dev
  libxext-dev libxfixes-dev libxfixes3 libxfont2 libxft2 libxi-dev libxi6
  libxinerama1 libxkbcommon0 libxkbfile1 libxmu-dev libxmu-headers libxnvctrl0
  libxrandr2 libxshmfence-dev libxshmfence1 libxt-dev libxtst6 libxv1
  libxxf86dga1 libxxf86vm-dev libxxf86vm1 linux-gcp-5.3-headers-5.3.0-1030
  linux-gcp-headers-5.0.0-1026 linux-headers-5.3.0-1030-gcp
  linux-image-5.3.0-1030-gcp linux-modules-5.3.0-1030-gcp
  linux-modules-extra-5.3.0-1030-gcp mesa-common-dev ocl-icd-libopencl1
  ocl-icd-opencl-dev opencl-c-headers openjdk-11-jre openjdk-11-jre-headless
  openjdk-8-jre openjdk-8-jre-headless pkg-config policykit-1-gnome
  python3-xkit screen-resolution-extra ubuntu-mono x11-utils x11-xkb-utils
  x11proto-core-dev x11proto-damage-dev x11proto-dev x11proto-fixes-dev
  x11proto-input-dev x11proto-xext-dev x11proto-xf86vidmode-dev
  xorg-sgml-doctools xserver-common xserver-xorg-core-hwe-18.04 xtrans-dev
Use 'sudo apt autoremove' to remove them.
The following NEW packages will be installed:
  tensorflow-model-server
0 upgraded, 1 newly installed, 0 to remove and 114 not upgraded.
Need to get 223 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.4.1 [223 MB]
Fetched 223 MB in 6s (40.3 MB/s)
Selecting previously unselected package tensorflow-model-server.
(Reading database ... 242337 files and directories currently installed.)
Preparing to unpack .../tensorflow-model-server_2.4.1_all.deb ...
Unpacking tensorflow-model-server (2.4.1) ...
Setting up tensorflow-model-server (2.4.1) ...

Bắt đầu chạy TensorFlow Serving

Đây là nơi chúng tôi bắt đầu chạy TensorFlow Serving và tải mô hình của chúng tôi. Sau khi nó tải, chúng ta có thể bắt đầu thực hiện các yêu cầu suy luận bằng cách sử dụng REST. Có một số thông số quan trọng:

  • rest_api_port : Cổng mà bạn sẽ sử dụng cho các yêu cầu REST.
  • model_name : Bạn sẽ sử dụng điều này trong URL của các yêu cầu REST. Nó có thể là bất cứ thứ gì.
  • model_base_path : Đây là đường dẫn đến thư mục bạn đã lưu mô hình của mình.
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
tail server.log

Đưa ra yêu cầu đối với mô hình của bạn trong TensorFlow Serving

Đầu tiên, hãy xem một ví dụ ngẫu nhiên từ dữ liệu thử nghiệm của chúng tôi.

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]]))

png

Ok, điều đó có vẻ thú vị. Bạn khó nhận ra điều đó như thế nào? Bây giờ, hãy tạo đối tượng JSON cho một loạt ba yêu cầu suy luận và xem mô hình của chúng tôi nhận dạng mọi thứ tốt như thế nào:

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]]]]}

Thực hiện các yêu cầu REST

Phiên bản mới nhất của dịch vụ

Chúng tôi sẽ gửi một yêu cầu dự đoán dưới dạng POST tới điểm cuối REST của máy chủ và chuyển cho nó ba ví dụ. Chúng tôi sẽ yêu cầu máy chủ của chúng tôi cung cấp cho chúng tôi phiên bản mới nhất của dịch vụ của chúng tôi bằng cách không chỉ định một phiên bản cụ thể.

!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]))

Một phiên bản cụ thể của dịch vụ

Bây giờ hãy chỉ định một phiên bản cụ thể của dịch vụ của chúng tôi. Vì chúng tôi chỉ có một, hãy chọn phiên bản 1. Chúng tôi cũng sẽ xem xét cả ba kết quả.

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]))

png

png

png