GPUで高速化！自然言語処理の機械学習基盤構築

#はじめに
これまで、自然言語に対して機械学習を行う時はCPUで頑張ってきました。しかし、トレーニングデータセットが多くなるにつれ、CPUでRNNやCNNを試そうものなら、いつまでたっても学習が終わりません。

そこで、これでは駄目だと一念発起して、GPUを搭載した機械学習基盤を作ることにしました。

#1. 機械学習基盤の構築
##1-1.基盤構成
なんと言っても機械学習の高速化といえばGPUの導入です。どれにするかは結構迷いましたが、折角作るのであればハイエンドで組んでみたい。。。その欲求に勝てませんでした。
NVIDIA Pascal世代の最上位グラフィックスカード「Titan Xp」を組み込みます。他のパーツはTitan Xpを軸に以下の感じで選定しました。

| 項番 | 種別 | 製品名  | コメント |
|:-:|:--|:--|:--|:--|:--|
| 1 | 筐体 | Define R6 FD-CA-DEF-R6 | 安い筐体でも代替可能だが、メンテを考慮して。 |
| 2 | CPU | Core i7 8700K BOX | 自然言語の前処理ではCPUを利用するので、i7に。 |
| 3 | CPUクーラー | 無限五 リビジョンB SCMG-5100 | 使い慣れたものを。いつか水冷... |
| 4 | マザーボード | Z390 Extreme4 | SLIかつ第9世代CPUに対応。 |
| 5 | メモリ | F4-3200C16D-32GTZR | 16gb x2。  |
| 6 | GPU | Titan Xp | NVIDIA Pascal世代の最上位グラフィックスカード。いつかSLI...  |
| 7 | 電源 | RM850x CP-9020180-JP |  Titan Xpのため、念のため850へ。 |
| 8 | M.2 SSD | WD Black NVMe WDS500G2X0C | 超高速のM.2 NVMe SSD。 |
| 9 | HDD | WD40EZRZ-RT2  | SSDのバックアップ兼データ置き場。 |
上記以外に、CPUグリスとM.2用ヒートシンクを購入しています。

##1-2.組み立て
早速組み立てます。
並べてみると、壮観です！
![全体.png](https://qiita-image-store.s3.amazonaws.com/0/53086/07ab7cb4-a1da-dd07-4ea5-6cce6008d533.png)
Titan Xpの化粧箱かっこいい...
![TitanXp.png](https://qiita-image-store.s3.amazonaws.com/0/53086/1d8b6a97-0e99-e459-9c90-6922a00f45be.png)

3時間程度で完成しました。凄く光ってます。
メモリやストレージはちゃんと認識できているようで一安心です。（ＨＤＤだけ到着が遅くて、後から搭載しました。）
![完成.png](https://qiita-image-store.s3.amazonaws.com/0/53086/47c0f7ea-f46f-5249-64e2-df4c064fbde9.png)

##1-3.OSのインストール
CentOS7.5をインストールします。機械学習用なので、GUIは不要なため、最小構成でインストールします。ファイルシステムはxfsです。

##1-4.Titan Xpのドライバインストール
機械学習にGPUを利用しますので、Titan Xpのドライバをインストールします。

まず、nouveauを無効化します。

```Bash
# defaultのnouveauの確認
$ lsmod | grep nouveau
# rootに変更
$ sudo su
# imgのバックアップ
$ mv /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r)-nouveau.img
# nouveauを利用しない起動イメージ作成
$ dracut --omit-drivers nouveau /boot/initramfs-$(uname -r).img $(uname -r)
# nouveauのロード禁止
$ echo 'blacklist nouveau' >> /etc/modprobe.d/modprobe.conf
$ echo 'blacklist nouveau' >> /etc/modprobe.d/nouveau_blacklist.conf
# 再起動
$ reboot
# nouveauがロードされていないこと(何も表示されないこと)を確認
$ lsmod | grep nouveau
```
次にTitan Xpのドライバをインストールします。
今回は、Titan Xpなので、[NVIDIAドライバダウンロードサイト](https://www.nvidia.co.jp/Download/index.aspx?lang=jp)からLinux用のドライバをダウンロードして、任意の場所に保存します。その後、以下の通りドライバのインストーラを実行します。

```Bash
$ sudo sh NVIDIA-Linux-x86_64-410.78.run
```

##1-5.dockerのインストール
dockerを導入することで、他のメンバやプロジェクトと機械学習基盤を共有する場合でも、それぞれのコンテナ内に環境分離することが可能です。

dockerを以下の公式手順に従ってインストールします。
https://docs.docker.com/engine/installation/linux/docker-ce/centos/

```Bash
# 既存のdockerがインストールされていれば、アンインストール
$ sudo yum remove docker docker-client docker-client-latest docker-common          docker-latest docker-latest-logrotate docker-logrotate docker-selinux               docker-engine-selinux docker-engine
# 必要なパッケージのインストール
$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# リポジトリの追加
$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# docker-ceのインストール
$ sudo yum install docker-ce
# dockerの起動とサービス自動化
$ sudo systemctl start docker
$ sudo systemctl enable docker
```

##1-6.nvidia-dockerのインストール
nvidia-dockerを利用することで、CUDA ToolkitとcuDNNはコンテナの中に配備されます。
その結果、コンテナ毎に異なるバージョンのCUDA ToolkitとcuDNNを組み合わせて使用できるため、ディープラーニングのライブラリが要求するCUDAバージョンの違い等を吸収して、色々なライブラリを容易に利用できます。

nvidia-dockerを以下の公式手順に従ってインストールします。
https://github.com/NVIDIA/nvidia-docker

```Bash
# リポジトリの追加
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
$ curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.repo | sudo tee /etc/yum.repos.d/nvidia-docker.repo
# nvidia-docker2のインストール
$ sudo yum install -y nvidia-docker2
# docker daemon の設定再読み込み
$ sudo pkill -SIGHUP dockerd
```

officialのcudaイメージからコンテナを作成して、nvidia-smiがコンテナ上でも利用できることを確認します。

```Bash
$ sudo docker run --runtime=nvidia --rm nvidia/cuda:9.0-base nvidia-smi
```

これで、GPUを利用できるコンテナが作成できるようになりました。後は利用したいライブラリに合わせて、コンテナを作っていくだけです。

##1-7.PyTorch環境の作成
自然言語処理では、私は主にPyTorchを使いますので、Docker上にPytorch環境を作成します。
また、併せてtorchtextのインストールも行います。
torchtextは自然言語処理の前処理を容易に実施できるライブラリです。
いつもはDockerfileにまとめてしまいますが、今回は順番にインストールします。

PyTorchを以下の公式手順に従ってインストールします。
Linuxでpython3.6、pip、cuda9.0の場合は以下のようになります。

```Bash
# PyTorchのインストール
$ pip3 install torch torchvision
# torchtextのインストール
$ pip3 install torchtext
```

Python3系のインストール等をはしょりましたが、とりあえずPyTorch環境の作成完了です

#2.自然言語処理の機械学習
やっと機械学習基盤の構築が完了しましたので、早速自然言語処理の機械学習を試します。
今回は、PyTorchのSNLI[サンプルソース](https://github.com/pytorch/examples/tree/master/snli)をCPUのみ、CPU+GPUの2パターンで動かしてみます。
ちなみに、[SNLI(Standord Natural Language Inference)](https://nlp.stanford.edu/projects/snli/)は自然言語推論のためのデータセットです。
PyTorchのサンプルソースでは、前提と仮説からなる２つの文章を用いて学習を行い、与えられた２つの文章が前提と仮説の関係になっているかどうかをクラス分類します。


なお、本来はデフォルトで50epochで実施するサンプルソースですが、CPUのみでの学習があまりにも遅いので、5epochにしています。

| パターン| CPU | GPU | メモリ | epoch数| 学習時間  | 
|:--|:--|:--|:--|:--|:--|
|CPUのみ| Core i7 8700K 3.7GHz | 無し| 32GB | 5 | 3時間29分 | 
|CPU+GPU| Core i7 8700K 3.7GHz | Titan Xp | 32GB | 5 | 5分27秒 |

GPUを使用した場合は、CPUのみの場合と学習速度が38倍になりました！
Titan Xp半端ないっす。

#まとめ

最後までお読みいただき、ありがとうございました。
GPUを導入することで高速に機械学習を行える学習基盤を構築できました。
これでこれまで時間がかかりすぎてできなかったあれやこれやを試すことができます。
次はSLI構成にチャレンジしてみたいと思います！