CUDA対応したWSL2+VSCodeでディープラーニングの開発環境を作ってみる

#CUDA対応したWSL2+VSCodeでディープラーニングの開発環境を作ってみる

## 概要
WSLこと**Windows Subsystem for Linux**は、Windowsで社内システムに繋ぎつつLinuxのシステム開発を行う我々サラリーエンジニアの強い味方です。
そんなWSLがWindows Insider Preview 20150.1000からCUDAに対応したという話を聞きつけました。[^1]
今回はWSL2とVSCodeを使ってディープラーニングの開発環境を作ってみようと思います。[^2] 
導入環境にはGPUが刺さっていることが前提です。
## この記事でやってみること
1. [Windows上にWSL2でUbuntuを導入する](#2-wsl2を有効化してubuntuをインストールする)
1. [WSL2のUbuntuでNvidia Driver for CUDAを認識する](#3-nvidia-drivers-for-cuda-on-wslをインストールする)
1. [VSCodeからWSL2の環境を触れるようにする](#4-vscodeからwslの環境へアクセスする)
1. [WSL2上にDockerをインストールして実行環境を構築する](#手順1-wsl2上にdockerをインストールする)
1. [TensorFlow on Dockerの環境をWSL上に構築する](#手順3-wsl--docker--cuda環境の動作確認)

## やってみる
### 1. Insider Previewに参加して最新のPreview Buildを導入する
要はWindows Updateで導入できるのですが、Insider Previewに参加することでプレビュー用の特殊なUpdateを入手することができます。
手順は簡単ですが、そこそこ時間がかかります。
ポイントは、Insiderをファストに設定すること。あとはいつもどおりにWindows Updateを実行するだけで、天から神(Cuda対応のWSL2)が降りてきます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/0390455e-a1a1-2b00-42ea-9f8a5bfaecd8.jpeg", alt="windows insider program.jpg", width=480px>

一晩寝かせたら無事に新しいバージョンが手に入りました。

![indows version.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/bb9ca280-bd46-d1c3-3d14-d108058284a9.jpeg)

```sh:カーネルチェック
PS C:\Users\hirose.tomoki> wsl --update
更新をチェック中...
利用できる更新はありません。
カーネル バージョン: 4.19.121
```

カーネルバージョンが4.19.121以上になっていない場合は、Windows Updateの適用が不十分です。
Windows Updateメニューの詳細オプションから**他のMicrosoft製品の更新プログラムを受け取る**を選択して、改めて更新がないかチェックしてみてください。

### 2. WSL2を有効化してUbuntuをインストールする

ここの作業は管理者権限のPowerShell上で実行していきます。
この手順に入る前にLinuxカーネルの更新をしておくと安心です。
https://docs.microsoft.com/ja-jp/windows/wsl/wsl2-kernel

#### 手順1. WSL1を有効化

```sh:WSL1の有効化
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
```
```sh:実行結果
展開イメージのサービスと管理ツール
バージョン: 10.0.20150.1000

イメージのバージョン: 10.0.20150.1000

機能を有効にしています
[==========================100.0%==========================]
操作は正常に完了しました。
```
#### 手順2. WSL2を有効化
```sh:WSL2の有効化
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
```
```sh:実行結果
展開イメージのサービスと管理ツール
バージョン: 10.0.20150.1000

イメージのバージョン: 10.0.20150.1000

機能を有効にしています
[==========================100.0%==========================]
操作は正常に完了しました。
```
有効化できたら一旦Windowsを再起動します。

#### 手順3. WSL2をデフォルトに設定する
``` sh:WSL2をデフォルト設定
wsl.exe --set-default-version 2
```

コマンドが実行できたらWSL2が有効化されたので、Ubuntuのインストールを行っていきます。

#### 手順4. Ubuntu on WSLをインストールする
Windows StoreにアクセスしてUbuntuを検索します。今回は仕事でも使っているので18.04LTSにしました。
Windows Storeのアクセス方法がわからない……という方は**Win+R**を開いて**ms-windows-store:**を実行するとURIスキームからStore起動が着火します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/691d5c15-8dc3-a079-1658-218c4fd3933f.jpeg", alt="winstore ubuntu.jpg", width=48%> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/08e0de3d-be6a-475b-59ac-0e2334950caa.jpeg", alt="winstore ubuntu install.jpg", width=48%>


インストールが終わったらスタートメニューからUbuntuを起動します。

#### 手順5. Ubuntuのセットアップ

無事にUbuntuが起動したのでセットアップを行います。
ユーザ名とPWを設定していきますが、**ここでのユーザ名はWindowsと独立している**点は注意事項です。

ユーザとパスワードを設定したのでWindows Terminalから開いてみました。
Windows上からunameコマンドが叩けています！
![ubuntu uname.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/660ebec4-f8fc-d512-72aa-213d2617f971.jpeg)

### 3. NVIDIA Drivers for CUDA on WSLをインストールする

この作業は特に悩むこともないと思いますので、下記URLから辿ってドライバのインストールを進めていきます。
https://developer.nvidia.com/cuda/wsl

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/269d5e58-ca48-8253-2dac-7053bdecb091.jpeg", alt="nvidia.jpg", width=48%>

今回はver455.41がインストールされました。


### 4. VSCodeからWSLの環境へアクセスする
とりあえずUbuntu on WSL2とCUDA on WSL2(Preview)が整ったので、続いてはVSCode on WindowsでLinuxの開発ができる環境を作っていきます。
VSCodeをインストールしていない方は、今すぐ[こちら](https://azure.microsoft.com/ja-jp/products/visual-studio-code/)からどうぞ。

#### 手順1. 拡張機能[Remote -WSL]をインストールする
![remote wsl.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/862e15dd-5375-2cc1-d263-e346af2e41b9.jpeg)

**Windows側のVSCode**にこちらをインストールしていきます。
WSLが有効化されているのでレコメンドしてくれているのでしょうか。

#### 手順2. WSLからVSCodeを起動する
拡張機能を入れたのはWindowsでしたが、実際に開発するにあたっては**WSLから起動**していきます。
とりあえずホームディレクトリで起動してみます。
![run vscode on wsl2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/ce688561-e9a4-798f-4c79-b5d84196abdc.jpeg)
すると、新しくVSCodeのウィンドウが立ち上がります。
立ち上がったVSCodeは、下の図のように**WSL:Ubuntu-18.04**とあるので、連携がうまく行っていることがわかります。
![vscode move wsl2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/6477e396-a198-3f5e-c7de-4d4d1708dba3.jpeg)

何が起きているかというと、WSL2上で起動したVSCodeへWindows上でリモートアクセスしています。
なので、ここで起動したVSCodeでコンソールを開くとPowerShellではなくbashにアタッチします。こうなれば後はただのUbuntuです。

一度起動させると、WSLを起動してcode .せずとも、**Ctr+Shift+P**のコマンドパレットや**Ctr+R**の最近使用した項目から**wsl**を検索すると、ショートカットすることができます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/7a8683a2-cf32-ae02-12bb-f3cba6eb0d21.jpeg", alt="wsl on vscode shotrcut1.jpg", width=48%> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/c27fa15a-46d3-6a4d-34d9-26a92430491d.jpeg", alt="wsl on vscode shotrcut.jpg", width=48%>

### 5. ディープラーニング用の環境構築

ここまで来たらもう一息です。
ようやく本題ですが、ディープラーニング用の環境を作っていきましょう。

#### 手順1. WSL2上にDockerをインストールする
詳しくは解説しませんが、Dockerは[ここ](https://docs.docker.com/engine/install/ubuntu/#install-using-the-convenience-script)にあるとおりです。

手順がスクリプトにまとまっているので、ダウンロードして実行すればOKです。

#### 手順2. NVIDIA Container Toolkitをインストールする
詳しくは解説しませんので、下記を参考にインストールしてください。

```sh 
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
curl -s -L https://nvidia.github.io/libnvidia-container/experimental/$distribution/libnvidia-container-experimental.list | sudo tee /etc/apt/sources.list.d/libnvidia-container-experimental.list

sudo apt update
sudo apt install -y nvidia-docker2
```
インストールできたらdockerを起動しておきます。

``` sh:デーモンの起動
# WSLの仕様でsystemctlだと動かないので注意
sudo service docker start
```

#### 手順3. WSL + Docker + CUDA環境の動作確認
うまく動くかどうか試していきます。
今回はDockerHubのTensorFlowリポジトリから[v2.2.0-gpu](https://hub.docker.com/layers/tensorflow/tensorflow/2.2.0-gpu/images/sha256-3f8f06cdfbc09c54568f191bbc54419b348ecc08dc5e031a53c22c6bba0a252e?context=explore)を利用していきます。

```sh:Docker+TensorFlow環境構築
$ docker run --gpus all -it --rm   tensorflow/tensorflow:2.2.0-gpu /bin/bash
________                               _______________
___  __/__________________________________  ____/__  /________      __
__  /  _  _ \_  __ \_  ___/  __ \_  ___/_  /_   __  /_  __ \_ | /| / /
_  /   /  __/  / / /(__  )/ /_/ /  /   _  __/   _  / / /_/ /_ |/ |/ /
/_/    \___//_/ /_//____/ \____//_/    /_/      /_/  \____/____/|__/

$ python
Python 3.6.9 (default, Apr 18 2020, 01:56:04)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from tensorflow.python.client import device_lib
>>> device_lib.list_local_devices()

name: "/device:GPU:0"
device_type: "GPU"
memory_limit: 3225865422
locality {
  bus_id: 1
  links {
  }
}
```

無事に認識されているようです。

## 出来上がり
### 開発画面
開発画面はこんな感じです。
下のカラムでbashにつないで、Dockerで作った開発環境へアクセスしています。
今回は省略していますが、VSCode上だけでDockerへのアクセスとGitリポジトリへのアクセスができる上、GPUを搭載したLinux環境での動きも確認できる様になっています。

![vs image.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/f48e95b9-489a-69cf-8d8d-bf2ab1bd3719.jpeg)

### 学習させてみる
![move.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/ff6d36e5-02bd-8596-39e4-58e1fe413ec5.jpeg)

MNISTだしFC層をちょろと繋いだだけだしで、あまり恩恵受けられていないですね……。
GPUはちゃんと使われていて、CUDA周りのライブラリが読み込まれているので、きちんと動作しているのはわかると思います。


## まとめ

CUDA対応したWSL2を利用して、UbuntuのインストールとDocker+VSCodeを使った開発環境の構築まで一気通貫で紹介してきました。

まだプレビュー版ということもあり、nvidia-smiコマンドでログが取れなかったりなど不便な点もありますが、WSLのCUDA対応はかなり凄い更新だと思います。
商品としてAIの導入をするにあたって、Linuxサーバを用意せずにスタンドアロンでローカルマシンで動かせる可能性があるって考えると、ちょっと楽しくなりませんか？

サラリーエンジニアとしては資産管理する物理マシンが減る可能性を想像するだけで震えが止まりません……。

それでは、良いディープラーニングライフを！！


[^1]: https://devblogs.nvidia.com/announcing-cuda-on-windows-subsystem-for-linux-2/
[^2]: 公式のWSL2/CUDAインストール方法は[こちら](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#getting-started)