
# Uczenie maszynowe w Dockerze

Jest to konfiguracja Dockera, umożliwiająca uruchamianie i modyfikowanie notatników Jupyter bez konieczności instalowania zależności na Twoim komputerze!<br/>No dobra, za wyjątkiem pakietów `docker` i `docker-compose`.<br /> Możesz również chcieć zainstalować `make`<br />Oraz kilku innych składników, jeśli zależy Ci na obsłudze GPU (szczegóły znajdziesz poniżej).

## Wymogi wstępne

Postępuj zgodnie z instrukcjami [instalacji Dockera](https://docs.docker.com/engine/installation/) i [zainstaluj Docker Compose](https://docs.docker.com/compose/install/) w swoim środowisku, jeżeli jeszcze ich nie masz.

Warto mieć ogólne pojęcie na temat infrastruktury Dockera (jest to samo w sobie ciekawe zagadnienie), ale nie koniecznie *wymagane*, jeżeli zamierzasz tylko uruchamiać notatniki.

## Zastosowanie

### Przygotowanie obrazu (czynność jednorazowa)

Pierwszą możliwością jest pobranie obrazu z serwisu Docker Hub (wymaga to ok. 1,9 GB miejsca na skompresowane dane):

```bash
$ docker pull ageron/handson-ml3
```

**Uwaga** Jest to obraz obsługujący wyłącznie standardowe procesy. Jeśli chcesz korzystać z procesorów graficznych, odpowiednie informacje znajdziesz w dalszej części notatnika.

Ewentualnie możesz samodzielnie stworzyć obraz. Proces ten może zająć trochę czasu, ale uzyskujesz dzięki niemu aktualny obraz z najnowszymi bibliotekami. W tym celu załóżmy, że pobrałeś już ten projekt do katalogu `/ścieżka/do/projektu/handson-ml3`:

```bash
$ cd /ścieżka/do/projektu/handson-ml3/docker
$ docker-compose build
```

Proces ten zajmie chwilę, ale jest wykonywany tylko jednorazowo.

Po zakończeniu przetwarzania otrzymasz obraz `ageron/handson-ml3:latest`, który będzie teraz stanowił bazę do eksperymentowania. Możesz to sprawdzić za pomocą poniższego polecenia:

```bash
$ docker images
REPOSITORY            TAG         IMAGE ID            CREATED             SIZE
ageron/handson-ml3    latest      3ebafebc604a        2 minutes ago       4.87GB
```

### Uruchamianie notatników

Jeżeli przyjmiemy, że pobrałeś już ten projekt do katalogu `/ścieżka/do/projektu/handson-ml3`, wpisz poniższe poleenia, aby uruchomić serwer Jupyter wewnątrz kontenera o nazwie `handson-ml3`:

```bash
$ cd /path/to/project/handson-ml3/docker
$ docker-compose up
```

Następnie wystarczy wstawić do przeglądarki wyświetlony adres URL (lub adres <http://localhost:8888>, jeżeli włączyłeś uwierzytelnianie hasłem wewnątrz pliku `jupyter_notebook_config.py` jeszcze przed utworzeniem obrazu), a po chwili będziesz mógł korzystać z notatników!

Serwer działa w katalogu zawierającym notatniki i wprowadzane przez Ciebie zmiany będą tam zapisywane.

Aby wyłączyć serwer, wystarczy wcisnąć kombinację klawiszy `Ctrl+C` w oknie terminala.

### Korzystanie z narzędzia `make` (opcjonalne)

Jeśli masz zainstalowane na komputerze narzędzie `make`, możesz za jego pomocą realizować polecenia `docker-compose`. Na przykład wpisanie polecenia `make rebuild` wywoła w rzeczywistości komendę `docker-compose build --no-cache`, która odbuduje obraz bez użycia pamięci podręcznej. Dzięki temu Twój obraz będzie wykorzystywał najnowszą wersję obrazu `continuumio/miniconda3`, na którym bazuje obraz `ageron/handson-ml3`.

Jeżeli nie masz narzędzia `make` (i nie zamierzasz go instalować), sprawdź zawartość `Makefile`, aby przekoanć się, z których poleceń `docker-compose` możesz skorzystać.

### Realizowanie dodatkowych poleceń w kontenerze

Wpisz polecenie `make exec` (lub `docker-compose exec handson-ml3 bash`), gdy serwer jest uruchomiony, aby uaktywnić dodatkową powłokę `bash` wewnątrz kontenera `handson-ml3`. Znajdujesz się teraz wewnątrz środowiska przygotowanego wewnątrz obrazu.

Jedną z przydatnych czynności wewnątrz tego środowiska jest uruchomienie narzędzia TensorBoard (na przykład za pomocą prostego polecenia `tb` - zob. plik bashrc).

Inną przydatną funkcją może być porównywanie wersji notatników za pomocą polecenia `nbdiff`, jeśli nie masz zainstalowanego lokalnie pakietu `nbdime` (w przypadku notatników jest ono **znacznie** lepsze od zwykłego polecenia `diff`). Szczegóły znajdziesz na [stronie opisującej narzędzia rozróżniania i scalania notatników Jupyter](https://github.com/jupyter/nbdime).

Możesz sprawdzać zmiany wprowadzane w porównaniu do wersji zawartych w repozytorium git za pomocą polecenia `git diff`, które stanowi część pakietu `nbdiff`.

Możesz także wypróbować polecenie `nbd NAZWA_NOTATNIKA.ipynb` (niestandardowe, zob. plik bashrc), aby porównać jeden z notatników z jego wersją zawierającą `punkt kontrolny`.<br/>
Mówiąc dokładniej, otrzymany wynik będzie określał, *jakie modyfikacje należy ponownie wprowadzić w **ręcznie zapisanej** wersji notatnika (umieszczonej w podkatalogu `.ipynb_checkpoints`) po to,aby zaktualizować ją do wersji **bieżącej** tj. **zapisanej automatycznie** (podanej jako argument polecenia - wewnątrz katalogu roboczego)*.

## Obsługa GPU w Linuksie (eksperymentalne)

### Wymogi wstępne

Jeżeli korzystasz z Linuksa i masz kartę graficzną zgodną z TensorFlow (kartę firmy NVidia z funkcją Compute Capability ≥ 3.5) i chciałbyś korzystać z biblioteki TensorFlow wewnątrz kontenera Dockera, to powinieneś pobrać i zainstalować najnowsze sterowniki dla swojej karty graficznej ze strony nvidia.com](https://www.nvidia.com/Download/index.aspx?lang=en-us). Będziesz musiał zainstalować także [obsługę NVidia Docker](https://github.com/NVIDIA/nvidia-docker): Jeżeli korzystasz z wersji 19.03 lub nowszej, musisz zainstalować pakiet `nvidia-container-toolkit`, natomiast w przypadku wcześniejszych wersji musisz zainstalować `nvidia-docker2`.

Następnie edytuj plik `docker-compose.yml`:

```bash
$ cd /ścieżka/do/projektu/handson-ml3/docker
$ edit docker-compose.yml  # użyj ulubionego edytora
```

* Zastąp wiersz `dockerfile: ./docker/Dockerfile` wierszem `dockerfile: ./docker/Dockerfile.gpu`
* Zastąp wiersz `image: ageron/handson-ml3:latest` wierszem `image: ageron/handson-ml3:latest-gpu`
* Jeżeli chcesz korzystać z `docker-compose` z obsługą GPU, potrzebujesz wersji 1.28 lub nowszej i musisz usunąć znak komentarza z całej sekcji `deploy` w pliku `docker-compose.yml`. 

### Przygotowywanie obrazu (czynność jednorazowa)

Jeżeli chcesz pobrać gotowy obraz z serwisu Docker Hub (trzeba zarezerwować ponad 3,5 GB na skompresowane dane):

```bash
$ docker pull ageron/handson-ml3:latest-gpu
```

Jeżeli wolisz zbudować obraz samodzielnie:

```bash
$ cd /ścieżka/do/projektu/handson-ml3/docker
$ docker-compose build
```

### Uruchamianie notatników za pomocą narzędzia `docker-compose` (w wersji 1.28 lub nowszej)

Jeżeli dysponujesz wersją 1.28 lub nowszą narzędzia `docker-compose`, to świetnie! Wystarczy wpisać:

```bash
$ cd /ścieżka/do/projektu/handson-ml3/docker
$ docker-compose up
[...]
     lub http://127.0.0.1:8888/?token=[...]
```

Następnie wprowadź w przeglądarce adres URL i powinno zostać wyświetlone środowisko Jupyter. Jeżeli teraz otworzysz lub utworzysz notatnik oraz wprowadzisz doń poniższy kod, powinna zostać wyświetlona lista dostępnych kart graficznych (sukces!):

```python
import tensorflow as tf

tf.config.list_physical_devices("GPU")
```

Aby zatrzymać serwer, wystarczy wcisnąć klawisze Ctrl-C.

### Uruchamianie notatników bez użycia narzędzia `docker-compose`

Jeżeli dysponujesz wersją starszą od 1.28 narzędzia `docker-compose`, będziesz musiał bezpośrednio skorzystać z polecenia `docker run`.

Jeżeli masz wersję 19.03 lub nowszą Dockera, możesz wpisać:

```bash
$ cd /ścieżka/do/projektu/handson-ml3
$ docker run --name handson-ml3 --gpus all -p 8888:8888 -p 6006:6006 --log-opt mode=non-blocking --log-opt max-buffer-size=50m -v `pwd`:/home/devel/handson-ml3 ageron/handson-ml3:latest-gpu /opt/conda/envs/homl3/bin/jupyter notebook --ip='0.0.0.0' --port=8888 --no-browser
```

W przypadku starszej wersji Dockera zamień parametr `--gpus all` parametrem `--runtime=nvidia`.

Teraz umieść w przeglądarce wyświetlony adres URL: powinno pojawić się środowisko Jupyter, a Ty możesz otworzyć notatnik i wpisać w nim (jak wyżej) `import tensorflow as tf` oraz `tf.config.list_physical_devices("GPU)`, aby potwierdzić, że biblioteka TensorFlow rzeczywiście widzi Twoje GPU.

Na koniec jeśli chcesz zatrzymać serwer, wciśnij Ctrl-C, a następnie wpisz:

```bash
$ docker rm handson-ml3
```

Zostanie usunięty kontener, dzięki czemu będziesz mógł później stworzyć nowy (ale nie martw się, ani obraz, ani obrazy nie zostaną usunięte!).

Udanej zabawy!

