
Cześć wszystkim! Jestem programistą CV w KROK. Od 3 lat realizujemy projekty w zakresie CV. W tym czasie zrobiliśmy wiele różnych rzeczy, takie jak: monitorowanie kierowców, aby w trakcie jazdy nie pili, nie palili, nie rozmawiali przez telefon, patrzyli na drogę, a nie we śnie lub w chmury; rejestrowanie tych, którzy jeżdżą po wydzielonych pasach i zajmują kilka miejsc na parkingu; dbanie o to, aby pracownicy nosili kaski, rękawice itp.; identyfikowanie pracownika, który chce wejść na teren obiektu; liczenie wszystkiego, co tylko możliwe.
Do czego zmierzam?
W trakcie realizacji projektów napotkaliśmy trudności, wiele trudności, z niektórymi problemami możecie być już zaznajomieni, lub poznacie je w przyszłości.
Modelujemy sytuację
Załóżmy, że przyjęliśmy pracę w młodej firmie „N”, która zajmuje się ML. Pracujemy nad projektem ML (DL, CV), a potem z jakiegoś powodu przeskakujemy na inną pracę, w każdym razie robimy przerwę i wracamy do naszej lub cudzej sieci neuronowej.
- Przychodzi moment prawdy, musimy jakoś przypomnieć sobie, na czym stanęliśmy, jakie hiperparametry próbowaliśmy i, co najważniejsze, do jakich wyników one prowadziły. Może być wiele sposobów, jak kto przechowywał informacje o wszystkich uruchomieniach: w głowie, w konfiguracjach, w notesie, w środowisku roboczym w chmurze. Miałem okazję zobaczyć wariant, w którym hiperparametry były przechowywane w postaci skomentowanych linii w kodzie, ogólnie rzecz biorąc, pole do wyobraźni. A teraz wyobraźcie sobie, że wracacie nie do swojego projektu, a do projektu osoby, która opuściła firmę, a w spadku dostaliście kod i model o nazwie model_1.pb. Dla pełniejszego obrazu i oddania całej bolesnej sytuacji, załóżmy, że jesteście również początkującym specjalistą.
- Idźmy dalej. Aby uruchomić kod, my i wszyscy, którzy będą z nim pracować, musimy stworzyć środowisko. Często zdarza się, że tego również z jakichś powodów nie pozostawiono nam w spadku. To także może być nietrywialne zadanie. Nie chcemy tracić czasu na ten krok, prawda?
- Trenujemy model (na przykład detektor samochodów). Dochodzimy do momentu, w którym staje się on całkiem zadowalający — to czas, aby zapisać wyniki. Nazwiemy go car_detection_v1.pb. Następnie trenujemy kolejny model — car_detection_v2.pb. Po pewnym czasie nasi koledzy lub my sami uczymy się kolejnych wersji, korzystając z różnych architektur. W końcu powstaje wiele artefaktów, które musimy dokładnie zebrać (ale tym zajmiemy się później, bo mamy teraz inne, ważniejsze sprawy).
- No i to wszystko! Mamy model! Możemy przejść do uczenia kolejnego modelu, opracowania architektury dla nowego zadania, czy może wyjść na herbatę? A kto zajmie się wdrożeniem?
Wykrywanie problemów
Praca nad projektem lub produktem to wysiłek wielu osób. Z biegiem czasu ludzie odchodzą i przychodzą, projektów przybywa, a same projekty stają się coraz bardziej skomplikowane. Tak czy inaczej, sytuacje z opisanego powyżej cyklu (i nie tylko) w różnych kombinacjach będą się pojawiać z iteracji na iterację. Wszystko to prowadzi do straty czasu, zamieszania, frustracji, a być może – do niezadowolenia klienta, a w końcu – do utraconych pieniędzy. Chociaż zwykle przechodzimy przez te same pułapki, sądzę, że nikt nie chce powtarzać tych chwil raz za razem.

I tak, przeszliśmy przez jeden cykl rozwoju i widzimy, że są problemy, które trzeba rozwiązać. W tym celu potrzebujemy:
- wygodnie przechowywać wyniki pracy;
- usprawnić proces wprowadzania nowych pracowników;
- uprościć proces wdrażania środowiska deweloperskiego;
- ustalić proces wersjonowania modeli;
- posiadać wygodny sposób walidacji modeli;
- znaleźć narzędzie do zarządzania stanem modeli;
- znaleźć sposób dostarczania modeli do produkcji.
Wygląda na to, że musimy wymyślić workflow, który umożliwi łatwe i wygodne zarządzanie tym cyklem życia? Taka praktyka nosi nazwę MLOps.
MLOps, czyli DevOps dla uczenia maszynowego, umożliwia zespołom danych i specjalistom IT współpracę oraz zwiększa tempo rozwoju i wdrażania modeli poprzez monitorowanie, walidację i zarządzanie modelami uczenia maszynowego.
Możesz , co na ten temat myślą chłopaki z Google. Z artykułu wynika, że MLOps to dość obszerna rzecz.

W swojej artykule opiszę tylko część procesu. Do realizacji wykorzystam narzędzie MLflow, ponieważ jest to projekt open-source, do połączenia potrzeba niewielkiej ilości kodu i istnieje integracja z popularnymi frameworkami ML. Możesz poszukać w internecie innych narzędzi, na przykład Kubeflow, SageMaker, Trains itp. i może znaleźć takie, które lepiej odpowiada Twoim potrzebom.
"Budujemy" MLOps na przykładzie wykorzystania narzędzia MLFlow
MLFlow to platforma z otwartym kodem źródłowym do zarządzania cyklem życia modeli ml ().
MLflow składa się z czterech komponentów:
- MLflow Tracking — zajmuje się rejestrowaniem wyników i parametrów, które prowadzą do tych wyników;
- MLflow Project — pozwala na pakowanie kodu i jego reprodukcję na dowolnej platformie;
- MLflow Models — odpowiada za wdrażanie modeli do produkcji;
- MLflow Registry — umożliwia przechowywanie modeli i zarządzanie ich stanem w centralnym repozytorium.
MLflow operuje na dwóch bytach:
- uruchomienie — to pełny cykl uczenia, parametry i metryki, które chcemy zarejestrować;
- eksperyment — to "temat", który łączy uruchomienia.
Wszystkie kroki przykładu zrealizowane są na systemie operacyjnym Ubuntu 18.04.
1. Rozwijamy serwer
Abyśmy mogli łatwo zarządzać naszym projektem i uzyskiwać wszystkie potrzebne informacje, rozwiniemy serwer. Serwer śledzenia MLflow ma dwa główne komponenty:
- backend store — odpowiada za przechowywanie informacji o zarejestrowanych modelach (obsługuje 4 bazy danych: mysql, mssql, sqlite i postgresql);
- artifact store — odpowiada za przechowywanie artefaktów (obsługuje 7 możliwości przechowywania: Amazon S3, Azure Blob Storage, Google Cloud Storage, serwer FTP, serwer SFTP, NFS, HDFS).
Jako artifact store dla uproszczenia weźmy serwer SFTP.
- tworzymy grupę
$ sudo groupadd sftpg - dodajemy użytkownika i ustawiamy mu hasło
$ sudo useradd -g sftpg mlflowsftp $ sudo passwd mlflowsftp - korygujemy kilka ustawień dostępu
$ sudo mkdir -p /data/mlflowsftp/upload $ sudo chown -R root.sftpg /data/mlflowsftp $ sudo chown -R mlflowsftp.sftpg /data/mlflowsftp/upload - dodajemy kilka linii do /etc/ssh/sshd_config
Match Group sftpg ChrootDirectory /data/%u ForceCommand internal-sftp - restartujemy usługę
$ sudo systemctl restart sshd
Jako backend store weźmy postgresql.
$ sudo apt update
$ sudo apt-get install -y postgresql postgresql-contrib postgresql-server-dev-all
$ sudo apt install gcc
$ pip install psycopg2
$ sudo -u postgres -i
# Utwórz nowego użytkownika: mlflow_user
[postgres@user_name~]$ createuser --interactive -P
Wprowadź nazwę roli do dodania: mlflow_user
Wprowadź hasło dla nowej roli: mlflow
Wprowadź je ponownie: mlflow
Czy nowa rola ma być superużytkownikiem? (y/n) n
Czy nowa rola ma mieć możliwość tworzenia baz danych? (y/n) n
Czy nowa rola ma mieć możliwość tworzenia nowych ról? (y/n) n
# Utwórz bazę danych mlflow_bd, której właścicielem jest mlflow_user
$ createdb -O mlflow_user mlflow_dbAby uruchomić serwer, należy zainstalować następujące pakiety Pythona (zalecam utworzenie osobnego wirtualnego środowiska):
pip install mlflow
pip install pysftpUruchamiamy nasz serwer
$ mlflow server
--backend-store-uri postgresql://mlflow_user:mlflow@localhost/mlflow_db
--default-artifact-root sftp://mlflowsftp:mlflow@sftp_host/upload
--host server_host
--port server_port2. Dodajemy śledzenie
Aby wyniki naszych treningów nie zniknęły, przyszłe pokolenia programistów rozumiały, co się działo, a starsi koledzy i Ty mogli spokojnie analizować proces uczenia, musimy dodać śledzenie. Pod śledzeniem rozumiemy zapisywanie parametrów, metryk, artefaktów oraz wszelkich dodatkowych informacji o przebiegu szkolenia, w naszym przypadku na serwerze.
Na przykład stworzyłem mały na Kerasie do segmentacji wszystkiego, co jest w . Aby dodać śledzenie, stworzyłem plik mlflow_training.py.
Oto linijki, w których dzieje się najciekawsze:
def run(self, epochs, lr, experiment_name):
# uzyskanie id eksperymentu, stworzenie eksperymentu w jego braku
remote_experiment_id = self.remote_server.get_experiment_id(name=experiment_name)
# stworzenie "uruchomienia" i uzyskanie jego id
remote_run_id = self.remote_server.get_run_id(remote_experiment_id)
# wskazujemy, że chcemy zapisać wyniki na zdalnym serwerze
mlflow.set_tracking_uri(self.tracking_uri)
mlflow.set_experiment(experiment_name)
with mlflow.start_run(run_id=remote_run_id, nested=False):
mlflow.keras.autolog()
self.train_pipeline.train(lr=lr, epochs=epochs)
try:
self.log_tags_and_params(remote_run_id)
except mlflow.exceptions.RestException as e:
print(e)Tutaj self.remote_server to niewielkie opakowanie nad metodami mlflow.tracking. MlflowClient (zrobiłem to dla wygody), za pomocą których tworzę eksperyment i uruchomienie na serwerze. Następnie wskazuję, gdzie powinny trafić wyniki uruchomienia (mlflow.set_tracking_uri(self.tracking_uri)). Aktywizuję automatyczne logowanie mlflow.keras.autolog(). Obecnie MLflow Tracking wspiera automatyczne logowanie dla TensorFlow, Keras, Gluon XGBoost, LightGBM, Spark. Jeśli nie znalazłeś swojego frameworka lub biblioteki, zawsze możesz logować w sposób jawny. Rozpoczynamy trening. Rejestrujemy tagi i parametry wejściowe na zdalnym serwerze.
Parę linii kodu i masz, jak wszyscy chętni, dostęp do informacji o wszystkich uruchomieniach. Fajnie?
3. Przygotowujemy projekt
Teraz sprawimy, że uruchomienie projektu będzie dziecinnie proste. W tym celu dodajemy do głównego katalogu plik MLproject i conda.yaml.
MLproject
name: flow_segmentation
conda_env: conda.yaml
entry_points:
main:
parameters:
categories: {help: 'lista kategorii z zestawu danych coco'}
epochs: {type: int, help: 'liczba epok w treningu'}
lr: {type: float, default: 0.001, help: 'wskaźnik uczenia'}
batch_size: {type: int, default: 8}
model_name: {type: str, default: 'Unet', help: 'Unet, PSPNet, Linknet, FPN'}
backbone_name: {type: str, default: 'resnet18', help: 'przykład resnet18, resnet50, mobilenetv2 ...'}
tracking_uri: {type: str, help: 'adres serwera'}
experiment_name: {type: str, default: 'My_experiment', help: 'nazwa eksperymentu zdalnego i lokalnego'}
command: "python mlflow_training.py
--epochs={epochs}
--categories={categories}
--lr={lr}
--tracking_uri={tracking_uri}
--model_name={model_name}
--backbone_name={backbone_name}
--batch_size={batch_size}
--experiment_name={experiment_name}"Projekt MLflow ma kilka właściwości:
- Name — nazwa Twojego projektu;
- Environment — w moim przypadku conda_env wskazuje, że do uruchomienia używana jest Anaconda, a opis zależności znajduje się w pliku conda.yaml;
- Entry Points — wskazuje, jakie pliki i z jakimi parametrami możemy uruchomić (wszystkie parametry podczas uruchomienia treningu są automatycznie logowane)
conda.yaml
name: flow_segmentation
channels:
- defaults
- anaconda
dependencies:
- python==3.7
- pip:
- mlflow==1.8.0
- pysftp==0.2.9
- Cython==0.29.19
- numpy==1.18.4
- pycocotools==2.0.0
- requests==2.23.0
- matplotlib==3.2.1
- segmentation-models==1.0.1
- Keras==2.3.1
- imgaug==0.4.0
- tqdm==4.46.0
- tensorflow-gpu==1.14.0Jako środowisko wykonawcze możesz użyć dockera, po więcej informacji odsyłam do .
4. Rozpoczynamy trening
Klonujemy projekt i przechodzimy do katalogu projektu:
git clone https://github.com/simbakot/mlflow_example.git
cd mlflow_example/Aby uruchomić, musisz zainstalować biblioteki
pip install mlflow
pip install pysftpPonieważ w przykładzie używam conda_env, na Twoim komputerze musi być zainstalowane Anaconda (ale można to również obejść, instalując wszystkie niezbędne pakiety samodzielnie i eksperymentując z parametrami uruchamiania).
Wszystkie przygotowania są zakończone i możemy przejść do uruchamiania szkolenia. Z katalogu głównego projektu:
$ mlflow run -P epochs=10 -P categories=cat,dog -P tracking_uri=http://server_host:server_port .Po wprowadzeniu polecenia automatycznie utworzy się środowisko conda i rozpocznie się trenowanie.
W powyższym przykładzie przekazałem liczbę epok do nauki oraz kategorie, na które chcemy segmentować (pełną listę można zobaczyć ) oraz adres naszego zdalnego serwera.
Pełną listę możliwych parametrów można znaleźć w pliku MLproject.
5. Oceniając wyniki szkolenia
Po zakończeniu szkolenia możemy przejść w przeglądarce pod adres naszego serwera

Tutaj widzimy listę wszystkich eksperymentów (w lewym górnym rogu), a także informacje o uruchomieniach (na środku). Możemy zobaczyć bardziej szczegółowe informacje (parametry, metryki, artefakty oraz dodatkowe informacje) dotyczące każdego uruchomienia.

Dla każdej metryki możemy obserwować historię zmian.

Oznacza to, że obecnie możemy analizować wyniki w "ręcznym" trybie, a także możesz ustawić automatyczną walidację za pomocą MLflow API.
6. Rejestracja modelu
Po przeanalizowaniu naszego modelu i stwierdzeniu, że jest gotowy do użycia, przystępujemy do jego rejestracji. Wybieramy odpowiednie uruchomienie (jak pokazano w poprzednim punkcie) i przechodzimy w dół.

Po nadaniu naszemu modelowi nazwy, pojawia się jego wersja. Przy zapisaniu innego modelu o tej samej nazwie wersja automatycznie wzrośnie.

Dla każdego modelu możemy dodać opis i wybrać jedno z trzech stanów (Staging, Production, Archived), co w połączeniu z wersjonowaniem daje dodatkową elastyczność w dostępie do tych stanów za pomocą API.

Mamy również wygodny dostęp do wszystkich modeli

i ich wersji.

Podobnie jak w poprzednim punkcie, wszystkie operacje można wykonać za pomocą API.
7. Wdrażamy model
Na tym etapie mamy już wytrenowany model (keras). Oto przykład, jak można go użyć:
class SegmentationModel:
def __init__(self, tracking_uri, model_name):
self.registry = RemoteRegistry(tracking_uri=tracking_uri)
self.model_name = model_name
self.model = self.build_model(model_name)
def get_latest_model(self, model_name):
registered_models = self.registry.get_registered_model(model_name)
last_model = self.registry.get_last_model(registered_models)
local_path = self.registry.download_artifact(last_model.run_id, 'model', '.\/')
return local_path
def build_model(self, model_name):
local_path = self.get_latest_model(model_name)
return mlflow.keras.load_model(local_path)
def predict(self, image):
image = self.preprocess(image)
result = self.model.predict(image)
return self.postprocess(result)
def preprocess(self, image):
image = cv2.resize(image, (256, 256))
image = image / 255.
image = np.expand_dims(image, 0)
return image
def postprocess(self, result):
return resultTutaj self.registry to ponownie mała osłona nad mlflow.tracking.MlflowClient, dla wygody. Chodzi o to, że łączę się z zdalnym serwerem i wyszukuję model o podanej nazwie, przy czym pobieram najnowszą wersję produkcyjną. Następnie ściągam artefakt lokalnie do katalogu .\/model i buduję model z tego katalogu za pomocą mlflow.keras.load_model(local_path). Teraz możemy korzystać z naszego modelu. Programiści CV (ML) mogą spokojnie skupić się na ulepszaniu modelu i publikowaniu nowych wersji.
Na zakończenie
Przedstawiłem system, który pozwala:
- centralnie przechowywać informacje o modelach ML, postępach i wynikach nauki;
- szybko uruchamiać środowisko programistyczne;
- śledzić i analizować postępy pracy nad modelami;
- wygodnie prowadzić wersjonowanie i zarządzać stanem modeli;
- łatwo wdrażać uzyskane modele.
Ten przykładowy projekt jest modelowy i służy jako punkt wyjścia do budowy własnego systemu, który być może będzie zawierał automatyzację oceny wyników i rejestracji modeli (pkt 5 i pkt 6 odpowiednio) lub dodasz wersjonowanie zbiorów danych, czy może coś jeszcze? Chciałem przekazać myśl, że potrzebujesz MLOps jako całości, MLflow to tylko środek do osiągnięcia celu.
Napisz, jakie problemy, z którymi się borykałeś, nie zostały przeze mnie odzwierciedlone?
Co byś dodał do systemu, aby spełniał Twoje potrzeby?
Jakie narzędzia i podejścia stosujesz, aby rozwiązywać wszystkie lub część problemów?
P.S. Zostawię kilka linków:
projekt github —
MLflow —
Mój służbowy e-mail na pytania — ikryakin@croc.ru
W naszej firmie okresowo organizowane są różne wydarzenia dla specjalistów IT, na przykład: 8 lipca o 19:00 czasu MSK odbędzie się meetup dotyczący CV w formacie online. Jeśli jesteś zainteresowany, możesz wziąć udział, rejestracja. .
Źródło: habr.com
