Uczenia maszynowe dla ubogich. Od trenowania modelu do dedykowanej usługi. Część 2

W poprzednim wpisie starałem się pokazać, jak  w prosty sposób uruchomić model uczenia maszynowego do klasyfikacji zdjęć.

Przygotowanie modelu odbyło się w aplikacji https://www.customvision.ai/

Do uruchomienia gotowego modelu  w postaci REST API wykorzystałem darmową piaskownicę https://labs.play-with-docker.com/.

Każda z tych usług ma swoje ograniczenia. Tak pierwsza pozwala na budowę jedynie dwóch modelu w planie darmowym. Tak druga ma czasowe ograniczenie do czterech godzin działania. Na początek to może wystarczyć, ale postanowiłem zająć się drugą częścią, czyli uruchomieniem gotowego modelu uczenia maszynowego w chmurze publicznej Azure. Wykorzystam dwie usługi ACR (Azure Container Registry) i ACI (Azure Container Instances).  Wdrożenie odbędzie się przy wykorzystaniu poleceń cli, albo z poziomu Visual Studio Code, albo z poziomu Azure Cloud Shell. Nie będzie potrzeby instalacji narzędzi typu Docker for Windows.

Będziemy głównie wykorzystywać linię komend, od czasu do czasu spoglądając w Portal.

Na początku definiujemy globalne zmienne

# definicja zmiennych globalnych 
ACR_LOCATION=northeurope 
ACR_GROUP=rg-machinelearning 
ACR_NAME=djkormoacrml 
ACI_NAME=djkormoaciml

Tworzymy prywatny rejestr ACR w ramach grupy zasobów ACR_GROUP. W tym przypadku nazwałem ją rg-machinelearning. Zasoby zostaną umieszczone w jednej lokalizacji northeurope  ( Dublin w Irlandii). Rejestr zasobów zostanie nazwany djkormoacrml , a utworzona instancja aplikacji djkormoaciml.

# katalog w którym umieścimy sklonowany kod

INIT_DIR=c:/developing/Azure/ML/

cd $INIT_DIR

# domyslna lokalizacja
az configure --defaults location=$ACR_LOCATION

# nowa grupa zasobow
az group create --name $ACR_GROUP

# domyslna grupa zasobow
az configure --defaults group=$ACR_GROUP

 

Wszelkie zasoby, będą celowo umieszczone w jednej grupie. W ten sposób łatwiej się zarządza kosztami. Dodatkowo dzięki ustawieniu domyślnej nazwy grupy i domyślnej lokalizacji nie musimy przy budowie usług podawać

--resource-group rg-grupa --location northeurope

Na początku pobieramy z publicznego repozytorium na Githubie kod naszej aplikacji i umieszczamy na dysku lokalnym (lub na dysku Cloud Shella). W tej wersji skryptu wykorzystałem laptop z Windows 7.

# klonujemy repozytorium, sprawdzając czy nie istnieje już taki katalog

if [ ! -d $INIT_DIR/ContainersSamples ]; then
  
  mkdir ssh
  echo "Nie masz katalogu ContainersSamples"  
  git clone https://github.com/djkormo/ContainersSamples.git
fi

# wchodzimy do podkatalogu naszego projektu 

cd ContainersSamples/Docker/customvision-ai-cat-dog-horse-sample/

# odswiezamy zawartosc repozytorium w ramach galęzi master 
git checkout master
# pobieramy zawartość repozytorium 
git pull
# weryfikujemy niespójności zawartości lokalnego i zdalnego repozytorium 
git status

# zawartosc pliku DockerFile
cat Dockerfile 

Tak wygląda konfiguracja naszej aplikacji zawarta w pliku Dockerfile. Więcej szczegółów można znaleźć w pierwszej części artykułu.

FROM python:3.5

ADD app /app

RUN pip install --upgrade pip
RUN pip install -r /app/requirements.txt

# Expose the port
EXPOSE 80

# Set the working directory
WORKDIR /app

# Run the flask server for the endpoints
CMD python app.py

Tworzymy nasz rejestr obrazków, na potrzeby tego demo wystarczy najtańsza wersja (Basic). Pozwala ona na umieszczenie zbioru obrazów od łącznej pojemności nie przekraczającej 10GB. To repozytorium otrzymuje wpis w DNS $ACR_NAME.azurecr.io

Uchomienie nslookup po zdbudowaniu rejestru…

Nieautorytatywna odpowiedź:
Nazwa: r0213neu.northeurope.cloudapp.azure.com
Address: 13.69.227.89
Aliases: djkormoacrml.azurecr.io
neu.fe.azcr.io
neu-acr-reg.trafficmanager.net

Rozpoczynamy budowę naszego rejestru, zanim umieścimy w nim jakikolwiek obraz.

# tworzymy rejestr dla kontenerow 
az acr create --name $ACR_NAME --sku Basic 

# wlaczenie konta administratorskiego 
az acr update -n $ACR_NAME --admin-enabled true

Uruchamiamy proces budowy naszego obrazu, potrwa to do około dwóch minut. Świeżo przygotowany obraz zostanie umieszczony w prywatnym repozytorium ACR.. Jako nazwę obrazu przyjąłem ai-customvision , a po : umieszczam numer wersji, w tym przypadku v1. Zawartość pliku Dockerfile  zawiera przepis z jakich komponentów i w jakiej kolejności  będzie przebiegał proces. Kod uruchamiamy w bieżącym katalogu (stąd kropka po nazwie obrazu).

# pobranie wygenerowanego użytkownika i hasła
ACR_USERNAME=$ACR_NAME
ACR_PASSWORD=$(az acr credential show --name $ACR_NAME --query "passwords[0].value")

echo "$ACR_USERNAME"
echo "$ACR_PASSWORD"

# budujemy obraz kontenerowy  na podstawie zawartości pliku Dockerfile w bieżącym katalogu (.)
az acr build --registry $ACR_NAME --image ai-customvision:v1 .


# lista zbudowanych obrazów
az acr repository list --name $ACR_NAME --output table

# szczegoly danego obrazu
az acr repository show -n $ACR_NAME -t ai-customvision:v1

Informacje zwracane przez ostanie polecenie:

{
  "changeableAttributes": {
    "deleteEnabled": true,
    "listEnabled": true,
    "readEnabled": true,
    "writeEnabled": true
  },
  "createdTime": "2019-03-09T21:29:56.021222Z",
  "digest": "sha256:656234a912ca0b613f57b4504a92395402ca650d89428a173bfb91b41e5b7f30",
  "lastUpdateTime": "2019-03-09T21:29:56.021222Z",
  "name": "v1",
  "signed": false
}

Jak widać, obraz może być zaktualizowany, wylistowany, usunięty, odczytany i zapisany.
Istnieje też możliwość zbudowania obrazu bez umieszczania go w repozytorium. Wystarczy dodać

 --no-push

w linii poleceń.

Po zbudowaniu naszego obrazu i umieszczeniu w prywatnym repozytorium ACR przyszedł czas na uruchomienie jednej instancji naszej aplikacji. Wykorzystamy do tego ACI (Azure Container Instances) .

az container create --resource-group $ACR_GROUP \
    --name $ACI_NAME \
    --image $ACR_NAME.azurecr.io/ai-customvision:v1 \
    --cpu 2 --memory 4 \
    --registry-login-server $ACR_NAME.azurecr.io \
    --dns-name-label ai-customvision \
    --ports 80 \
    --ip-address=Public \
    --registry-username $ACR_USERNAME \
    --registry-password $ACR_PASSWORD

Aplikacja jest dostępna pod adresem http://ai-customvision.northeurope.azurecontainer.io, ważne by dodać parametr –dns-name-label, inaczej będziemy skazani na wygenerowany przez Azure numer IP, Usługa będzie pracować na porcie 80. Wykorzystane jest REST API i metoda POST. Wystawiony zostanie publiczny adres IP Do pobrania obrazu z ACR potrzebne są odpowiednie uprawnienia. Dlatego też przekazywana jest para zmiennych $ACR_USERNAME i $ACR_PASSWORD. Pilnujmy  tych poświadczeń, istnieje możliwość ponownego wygenerowania, co jest zresztą zalecaną praktyką. Tego typu dane nie powinny nigdy znaleźć się w repozytorium kodu.

Wykorzystaliśmy do tej pory dwie usługi umieszczone w jednej grupie zasobów. Dobrze widać to z poziomu Portalu.

To samo możemy obejrzeć, do czego zachęcam, z poziomu linii komend:

# lista zasobów umieszczony w  danej grupie zasobów
az resource list --resource-group  $ACR_GROUP -o table

Wynik w postaci tabeli (-o table)

Name          ResourceGroup       Location     Type                                         Status
------------  ------------------  -----------  -------------------------------------------  --------
djkormoaciml  rg-machinelearning  northeurope  Microsoft.ContainerInstance/containerGroups
djkormoacrml  rg-machinelearning  northeurope  Microsoft.ContainerRegistry/registries

Widok instancji z poziomu Portalu

Nadeszła pora, by zweryfikować, czy nasz program działa, tak jak został zaprojektowany.

Przetestujemy naszą uruchomioną aplikację przez API REST, wykorzystując curl i przygotowane w repozytorium Gituba zdjęcia.

Pliki ze zdjęciami znajdują się w podkatalogu images.

# aplikacja dostępna pod adresem 
# http://ai-customvision.northeurope.azurecontainer.io:80

# testowanie
clear
# koty
curl -X POST http://ai-customvision.northeurope.azurecontainer.io/image -F imageData=@images/cat1.jpg
curl -X POST http://ai-customvision.northeurope.azurecontainer.io/image -F imageData=@images/cat2.jpg

# psy
curl -X POST http://ai-customvision.northeurope.azurecontainer.io/image -F imageData=@images/dog1.jpg
curl -X POST http://ai-customvision.northeurope.azurecontainer.io/image -F imageData=@images/dog2.jpg

# konie
curl -X POST http://ai-customvision.northeurope.azurecontainer.io/image -F imageData=@images/horse1.jpg
curl -X POST http://ai-customvision.northeurope.azurecontainer.io/image -F imageData=@images/horse2.jpg

Zwracane rezultaty są w formacie JSON.

$ curl -X POST http://ai-customvision.northeurope.azurecontainer.io/image -F imageData=@images/cat1.jpg
{
  "created": "2019-03-09T22:05:31.011407",
  "id": "",
  "iteration": "",
  "predictions": [
    {
      "boundingBox": null,
      "probability": 1.0,
      "tagId": "",
      "tagName": "cat"
    }
  ],
  "project": ""
}

$ curl -X POST http://ai-customvision.northeurope.azurecontainer.io/image -F imageData=@images/cat2.jpg
{
  "created": "2019-03-09T22:05:36.650195",
  "id": "",
  "iteration": "",
  "predictions": [
    {
      "boundingBox": null,
      "probability": 1.0,
      "tagId": "",
      "tagName": "cat"
    }
  ],
  "project": ""
}

$ # psy

$ curl -X POST http://ai-customvision.northeurope.azurecontainer.io/image -F imageData=@images/dog1.jpg
{
  "created": "2019-03-09T22:05:42.647791",
  "id": "",
  "iteration": "",
  "predictions": [
    {
      "boundingBox": null,
      "probability": 1.0,
      "tagId": "",
      "tagName": "dog"
    }
  ],
  "project": ""
}


$ curl -X POST http://ai-customvision.northeurope.azurecontainer.io/image -F imageData=@images/dog2.jpg
{
  "created": "2019-03-09T22:05:49.057433",
  "id": "",
  "iteration": "",
  "predictions": [
    {
      "boundingBox": null,
      "probability": 1.0,
      "tagId": "",
      "tagName": "dog"
    }
  ],
  "project": ""
}


$ # konie

$ curl -X POST http://ai-customvision.northeurope.azurecontainer.io/image -F imageData=@images/horse1.jpg
{
  "created": "2019-03-09T22:05:54.683635",
  "id": "",
  "iteration": "",
  "predictions": [
    {
      "boundingBox": null,
      "probability": 1.0,
      "tagId": "",
      "tagName": "dog"
    }
  ],
  "project": ""
}


$ curl -X POST http://ai-customvision.northeurope.azurecontainer.io/image -F imageData=@images/horse2.jpg
{
  "created": "2019-03-09T22:06:00.648413",
  "id": "",
  "iteration": "",
  "predictions": [
    {
      "boundingBox": null,
      "probability": 0.0015334499767050147,
      "tagId": "",
      "tagName": "dog"
    },
    {
      "boundingBox": null,
      "probability": 0.9984666109085083,
      "tagId": "",
      "tagName": "horse"
    }
  ],
  "project": ""
}


Jeden z obrazów (horse1) został błędnie sklasyfikowany jako pies i to na dodatek z prawdopodobieństwem 100%. Czy można coś z tym zrobić ? Można, ale to temat na kolejne wpisy autora.

Pamiętajmy o ostatniej linii, która usuwa wszelkie zasoby w obrębie grupy. Usługi w chmurze są ulotne, pamiętajmy o tym, że jeśli nie są potrzebne, nie powinny być aktywne, gdyż z reguły generują niepotrzebne koszty.
Tutaj użyłem wersji bez potwierdzania (-y) i czekania aż się zakończy proces (–no-wait).

# usuwamy zasoby w ramach calej grupy 
az group delete --name $ACR_GROUP --no-wait -y

Poniżej pełny kod skryptu umieszczony w repozytorium.

 

Literatura:

https://markheath.net/post/build-container-images-with-acr

 

 

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.