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

    No Comments

    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

    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.

     

    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ć

    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.

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

    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…

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

    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).

    Informacje zwracane przez ostanie polecenie:

    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ć

    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) .

    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:

    Wynik w postaci tabeli (-o table)

    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.

    Zwracane rezultaty są w formacie JSON.

    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).

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

     

    Literatura:

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

     

     

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

    2 komentarze

    Jeden z pierwszych  moich wpisów na blogu dotyczył  uczenia maszynowego według wujka Google.

    Od tego czasu pojawiły się nowe usługi , które znacznie ułatwiają rozpoczęcie pracy z modelami uczenia maszynowego.

    Postanowiłem przybliżyć jedną z usług Microsoftu, którą można przetestować za darmo.

    https://www.customvision.ai/

     

    Spróbujmy przygotować model, który potrafi klasyfikować zdjęcia. Na zdjęciach będziemy rozpoznawać różne zwierzęta.

    Serwis wymaga zalogowania się na konto Microsoft, ja wykorzystałem swój LiveID.

    Po zalogowaniu tworzymy nowy projekt:

     

    Wypełniamy nazwę projektu, jego opis, projekt typu klasyfikatora (Classification) i typ klasyfikacji (MultiClass). W ten sposób każdy z obrazów będzie otagowany jedną cechą. Jako domenę użyłem (General compact). Ustawienie modelu jako przenośnego będzie ważne dla dalszej części naszej przygody, gdy będziemy eksportować gotowy model.

    Jako zbioru treningowego dla trzech kategorii (kot, pies, koń) użyłem zdjęć z poniższych adresów:

    a) psy i koty

    https://www.floydhub.com/fastai/datasets/cats-vs-dogs/2/train

    b) konie:

    https://jamie.shotton.org/work/data.html

    Dla każdego z gatunków zwierząt załadowałem odpowiednio po sto zdjęć. To jest nasz zbiór treningowy, który zawiera trzysta przypadków oznakowanych “cat”, “dog” lub “horse”. Ten zbiór zawiera poprawnie przypisane otagowanie i jest bazą do przygotowania modelu uczenia maszynowego, który będzie potrafił odpowiedzieć na pytanie: Czy na zdjęciu (którego model nie zna, gdyż nie ma go w bazie treningowej) jest widoczny kot, pies, czy koń? Budujemy klasyfikator, który jest jednym z głównych algorytmów uczenia nadzorowanego. To my dostarczamy bazę wiedzy i w niej zawieramy cechę, która jest dla modelu prawdą.

    Po załadowaniu wszystkich zdjęć przystępujemy do trenowania danych.

    Parametrami tak wytrenowanego modelu są precyzja (precision) i czułość (recall).

    Co oznaczają te dwie liczby?

    Zacznijmy od tego czym jest  macierz pomyłek

    Image result for "macierz pomyłek"

    Na obrazie pokazano macierz pomyłek dla modelu uproszczonego do dwóch stanów.

    Załóżmy, że na zdjęciu może być kot (prawda, T(rue)) lub nie-kot (fałsz, F(false)). Nasz model predykcyjny wykrywa, tylko te dwa przypadki.

    Mamy cztery możliwości:

    1. Na zdjęciu jest kot, model wykrywa kota, mamy przypadek TP (True Positive)
    2. Na zdjęciu jest nie-kot, model wykrywa nie-kota, mamy przypadek TN (True Nagative)
    3. Na zdjęciu jest kot, model wykrywa nie-kota, mamy przypadek FN (False Negative)
    4. Na zdjęciu jest nie-kot, model wykrywa nie-kota, mamy przypadek  FP (False Positive)

    Precision (precyzja) to iloraz -> TP/(TP+FP)

    Recall (czułość)  to iloraz TP/(TP+FN)

    Po przygotowaniu wytrenowanego modelu możemy go wyeksportować. Mamy kilka możliwości, możemy zapisać go do rozwoju na urządzeniach mobilnych (iOS lub Android). Najciekawszą opcją jest, moim zdaniem możliwość, która kryje się pod nazwa DF (Dockerfile) .

    Model raz nauczony można też w prosty sposób testować na nowych zdjęciach. Te nowe zdjęcia mogą być kolejnym wsadem, który rozbudowuje naszą bazę wiedzy dla danych treningowych.  Pamiętajmy, że proces uczenia ma często cykliczny charakter.

    Pobieramy spakowany kod aplikacji w Pythonie, która wykorzystuje jako silnik uczenia maszynowego TensorFlow, serwerem www jest Flask.

    Częścią takiej aplikacji nie jest żadne ze zdjęć, które były używane w procesie uczenia modelu. Zapisywany jest jedynie plik, który zawiera konfigurację całego modelu zapisanego w formacie danego silnika. W tym przypadku jest to TensorFlow, a plik nosi nazwę model.pb.

    Zawartość pliku Dockerfile

    Najważniejsze pliki znajdują się w katalogu app

    app.py – główny kod aplikacji

    labels.txt – lista możliwych oznaczeń dla zdjęcia

    cat
    dog
    horse

    model.pb –  zapisane parametry wytrenowanego modelu w TensorFlow
    predict.py – kod w Pythonie obsługujący model uczenia maszynowego

    requirements.txt – lista modułów, które są potrzebne do działania aplikacji

    tensorflow==1.5.0
    pillow==5.0.0
    numpy==1.14.1
    flask==0.12.3

    Całość aplikacji umieściłem  na moim koncie na Githubie.

    Ciekawostką jest to, że po dodaniu projektu do repozytorium pojawia się informacja o potencjalnym problemie z bezpieczeństwem takiej aplikacji:

    djkormo,

    We found a potential security vulnerability in a repository for which you have been granted security alert access.

    @djkormo djkormo/ContainersSamples
    Known moderate severity security vulnerability detected in flask < 0.12.3 defined in requirements.txt.
    requirements.txt update suggested: flask ~> 0.12.3.
    Always verify the validity and compatibility of suggestions with your codebase.

     

    Trudno, jedna usługa Microsoftu generuje kod, a druga usługa Microsoftu uznaje go za niebezpieczny. Prawdziwa moc synergii.

    Po poprawieniu kodu zgodnie z sugestią, komunikat znika.

    Przykładowy kod umieściłem w repozytorium githuba.

    https://github.com/djkormo/ContainersSamples/tree/master/Docker/customvision-ai-cat-dog-horse-sample

    Spróbujmy zbudować z gotowego projektu REST API, które po załadowaniu na wejściu dowolnego zdjęcia, będzie w stanie odpowiedzieć na pytanie, czy znajduje się na nim jedno z trzech gatunków zwierząt.

    Wykorzystam do tego darmową piaskownicę

    https://labs.play-with-docker.com/

     

    Serwis wymaga założenia konta  w publicznym repozytorium DockerHuba.

    Po podaniu naszych poświadczeń pojawia się przycisk start

    Piaskownica wyłączy się sama po czterech godzinach. Pamiętajmy, aby nie trzymać tam poufnych i wrażliwych danych.

    Naciskamy przycisk +Add New Instance i uruchamiany nasz wirtualny kontener.

    Na początek klonujemy repozytorium z githuba

    Operacja wklejania to skrót klawiszowy Shift+Insert
    Operacja kopiowania to skrót klawiszowy Control+Insert

    Wchodzimy do odpowiedniego podkatalogu. To repozytorium zawiera też inne aplikacje.

    Zawartość wnętrza tego katalogu

    Rozpoczynamy proces budowy kontenera.

    Po krótkiej chwili następuje zbudowanie obrazu aplikacji.

    Konfiguracja tego procesu jest zapisana w pliku Dockerfile.

    Krótki opis dla osób, które nigdy nie korzystały z technologii konteneryzacji.

    Najpierw pobierany jest z repozytorium DockerHub obraz bazowy python:3.5.

    FROM python:3.5

    Dodawana jest zawartość katalogu app z hosta do katalogu app kontenera

    ADD app /app

    Instalowana jest aktualizacja oprogramowania do kontroli pakietów w Pythonie.

    RUN pip install –upgrade pip

    Instalowane są pakiety do Pythona zawarte w pliku  requirements.txt.

    RUN pip install -r /app/requirements.txt

    Port 80 zostaje udostępniony z kontenera do hosta.

    EXPOSE 80

    Ustalany jest katalog roboczy aplikacji w kontenerze.

    WORKDIR /app

    Uruchamiane jest polecenie, dzięki któremu  powstaje serwer www na udostępnionym wcześniej porcie 80.

    CMD python app.py

    Po zbudowaniu obrazu możemy uruchomić aplikację

    Podczas uruchamiania aplikacji wskazujemy jej nazwę customvision-ai:v1, mapowaniu portu kontenera (80) na port hosta (33000), flaga -d oznacza odłączenie się od kontenera. Będzie on uruchomiony niejako w tle.

    Nad oknem terminala pojawia się port, który został wystawiony po uruchomieniu aplikacji w kontenerze. W naszym przypadku jest to port 33000.

    Po uruchomieniu aplikacji możemy sprawdzić, czy usługa działa. Pamiętamy, że aplikacja ma pobrać zdjęcie i określić, czy jest na nim pies, kot czy koń.

    W katalogu images umieściłem sześć zdjęć. Żadne z nich nie było wykorzystywane podczas trenowania modelu.

    Do testowania modelu wykorzystałem polecenie curl. Aplikacja wystawia punkt końcowy (endpoint) pod adresem http://127.0.0.1:33000/image. Wykorzystywany jest  POST.

     

    Bystre oko zauważy, że  jeden z koni został sklasyfikowany z prawdopodobieństwem 100% jako pies.

    Literatura:

    https://pl.wikipedia.org/wiki/Tablica_pomy%C5%82ek

    https://github.com/djkormo/ContainersSamples/tree/master/Docker/customvision-ai-cat-dog-horse-sample

    Mała aktualizacja,  wygląda na to że serwis będzie wkrótce zamknięty. Kolejna część o tematyce ML w przygotowaniu

     

     

    Pierwsze zabawy z Kubernetes – część 1 Docker

    No Comments

    Konteneryzacja aplikacji wykorzystująca Dockera i ich orkiestracja  za pomocą Kubernetes  przebojem zdobywają coraz większą grupę zwolenników. Postanowiłem zmierzyć się z tymi produktami.

    Założyłem, ze wykorzystam jakieś darmowe środowisko, które pozwoli mi na pierwsze zabawy, bez zbędnej instalacji oprogramowania na stacji roboczej.  Powtarzalne operacje zostaną zapisane w plikach na Githubie.

    Niniejszy wpis wykorzystuje https://labs.play-with-k8s.com/ .

    Uruchomienie środowiska wymaga zalogowania się na konto GitHub lub na konto DockerHuba. Wybrałem tę drugą opcję.

    Po podaniu naszych poświadczeń i po naciśnięciu zielonego przycisku Start mamy do dyspozycji uruchomione gotowe środowisko do testów, które zostaje powołane na czas czterech godzin.

    Na początku musimy dodać pierwszą instancję. Po krótkiej chwili mamy już gotowe środowisko z dostępem do shella.

    Pierwsze polecenie uname -a

    Mamy dostęp do schowka
    Shift + insert = kopiuj
    Ctrl + insert = wklej

    Co więcej możemy doinstalować sobie potrzebne pakiety, np. edytor nano, midnight commander i inne cuda…

    Spróbujmy uruchomić prostą aplikację w kontenerze z gotowego publicznego repozytorium Docker Hub.

    Na początku uruchomimy pusta aplikacje www opartą o popularny serwer apache2.

    Polecenie to uruchamia obraz o nazwie httpd w wersji 2.4 z wystawiony portem tcp 80 od strony kontenera, port hosta zostanie zmapowany automatycznie. Za chwile zobaczymy na jaką wartość. Obrazu tego nie było do tej pory lokalnie, stąd komunikat “unable to find image”.  Przed budową zostanie on ściągnięty z repozytorium Docker Huba.

    Otrzymany wynik:

    Sprawdźmy listę naszych obrazów

    Otrzymany wynik:

    Sprawdzamy, które obrazy są uruchomione.

    Otrzymany wynik:

    Port 80 tcp kontenera zostaje zmapowany na port 32768 hosta.

    Pojawia się niebieskie łącze o nazwie wystawionego portu. Po kliknięciu mamy stronę z napisem

    It works!

     

    Wykorzystując gotowe obrazy, które  można traktować jako bazę, mamy możliwość tworzenia na ich podstawie bardziej skomplikowanych funkcjonalnie aplikacji. W tym celu możemy wykorzystać plik konfiguracyjny, tak zwany Dockerfile.

    Przykładowy Dockerfile:

    Uzupełniamy zawartość pustego pliku Dockerfile i budujemy nasz kontener.

    Uruchamiamy nasz kontener

    Jak widać kontener nasłuchuje na porcie 32770 hosta

    Można tę informację pobrać w bardziej elegancji sposób:

    Na górze powinien pojawić się niebieski link z takim numerem portu.
    Po uruchomieniu
    http://ip172-18-0-22-bf4df2mn5ugg00cc6t5g-32770.direct.labs.play-with-k8s.com/

    Mamy widoczną naszą aplikację.

    Warto zapamiętać budowę linku:

    http://ip<hyphen-ip>-<session_jd>-<port>.direct.labs.play-with-k8s.com

    A naszym przypadku był to

    http://ip172-18-0-22-bf4df2mn5ugg00cc6t5g-32770.direct.labs.play-with-k8s.com/

    Warto pamiętać o kilku zasadach, które pomagają budować obrazy kontenerowe w sposób efektywny

    1. Wybieramy obraz, z najbardziej optymalną  funkcjonalnością, czyli np. taki który zawiera tylko potrzebne oprogramowanie i nic więcej.
    2. Instalujemy tylko potrzebne oprogramowanie.
    3. Niepotrzebne pliki tymczasowe, które powstają podczas instalacji oprogramowania powinny zostać usunięte.
    4. Każde polecenie RUN tworzy nowa warstwę w obrazie, eliminujmy ich liczbę. Stad często widoczne jest sklejanie poleceń przez &&.
    5. Każdy projekt powinien mieć własny plik .dockerignore, w którym zawarte są reguły wykluczające pliki i katalogi w procesie budowania.

    Wykonajmy prosty eksperyment.

    Przygotowałem dwie wersje pliku DockerFile,

    jeden to obraz ubuntu:16.04,

    https://github.com/djkormo/ContainersSamples/blob/master/Docker/chess-ai/ubuntu/Dockerfile

    drugi to alpine:3.7.

    https://github.com/djkormo/ContainersSamples/tree/master/Docker/chess-ai/alpine/Dockerfile

    Otrzymany wynik:

    Wchodzimy do podkatalogów

    W podkatalogu mamy trzy wersje plików Dockeffile

    W każdym z podkatalogów budujemy lokalna wersje obrazu, tu dla przykładu dla obrazu pochodzącego z dystrybucji alpine.

    Dla obrazu Ubuntu

    Po zbudowaniu tych dwóch obrazów , możemy porównać ich rozmiar

    Otrzymujemy następujące wartości:

    To co widać od razu, z obrazu bazowego alpine:3.7 o wielkości 4.2MB powstał obraz local/chess-ai-alpine o wielkości 28.3MB. Natomiast z bazowego obrazu ubuntu:16.04 o wielkości 116MB powstał obraz localhost/chess-ai-ubuntu o wielkości 275MB. Funkcjonalność obu aplikacji, czyli prosta gra w szachy napisania w Javascript, jest taka sama, a różnica w wielkości jest dziesięciokrotna.

    Literatura:

    https://medium.com/@marcosnils/introducing-pwk-play-with-k8s-159fcfeb787b

    https://github.com/play-with-docker/play-with-docker/issues/259

    http://phusion.github.io/baseimage-docker/

    http://collabnix.com/kubernetes-hands-on-lab-1-setting-up-5-node-k8s-cluster/

    Categories: konteneryzacja, migawka