Python - развертывание модели с использованием обслуживания TensorFlow
Самая важная часть конвейера машинного обучения - это развертывание модели. Развертывание модели означает, что развертывание - это метод, с помощью которого вы интегрируете модель машинного обучения в существующую производственную среду, чтобы ее можно было использовать в практических целях в режиме реального времени.
Есть много способов развернуть модель. Один из способов - интегрировать модель с приложением 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] !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 # 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""" # Our REST URL should be http://127.0.0.1:8501/v1/models/cifar_10/predict 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, обслуживающая документы