WSLとDockerを使ってWindowsでTensorflowとPytorchをCUDAを使える状態で共存させる

はじめまして、ふかふかです。

 

この記事はTCU-CTRL場外乱闘 Advent Calendar 2021 - Adventar15日の記事です。

ブログは初めてなので色々至らぬ点もあるかもしれませんが暖かい目でお願いします。

前回の記事はコットンさんです。

最近やってることについて書こうかと思ってましたが、環境構築で苦労したので備忘録も兼ねてやり方を記録しておきます。

いきさつ

普段の課題などでは手持ちのGeforceグラフィックボードを搭載したWindowsマシンでTensorflowを使っており、最近Pytorchを使う必要が出てきて環境構築の仕方を調べました。するとどうもGPU使用の前提となるCUDAのバージョンが競合しているようで、そのままでの両立は不可能なようでした。

f:id:fuKa-Huka:20211216010205p:plain

TensorFlowのソフトウェア要件。TensorFlowの公式ドキュメントより

f:id:fuKa-Huka:20211216010429p:plain

Pytorchのプラットフォーム選択画面。Pytorch公式サイトより

ご覧の通り要求するCUDAのバージョンが一致していません。Windowsで複数のバージョンのCUDAを両立させる方法を調べてみましたが、複数インストールして都度環境変数を変更するとかいうミスと不具合を誘発しそうなやり方しか見つかりません。

方針

そもそも事の原因は複数のバージョンのCUDAのpassが同一ということにあるようです。

そこでDockerを使います。幸い最近のWindowsにはWSL(Windows subsystem for Linux)というものがあります。これがあればWindows上でDockerを動かし、そこからCUDAやそれを要求するソフトウェアを動かせます。

f:id:fuKa-Huka:20211216013237p:plain

今回やることをよくある感じの図で表現したもの

つまりどういうことかと言うと、Windows上でWSLでLinuxを動かし、Linux上でDockerを動かし、Docker ContainerでTensorflowやPytorchやその前提ソフトウェアを動かします(厳密には他のソフトウェアも関わったり、名前が正確じゃないですがイメージってことで)

この方法の難点は、動かせるのはLinux用のソフトウェアだけでWindows用のソフトウェアは対応しないという点です。TensorflowやPytorchは当然Linuxで動作しますが、Windowsのみのソフトウェアでは利用できません。じゃあ初めからLinux使えよとか言うな

WindowsコンテナというDockerでwindowsを動かす技術もありますが、そちらでCUDAを動かす方法というのは無さそうです。

 

 

 

準備

ここから本題です。

まず、今回実行した環境を示します。

OSについてはWin11を勧めます。後述しますが、WSLでGUIが簡単に使えるので便利です。Win10についても執筆時点(2021/12)でinsider preview版ではGUIに対応しているのでそのうち利用できるようになると思います。

また、今回は複数のCUDAの両立が問題なので、Nvidia GPUが必須です。と言うか、使用していない場合はそもそもこの記事の対象じゃないです。

加えて今回はVisual Studio Codeを使用します。簡単にコンテナに接続できるので便利です。

この記事ではDockerを使用しますが説明が面倒なので詳しい用語までは逐一説明しません。

 

WSLのインストール

まずはDockerを動かすWSLをインストールします。

docs.microsoft.com

wsl --install

上のガイドのようにPowerShellでインストールしてもよいですし、Microsoft Storeでディストリビューションを選んでインストールすることも可能です。

f:id:fuKa-Huka:20211216113139p:plain


起動するとアカウントの作成を求められるので行いましょう。

WSL用CUDAドライバのインストール

次にWSL用のCUDAドライバーをインストールします。

developer.nvidia.comページ下部のGet CUDA Driver からダウンロードページに飛び、GeforceQuadroの使用している方を選んでダウンロードします。

Visual Studio Codeのインストール

次にVSCode(Visual Studio Code)をインストールします。

azure.microsoft.comこちらも公式サイトでダウンロードしてもよいですし、wingetを使ってpowershellでインストールしてもいいです。

Docker Desktopのインストール

最後にDocker Desktopをインストールします。

www.docker.com

インストール時にInstall required Windows components for WSL 2というオプションについて聞かれるのでチェックを入れます。(デフォルトで入っているはず。)

チェックを入れ忘れたとか既にインストールしている人はDocker DesktopのSettingから設定できます。

VScodeの設定

必要なソフトウェアのダウンロードが終わったので次はVSCodeの設定を行います。VScodeのRemote-Containersという拡張機能を使うことで、コンテナ内でVScodeを立ち上げてローカル環境のように開発を行うことができます。

f:id:fuKa-Huka:20211216195213p:plain

VScodeのドキュメントにあるコンテナ内開発のイメージ

拡張機能のインストールのためにVScodeを起動し拡張機能タブを開き、Remote Developmentをインストールします。

注意点として、コンテナ内でVScodeを使用する際VScodeの主体として稼働しているのは図のようにコンテナ内のVScodeサーバであることがあります。従って、Windows上のVScode拡張機能はUIをカスタマイズするタイプのものを除きコンテナ内のVScodeにインストールする必要があります。

f:id:fuKa-Huka:20211216202442p:plain

Remote Developmentの拡張機能。この画面では日本語拡張機能が適用されているので注意

この拡張機能はRemote-WSL,Remote-Containers,Remote-SSHの三つの拡張機能がセットになっています。

WSLの設定

続けて、WSLでもいくつか設定をしていきます。まず、VScodeでWSLに接続します。左下の緑色の><が重なった感じのボタンを押します。

f:id:fuKa-Huka:20211216203337p:plain

これ

f:id:fuKa-Huka:20211216225325p:plain

リモート接続先の選択

ここでNew WSL Windowを選び、WSLに接続します。

すると新しくウィンドウが開き、左下の表示がWSL:OS名になっていることが確認できます。これでVScodeでWSL上のファイルにアクセスできます。

f:id:fuKa-Huka:20211216230251p:plain

こんな感じ

接続が確認できたら上のメニューからターミナルを選び、WSLのターミナルにアクセスします。別にVScodeでターミナルを開く必要性はないですが、ファイル編集が楽なのでこのまま続けます。

$ apt update
$ apt upgrade
$ apt install x11-xserver-utils
$ xhost local:

ターミナルを開いたら、x11-xserver-utilsをインストールします。apt updateやupgradeがまだならそちらもやっておきます。

このx11-xserver-utilsというのはLinuxでGUI表示に使われるX Window System関連のソフトで、これをインストールすることでマシンをxサーバとして利用できます。これによってDockerコンテナ上でGUIアプリが使えます。X Window SystemではGUIを使う側がサーバなのに注意してください。

また、この操作でGUIが使えるのはWSLgが利用できるWin11とWin10 insider previewのみです。執筆時点のWin10では対応していないので該当する方は別途Windows上にXサーバーアプリを用意するなどして対応してください。

そして末尾のxhost local:というのはX Windowのホストにlocalのアクセスを許可するという意味になります。あまりないシチュエーションかと思いますが、localからであれば全許可してしまうので多人数で使用するマシンの場合には注意してください。

最後にターミナルで

env

を実行し、その中からDISPLAY=の数値を確認しましょう。多分:0か0:0だと思います。

WSLの動作確認

ここからDockerの設定をする前に、一度WSLの動作確認をしてみましょう。まずLinuxがグラフィックボードを認識しているか確認するために

$ nvidia-smi

を実行します。

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 510.00       Driver Version: 510.06       CUDA Version: 11.6     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  On   | 00000000:01:00.0  On |                  N/A |
|  0%   45C    P8    40W / 320W |   1023MiB / 10240MiB |     N/A      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

いくつかWSLの仕様上表示されないパラメータがありますが、グラボを認識していることがわかります。

次にGUI表示を試します。

$ xeyes

を実行します。win11ならGUIが起動するはずです。

f:id:fuKa-Huka:20211216235109p:plain

うまくいけばこんな感じ

便利な時代になりましたね。

コンテナ設定

ではここから実際にコンテナを作成します。

まずWSLで作業用のディレクトリを作成します。この記事ではホームディレクトリ直下にProject1という名前で作成していきます。この作業用ディレクトリがDockerコンテナにマウントされます。

ホームディレクトリにいろいろモノがあって紛らわしい場合はVScodeの左上のファイルからフォルダーを開くを選んで作成したディレクトリに移動しましょう。

Project1/
    └ .devcontainer/
        ├ Dockerfile
        └ devcontainer.json

ディレクトリに移動したら、上の構成でフォルダとファイルを作成します。.devcontainerのドット'.'を忘れないように。

一ファイルづつ確認していきましょう。

まずはDockerfileです。最小構成はこんな感じです。

FROM nvcr.io/nvidia/pytorch:21.11-py3
# これはPytorchの場合
ENV TZ=Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt update && apt install -y x11-apps python3-tk
RUN pip install matplotlib

Dockerイメージは、NVIDIA のコンテナカタログから選びます。

コンテナカタログにアクセスしてTensorflowやPytorch等の必要なコンテナを選び(紛らわしいのが多いので注意!)右上のPull Tagを押すと、クリップボードにコンテナ名がコピーされます。そのままDockerfileに貼り付けると余計なコマンドまでついてくるので貼り付けたときに削除します。

また、Tensorflowが使いたい場合はこのイメージの部分をTensorflow用のイメージに変更してください。

f:id:fuKa-Huka:20211217003002p:plain

Pytorchのコンテナの詳細画面

このDockerfileはイメージの指定とGUI表示に必要なソフトをダウンロードするだけなので、別に必要なソフトウェアのインストールやユーザー生成、設定などは適宜Dockerfileに追記するかコンテナにログインして行って下さい。

TZ云々の設定をしているのは、ビルド中にTZを聞かれてビルドが止まるのを防ぐためです。

次にdevcontainer.jsonを編集します。

{
    "name": "TestProjectContainer", // コンテナ表示名
    "dockerFile": "Dockerfile", //Dockerfileの指定
    "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/Project1,type=bind,consistency=cached",// Workspaceとしてマウントするフォルダを指定
    "workspaceFolder": "/workspaces/Project1", // Workspaceのフォルダを指定
    "runArgs": ["--shm-size=1g" ,"--ulimit" ,"memlock=-1", "--ulimit","stack=67108864","--gpus","all","-it"],//その他引数を指定
    "mounts": ["source=/tmp/.X11-unix/,target=/tmp/.X11-unix,type=bind"],//マウントするフォルダを指定
    "containerEnv": {"DISPLAY":":0"}//変数の設定
}

 

このように設定します。基本的にはコメントの通りですが、最後の方はわかりにくいので説明します。"mounts"ではディレクトリをマウントすることでDockerホストのXサーバのソケットに接続します。

また、"containerEnv": {"DISPLAY":":0"}では先ほどメモしたWSLのDISPLAYの値を入力します。

 

それ以外にも使用する拡張機能などがあれば設定できます。詳しくは以下のリファレンスを参照して下さい。

code.visualstudio.com

実行

ここまで設定して、やっと準備が終わりました。

WSLを開いた時のように左下の緑のボタンを押し、開いたメニューからReopen Containerを選びましょう。ここまでの設定が間違っていなければ、イメージがビルドされてVScodeがコンテナにアクセスするはずです。(初回は時間がかかる)

また、原因不明の謎現象として、この時失敗してどうやってもビルドが通らなくなった時、Dockerfileを使用せずイメージ単体で一度コンテナを作成するとうまくいくようになります。

テスト

コンテナに接続できたら、GPU認識とGUIをテストしてみましょう。

まずはさっきも使ったnvidia-smiを実行してみましょう。

次にtorchのCUDA識別とgui機能を試します。

Pythonを起動し以下のコマンドを入力してみてください。

import torch

print(torch.__version__)
print(torch.cuda.is_available())#正しく認識していればTrue

さらにVScodeで適当なpythonファイルを作成し、以下のプログラムを実行してみましょう。

import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, np.pi * 2, 0.1) y = np.sin(x) plt.plot(x, y) plt.show()

無事グラフが表示できたかと思います。

f:id:fuKa-Huka:20211218020858p:plain

OK!

まとめ

このような方法でWindows上にDockerでグラフィックボードを用いた環境を実現できました。今回の例ではPytorchで構築しましたが、別ディレクトリに新たにdevcontainerを設定することで、容易にTensorflowの環境を用意できます。これでPytorchとTensorflowをwindows上に両立できるかと思います。

 

次回は

たちさんの記事です。よろしくお願いします。

参考サイト

CUDA on WSL :: CUDA Toolkit Documentation

待ってました CUDA on WSL 2 - Qiita 

Developing inside a Container using Visual Studio Code Remote Development

Dockerコンテナの中でGUIアプリケーションを起動させる | Unskilled?