Пошаговое руководство по запуску моделей машинного обучения в производство

Что вы узнаете:

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://upload.wikimedia.org/wikipedia/commons/thumb/6/67/Reverse_proxy_h2g2bob.svg/1200px-Reverse_proxy_h2g2bob.svg.png

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

Https://skillvalue.com/jobs/wp-content/uploads/sites/7/2019/10/data-scientist-freelance-remote-new-york.jpg