Пошаговое руководство по запуску моделей машинного обучения в производство
Что вы узнаете:
TL; DR:
Специалисты по анализу данных часто забывают, что их новые потрясающие модели не так полезны, если их нельзя использовать в производственной среде. В этом руководстве я покажу вам, как развернуть модель Tensorflow в экземплярах контейнеров Azure и защитить ее с помощью SSL.
Будет 3 типа развертывания:
- Развертывание №1: локальное развертывание:
Модель будет помещена в контейнер докеров и представлена как API на локальном хосте.
- Развертывание №2: глобальное развертывание без шифрования:
Docker, содержащий модель, будет загружен в реестр контейнеров Azure. Сохраненный контейнер будет представлен через API, который вы можете вызвать. Обратной стороной этого является то, что каждый может получить к нему доступ, если у него есть IP-адрес, и он не зашифрован.
- Развертывание №3: глобальное развертывание, зашифрованное с помощью SSL:
Docker, содержащий модель, будет загружен в реестр контейнеров Azure. Мы создадим центр сертификации (CA) и самозаверяющие сертификаты для использования API.
Предпосылки:
- Машина Linux с параметрами sudo
- Докер установлен в Linux
- Azure CLI установлен в Linux
- openssl установлен в Linux
- Действующая подписка для Azure
ПРИМЕЧАНИЕ.
Если у вас нет действующей подписки, вы можете создать бесплатную учетную запись для Azure и получить 2 недели пробного периода.
Шаг 1. Нам нужна модель
Обучите модель Tensorflow и сохраните ее с помощью: tf.saved_model.save(model, "path / to / model")
OR:
Вот демонстрационная модель, которая представляет f (x) = 2 * x -1. Он был сохранен в «./demo_model/»
import os import numpy as np import tensorflow as tf xs = np.array([-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], dtype=float) ys = np.array([-3.0, -1.0, 1.0, 3.0, 5.0, 7.0], dtype=float) model = tf.keras.Sequential([tf.keras.layers.Dense(units=1, input_shape=[1])]) model.compile(optimizer='sgd', loss='mean_squared_error') history = model.fit(xs, ys, epochs=500, verbose=0) print("Finished training the model") MODEL_DIR = "./demo_model" version = 1 export_path = os.path.join(MODEL_DIR, str(version)) model.save(export_path, save_format="tf") print('\nexport_path = {}'.format(export_path))
Я не буду вдаваться в подробности обслуживания Tensorflow, потому что есть отличный курс Coursera. Этот код используется выше - это код, используемый из этого курса, но только сжатый для этой демонстрации. Вы можете узнать больше о TFServing здесь.
Шаг 2. Поместите модель в контейнер TFServing.
Откройте терминал и перейдите в каталог, в котором вы сохранили модель.
$ MODEL_NAME=demo_model #REPLACE with your model $ MODEL_PATH="./$MODEL_NAME" $ CONTAINER_NAME="$MODEL_NAME" $ sudo docker run -d --name serving_base tensorflow/serving $ sudo docker cp "$MODEL_PATH" serving_base:"/models/$MODEL_NAME" $ sudo docker commit --change "ENV MODEL_NAME $MODEL_NAME" serving_base "$CONTAINER_NAME" $ sudo docker run -d -p 8501:8501 "$MODEL_NAME"
Теперь у вас есть ваша модель, представленная в виде API на localhost, и вы можете вызывать ее с терминала:
$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -X POST "http://localhost:8501/v1/models/$MODEL_NAME:predict"
Или из питона:
import requests import json api_url = "http://localhost:8501/v1/models/demo_model:predict" data = json.dumps({"signature_name": "serving_default", "instances": [[10.0],[9.0]]}) headers = {"content-type": "application/json"} json_response = requests.post(api_url, data=data, headers=headers) print(json_response.text)
#STOP docker image sudo docker container stop IMAGE_ID #DELETE docker image sudo docker container rm IMAGE_ID
Развертывание №1: ЗАВЕРШЕНО!
Теперь вы можете использовать свою модель в localhost.
БОНУСНЫЙ СОВЕТ. Другой вариант - переадресация портов. Подключаешься к машине по SSH.
ssh -N -f -L localhost:8501:localhost:8501 [email protected]
ДАУНСИДЫ:
- Вы можете использовать его только локально
- Чтобы использовать переадресацию портов, вам нужен доступ к VPN.
- Не масштабируемый
Шаг 3. Создайте реестр контейнеров
Используется для хранения контейнеров с моделями.
Перейдите на портал Azure и найдите Реестры контейнеров. Нажмите кнопку Добавить новый реестр, чтобы создать новый реестр.
ВАЖНО:
- Выберите действительную подписку и группу ресурсов.
- Имя реестра должно быть уникальным, оно нам понадобится позже, так что примите это к сведению. (СОВЕТ: используйте только строчные буквы)
- Местоположение является обязательным, и вы должны выбрать ближайший к вам
- Базового SKU должно хватить в большинстве случаев.
Теперь вы можете нажать на кнопку Проверить и создать этот реестр.
ВАЖНО: перейдите к своему ресурсу и включите пользователя с правами администратора. Запишите имя пользователя и пароль.
Шаг 4. Отправьте контейнер Docker в реестр контейнеров.
$ CONTAINER_REGISTRY_NAME=blogdemo #REPLACE (lower letters only) $ sudo az login #login to Azure $ sudo az acr login --name "$CONTAINER_REGISTRY_NAME" $ sudo docker images
$ VERSION_NAME="$CONTAINER_REGISTRY_NAME.azurecr.io/version1" $ sudo docker tag 2b458f67dac3 "$VERSION_NAME" #REPLACE $ sudo docker push "$VERSION_NAME"
Поздравляю:
Вы успешно сохранили контейнер с моделью.
Шаг 5. Откройте модель с помощью экземпляра контейнера.
После хранения контейнера экспонировать модель довольно просто.
Самый простой способ - войти на портал Azure, перейти к Экземплярам контейнеров и нажать кнопку Добавить. После присвоения имени контейнеру выберите желаемый регион и выберите реестр, в который вы загрузили модель.
ПРИМЕЧАНИЕ.
Размер действительно зависит от размера вашей модели. Размер, использованный в этом примере, более чем достаточен для этой модели. Я развернул модель с ~ 150 миллионами параметров (600 МБ), и ей потребовалось 2 виртуальных процессора и 4 ГБ памяти.
Теперь нажмите кнопку Далее: Сеть.
ПРИМЕЧАНИЕ. DNS-имя не является обязательным и должно быть уникальным.
Нажмите Просмотреть и создать, и ваша модель готова к использованию. (Чтобы удалить экземпляр, просто перейдите к ресурсу и нажмите кнопку Удалить)
ИСПОЛЬЗОВАНИЕ:
$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -X POST "http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict" #REPLACE blogdemo.westeurope.azurecontainer.io WITH DNS #OR: $ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -X POST "http://IP.ADDRESS:8501/v1/models/$MODEL_NAME:predict" #REPLACE IP.ADDRESS WITH IP
Использование модели аналогично использованию модели, доступной локально, с той лишь разницей, что это URL-адрес. Вы можете использовать URL-адрес DNS или IP-адрес.
Развертывание №2: ЗАВЕРШЕНО!
Вы развернули модель, которую можно использовать в Интернете. Вы можете отслеживать использование модели и при необходимости увеличивать размер контейнера, что делает его масштабируемым.
ДАУНСИДЫ:
- Все, кто знает IP-АДРЕС, могут получить доступ к модели.
- Не зашифровано
Шаг 6. Создайте центр сертификации и пользователей.
Для защиты API нам понадобится шифрование. Если вы используете коммерческий центр сертификации, вы можете пропустить этот шаг. Создадим собственные и самоподписные сертификаты.
$ mkdir certs $ cd certs $ openssl ecparam -genkey -name secp256r1 | openssl ec -out ca.key $ openssl req -new -x509 -days 3650 -key ca.key -out ca.pem
Вам будет предложено заполнить форму. Заполните его своей информацией.
ПРИМЕЧАНИЕ.
Это пример эллиптического шифрования для простоты. Я рекомендую использовать ключ RSA. Это отличная суть, объясняющая, как это сделать.
$ CLIENT_ID=”client” $ CLIENT_SERIAL=01 #when creating new user make sure that serial is unique
$ openssl ecparam -genkey -name secp256r1 | openssl ec -out “${CLIENT_ID}.key”
$ openssl req -new -key “${CLIENT_ID}.key” -out “${CLIENT_ID}.csr” #password should be empty
$ openssl x509 -req -days 3650 -in “${CLIENT_ID}.csr” -CA ca.pem -CAkey ca.key -set_serial “${CLIENT_SERIAL}” -out “${CLIENT_ID}.pem”
$ cat “${CLIENT_ID}.key” “${CLIENT_ID}.pem” ca.pem > “${CLIENT_ID}.full.pem”
#OPTIONAL:$ openssl pkcs12 -export -out “${CLIENT_ID}.full.pfx” -inkey “${CLIENT_ID}.key” -in “${CLIENT_ID}.pem” -certfile ca.pem
#remember passoword and you will pass it with pfx file
Шаг 7. Настройте NGINX
Зачем нам NGINX?
Чтобы отклонить все запросы, у которых нет действующего сертификата.
Создайте файл nginx.config следующим образом:
$ cd .. $ cat > nginx.conf
Скопируйте все и вставьте в файл. После вставки нажмите CTRL + D, чтобы закрыть редактор.
user nginx;
worker_processes auto;
events { worker_connections 1024; }
pid /var/run/nginx.pid;
http {
server { listen [::]:443 ssl; listen 443 ssl;
server_name localhost;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK; ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m; # a 1mb cache can hold about 4000 sessions, so we can hold 40000 sessions ssl_session_timeout 24h;
keepalive_timeout 300; # up from 75 secs default
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';
ssl_certificate /etc/nginx/server.pem; ssl_certificate_key /etc/nginx/server.key; ssl_client_certificate /etc/nginx/ca.pem; ssl_verify_client on; location / { proxy_pass http://localhost:8501; # TODO: replace with correct port proxy_set_header Connection ""; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; } } }
Я не буду вдаваться в подробности NGINX, но вот ссылка для подробностей. Эта конфигурация представляет собой смесь этого блога и этой статьи. Вкратце, он слушает только https (порт 443) и проверяет полученный сертификат. Если сертификат действителен, он перенаправляет расшифрованный запрос в контейнер с моделью и зашифровывает ответ, который отправляется обратно клиенту.
Шаг 8. Подготовьте файл настроек для Azure
Создайте файл test-deploy.yaml для Azure:
$ cat > test-deploy.yaml
Скопируйте все и вставьте в файл. После вставки нажмите CTRL + D, чтобы закрыть редактор.
location: LOCATION name: NAME properties: containers: - name: model properties: image: IMAGE.azurecr.io/version1:latest ports: - port: 8501 protocol: TCP resources: requests: cpu: 1.0 memoryInGB: 1.5 - name: nginx-with-ssl properties: image: nginx ports: - port: 443 protocol: TCP resources: requests: cpu: 1.0 memoryInGB: 1.5 volumeMounts: - name: nginx-config mountPath: /etc/nginx imageRegistryCredentials: - server: SERVER.azurecr.io username: USERNAME password: PASSWORD volumes: - secret: server.pem: PEM server.key: KEY ca.pem: CA nginx.conf: CONF name: nginx-config ipAddress: ports: - port: 443 protocol: TCP type: Public dnsNameLabel: DNS osType: Linux tags: null type: Microsoft.ContainerInstance/containerGroups
Мы будем использовать команду sed для замены частей этого шаблона. Это команды, которые вы можете запускать без редактирования.
$ sed -i -e "s/name: NAME/name: $MODEL_NAME/" azure.yaml $ sed -i -e "s/image: IMAGE/image: $CONTAINER_REGISTRY_NAME/" azure.yaml $ sed -i -e "s/server: SERVER/server: $CONTAINER_REGISTRY_NAME/" azure.yaml $ sed -i -e "s/username: USERNAME/username: $CONTAINER_REGISTRY_NAME/" azure.yaml $ sed -i -e "s/server.pem: PEM/server.pem: $(cat ./certs/ca.pem | base64 -w 0)/" azure.yaml $ sed -i -e "s/server.key: KEY/server.key: $(cat ./certs/ca.key| base64 -w 0)/" azure.yaml $ sed -i -e "s/ca.pem: CA/ca.pem: $(cat ./certs/ca.pem | base64 -w 0)/" azure.yaml $ sed -i -e "s/nginx.conf: CONF/nginx.conf: $(cat ./nginx.conf | base64 -w 0)/" azure.yaml
Эти команды необходимо отредактировать. Замените выделенные жирным шрифтом части местоположением и паролем из реестра и используйте свой DNS для замены DNS_NAME.
$ sed -i -e "s/location: LOCATION/location: westeurope/" azure.yaml $ sed -i -e "s/password: PASSWORD/password: REGISTRY_PASS/" azure.yaml #TIP: If your generated password has some special characters #like / you will have to manually put \/ infront $ sed -i -e "s/dnsNameLabel: DNS/dnsNameLabel: DNS_NAME/" azure.yaml
Шаг 9: Разверните модель
Замените ‹AZURE RESOURCE GROUP› своей действующей группой ресурсов, и все готово.
$ az container create --resource-group <AZURE RESOURCE GROUP> --name "$MODEL_NAME" -f azure.yaml
Шаг 10: использование
Убедитесь, что у вас есть правильный путь к client.key и client.pem, и вы можете продолжить вызов API следующим образом:
$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -v --key "./certs/client.key" --cert "./certs/client.full.pem" -X POST -k https://blogdemo.westeurope.azurecontainer.io/v1/models/demo_model:predict #REPLACE blogdemo.westeurope.azurecontainer.io WITH DNS #OR: $ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -v --key "./certs/client.key" --cert "./certs/client.full.pem" -X POST -k https://IP.ADDRESS/v1/models/demo_model:predict #REPLACE IP.ADDRESS WITH IP
Если все работает нормально, вы получите такой ответ:
ВАЖНО: поскольку это самозаверяющий сертификат, вы должны добавить флаг -k в cURL.
Версия Python:
import requests import json api_url = "https://blogdemo.westeurope.azurecontainer.io/v1/models/demo_model:predict" data = json.dumps({"signature_name": "serving_default", "instances": [[10.0],[9.0]]}) headers = {"content-type": "application/json"} json_response = requests.post(api_url, data=data, headers=headers,verify=False,cert=("./certs/client.pem","./certs/client.key"),) print(json_response.text)
ВАЖНО: поскольку это самозаверяющий сертификат, необходимо установить verify = False.
При вызове без действующего сертификата:
Развертывание №3: ЗАВЕРШЕНО!
Шаг 11: Монитор
Вы можете отслеживать использование модели в экземпляре контейнера и видеть запросы, сделанные в журналах nginx.
ЧАСТО ЗАДАВАЕМЫЕ ВОПРОСЫ:
В: Могу ли я развернуть пользовательскую модель (без обслуживания TF)?
О: Да. Вы должны докерировать модель, и если вы используете правильные порты, все остальное должно работать должным образом.
В: Могу ли я развернуть его на AWS?
О: Да, используя nginx.config и конфигурацию CA, представленную в этом блоге.
В: Могу ли я хранить контейнер в Docker Hub?
О: Да. Просто замените изображение на имя пользователя / репо: версию из Docker Hub и удалите imageRegistryCredentials из файла YAML.
РЕСУРСЫ:
https://i2.wp.com/miro.medium.com/max/800/1*dIKRcFllFP3_-yqCysDitw.png
https://i2.wp.com/miro.medium.com/max/600/0*7eCQeU5D86SQeFHa.png
Https://cdn0.iconfinder.com/data/icons/home-security-2/45/security-06-512.png