Dockerを使った深層学習環境構築

# 概要

Kaggle用にローカルPCで深層学習が実行できる環境構築にだいぶ苦労したので、それを備忘録の為にまとめる。

# 前提条件

- Hardware
  - CPU : Intel(R) Core(TM) i7-10750H CPU
    - Memory : 16GB
  - GPU : NVIDIA GeForce GTX 1650
    - memory : 12GB
- OS
  - default : Windows11 Home 64bit
  - **Linux: WSL2**
  - **Linux distribution: Ubuntu**
    - Version : 22.04
  - **NVIDIA CUDA(Compute Unified Device Architecture) Driver**
- Application
  - **Docker Engine**
  - **Nvidia Container Toolkit(CUDA toolkit)**

## インストールするもの

![docker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751832/84472b29-ae04-dac4-3478-3f3a26587079.png)

- **WSL2** : Dockerを動かすために必要
- **Ubuntu :** Linuxのディストリビューション
- **NVIDIA CUDA(Compute Unified Device Architecture) Driver** : GPUの並列処理能力を活用し、高性能な計算を実現するための低レベルのソフトウェアで、GPUとアプリケーション間のインターフェースを提供する。GPUに応じた特有のものをインストールしてやる必要がある。
- **Docker Engine** : アプリケーションを構築して コンテナ化するためのオープンソースの コンテナ化技術。Docker クライアント（Docker CLI）、REST API、Dockerデーモンで構成されており、Docker クライアントがUNIXドメインソケット`/var/run/docker.sock`を介してREST APIが叩かれ、Dockerデーモンが実際にコンテナを立てたり、イメージビルド等の処理を行う。
    
    ![docker_component.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751832/1bf112c2-53e1-efe0-de7f-224cf0b97946.png)

    
- **NVIDIA Container Toolkit(CUDA toolkit) :** CUDAアプリケーションを開発するための高レベルの開発ツールやライブラリを提供するソフトウェアパッケージ。TensorFlow、PyTorchがGPUを使うのに必要。

# 構築手順

1. クリーンナップ（仮に元から入っていた場合など）
    1. 「コントロールパネル」→「プログラム」→「プログラムをアンインストール」
    2. 「設定」→「インストール済みアプリ」→「アプリをアンインストール」★Windows10以降推奨
        1. [**WSL2 のインストールとアンインストール**](https://qiita.com/zakoken/items/61141df6aeae9e3f8e36)
2. Windows上でWSL2を有効化
    1. 「コントロールパネル」→「プログラム」→「Windowsの機能の有効化または無効化」を選択
    2. 「Linux用Windowsサブシステム」と「仮想マシンプラットホーム」にチェックを入れてOKを選択
        
        ![windows_settings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751832/13da07fa-865a-18b1-9a30-bb5f6906a2a7.png)

        
3. **WSL2・Ubuntu**をインストール
    1. WSL**・Ubuntu**をコマンドプロンプトからインストール
        
        ```bash
        wsl --install
        ```
        
    2. ユーザー名・PWを作成
4. 下記のサイトから**Windows向けのNvidia driver**のインストール（恐らく2とは順序不同）
    1. https://www.nvidia.com/download/index.aspx
        1. GPUの製品名・バージョンを選択
        2. Download Typeは**「Studio Drivers」**を選択
        
        ![nvidia_driver.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751832/baa9efb5-a86a-8180-688c-c92bc0a431d7.png)

        
    2. インストーラー実行
        1. 以下のサイトを参照に手順にしたがってインストール
            1. [**GeForce Experience driver installation failed. How do I manually clean install the NVIDIA driver for my graphics card?**](https://nvidia.custhelp.com/app/answers/detail/a_id/10/related/1)
5. WSL上 or Windows上にて以下のサイトを参考に**docker**をインストール
    1. [**Docker Desktop WSL 2 backend on Windows**](https://docs.docker.com/desktop/wsl/#download)
    2. Windows上からGUIにてDocker Desktop for Windowsをインストールしたほうが良い。
        1. 理由
            1. Docker Desktopの統合により、複雑な設定をすることなくGPUを利用できるから。
            2. Docker DesktopはWSL 2との統合により、NVIDIA GPUを利用するための公式サポートがあるため。
6. **WSL上でNvidia Container Toolkit**をインストール
    1. ディストリビューションとバージョンの情報を取得
    2. GPGキーをgpg --dearmorでバイナリ形式に変換し、ダウンロードして保存
    3. libnvidia-containerのリポジトリリストをダウンロードして設定し、sedを用いてリポジトリURLに手順5-2で保存したGPGキーで署名されていることを指定し、sudo teeを使って保存。
        
        ```bash
        distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
              && curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
              && curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
                    sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
                    sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
        ```
        
    4. nvidia-container-toolkitのインストールして、GPUがコンテナから読み取れるか確認。以下のような画像が出れば成功。
    
    ```bash
    sudo apt-get update
    sudo apt-get install -y nvidia-container-toolkit
    sudo docker run --rm --gpus all nvidia/cuda:11.6.2-base-ubuntu20.04 nvidia-smi
    ```
    
    ![result_of_nvidia_smi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751832/38144764-1460-4f76-6788-102852bdbda6.png)

    
7. Dockerデーモンの起動
    1. CUDA toolkitを用いてNVIDIA コンテナー ランタイムを認識するように Docker デーモンを構成
    
    ```bash
    sudo nvidia-ctk runtime configure --runtime=docker
    sudo systemctl restart docker
    ```
    
8. 実行環境となるコンテナを起動させる
    1. 任意のディレクトリ上でコンテナ起動だけでなく、Pythonや必要なライブラリインストールまで行ってくれる以下のdocker-compose.ymlファイルを作成する。
        
        ```yaml
        version: '3.8'
        services:
          pytorch-jupyter:
            image: nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22.04
            container_name: pytorch-jupyter
            deploy:
              resources:
                reservations:
                  devices:
                    - capabilities: [gpu]
            environment:
              - NVIDIA_VISIBLE_DEVICES=all
              - NVIDIA_DRIVER_CAPABILITIES=compute,utility
            command: >
              /bin/bash -c "apt-get update && apt-get install -y python3 python3-pip &&
              pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 &&
              pip install jupyterlab &&
              jupyter lab --ip='*' --port=8888 --no-browser --allow-root --NotebookApp.token='' --NotebookApp.password=''"
            ports:
              - "8888:8888"
            volumes:
              - ./target:/workspace
            working_dir: /workspace
        ```
        
    2. docker composeでコンテナを起動する
        
        ```bash
        docker-compose up
        ```
        
    3. ブラウザ上からjupyterを開き、GPUの疎通が取れるか確認する。
        
        ```python
        import torch
        import torch.cuda as cuda
        
        # Check if GPU is available
        if torch.cuda.is_available():
            print(f"CUDA is available. GPU name: {torch.cuda.get_device_name(0)}")
            
            # Get the device (GPU) properties
            device = torch.device("cuda")
            
            # Get current GPU memory usage
            total_memory = torch.cuda.get_device_properties(device).total_memory
            reserved_memory = torch.cuda.memory_reserved(device)
            allocated_memory = torch.cuda.memory_allocated(device)
            free_memory = reserved_memory - allocated_memory
            
            print(f"Total GPU memory: {total_memory / 1e9:.2f} GB")
            print(f"Reserved GPU memory: {reserved_memory / 1e9:.2f} GB")
            print(f"Allocated GPU memory: {allocated_memory / 1e9:.2f} GB")
            print(f"Free GPU memory: {free_memory / 1e9:.2f} GB")
            
        else:
            print("CUDA is not available. Running on CPU.")
        ```

# 参考文献
- [**WSL2+docker+GPU環境構築(win11)**](https://qiita.com/nabion/items/4c4d4d4119c8586cbd9e)
- [**Windows+WSL2+DockerでGPUの環境構築**](https://soroban.highreso.jp/article/article-091#6623af5295349a2e42fb7baa-48161e84dc21b7dae9289c7c)