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