В этом руководстве вы узнаете, как определить модель машинного обучения в Python, а затем развернуть ее с помощью Amazon SageMaker. Следуйте за репозиторием GitHub для получения дополнительной информации и ссылок. AWS также поддерживает обширную коллекцию примеров, которую вы можете использовать в качестве дополнительной справки.
Обзор моделей SageMaker
SageMaker использует контейнеры Docker для разделения алгоритмов машинного обучения. Этот контейнерный подход позволяет SageMaker предлагать широкий спектр легкодоступных алгоритмов для распространенных случаев использования, оставаясь при этом достаточно гибким для поддержки моделей, разработанных с использованием общих библиотек или пользовательских написанных моделей. Контейнеры модели можно использовать на трех основных уровнях:
- Готовые алгоритмы — фиксированный класс алгоритмов, полностью поддерживаемый AWS.
- «Режим сценария» — позволяет использовать популярные фреймворки машинного обучения с помощью сценария.
- «Режим контейнера» — позволяет использовать полностью настраиваемый алгоритм машинного обучения.
Эти режимы предлагают различные степени сложности и простоты использования.
Ниже вы найдете краткое описание каждого режима. Основное внимание в этом руководстве будет уделено пошаговому процессу использования режима контейнера для развертывания модели машинного обучения. Если вы новичок в использовании SageMaker, AWS подготовила серию глубоких видеороликов, на которые вы можете ссылаться.
В дополнение к стандартным AWS SDK у Amazon также есть пакет Python более высокого уровня, SageMaker Python SDK, для обучения и развертывания моделей с помощью SageMaker, который мы будем использовать здесь.
Предустановленные алгоритмы
SageMaker предлагает предварительно созданные алгоритмы, которые могут решать широкий спектр типов задач и вариантов использования. AWS поддерживает все контейнеры, связанные с этими алгоритмами. Вы можете найти полный список доступных алгоритмов и узнать больше о каждом из них в Документации SageMaker.
Режим сценария
Режим сценария позволяет вам писать сценарии Python для часто используемых фреймворков машинного обучения. AWS по-прежнему поддерживает базовый контейнер, на котором размещена любая выбранная вами платформа, а ваш скрипт встраивается в контейнер и используется для управления логикой во время выполнения. Чтобы сценарий был совместим с поддерживаемым AWS контейнером, он должен соответствовать определенным требованиям к дизайну.
Контейнерный режим
Режим контейнера позволяет использовать пользовательскую логику для определения модели и развертывания ее в экосистеме SageMaker; в этом режиме вы поддерживаете как контейнер, так и базовую логику, которую он реализует. Этот режим является наиболее гибким и позволяет получить доступ ко многим доступным библиотекам Python и инструментам машинного обучения. Чтобы контейнер был совместим с SageMaker, он должен соответствовать определенным требованиям к дизайну. Это может быть выполнено одним из двух способов:
- Определите свой собственный контейнер, расширив один из существующих, поддерживаемых AWS.
- Используйте Библиотеку контейнеров SageMaker, чтобы определить свой контейнер
Здесь мы сосредоточимся на использовании метода 1, но AWS действительно приложила все усилия, чтобы максимально упростить использование собственной пользовательской логики в SageMaker.
После разработки контейнера необходимо загрузить его в реестр AWS Elastic Container Registry (ECR). Это образ модели, на который вы будете указывать SageMaker при обучении или развертывании модели.
Шаги
Здесь мы опишем основные шаги, связанные с созданием и развертыванием пользовательской модели в SageMaker:
- Определить логику модели машинного обучения
- Определите изображение модели
- Создайте образ контейнера и отправьте его в Amazon Elastic Container Registry (ECR).
- Обучение и развертывание образа модели
В качестве общего обзора вся структура нашей пользовательской модели будет выглядеть примерно так:
. ├── container │ ├── build_and_push.sh │ ├── code │ │ ├── model_logic.py │ │ └── requirements.txt │ ├── Dockerfile │ ├── gam_model │ │ ├── gam_model │ │ │ ├── __init__.py │ │ │ └── _models.py │ │ ├── gen_pack.sh │ │ └── setup.py
Каталог gam_model
содержит основную логику пользовательской модели. Каталог code
содержит код, который указывает нашему контейнеру, как использовать модель в SageMaker (обучение модели, сохранение, загрузка и вывод). Из оставшихся файлов DockerFile
определяет образ докера, а build_and_push.sh
— это вспомогательный bash-скрипт (который я нашел здесь) для отправки нашего контейнера в ECR, чтобы мы могли использовать его в SageMaker. Мы рассмотрим каждую часть более подробно по мере прохождения каждого шага.
Определение логики модели
Для нашей пользовательской модели машинного обучения мы будем использовать обобщенную аддитивную модель (или GAM). GAM — это мощный, но интерпретируемый алгоритм, который может обнаруживать нелинейные отношения и, возможно, взаимодействия. Если вы не знакомы с GAM, Ким Ларсон и Майкл Кларк содержат полезные сведения о нем. Также обратите внимание, что существует пакет Python, реализующий GAM с надежными функциями, pyGAM. Для наших целей мы будем использовать пакет statsmodels.
При создании контейнера с пользовательской моделью я обычно предпочитаю помещать реальную реализацию алгоритма машинного обучения в отдельный пакет Python. Это позволяет мне отделить логику модели от логики, необходимой для ее запуска в SageMaker, а также независимо изменять и тестировать каждую часть. Затем модель можно повторно использовать и в других средах.
Мы назовем наш пакет gam_model.
. Я включил его в наш каталог определений контейнера только для того, чтобы упростить его включение в контейнер. Мы определим его здесь в ближайшее время.
В этом случае наш пакет будет выглядеть так:
.
├── gam_model
│ ├── __init__.py
│ └── _models.py
├── gen_pack.sh
└── setup.py
Это довольно простой модуль Python, который превращает реализацию GAM statsmodel в модель, подобную scikit-learn. Содержание _models.py
гласит:
import numpy as np from sklearn.base import BaseEstimator, RegressorMixin from sklearn.utils.validation import check_is_fitted from sklearn.utils import check_array from statsmodels.gam.api import GLMGam, BSplines class GAMRegressor(BaseEstimator, RegressorMixin): def __init__(self, df = 15, alpha = 1.0, degree = 3): self.df = df self.alpha = alpha self.degree = degree def fit(self, X, y): X, y = self._validate_data(X, y, y_numeric=True) self.spline = BSplines( X, df = [self.df] * self.n_features_in_, degree = [self.degree] * self.n_features_in_, include_intercept = False ) gam = GLMGam( y, exog = np.ones(X.shape[0]), smoother = self.spline, alpha = self.alpha ) self.gam_predictor = gam.fit() return self def predict(self, X): check_is_fitted(self, attributes = "gam_predictor") X = check_array(X) return self.gam_predictor.predict( exog = np.ones(X.shape[0]), exog_smooth = X ) @property def summary(self): return self.gam_predictor.summary() if \ hasattr(self, "gam_predictor") else None
gen_pack.sh
— это вспомогательный скрипт, который перестраивает и устанавливает пакет каждый раз, когда мне нужно его изменить. Остальные компоненты пакета достаточно стандартны и находятся на соответствующей странице GitHub.
Определение образа модели
Теперь, когда наша модель реализована и помещена в пакет, следующим шагом будет определение образа контейнера Docker, в котором будет храниться наша модель в экосистеме AWS. Для этого сначала напишем наш DockerFile
:
ARG REGION=us-east-1 FROM 683313688378.dkr.ecr.us-east-1.amazonaws.com/sagemaker-scikit-learn:0.23-1-cpu-py3 ENV PATH="/opt/ml/code:${PATH}" COPY /code /opt/ml/code COPY gam_model/dist/gam_model-0.0.1-py3-none-any.whl /opt/gam_model-0.0.1-py3-none-any.whl RUN pip install -r /opt/ml/code/requirements.txt /opt/gam_model-0.0.1-py3-none-any.whl ENV SAGEMAKER_PROGRAM model_logic.py
Здесь мы используем один из образов контейнеров, созданных и поддерживаемых AWS для среды scikit-learn. Текущие контейнеры фреймворка можно найти на страницах документации SageMaker (здесь и здесь). Расширяя их контейнер, мы можем воспользоваться всем, что они уже сделали для его настройки, и просто беспокоиться о включении нашего дополнительного кода и функций (мы рассмотрим это позже). Затем мы копируем файл колеса пакета gam_model и устанавливаем его и другие зависимости. Наконец, мы устанавливаем файл Python, model_logic.py
, в качестве точки входа для контейнера.
Поскольку мы расширяем один из контейнеров платформы AWS, нам необходимо убедиться, что инструкции для логики, которую должен выполнять контейнер, соответствуют требованиям к дизайну, изложенным в документации sagemaker-python-sdk. Подробнее об общих требованиях к контейнерам SageMaker можно прочитать в их документации, а также на странице sagemaker-containers.
В нашем случае каталог code
выглядит так:
├── model_logic.py
└── requirements.txt
requirements.txt
содержит некоторые дополнительные пакеты, которые нам нужно установить в контейнере, а model_logic.py
содержит инструкции о том, как мы хотим, чтобы контейнер обучал, загружал и обслуживал модель.
Учебная часть выглядит так:
import argparse import os import json import pandas as pd import numpy as np import joblib from gam_model import GAMRegressor if __name__ =='__main__': print('initializing') parser = argparse.ArgumentParser() gam = GAMRegressor() gam_dict = gam.get_params() # Data, model, and output directories parser.add_argument('--output-data-dir', type = str, default = os.environ.get('SM_OUTPUT_DATA_DIR')) parser.add_argument('--model-dir', type = str, default = os.environ.get('SM_MODEL_DIR')) parser.add_argument('--train', type = str, default = os.environ.get('SM_CHANNEL_TRAIN')) parser.add_argument('--train-file', type = str) parser.add_argument('--test', type = str, default = os.environ.get('SM_CHANNEL_TEST')) parser.add_argument('--test-file', type = str, default = None) for argument, default_value in gam_dict.items(): parser.add_argument(f'--{argument}', type = type(default_value), default = default_value) print('reading arguments') args, _ = parser.parse_known_args() print(args) print('setting parameters') gam_dict.update({key: value for key, value in vars(args).items()\ if key in gam_dict and value is not None}) gam.set_params(**gam_dict) print(gam) print('reading training data') # assume there's no headers and the target is the last column data = np.loadtxt(os.path.join(args.train, args.train_file), delimiter = ',') X = data[:, :-1] y = data[:, -1] print("X shape:", X.shape) print("y shape:", y.shape) if args.test_file is not None: print('reading training data') # assume there's no headers and the target is the last column data = np.loadtxt(os.path.join(args.test, args.test_file), delimiter = ',') X_test = data[:, :-1] y_test = data[:, -1] print("X_test shape:", X_test.shape) print("y_test shape:", y_test.shape) else: X_test = None y_test = None print('fitting model') gam.fit(X, y) print("R2 (train):", gam.score(X, y)) if X_test is not None: print("R2 (test):", gam.score(X_test, y_test)) print('saving model') path = os.path.join(args.model_dir, "model.joblib") print(f"saving to {path}") joblib.dump(gam, path)
Загрузка модели:
def model_fn(model_dir): model = joblib.load(os.path.join(model_dir, "model.joblib")) return model
Использование модели для прогнозирования:
def predict_fn(input_object, model): return model.predict(input_object)
Обратите внимание: если бы мы хотели иметь возможность использовать различные методы сериализации/десериализации с нашей моделью в SageMaker, мы могли бы также определить input_fn
и output_fn
, но мы будем использовать реализации по умолчанию.
Создание образа контейнера и отправка его в Amazon Elastic Container Registry (ECR)
Теперь, когда у нас есть все ингредиенты для нашего контейнера, мы можем собрать его и отправить в ECR.
./build_and_push.sh gam-model
Примечание.Я жестко запрограммировал регион как в DockerFile
, так и в build_and_push.sh
, чтобы извлечь его из us-east-1 (account id 683313688378)
. Вы можете настроить это для другого региона, обратившись к документам.
Сделав это, перейдите в консоль AWS, перейдите к ECR и запишите URI образа вашей модели.
Обучение и развертывание пользовательской модели
Теперь, когда мы определили образ нашей модели и зарегистрировали его в ECR, мы можем использовать SageMaker для обучения и развертывания нашей модели! Вы можете следить за этим процессом, ссылаясь на блокнот example.ipynb.
В этом примере мы будем использовать небольшой, относительно простой набор данных, который продемонстрирует способность GAM моделировать нелинейные отношения: набор данных Gauss3.
Сначала мы импортируем необходимые библиотеки и запускаем некоторую инициализацию:
import requests import sagemaker import boto3 import s3fs import json import io import numpy as np from sklearn.model_selection import train_test_split from sklearn.metrics import r2_score from sagemaker.estimator import Estimator from sagemaker.predictor import Predictor from sagemaker.serializers import NumpySerializer from sagemaker.deserializers import NumpyDeserializer from sagemaker.local import LocalSession from matplotlib import pyplot as plt import matplotlib as mpl import seaborn as sns %matplotlib inline sns.set() seed = 42 rand = np.random.RandomState(seed) local_mode = False # activate to use local mode with open("config.json") as f: configs = json.load(f) default_bucket = configs["default_bucket"] #put your bucket name here role = configs["role_arn"] # put your sagemaker role arn here boto_session = boto3.Session() if local_mode: sagemaker_session = LocalSession(boto_session = boto_session) sagemaker_session._default_bucket = default_bucket else: sagemaker_session = sagemaker.Session( boto_session = boto_session, default_bucket = default_bucket ) ecr_image = configs["image_arn"] #put the image uri from ECR here prefix = "modeling/sagemaker" data_name = f"gauss3" test_name = "gam-demo"
Обратите внимание, что я использую файл конфигурации для хранения имени корзины S3, роли SageMaker и URI обучающего образа, но вы можете установить их напрямую. Далее мы определяем две вспомогательные функции. Я также включаю логику для обучения и развертывания модели локально или на экземплярах SageMaker.
def get_s3fs(): return s3fs.S3FileSystem(key = boto_session.get_credentials().access_key, secret = boto_session.get_credentials().secret_key, token = boto_session.get_credentials().token) def plot_and_clear(): plt.show() plt.clf() plt.cla() plt.close()
Мы можем получить данные Гаусса с помощью модуля запросов и применить разделение поезда-теста.
url = "https://www.itl.nist.gov/div898/strd/nls/data/LINKS/DATA/Gauss3.dat" r = requests.get(url) y, x = np.loadtxt( io.StringIO(r.text[r.text.index("Data: y x"):]), skiprows=1, unpack=True ) x = x.reshape(-1, 1) X_train, X_test, y_train, y_test = train_test_split( x, y, test_size = 0.25, random_state = rand )
После записи данных обучения в нашу корзину S3,
file_fn = f"{default_bucket}/{prefix}/{data_name}/train/data.csv" file_path = f"s3://{file_fn}" s3 = get_s3fs() with s3.open(file_fn, 'wb') as f: np.savetxt(f, np.c_[X_train, y_train], delimiter = ',')
мы можем обучить нашу модель:
hyperparameters = { "train-file": "data.csv", "df": "20" } data_channels = { "train": file_path } estimator = Estimator( role = role, sagemaker_session = sagemaker_session, instance_count = 1, instance_type = "local" if local_mode else "ml.m5.large", image_uri = ecr_image, base_job_name = f'{data_name}-{test_name}', hyperparameters = hyperparameters, output_path = f"s3://{default_bucket}/{prefix}/{data_name}/model" ) estimator.fit(data_channels, wait = True, logs = "None") job_name = estimator.latest_training_job.name print(job_name)
Как только модель обучена, мы можем развернуть ее, чтобы делать выводы в реальном времени.
np_serialize = NumpySerializer() np_deserialize = NumpyDeserializer() predictor = estimator.deploy( initial_instance_count = 1, instance_type = "local" if local_mode else "ml.t2.medium", serializer = np_serialize, deserializer = np_deserialize )
Теперь давайте получим прогнозы модели на данных обучения и тестирования и сравним их с фактическими данными.
y_hat_train = predictor.predict(X_train) y_hat_test = predictor.predict(X_test)
Мы видим, что GAM нашел гладкое представление, которое фиксирует нелинейность данных.
Не забудьте удалить конечную точку модели, когда закончите тестирование модели.
predictor.delete_endpoint() predictor.delete_model()
Вывод
В этом руководстве описан процесс создания уникального образа контейнера в SageMaker и показано, как его можно использовать для обучения и развертывания пользовательской модели машинного обучения. Надеюсь, это было полезно и послужит полезным справочником.
Примечание. Если у вас возникнут проблемы во время этого процесса, обязательно проверьте группы журналов CloudWatch для экземпляров сборки, обучения и развертывания SageMaker. Они ваш лучший друг для поиска и решения проблем!
PREDICTif является избранным партнером-консультантом Amazon Web Services. Для получения дополнительной информации посетите нашу страницу партнера Amazon. Эта статья была написана Дэвидом Хреном, ведущим специалистом по данным в PREDICTif Solutions.
Первоначально опубликовано на https://www.predictifsolutions.com 4 сентября 2020 г.