💨

【2021/08メモ】windows+dockerでGPU対応の機械学習環境をつくる

2021/08/12に公開

2021/8時点での情報です。安定版になっていない内容が多く、一年もたったら役に立たなくなっていると思われます
わざわざ怪しいタイトルをつけたので、信憑性はそんなもんです。
ほぼ作業メモですが、ちょっとハマりどころがあったのでまとめておきます

特に参考にしたサイト様

https://qiita.com/ksasaki/items/ee864abd74f95fea1efa
https://nykergoto.hatenablog.jp/entry/2020/07/25/機械学習なdockerfileを書くときに気をつけとくと良いこ

注意点

WSL2のGPU認識などを利用するためにはwindows insider programに登録して、開発段階のwindows updateを受け取る必要があり、環境が通常のwindowsと変わります。
実際私は今windows11相当の環境になっています…

環境概要

Windows + WSL2 + Docker(WSL2バックエンド)です。
WSL2バックエンドを使用することで、nvidia周りのドライバなどを頑張って準備しなくてもGPUを認識してくれます。

1.Windows Insider Programへの登録とアップデート

あくまで2021年8月頃では必須なだけで、そのうち必須じゃなくなるかもしれませんので、この記事を見た未来の方は一度調べてください

https://insider.windows.com/ja-jp/
ここからWindows Insider Programへ登録してアップデートを受け取ります.
私はBeta Channelを選択してしまいましたが、リリースプレビューで可能ならばそれに越したことは無い(最も安定しているため)ので、これからやる方はまずリリースプレビュー Channelを試すことを個人的にはお勧めします。

ここのやり方については詳しく書きません。ここを自力で突破できない人はwindows insiderで発生する問題に自力で対処できなくて苦労しそうだからです。

2. CUDA on Windows Subsystem for Linux (WSL) - Public Previewのインストール

https://developer.nvidia.com/cuda/wsl
のget cuda driverからWSL用のcudaドライバをインストールします。
これはwindowsにインストールします。

3. WSL2のインストール

https://docs.microsoft.com/ja-jp/windows/wsl/install-win10
これも手順がコロコロ変わるので、公式docを貼っておきます。
現状では

wsl --install -d Ubuntu-20.04

などとたたけば指定のディストリビューションでインストールされます。

4.DockerDesktopのインストールとWSL2バックエンドの設定

https://www.docker.com/products/docker-desktop
ここからdocker Desktopをインストールします。特に説明不要だと思います。
インストールしたらdocker Desktopを開いて、WSL integrationを有効化します。

4.5 一応終わり、以降オプションです。

なんとここまでで既にWindows上で動かしたdockerコンテナから、GPUが使えるようになっています。ところがなんと、lspciなどしてもGPUを直接みることができません
(参考:https://qiita.com/ksasaki/items/ee864abd74f95fea1efa#生の-linuxとは違うところも)

これで認識されてないのかとハマりました。
皆さんは気にせず次行きましょう

5.DockerFileの準備

機械学習のための全部盛りイメージといえばKaggleのイメージなどが思いつくかもしれませんが、Kaggleのイメージはうまく動きませんでした。(nvidiaドライバの扱いが通常と異なるためだと思われます)

また、Kaggleのイメージは結構重たい(50GB弱くらいあった気がします)ので、イメージを適当に準備しましょう。

オレオレ適当DockerFileとrequirements.txtをここに置いておくので、皆さんも最強のイメージを作ってください。ベースはnvidiaのcuda+cudannイメージで、そこにこちらのサイトをパク…参考にいろいろ追加したものです。
jupyter labを動かすこと前提で、jupyterのホーム(/mlwsで設定)をvolumeマウントとして永続化します。

Dockerfile
FROM  nvidia/cuda:11.4.0-cudnn8-devel-ubuntu20.04

ENV NOTO_DIR /usr/share/fonts/opentype/notosans

##ミラーを使うなどをする人が多いので入れてましたが、そこまで早くもならず無用だったので外しています。
# RUN sed -i.bak -r 's@http://(jp.)?archive.ubuntu.com/ubuntu/@http://ftp.jaist.ac.jp/pub/Linux/ubuntu/@g' /etc/apt/sources.list


## aptに時間がかなりかかるので、パッケージを絞っています。
RUN apt update \
    && apt install -y \
    wget \
    bzip2 \
    # ca-certificates \
    # libglib2.0-0 \
    # libxext6 \
    # libsm6 \
    # libxrender1 \
    git \
    # mercurial \
    # subversion \
    # zsh \
    # openssh-server \
    # gcc \
    # g++ \
    # libatlas-base-dev \
    # libboost-dev \
    # libboost-system-dev \
    # libboost-filesystem-dev \
    curl \
    # make \
    unzip \
    vim \
    # ffmpeg \
    mecab \
    libmecab-dev \
    mecab-ipadic-utf8 \
    file \
    xz-utils \
    sudo \
    python3 \
    python3-pip


RUN mkdir -p ${NOTO_DIR} &&\
  wget -q https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip -O noto.zip &&\
  unzip ./noto.zip -d ${NOTO_DIR}/ &&\
  chmod a+r ${NOTO_DIR}/NotoSans* &&\
  rm ./noto.zip

# remove cache files
RUN apt-get autoremove -y && apt-get clean && \
  rm -rf /usr/local/src/*

RUN git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git \
    && cd mecab-ipadic-neologd \
    && bin/install-mecab-ipadic-neologd -n -y

COPY requirements.txt /tmp/

RUN pip install --no-cache-dir -U pip setuptools wheel \
    && pip install --no-cache-dir -r /tmp/requirements.txt

# nodejsの導入
RUN curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - \
    && sudo apt-get install -y nodejs

# ## JupyterLabの拡張機能

# 変数や行列の中身を確認
RUN jupyter labextension install @lckr/jupyterlab_variableinspector@3.0.7

# 自動整形
RUN pip install autopep8 \
    && pip install jupyterlab_code_formatter \
    && jupyter labextension install @ryantam626/jupyterlab_code_formatter \
    && jupyter serverextension enable --py jupyterlab_code_formatter

# jupyter の config ファイルの作成
RUN mkdir /mlws && echo "c.NotebookApp.notebook_dir = '/mlws' \n" | tee -a ${HOME}/.jupyter/jupyter_notebook_config.py

EXPOSE 8888

CMD ["python3","-m","jupyter", "lab", "--ip=0.0.0.0", "--port=8888","--allow-root","--NotebookApp.token=''"]

requirements.txt
keras
tensorflow
tensorflow-addons
pandas
numpy
scipy
matplotlib
seaborn
lightgbm
xgboost
scikit-learn
mecab-python3
jupyterlab
spacy
torch
torchvision
timm
transformers
requests
beautifulsoup4
albumentations
gensim
joblib
pillow
librosa

分離環境なのでanacondaとか入れちゃってもいいですし、お好きに

6.テスト

 docker volume create mlws
 docker build -t ml_base:latest .
 docker run --rm -it -p 8888:8888 --gpus all --mount source=mlws,target=/mlws -t  ml_base:latest

http://localhost:8888 にアクセスすると、jupyterlabにつながるはずです。
ところで、確認できなかったGPUがちゃんと使われるのか見てみます。

jupyte labで

from tensorflow.python.client import device_lib
device_lib.list_local_devices()

とすると、device type GPUがいることが確認できます。また、適当なirisで学習を回してみると、

iris学習コード
import tensorflow as tf
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from tensorflow.keras.utils import to_categorical
iris = load_iris()

data_X = iris.data
data_y = to_categorical(iris.target)

train_X, test_X, train_y, test_y = train_test_split(data_X, data_y, test_size=0.3, random_state=0)

model = tf.keras.models.Sequential([
    tf.keras.layers.Input(4),
    tf.keras.layers.Dense(300, activation='relu'),
    tf.keras.layers.Dense(300, activation='relu'),
    tf.keras.layers.Dense(300, activation='relu'),
    tf.keras.layers.Dense(500, activation='relu'),
    tf.keras.layers.Dense(700, activation='relu'),
    tf.keras.layers.Dense(3, activation='softmax')
])

model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.01),
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.summary()

result = model.fit(train_X, train_y, batch_size=32, epochs=3, validation_data=(test_X, test_y), verbose=1)
WARNING:tensorflow:Please add `keras.layers.InputLayer` instead of `keras.Input` to Sequential model. `keras.Input` is intended to be used by Functional model.
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_18 (Dense)             (None, 300)               1500      
_________________________________________________________________
dense_19 (Dense)             (None, 300)               90300     
_________________________________________________________________
dense_20 (Dense)             (None, 300)               90300     
_________________________________________________________________
dense_21 (Dense)             (None, 500)               150500    
_________________________________________________________________
dense_22 (Dense)             (None, 700)               350700    
_________________________________________________________________
dense_23 (Dense)             (None, 3)                 2103      
=================================================================
Total params: 685,403
Trainable params: 685,403
Non-trainable params: 0
_________________________________________________________________
Epoch 1/3
4/4 [==============================] - 0s 43ms/step - loss: 3.5071 - accuracy: 0.2476 - val_loss: 1.0908 - val_accuracy: 0.2444
Epoch 2/3
4/4 [==============================] - 0s 10ms/step - loss: 0.9079 - accuracy: 0.5714 - val_loss: 1.2686 - val_accuracy: 0.6000
Epoch 3/3
4/4 [==============================] - 0s 10ms/step - loss: 0.6718 - accuracy: 0.6571 - val_loss: 0.4582 - val_accuracy: 0.7556

ということでさすがにCPUでは出なそうな速さが出てるので大丈夫そうです。

7.おわりに

繰り返しますが、ベータ版とかpublic previewとかがすごく多いので情報がコロコロ変わると思いますので、ご注意ください。

とはいえ、gpuが直接は見えなかったり、kaggleイメージそのまま使おうとしたらうまくいかなかったりいろいろあったので今回メモにしました。誰かの参考になればうれしいです。

Discussion