Загрузить файлы на S3-хранилище и собрать образ для деплоя

Для развертывания модели в модуле Deployments необходимы:

  • Обученная и сериализованная модель.

  • Скрипт, описывающий взаимодействие с моделью (serving-скрипт).

Важно

При обучении модели необходимо сериализовать ее в формат h5 средствами библиотеки h5py или в формат pickle средствами библиотеки pickle.

До начала работы обученную модель и serving-скрипт необходимо загрузить в объектное хранилище S3.

Для загрузки данных в объектное хранилище S3 можно использовать:

  • Data catalog → Обзор хранилища, кнопка Загрузить на странице бакета. См. Загрузить данные в хранилище S3.

  • Сторонние клиентские приложения с графическим интерфейсом (Cyberduck, S3 Browser).

Начало работы с модулем Deployments

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

До начала работы с модулем необходимо подготовить обученную модель и служебный скрипт (serving script), описывающий порядок взаимодействия с моделью. См. Скрипты для прогнозирования на основе обученных моделей.

Важно

В рамках модуля Deployments используются сериализованные модели. Для этой цели можно использовать встроенные средства фреймворка обучения, сохраняя контрольные точки в формате bin, h5, pth и др. Также модели можно сериализовать в формат h5 средствами библиотеки h5py или в формат pickle средствами библиотеки pickle.

Работа с модулем предполагает следующую последовательность действий:

  1. Загрузка модели и скрипта, описывающего взаимодействие с моделью, на S3.

  2. Сборка Docker-образа с моделью на основе базового или кастомного образа. В кастомном образе предполагается использование зависимостей, которые отсутствуют в базовом образе.

  3. Разворачивание образа с моделью на указанной конфигурации.

  4. Отправка HTTP-запросов к модели вручную или от автоматизированных систем.

Предположим, у пользователя есть модель, обученная распознавать кошек и собак на изображениях.

Код обучения модели

from matplotlib import pyplot
from matplotlib.image import imread
from os import listdir
from numpy import asarray
from numpy import save
import tensorflow.keras as keras
from tensorflow.keras.preprocessing.image import load_img
from tensorflow.keras.preprocessing.image import img_to_array
from os import makedirs
from shutil import copyfile
from random import seed
from random import random

# Plot dog photos from the dogs vs cats dataset
# Define location of dataset
folder = 'train/'
# plot first few images
for i in range(9):
    # Define subplot
    pyplot.subplot(330 + 1 + i)
    # Define filename
    filename = folder + 'dog.' + str(i) + '.jpg'
    # Load image pixels
    image = imread(filename)
    # Plot raw pixel data
    pyplot.imshow(image)
# Show the figure
pyplot.show()

# Plot cat photos from the dogs vs cats dataset
# Define location of dataset
folder = 'train/'
# Plot first few images
for i in range(9):
    # Define subplot
    pyplot.subplot(330 + 1 + i)
    # Define filename
    filename = folder + 'cat.' + str(i) + '.jpg'
    # Load image pixels
    image = imread(filename)
    # Plot raw pixel data
    pyplot.imshow(image)
# Show the figure
pyplot.show()

# Load dogs vs cats dataset, reshape and save to a new file
# Define location of dataset
folder = 'train/'
photos, labels = list(), list()
# Enumerate files in the directory
for file in listdir(folder):
# Determine class
    output = 0.0
    if file.startswith('cat'):
        output = 1.0
    # Load image
    photo = load_img(folder + file, target_size=(200, 200))
    # Convert to numpy array
    photo = img_to_array(photo)
    # Store
    photos.append(photo)
    labels.append(output)
# Convert to a numpy arrays
photos = asarray(photos)
labels = asarray(labels)
print(photos.shape, labels.shape)
# Save the reshaped photos
save('dogs_vs_cats_photos.npy', photos)
save('dogs_vs_cats_labels.npy', labels)

# Organize dataset into a useful structure
# Create directories
dataset_home = 'dataset_dogs_vs_cats/'
subdirs = ['train/', 'test/']
for subdir in subdirs:
    # Create label subdirectories
    labeldirs = ['dogs/', 'cats/']
    for labldir in labeldirs:
        newdir = dataset_home + subdir + labldir
        makedirs(newdir, exist_ok=True)
# Seed random number generator
seed(1)
# Define ratio of pictures to use for validation
val_ratio = 0.25
# Copy training dataset images into subdirectories
src_directory = 'train/'
for file in listdir(src_directory):
    src = src_directory + '/' + file
    dst_dir = 'train/'
    if random() < val_ratio:
        dst_dir = 'test/'
    if file.startswith('cat'):
        dst = dataset_home + dst_dir + 'cats/' + file
        copyfile(src, dst)
    elif file.startswith('dog'):
        dst = dataset_home + dst_dir + 'dogs/' + file
        copyfile(src, dst)

# Organize dataset into a useful structure
from os import makedirs
from os import listdir
from shutil import copyfile
# Create directories
dataset_home = 'finalize_dogs_vs_cats/'
# Create label subdirectories
labeldirs = ['dogs/', 'cats/']
for labldir in labeldirs:
    newdir = dataset_home + labldir
    makedirs(newdir, exist_ok=True)

# Copy training dataset images into subdirectories
src_directory = 'dataset_dogs_vs_cats/train'
for file2 in listdir(src_directory):
    ext_path = src_directory + '/' + file2
    for file in listdir(ext_path):
        src = ext_path + '/' + file
        if file.startswith('cat'):
            dst = dataset_home + 'cats/' + file
            # print(dst)
            copyfile(src, dst)
        elif file.startswith('dog'):
            dst = dataset_home + 'dogs/' + file
            # print(dst)
            copyfile(src, dst)

# Save the final model to file
import tensorflow.keras as keras
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Flatten
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# Define cnn model
def define_model():
    # Load model
    model = VGG16(include_top=False, input_shape=(224, 224, 3))
    # Mark loaded layers as not trainable
    for layer in model.layers:
        layer.trainable = False
        # Add new classifier layers
        flat1 = Flatten()(model.layers[-1].output)
        class1 = Dense(128, activation='relu', kernel_initializer='he_uniform')(flat1)
        output = Dense(1, activation='sigmoid')(class1)
        # Define new model
        model = Model(inputs=model.inputs, outputs=output)
        # Compile model
        opt = SGD(lr=0.001, momentum=0.9)
        model.compile(optimizer=opt, loss='binary_crossentropy', metrics=['accuracy'])
    return model

# Run the test harness for evaluating a model
def run_test_harness():
# Define model
    model = define_model()
    # Create data generator
    datagen = ImageDataGenerator(featurewise_center=True)
    # Specify imagenet mean values for centering
    datagen.mean = [123.68, 116.779, 103.939]
    # Prepare iterator
    train_it = datagen.flow_from_directory('finalize_dogs_vs_cats/',
        class_mode='binary', batch_size=64, target_size=(224, 224))
    # Fit model
    # model = keras.utils.multi_gpu_model(model, gpus=8)
    model.fit_generator(train_it, steps_per_epoch=len(train_it), epochs=10, verbose=1)

Также у пользователя есть serving-скрипт, который описывает взаимодействие с моделью.

Код serving-скрипта

import kfserving
import boto3
from typing import List, Dict
import re
import os
import tensorflow as tf
from tensorflow.keras.preprocessing.image import load_img
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.models import load_model
import numpy as np
import json

class KFServingExplainModel(kfserving.KFModel):
    def __init__(self, name: str):
        super().__init__(name)
        self.name = name
        self.ready = False
        self.model = None
        self.gpu = True

    def load(self):
        self.model = load_model('final_model.h5')
        self.ready = True

    def predict(self, request: Dict) -> Dict:
        image_link = request['instances'][0]['image_link']
        img = "123.jpg"

        session = boto3.session.Session()
        s3_client = session.client(
        service_name='s3',
        aws_access_key_id='<INPUT S3 ACCESS KEY HERE>',
        aws_secret_access_key='<INPUT S3 SECRET KEY HERE>',
        endpoint_url='<INPUT S3 CREDENTIALS HERE>',
        region_name='ru-1a'
        )
        s3_client.download_file('<INPUT S3 BUCKETNAME CREDENTIALS HERE>', image_link, img)

        img = load_img(img, target_size=(224, 224))
        img = img_to_array(img)
        img = img.reshape(1, 224, 224, 3)
        img = img.astype('float32')
        img = img - [123.68, 116.779, 103.939]

        result = self.model.predict(img)
        result = result[0][0]

        if(result >= 0.5):
        result = 'dog'
        elif(result < 0.5):
            result = 'cat'

        # result = json.dumps(str(result))
        # print(type(result))
        return {"predictions": result}


if __name__ == "__main__":
    x = re.compile('(kfserving-\d+)').search(os.environ.get('HOSTNAME'))
    name = "kfserving-default"
    if x:
        name = x[0]

    model = KFServingExplainModel(name)
    model.load()
    kfserving.KFServer(workers=1).start([model])

Задача состоит в том, чтобы развернуть эту модель и впоследствии передавать ей новые данные в виде HTTP-запросов.

Для решения поставленной задачи необходимо:

  1. Сериализовать модель, например, в формат h5:

    model.save('final_model.h5')
    
  2. Загрузить модель и serving-скрипт в бакет пользователя на S3 (см. Загрузить файлы на S3-хранилище и собрать образ для деплоя).

  3. Собрать Docker-образ c моделью на основе кастомного образа.

    Для этого:

    • Перейти на вкладку Деплои.

    • Нажать Создать деплой. Откроется диалог Создать деплой.

    • Выбрать Создайте новый.

    • В поле Base image указать базовый образ.

    • Заполнить остальные поля. См. Развернуть модель (создать деплой).

    • Нажать Создать образ и дождаться сборки образа.

  4. Развернуть образ в модуле. Обратите внимание на возможность динамически изменять конфигурацию деплоя.

После развертывания образа можно передавать модели новые данные в виде HTTP-запросов. Подробнее см. в Отправить синхронный HTTP-запрос к развернутой модели.