Python - развертывание модели с использованием обслуживания TensorFlow

Опубликовано: 4 Января, 2022

Самая важная часть конвейера машинного обучения - это развертывание модели. Развертывание модели означает, что развертывание - это метод, с помощью которого вы интегрируете модель машинного обучения в существующую производственную среду, чтобы ее можно было использовать в практических целях в режиме реального времени.

Есть много способов развернуть модель. Один из способов - интегрировать модель с приложением Django / Flask со сценарием, который принимает ввод, загружает модель и генерирует результаты. Таким образом, мы можем легко передать данные изображения в модель и отобразить результаты после того, как модель сгенерирует выходные данные. Ниже приведены ограничения вышеуказанного метода:

  • В зависимости от размера модели для обработки входных данных и получения результатов потребуется некоторое время.
  • Модель не может использоваться в других приложениях. (Учтите, никакого REST / gRPC API мы не пишем).
  • Обработка ввода-вывода в Flask выполняется медленно по сравнению с Node.
  • Обучение модели также требует значительных ресурсов и времени (поскольку требует большого количества операций ввода-вывода и вычислений.

Другой способ - развернуть модель с помощью обслуживания TensorFlow. Поскольку он также предоставляет API (в форме REST и gRPC), он переносится и может использоваться на различных устройствах с помощью своего API. Его легко развернуть, и он хорошо работает даже с более крупными моделями.

Преимущества обслуживания TensorFlow:

  • Часть экосистемы TensorFlow Extended (TFX).
  • Хорошо работает для больших моделей (до 2 ГБ).
  • Предоставляет согласованные структуры API для клиентских запросов RESTful и gRPC.
  • Может управлять версией модели.
  • Используется внутри компании Google

RESTful API:

TensorFlow Serving поддерживает два типа формата клиентского запроса в виде RESTful API.

  • API классификации и регрессии
  • Predict API (для задачи прогнозирования)

Здесь мы будем использовать прогнозирование API, формат URL для этого будет:

 POST http: // {host}: {port} / v1 / models / $ {MODEL_NAME} [/ versions / $ {VERSION} | / labels / $ {LABEL}]: предсказать

а тело запроса содержит объект JSON в виде:

 {
  // (Необязательно) Подпись для использования.
  // по умолчанию: 'serv-default'
  "имя_сигнатуры": <строка>,

 // Экземпляр: для формата строки (список, массив и т. Д.), Входы: для формата столбца.
 // может быть любой из них
  "экземпляры": <значение> | <(вложенный) список> | <список-объектов>
  "входы": <значение> | <(вложенный) список> | <объект>
}

gRPC API:

Чтобы использовать gRPC API, мы устанавливаем пакетный вызов tenorflow-serving-api с помощью pip. Дополнительные сведения о конечной точке API gRPC приведены в коде.

Реализация:

  • Мы продемонстрируем возможности обслуживания TensorFlow. Сначала мы импортируем (или устанавливаем) необходимые модули, затем обучим модель на наборе данных CIFAR 10 100 эпохам. Для производственного использования мы можем сохранить этот файл как train.py

Код:

# General import
!pip install - Uq grpcio = = 1.26 . 0
import numpy as np
import matplotlib.pyplot as plt
import os
subprocess import
requests import
import json
# TensorFlow Imports
from tensorflow import keras
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten,Dense, Dropout
from tensorflow.keras.models import Sequential,save_model
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.datasets import cifar10
class_names = [ "airplane" , "automobile" , "bird" , "cat" , "deer" , "dog" ,
"frog" , "horse" , "ship" , "truck" ]
# load and preprocessdataset
def load_and_preprocess():
(x_train, y_train), (x_test,y_test) = cifar_10.load_data()
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
x_train = x_train.astype( 'float32' )
x_test = x_test.astype( 'float32' )
x_train = x_train / 255
x_test = x_test / 255
return (x_train, y_train), (x_test,y_test)
# define model architecture
def get_model():
model = Sequential([
Conv2D( 32 , ( 3 , 3 ), activation = 'relu' , padding = 'same' , input_shape = ( 32 , 32 , 3 )),
Conv2D( 32 , ( 3 , 3 ), activation = 'relu' , padding = 'same' ),
MaxPooling2D(( 2 , 2 )),
Dropout( 0.2 ),
Conv2D( 64 , ( 3 , 3 ), activation = 'relu' , padding = 'same' ),
Conv2D( 64 , ( 3 , 3 ), activation = 'relu' , padding = 'same' ),
MaxPooling2D(( 2 , 2 )),
Dropout( 0.2 ),
Flatten(),
Dense( 64 , activation = 'relu' ),
Dense( 10 , activation = 'softmax' )
])
model. compile (
optimizer = SGD(learning_rate = 0.01 , momentum = 0.1 ),
loss = 'categorical_crossentropy' ,
metrics = [ 'accuracy' ]
)
model.summary()
model return
# train model
model = get_model()
model.fit(
x_train,
y_train,
epochs = 100 ,
validation_data = (x_test, y_test),
 Модель: "последовательный_1"
_________________________________________________________________
Слой (тип) Параметр формы вывода #   
================================================== ===============
conv2d_4 (Conv2D) (Нет, 32, 32, 32) 896       
_________________________________________________________________
conv2d_5 (Conv2D) (Нет, 32, 32, 32) 9248      
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (Нет, 16, 16, 32) 0         
_________________________________________________________________
dropout_2 (Отсев) (Нет, 16, 16, 32) 0         
_________________________________________________________________
conv2d_6 (Conv2D) (Нет, 16, 16, 64) 18496     
_________________________________________________________________
conv2d_7 (Conv2D) (Нет, 16, 16, 64) 36928     
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (Нет, 8, 8, 64) 0         
_________________________________________________________________
dropout_3 (Отсев) (Нет, 8, 8, 64) 0         
_________________________________________________________________
flatten_1 (Flatten) (Нет, 4096) 0         
_________________________________________________________________
плотный_2 (плотный) (нет, 64) 262208    
_________________________________________________________________
плотный_3 (Плотный) (Нет, 10) 650       
================================================== ===============
Всего параметров: 328 426
Обучаемые параметры: 328 426
Необучаемые параметры: 0
_________________________________________________________________
Эпоха 1/100
1563/1563 [==============================] - 7 с 
5 мс / шаг - потеря: 2,0344 - точность: 0,2537 - val_loss: 1,7737 - val_accuracy: 0,3691
Эпоха 2/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 1.6704 - точность: 0.4036 - val_loss: 1.5645 - val_accuracy: 0.4289
Эпоха 3/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 1,4688 - точность: 0,4723 - val_loss: 1,3854 - val_accuracy: 0,4999
Эпоха 4/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 1,3209 - точность: 0,5288 - val_loss: 1,2357 - val_accuracy: 0,5540
Эпоха 5/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 1.2046 - точность: 0.5699 - val_loss: 1.1413 - val_accuracy: 0.5935
Эпоха 6/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 1,1088 - точность: 0,6082 - val_loss: 1,2331 - val_accuracy: 0,5572
Эпоха 7/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 1.0248 - точность: 0.6373 - val_loss: 1.0139 - val_accuracy: 0.6389
Эпоха 8/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 0,9613 - точность: 0,6605 - val_loss: 0,9723 - val_accuracy: 0,6577
.
.
.
.
.

Эпоха 90/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 0,0775 - точность: 0,9734 - val_loss: 1,3356 - val_accuracy: 0,7473
Эпоха 91/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 0,0739 - точность: 0,9740 - val_loss: 1,2990 - val_accuracy: 0,7681
Эпоха 92/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 0,0743 - точность: 0,9739 - val_loss: 1,2629 - val_accuracy: 0,7655
Эпоха 93/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 0,0740 - точность: 0,9743 - val_loss: 1,3276 - val_accuracy: 0,7635
Эпоха 94/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 0,0724 - точность: 0,9746 - val_loss: 1,3179 - val_accuracy: 0,7656
Эпоха 95/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 0,0737 - точность: 0,9740 - val_loss: 1,3039 - val_accuracy: 0,7677
Эпоха 96/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 0,0736 - точность: 0,9734 - val_loss: 1,3243 - val_accuracy: 0,7653
Эпоха 97/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 0,0704 - точность: 0,9756 - val_loss: 1,3264 - val_accuracy: 0,7660
Эпоха 98/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 0,0693 - точность: 0,9757 - val_loss: 1,3284 - val_accuracy: 0,7658
Эпоха 99/100
1563/1563 [==============================] - 7 с 
4 мс / шаг - потеря: 0,0668 - точность: 0,9764 - val_loss: 1,3649 - val_accuracy: 0,7636
Эпоха 100/100
1563/1563 [==============================] - 7 с 
5 мс / шаг - потеря: 0,0710 - точность: 0,9749 - val_loss: 1,3206 - val_accuracy: 0,7682
<tensorflow.python.keras.callbacks.History в 0x7f36a042e7f0>





  • Затем мы сохраняем модель во временной папке с помощью TensorFlow save_model () и экспортируем ее в Tar Gz для загрузки.

Код:



import tempfile
= MODEL_DIR tempfile.gettempdir()
version = 1
export_path = os.path.join(MODEL_DIR, str (version))
print ( 'export_path = {} ' . format (export_path))
save_model(
model,
export_path,
overwrite = True ,
include_optimizer = True
)
print ( ' Saved model:' )
!ls - l {export_path}
# The command display input and output kayers with signature and data type
# These details are required when we make gRPC API call
!saved_model_cli show - - dir {export_path} - - all
# Create a compressed model from the savedmodel .
!tar - cz - f model.tar.gz - - owner = 0 - - group = 0 - C / tmp / 1 / .
  • Теперь мы разместим модель с помощью TensorFlow Serving, мы продемонстрируем хостинг двумя способами.
  • Во-первых, мы воспользуемся преимуществами среды colab и установим TensorFlow Serving в этой среде.
  • Затем мы будем использовать среду докеров для размещения модели и использовать как gRPC, так и REST API для вызова модели и получения прогнозов. Теперь реализуем первый метод.

Код:

# Install TensorFlow Serving using Aptitude [For Debian]
"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
# Install TensorFlow Server
!apt - get install tensorflow - model - server
# Run TensorFlow Serving on a new thread
os.environ[ "MODEL_DIR" ] = MODEL_DIR
% % bash - - bg
nohup tensorflow_model_server
- - rest_api_port = 8501
- - model_name = cifar_10
- - model_base_path = "${MODEL_DIR}" >server.log 2 >& 1
 Запуск задания №0 в отдельном потоке.
  • Теперь мы определяем функцию для выполнения запросов REST API к серверу. Это будет брать случайные изображения из тестового набора данных и отправлять их в модель (в формате JSON). Затем модель возвращает прогноз в формате JSON.

  • Теперь запустим нашу модель в Docker Environment. Он поддерживает архитектуру как CPU, так и GPU, а также рекомендован разработчиками TensorFlow. Для обслуживающей модели он предоставляет конечные точки REST (по умолчанию PORT: 8501) и gRPC [Remote Procedure Call] (по умолчанию PORT: 8500) для связи с моделями. Мы разместим нашу модель на докере, используя следующие команды.

Код:

# To TensorFlow Image from Docker Hub
!docker pull tensorflow / serving
# Run our model
!docker run - p 8500 : 8500 - p 8501 : 8501
- - type mount = bind,source = `pwd` / cifar_10 / ,target = / models / cifar_10
- e MODEL_NAME = cifar_10 - t tensorflow / serving
  • Теперь нам нужно написать сценарий для связи с нашей моделью с помощью конечных точек REST и gRPC. Ниже приведен сценарий для конечной точки REST. Сохраните этот код и запустите его в терминале с помощью python. Загрузите несколько изображений из набора данных CIFAR-10 и проверьте результаты

Код:

import json
requests import
import sys
from PIL import Image
import numpy as np
def get_rest_url(model_name, host = '127.0.0.1' ,
port = '8501' , task = 'predict' , version = None ):
""" This function takes hostname, port, task (b/w predict and classify)
and version to generate generate the URL path for REST API"""
url = " http:// {host}:{port}/v1/models/{model_name}" . format (host = host,
port = port, model_name = model_name)
if version:
url + = 'versions/{version}' . format (version = version)
url + = ':{task}' . format (task = task)
return url
def get_model_prediction(model_input, model_name = 'cifar_10' ,
signature_name = 'serving_default' ):
""" This function sends request to the URL and get prediction
in the form of response"""
url = get_rest_url(model_name)
image = Image. open (model_input)
# convert image to array
im = np.asarray(image)
# add the 4th dimension
im = np.expand_dims(im, axis = 0 )
im = im / 255
print ( "Image shape: " ,im.shape)
data = json.dumps({ "signature_name" : "serving_default" ,
"instances" : im.tolist()})
headers = { "content-type" : "application/json" }
# Send the post request and get resonse
rv = requests.post(url, data = data, headers = headers)
return rv.json()[ 'predictions' ]
if __name__ = = '__main__' :
class_names = [ "airplane" , "automobile" , "bird" , "cat" , "deer"
, "dog" , "frog" , "horse" , "ship" , "truck" ]
print ( " Generate REST url ..." )
url = get_rest_url(model_name = 'cifar_10' )
print (url)
while True :
print ( " Enter the image path [:q for Quit]" )
if sys.version_info[ 0 ] > = 3 :
path = str ( input ())
if path = = ':q' :
break
model_prediction = get_model_prediction(path)
print ( "The model predicted ..." )
print (class_names[np.argmax(model_prediction)])

  • И код ниже запроса gRPC. Здесь важно получить правильное имя подписи (по умолчанию это «обслуживает по умолчанию») и имя входного и выходного слоев.

Код:

import sys
import grpc
from grpc.beta import implementations
import tensorflow as tf
from PIL import Image
import numpy as np
# import prediction service functions from TF-Serving API
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2, get_model_metadata_pb2
from tensorflow_serving.apis import prediction_service_pb2_grpc
def get_stub(host = '127.0.0.1' , port = '8500' ):
channel = grpc.insecure_channel( '127.0.0.1:8500' )
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)
return stub
def get_model_prediction(model_input, stub, model_name = 'cifar_10' , signature_name = 'serving_default' ):
""" input => (image path, url, model_name, signature)
output the results in the form of tf.array"""
image = Image. open (model_input)
im = np.asarray(image, dtype = np.float64)
im = (im / 255 )
im = np.expand_dims(im, axis = 0 )
print ( "Image shape: " ,im.shape)
# We will be using Prediction Task so it uses predictRequest fucntion from predict_pb2
request = predict_pb2.PredictRequest()
request.model_spec.name = model_name
request.model_spec.signature_name = signature_name
#pass Image input to input layer (Here it is named 'conv2d_4_input')
request.inputs[ 'conv2d_4_input' ].CopyFrom(tf.make_tensor_proto(im, dtype = tf.float32))
response = stub.Predict.future(request, 5.0 )
# get results from final layer(dense_3)
return response.result().outputs[ "dense_3" ].float_val
def get_model_version(model_name, stub):
request = get_model_metadata_pb2.GetModelMetadataRequest()
request.model_spec.name = 'cifar_10'
request.metadata_field.append( "signature_def" )
response = stub.GetModelMetadata(request, 10 )
# signature of loaded model is available here: response.metadata['signature_def']
return response.model_spec.version.value
if __name__ = = '__main__' :
class_names = [ "airplane" , "automobile" , "bird" , "cat" , "deer" , "dog" , "frog" , "horse" , "ship" , "truck" ]
print ( " Create RPC connection ..." )
stub = get_stub()
while True :
print ( " Enter the image path [:q for Quit]" )
if sys.version_info[ 0 ] < = 3 :
path = raw_input () if sys.version_info[ 0 ] < 3 else input ()
if path = = ':q' :
break
model_input = str (path)
model_prediction = get_model_prediction(model_input, stub)
print ( " Predictiom from Model ..." )
print (class_names[np.argmax(model_prediction)])


Использованная литература:

  • TensorFlow, обслуживающая документы

Предыдущий
Преобразование изображения в изображение с помощью Pix2Pix
Следующий
Функция turtle.setworldcoordinates () в Python
Рекомендуемые статьи
Страница :