Posted at

Dockerコンテナで、Keras + TensorFlow / Jupyter notebook環境を簡単にデプロイする

Kerasを活用して、デープラーニングを動作させようと考えた場合、環境整備に手間取るのがめんどくさいですよね。

今回は、Dockerコンテナから、Keras + tensorFlow / Jupyter notebook環境を簡単に整備できるようになりました。

私の個人リポジトリ"ttsubo/study_of_deeplearning_with_keras"と、このQiita記事にて、作業履歴を共有しておきます。


⬛︎ 事前準備

Dockerコンテナを動かすUbuntuサーバから、GPUが扱えるようにしておく必要があります。


(1) Ubuntuサーバ側の環境セットアップ

具体的には、Qiita記事: "Dockerで、GPU対応なコンテナ環境を整備する"のセットアップが終わっているものとします。


(2) "study_of_deeplearning_with_keras"リポジトリの取得

Dockerコンテナから、Keras + tensorFlow / Jupyter notebook環境を整備できるように、事前にリポジトリを用意しておきましたので、以下の要領で、リポジトリを取得しておいてください。

$ git clone https://github.com/ttsubo/study_of_deeplearning_with_keras.git


(3) cuDNNファイルを別途、入手する

Dockerコンテナ内にcnDNNパッケージをインストールする必要があるので、cuDNN Archive からcuDNN v7.6.2 (July 22, 2019), for CUDA 10.0をダウンロードしておいてください。なお、ダウンロードするためには事前にメンバー登録が必要です。


  • cuDNN Runtime Library for Ubuntu18.04 (Deb)

  • cuDNN Developer Library for Ubuntu18.04 (Deb)

  • cuDNN Code Samples and User Guide for Ubuntu18.04 (Deb)

そして、build/downloadフォルダに配置しておいてください。

$ cd build/download/

$ ls -l
total 314384
-rw-r--r-- 1 ttsubo ttsubo 164426244 Sep 29 14:36 libcudnn7_7.6.2.24-1+cuda10.0_amd64.deb
-rw-r--r-- 1 ttsubo ttsubo 152045132 Sep 29 14:36 libcudnn7-dev_7.6.2.24-1+cuda10.0_amd64.deb
-rw-r--r-- 1 ttsubo ttsubo 5442884 Sep 29 14:36 libcudnn7-doc_7.6.2.24-1+cuda10.0_amd64.deb


(4) Dockerイメージをビルドします

$ docker-compose build

ちなみに、Dockerfileファイルは、こんな感じです。


Dockerfile

FROM nvidia/cuda:10.0-devel-ubuntu18.04

MAINTAINER Toshiki Tsuboi <t.tsubo2000@gmail.com>

RUN apt update \
&& apt install -y \
git-core \
build-essential \
python-dev \
python-openssl \
libssl-dev \
libbz2-dev \
libffi-dev \
libsqlite3-dev \
libreadline-dev \
zlib1g-dev \
libsm6 \
libxext6 \
libxrender-dev \
libblas-dev \
curl \
vim \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# install pyenv
ENV HOME /root
ENV PYENV_ROOT $HOME/.pyenv
ENV PATH $PYENV_ROOT/bin:$PATH
RUN git clone https://github.com/pyenv/pyenv.git $HOME/.pyenv
RUN echo 'eval "$(pyenv init -)"' >> $HOME/.bashrc \
&& eval "$(pyenv init -)"

# install python using pyenv
RUN apt update \
&& apt install -y libssl1.0-dev \
&& pyenv install anaconda3-5.3.1 \
&& pyenv global anaconda3-5.3.1

# install pip
WORKDIR /
ADD https://bootstrap.pypa.io/get-pip.py /
RUN python get-pip.py \
&& rm get-pip.py

# install python package
WORKDIR /root
COPY requirements.txt /root
RUN /root/.pyenv/shims/pip install -r requirements.txt

# install cuDNN
COPY download /root/debian_packages
RUN cd /root/debian_packages \
&& dpkg -i \
libcudnn7_7.6.2.24-1+cuda10.0_amd64.deb \
libcudnn7-dev_7.6.2.24-1+cuda10.0_amd64.deb \
libcudnn7-doc_7.6.2.24-1+cuda10.0_amd64.deb

#setup jupyter
RUN /root/.pyenv/shims/jupyter notebook --generate-config \
&& sed -i -e "s/#c.NotebookApp.ip = 'localhost'/c.NotebookApp.ip = '0.0.0.0'/" /root/.jupyter/jupyter_notebook_config.py \
&& sed -i -e "s/#c.NotebookApp.allow_remote_access = False/c.NotebookApp.allow_remote_access = True/" /root/.jupyter/jupyter_notebook_config.py \
&& sed -i -e "s/#c.NotebookApp.token = '<generated>'/c.NotebookApp.token = ''/" /root/.jupyter/jupyter_notebook_config.py

EXPOSE 8888
ENTRYPOINT ["sh", "-c", "/root/.pyenv/shims/jupyter notebook --allow-root"]



⬛︎ 実際に、動かしてみる

今回、動かしてみるデープラーニング用Pythonスクリプトは、Keras repoが提供しているサンプルアプリです。


keras/examples/mnist_cnn.py

'''Trains a simple convnet on the MNIST dataset.

Gets to 99.25% test accuracy after 12 epochs
(there is still a lot of margin for parameter tuning).
16 seconds per epoch on a GRID K520 GPU.
'''

from __future__ import print_function
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K

batch_size = 128
num_classes = 10
epochs = 12

# input image dimensions
img_rows, img_cols = 28, 28

# the data, split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()

if K.image_data_format() == 'channels_first':
x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)

x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
activation='relu',
input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))

model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(),
metrics=['accuracy'])

model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])



(1) Dockerコンテナを起動する

Docker-composeを使って、Dockerコンテナを起動します。

この段階で、Dockerコンテナ内では、Jupyter notebook環境も常駐するようになっています。

$ docker-compose up

Creating Keras ... done
Attaching to Keras
Keras | [I 05:43:15.263 NotebookApp] Writing notebook server cookie secret to /root/.local/share/jupyter/runtime/notebook_cookie_secret
Keras | [W 05:43:15.379 NotebookApp] All authentication is disabled. Anyone who can connect to this server will be able to run code.
Keras | [I 05:43:15.403 NotebookApp] JupyterLab extension loaded from /root/.pyenv/versions/anaconda3-5.3.1/lib/python3.7/site-packages/jupyterlab
Keras | [I 05:43:15.404 NotebookApp] JupyterLab application directory is /root/.pyenv/versions/anaconda3-5.3.1/share/jupyter/lab
Keras | [I 05:43:15.406 NotebookApp] Serving notebooks from local directory: /root
Keras | [I 05:43:15.406 NotebookApp] The Jupyter Notebook is running at:
Keras | [I 05:43:15.406 NotebookApp] http://(Keras or 127.0.0.1):8888/
Keras | [I 05:43:15.406 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
Keras | [W 05:43:15.407 NotebookApp] No web browser found: could not locate runnable browser.


(2) Dockerコンテナ内に、入って、サンプルアプリを起動する

まずは、Dockerコンテナに入ります

$ docker exec -it Keras bash

そして、サンプルアプリを起動します。$ python examples/mnist_cnn.py

root@Keras:~# python examples/mnist_cnn.py

Using TensorFlow backend.
x_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples

... (snip)

Train on 60000 samples, validate on 10000 samples
Epoch 1/12
2019-09-29 06:00:43.489125: I tensorflow/stream_executor/dso_loader.cc:152] successfully opened CUDA library libcublas.so.10.0 locally
60000/60000 [==============================] - 8s 127us/step - loss: 0.2654 - accuracy: 0.9186 - val_loss: 0.0628 - val_accuracy: 0.9785
Epoch 2/12
60000/60000 [==============================] - 7s 115us/step - loss: 0.0888 - accuracy: 0.9736 - val_loss: 0.0430 - val_accuracy: 0.9849
Epoch 3/12
60000/60000 [==============================] - 7s 112us/step - loss: 0.0677 - accuracy: 0.9802 - val_loss: 0.0394 - val_accuracy: 0.9871
Epoch 4/12
60000/60000 [==============================] - 7s 114us/step - loss: 0.0566 - accuracy: 0.9832 - val_loss: 0.0321 - val_accuracy: 0.9885
Epoch 5/12
60000/60000 [==============================] - 7s 114us/step - loss: 0.0480 - accuracy: 0.9860 - val_loss: 0.0350 - val_accuracy: 0.9882
Epoch 6/12
60000/60000 [==============================] - 7s 110us/step - loss: 0.0450 - accuracy: 0.9864 - val_loss: 0.0274 - val_accuracy: 0.9897
Epoch 7/12
60000/60000 [==============================] - 7s 113us/step - loss: 0.0395 - accuracy: 0.9883 - val_loss: 0.0292 - val_accuracy: 0.9903
Epoch 8/12
60000/60000 [==============================] - 7s 114us/step - loss: 0.0351 - accuracy: 0.9890 - val_loss: 0.0309 - val_accuracy: 0.9893
Epoch 9/12
60000/60000 [==============================] - 7s 112us/step - loss: 0.0342 - accuracy: 0.9892 - val_loss: 0.0288 - val_accuracy: 0.9906
Epoch 10/12
60000/60000 [==============================] - 7s 111us/step - loss: 0.0328 - accuracy: 0.9900 - val_loss: 0.0264 - val_accuracy: 0.9918
Epoch 11/12
60000/60000 [==============================] - 7s 112us/step - loss: 0.0305 - accuracy: 0.9901 - val_loss: 0.0280 - val_accuracy: 0.9916
Epoch 12/12
60000/60000 [==============================] - 7s 117us/step - loss: 0.0289 - accuracy: 0.9911 - val_loss: 0.0268 - val_accuracy: 0.9917
Test loss: 0.026753987711756782
Test accuracy: 0.9916999936103821

GPUを使用した場合、1エポック当たり、7秒程度で、学習が進んでいく様子が確認できますね。

ちなみに、CPUを使用した場合は、1エポック当たり、41秒程度で学習が進んでいきました。

私のGPU環境GeForce GTX 1060 3GBだと、CPUよりも、約6倍も、学習に要する時間が短縮できるようです。

root@Keras:~# python examples/mnist_cnn.py

Using TensorFlow backend.
Downloading data from https://s3.amazonaws.com/img-datasets/mnist.npz
11493376/11490434 [==============================] - 8s 1us/step
x_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples

... (snip)

Train on 60000 samples, validate on 10000 samples
Epoch 1/12
60000/60000 [==============================] - 41s 679us/step - loss: 0.2666 - accuracy: 0.9189 - val_loss: 0.0607 - val_accuracy: 0.9819
Epoch 2/12
60000/60000 [==============================] - 41s 677us/step - loss: 0.0895 - accuracy: 0.9733 - val_loss: 0.0402 - val_accuracy: 0.9866
Epoch 3/12
60000/60000 [==============================] - 41s 677us/step - loss: 0.0673 - accuracy: 0.9798 - val_loss: 0.0365 - val_accuracy: 0.9879
Epoch 4/12
60000/60000 [==============================] - 41s 677us/step - loss: 0.0550 - accuracy: 0.9835 - val_loss: 0.0341 - val_accuracy: 0.9885
Epoch 5/12
60000/60000 [==============================] - 41s 678us/step - loss: 0.0477 - accuracy: 0.9857 - val_loss: 0.0293 - val_accuracy: 0.9908
Epoch 6/12
60000/60000 [==============================] - 41s 677us/step - loss: 0.0424 - accuracy: 0.9871 - val_loss: 0.0283 - val_accuracy: 0.9913
Epoch 7/12
60000/60000 [==============================] - 41s 678us/step - loss: 0.0387 - accuracy: 0.9881 - val_loss: 0.0275 - val_accuracy: 0.9906
Epoch 8/12
60000/60000 [==============================] - 41s 678us/step - loss: 0.0340 - accuracy: 0.9897 - val_loss: 0.0261 - val_accuracy: 0.9909
Epoch 9/12
60000/60000 [==============================] - 41s 677us/step - loss: 0.0319 - accuracy: 0.9902 - val_loss: 0.0291 - val_accuracy: 0.9906
Epoch 10/12
60000/60000 [==============================] - 41s 676us/step - loss: 0.0307 - accuracy: 0.9906 - val_loss: 0.0287 - val_accuracy: 0.9918
Epoch 11/12
60000/60000 [==============================] - 41s 676us/step - loss: 0.0277 - accuracy: 0.9916 - val_loss: 0.0282 - val_accuracy: 0.9917
Epoch 12/12
60000/60000 [==============================] - 41s 676us/step - loss: 0.0271 - accuracy: 0.9915 - val_loss: 0.0267 - val_accuracy: 0.9921
Test loss: 0.026685928908488858
Test accuracy: 0.9921000003814697


(3) 最後に、jupyter notebook経由で、サンプルアプリを起動してみる

Web browserから、http://(Keras or 127.0.0.1):8888/にアクセスします。

そして、notebookを起動すると、こんな感じの結果が確認できました。

minist_cnn.png


⬛︎ 終わりに、、、

Keras + TensorFlow / Jupyter notebook環境が簡単に、デプロイできるようになりました。

デープラーニング動作検証を通じて知識を習得する場合には、デープラーニング環境を色々とスクラップ&ビルドできるのが望ましいと思うので、まさに、Dockerコンテナ活用のわかりやすい事例だと思います。