GCPとDockerでKaggle環境を作る(2023年10月現在)

Kaggleを行っており、何とか無料で機械学習モデルを学習させる環境がほしく、GCPの無料枠を使ってみました。
Kaggleのカーネルと同じ環境に揃えるためにDockerを使ったのですが、めちゃくちゃ環境構築が大変だったので備忘録も兼ねてここに情報を残しておきます。
# 実現したいこと
- GCPの無料枠でお金をかけずに学習させたい
- Kaggleのカーネルと同じ環境を作りたい
- ローカルからSSH接続して学習を行いたい

## GCPの無料枠でお金をかけずに学習させたい
GCPに利用登録すると300米ドル分のクレジットをもらえます。
このクレジットの分だけでモデルの学習をさせたい。
GCPの利用登録は指示に従えばすぐできて、無料トライアルが始まります。
ここから行うべきことは大きく分けて３つ。
①予算アラートを設定
②有料のCloud請求先アカウントにアップグレード
③GPUの利用申請

### 予算アラートの設定
不測な課金が怖いのでアラートを設定しておきます。

![スクリーンショット 2023-10-12 19.40.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2708192/0329b25c-6db9-00f7-dcaa-840529e4ce86.png)


コンソール(「ようこそ、(自分のアカウント名)さん」と表示されているトップページ)に移動し、左上のハンバーガーメニューから「お支払い」にジャンプする。
↓
「予算アラートを設定」にジャンプ
↓
「予算の作成」にジャンプ
↓
自分の許容できる額に従量課金額が達した場合はアラートを発するように設定する

### 有料のCloud請求先アカウントにアップグレード
なぜ有料版にアップグレードする必要があるかというと、そうしないとGPUを使うことができないからです。
有料版にしても使っていない分のクレジットは引き継げます。

アップグレード方法
コンソールの左上にあるハンバーガーメニューを展開し、「お支払い」にジャンプ
↓
画面の左下に表示されている「アップグレード」というボタンを押す
↓
ポップアップが出てくるため、続けてアップグレードを選択する

### GPUの利用申請

これもコンソールの左上のハンバーガーメニューを展開して、[IAMと管理]から[割り当て]にジャンプして、各リージョン毎と全リージョンでのGPUの割り当て上限の上昇をリクエストする必要があります。
ただこの方法だとどのリージョンを使ったら良いのかだとか、どれがどの設定なのかといったことで混乱してしまいがちです。
なので、実際にGPUインスタンスを作成して、その時にGPUの利用上限に達しているという旨のエラーが出るので、その時の誘導に従って上限の上昇をリクエストするのが分かりやすいと思います(最初の状態では上限は0である)。

![スクリーンショット 2023-10-12 19.57.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2708192/32df1b8d-dfac-a74e-caf0-1678360443f6.png)

上の画像の「割り当てをリクエスト」を押すと以下のような状態になり、GPUの利用上限の増加をリクエストできます。
上限のGPUの個数を入力すると、その理由を尋ねられるので適当に英語で理由を書けばそれでOKです(例："to run a deep learning model"など)。

![スクリーンショット 2023-10-12 20.00.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2708192/a34bfe22-76f4-e6d4-06fd-52b255def442.png)


この時、GPUの上限は少し多め(1つのGPUで足りるかなと思っているのなら3つとか)に申請しておいたほうが良いと思われます。というのも、GCPの利用して月日が浅いからなのか、私の場合は1つのGPUでは足りないような気がして2つに上限を上げるようにリクエストをしたら却下されてしまったからです。
なお、メールで２営業日以内にリクエストの可否が来るとのことですが、経験的には5分くらいしかかかっていません。


## Kaggleのカーネルと同じ環境を作りたい
### GPUインスタンスの作成
コンソールから「VMインスタンス」を押し、画面上側にある「インスタンスを作成」にジャンプ。
GPUインスタンスを以下の構成要素で作りました。
| 構成要素 | 内容 | 備考 |
|:-:|:-:|:-:|
|  GPUタイプ |  NVIDIA V100 |  本当はNVIDIA A100 40GBを使いたいが需要が大きいためか、なかなか使用できない(現在は使用できませんという旨のエラーが出る) |
|  リージョン | 台湾  |  台湾リージョンだと比較的安い |
|  VMプロヴィジョニングモデル |  スポット |  スポットは途中で実行が止められる恐れがあるが経験上止められたことはない(絶対に止められたくない場合は「標準」にする) |
| メモリ  |  ハイメモリ(n1-highmem-4) |  LLMの読み込みや学習ではこれくらいは必要(26GB) |
|  ディスク容量 |  200GB |  後述するKaggelのDockerイメージが80GBくらいディスクを食うので余裕を持ったサイズにする必要アリ |
|  OS |  ubuntu 20.0.4 LTS |  他のディストリービューションでも同様なことはできると思われるが未検証 |


上記の構成でGPUインスタンスを作成すると、このインスタンスは「開始」された状態で出来上がります。
「開始」された状態のインスタンスは課金されている状態なので、使わない時は「停止」させておきましょう。
これらのことはハンバーガーメニューから「VMインスタンス」のページに飛ぶと、作成されたインスタンス一覧が表示されているので、そこからインスタンスの右端のボタンから「開始/再開」や「停止」や「削除」などで行えます。

### 作成したインスタンスに接続
作成されたインスタンス一覧のページから、接続したいインスタンスの右端にある「接続」のメニューで「ブラウザウィンドウで開く」を選択することで、ブラウザで作成したインスタンスのコンソールを操作することができる。

![スクリーンショット 2023-10-12 20.15.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2708192/541220c4-92e2-38e9-528b-e4b396f74662.png)


### NVIDIAのドライバーまわりのインストールを行う

```
sudo su # スーパーユーザーに切り替えを最初に行う。
```
```
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-keyring_1.0-1_all.deb
dpkg -i cuda-keyring*.deb
apt-get update
apt-get install cuda-11-8 # 20分ほどかかる
```
バージョンが合わないことによるエラーがでがちなので、その場合は`cuda-11-8`をふさわしいバージョンに変えましょう。
正しいバージョンは
https://github.com/Kaggle/docker-python/blob/main/config.txt 
で確認できます。
例えば
```
CUDA_MAJOR_VERSION=11
CUDA_MINOR_VERSION=8
```
であれば`cuda-11-8`です。

### DockerのKaggleイメージをビルドして、作成したコンテナの開始
まずはDocker のインストールをします。
```
curl https://get.docker.com | sh
systemctl start docker && sudo systemctl enable docker
```
次にDockerでNVIDIA GPUを利用するのに必要なツールキットをインストールします。
```
curl -s -L https://nvidia.github.io/nvidia-container-runtime/gpgkey | apt-key add -
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-container-runtime/$distribution/nvidia-container-runtime.list | tee /etc/apt/sources.list.d/nvidia-container-runtime.list
apt-get update
apt-get install nvidia-container-runtime
service docker restart
```



そしてKaggleの公式イメージを取得してビルドします。
```
# 公式イメージ 
# https://github.com/Kaggle/docker-python 

git clone https://github.com/Kaggle/docker-python.git 
cd docker-python 
```
```
./build --gpu
```
ビルドにはおよそ1時間ほどかかりました。
次に作成したコンテナの生成と起動は以下のように行います。
```
docker run -itd -p 8888:8080 -v /home:/home --name kaggle --gpus all gcr.io/kaggle-gpu-images/python /bin/bash
```
こちらでもおよそ20分ほどかかります。
「kaggle」の部分は任意のコンテナの名前です。 
もしポート番号8888でエラーになる場合は、おそらく他のプログラムがそのポートを使っているので8889などに変えてみてください。

なお、一度インスタンスを停止するなどした場合は、すでにコンテナが作成されているため、再びイメージのビルドなどをする必要はなく次のコマンドを打ち込むだけで良いです(ただし、インスタンスを切っていた場合は再度`sudo su`をするなどして管理者権限で行う必要はあります)。
```
docker start kaggle #「kaggle」は自分でつけた名前
```

コンテナが起動しているかは以下のコマンドで確認します。
```
# コンテナを起動 
docker ps
```
きちんと起動していればコンテナ情報が表示され、STATUSが「Up」になっているはずです。
何らかの理由でコンテナが起動していない場合はコンテナ情報が何も表示されません。

コンテナが起動していたら、以下のコマンドでコンテナの内部に入りましょう。
```
# コンテナの中に入る 
docker attach kaggle #「kaggle」は自分でつけた名前
```
以下のように出ていたらコンテナの中に入ることに成功です。 
`root@{CONTAINER ID}:/#  `

ここからコンテナ内でGPUが認識できているか確認します。
```
#コンテナの中でGPUのパスを通す 
# パスの設定 
export LD_LIBRARY_PATH=/usr/local/cuda/lib64 
```

```python
# 最新版のpytorchをインポート
python -m pip install torch==2.0.1
python
>>>  import torch 
>>>  torch.cuda.is_available() #TrueとでたらGPUが認識されている
True 
>>>  exit() #Pythonから抜ける
```
　無事、Kaggleのコンテナ内でGPUが使えることがわかりました。

### ローカルからSSH接続して学習を行いたい
ローカルのマシンでgcloudをインストールして初期設定を行います(詳細は[ここ](https://cloud.google.com/sdk/docs/install-sdk?hl=ja))。
gcloudのインストールと設定が終わったら、ローカルの{username}/.ssh/配下に秘密鍵と公開鍵を設置しconfigを作成し、gcpの方に公開鍵を登録しておきます。

configの中身は、
```config
Host instance名
    HostName 作成したGCPインスタンスの外部IP
    User Googleアカウントのユーザー名
    Port 22
    IdentityFile ~/.ssh/google_compute_engine
    IdentitiesOnly yes
```
です。

ここまでの準備ができたらローカルのターミナルから、
```
gcloud compute ssh --project={project ID} --zone=asia-east1-c {instance名} -- -L 8888:127.0.0.1:8888
```
でGCPで開始されているGPUインスタンスにローカルからSSH接続します。
`asia-east1-c`は台湾リージョンのゾーンの一つで、作成したインスタンスのゾーンに応じて書き換えてください。

SSH接続ができたらすでにKaggleのコンテナができているはずなので、先ほどのようにコンテナを開始させます。

```
# スーパーユーザーに切り替え
sudo su
#コンテナの開始
docker start kaggle 
# コンテナの中に入る 
docker attach kaggle 
```
これでローカルでGCPのインスタンスにSSH接続ができました。
あとはこのインスタンスにファイル転送を行ってファイルの実行を行えばモデルの学習を行うことができます。
Kaggleと同じ環境のはずなので、Kaggleのカーネルでうまくいくことはここでもうまくいきます。
(ただしGPUの種類とかメモリに関しては異なるので、そういった差異による問題は起き得ます)

### Jupyter Labで対話的に学習や分析を行いたい場合
```
jupyter lab --ip=0.0.0.0 --allow-root --LabApp.token="" 
```
↑によってローカルマシンでサーバーが立つので、ブラウザで`localhost:8888`にアクセスすることでJupyter Labにアクセスできます。
Kaggleでは基本的にはnotebookで作業をするので、Juptyer Labで作業を行うほうが実態に近くて良いかもしれませんね。

![スクリーンショット 2023-10-12 20.11.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2708192/0829d00b-3ee9-4924-4aa8-c16341a773e8.png)

↑Jupyter Labを開いた時の画面

# 参考にした記事
以上のことはいくつものWebページを参考にさせていただきましたが、特に以下の記事に追うところが大きいです。
大変参考になりました。
https://optigressor.com/dev/kaggle-gpu-image-gce-vscode-environment/
https://qiita.com/calpis10000/items/fb18b8d6a1e021a6f755