AL2023でPythonからcudaを動かす

# 実施すること
* LLMをビルドしてみたいので、環境構築を進める
* 今回は前提となるcudaをセットアップし、Pythonからcudaを扱えるところまでを実施していく

# 環境

* インスタンスタイプ：g6e.xlarge
    * GPUタイプ：L40s
* AMI: al2023-ami-2023.6.20250115.0-kernel-6.1-x86_64
* ストレージ：30GB
* cuda version : 12.6  ※2025/1/20現在の最新
* python version: 3.10.10


# セットアップ手順
## nvidiaのデバイス存在を確認

```
[ec2-user@ip-10-0-37-42 ~]$ lspci | grep -i nvidia
30:00.0 3D controller: NVIDIA Corporation AD102GL [L40S] (rev a1)
```

## cuda toolkit

* 以下のnvidia公式の手順に沿って進める
  * https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#amazon

```
sudo dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/amzn2023/x86_64/cuda-amzn2023.repo
sudo dnf clean all
sudo dnf install cuda-toolkit
sudo dnf install nvidia-gds
sudo reboot
```

* cuda に PATH を通す

```
export PATH=/usr/local/cuda-12.6/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-12.6/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
```

## Cudaサンプルを動かす
* サンプル集をビルドする

```
sudo dnf install git cmake
git clone https://github.com/NVIDIA/cuda-samples.git
cd cuda-samples
make
```

* 適当に何か動かす

```
[ec2-user@ip-10-0-37-42 0_Introduction]$ pwd
/home/ec2-user/cuda-samples/Samples/0_Introduction
[ec2-user@ip-10-0-37-42 0_Introduction]$ ./clock/clock 
CUDA Clock sample
GPU Device 0: "Ada" with compute capability 8.9

Average clocks/block = 2326.281250
[ec2-user@ip-10-0-37-42 0_Introduction]$ ./simpleMultiGPU/simpleMultiGPU 
Starting simpleMultiGPU
CUDA-capable device count: 1
Generating input data...

Computing with 1 GPUs...
  GPU Processing time: 11.306000 (ms)

Computing with Host CPU...

Comparing GPU and Host CPU results...
  GPU sum: 16777296.000000
  CPU sum: 16777294.395033
  Relative difference: 9.566307E-08
```

## pyenvを入れ、python3.10.10をインストールする
```
curl https://pyenv.run | bash
```

* 以下を、~/.bashrcに追記

```
# pyenv
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

eval "$(pyenv virtualenv-init -)"
```

* pyenvを確認

```
[ec2-user@ip-10-0-37-42 ~]$ source ~/.bashrc 
[ec2-user@ip-10-0-37-42 ~]$ pyenv --version
pyenv 2.5.1
```

* pythonをインストール
    * まずビルド関係のパッケージをインストールしてから、3.10.10をインストールする
    * 最後に、Python 3.10.10が表示されればOK
```
sudo yum groupinstall "Development Tools" -y
sudo yum install -y gcc gcc-c++ make zlib-devel bzip2 bzip2-devel readline-devel \
    sqlite sqlite-devel openssl-devel libffi-devel xz-devel tk-devel \
    gdbm-devel ncurses-devel libuuid-devel
pyenv install 3.10.10
pyenv local 3.10.10
python --version
```


# Megatron-Deepspeed環境構築
## apexインストール

* https://github.com/NVIDIA/apex#from-source に沿って実行。Megatron-DeepSpeedフォルダ内で実行する

```
git clone https://github.com/microsoft/Megatron-DeepSpeed
cd Megatron-DeepSpeed
pyenv local 3.10.10
python --version
python -m venv .venv
source .venv/bin/activate

git clone https://github.com/NVIDIA/apex
cd apex
pip install -v --disable-pip-version-check --no-cache-dir --no-build-isolation --global-option="--cpp_ext" --global-option="--cuda_ext" ./
```

* 上記のインストールは一筋縄でいかないので、エラーを調べつつ解消していく
    * 以下で再実行したら成功

```
pip install wheel
pip install -v --disable-pip-version-check --no-cache-dir --no-build-isolation --global-option="--cpp_ext" --global-option="--cuda_ext" ./
```

## torchでPythonからCudaが扱えることを確認する
### 確認コマンド

```python
import torch
print(torch.__version__)
print(torch.cuda.is_available())
print(torch.version.cuda)
```

### 実行例

* うまくCudaにアクセスできているようだ

```
(.venv) [ec2-user@ip-10-0-37-42 apex]$ python
Python 3.10.10 (main, Jan 20 2025, 05:02:43) [GCC 11.4.1 20230605 (Red Hat 11.4.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> print(torch.__version__)
2.5.1+cu124
>>> print(torch.cuda.is_available())
True
>>> print(torch.version.cuda)
12.4
```

# PythonからCudaを動かす！

* サンプルコード

```python
import torch

# CUDAが利用可能か確認
if not torch.cuda.is_available():
    print("CUDAが利用できません。GPU環境を確認してください。")
    exit()

# デバイスをCUDAに設定
device = torch.device("cuda")

# GPUメモリ使用量を確認（計算前）
print("計算前のGPUメモリ使用量:")
print(f"  使用量: {torch.cuda.memory_allocated(device) / 1024**2:.2f} MB")
print(f"  キャッシュ: {torch.cuda.memory_reserved(device) / 1024**2:.2f} MB")

# 2つのベクトルを生成
size = 1000000  # ベクトルの要素数
vector_a = torch.ones(size, device=device)  # 全て1のベクトル
vector_b = torch.full((size,), 2.0, device=device)  # 全て2のベクトル

# ベクトル加算
result = vector_a + vector_b

# GPUメモリ使用量を確認（計算後）
print("計算後のGPUメモリ使用量:")
print(f"  使用量: {torch.cuda.memory_allocated(device) / 1024**2:.2f} MB")
print(f"  キャッシュ: {torch.cuda.memory_reserved(device) / 1024**2:.2f} MB")

# 最初の5つの要素を表示
print("計算結果の先頭5つの要素:", result[:5].tolist())

# GPUメモリを解放
torch.cuda.empty_cache()
print("GPUメモリ解放後のキャッシュ使用量:")
print(f"  キャッシュ: {torch.cuda.memory_reserved(device) / 1024**2:.2f} MB")
```

## 動作ログを元に、Cudaが利用されていることを確認する

* 別のコンソールでログインして、以下でログを取り始める

```
nvidia-smi --query-gpu=timestamp,name,utilization.gpu,memory.used --format=csv -lms 50 > gpu_usage.log
```

* すぐさまサンプルアプリを実行する

```
python cuda_sample.py
```

* ログを見る
    * 一応、動いているっぽい。計算量がショボすぎてGPU Usageが上がらない

```
timestamp,name,utilization.gpu [%], memory.used [MiB]
2025/01/20 05:39:12.854, NVIDIA L40S, 0 %, 1 MiB
2025/01/20 05:39:12.905, NVIDIA L40S, 0 %, 1 MiB
2025/01/20 05:39:12.955, NVIDIA L40S, 0 %, 1 MiB
2025/01/20 05:39:13.005, NVIDIA L40S, 0 %, 20 MiB
2025/01/20 05:39:13.070, NVIDIA L40S, 0 %, 21 MiB
2025/01/20 05:39:13.121, NVIDIA L40S, 0 %, 521 MiB
2025/01/20 05:39:13.171, NVIDIA L40S, 0 %, 521 MiB
2025/01/20 05:39:13.221, NVIDIA L40S, 0 %, 521 MiB
2025/01/20 05:39:13.271, NVIDIA L40S, 0 %, 521 MiB
2025/01/20 05:39:13.322, NVIDIA L40S, 0 %, 521 MiB
2025/01/20 05:39:13.372, NVIDIA L40S, 0 %, 521 MiB
2025/01/20 05:39:13.422, NVIDIA L40S, 0 %, 518 MiB
2025/01/20 05:39:13.472, NVIDIA L40S, 0 %, 518 MiB
2025/01/20 05:39:13.523, NVIDIA L40S, 0 %, 518 MiB
2025/01/20 05:39:13.573, NVIDIA L40S, 0 %, 518 MiB
2025/01/20 05:39:13.623, NVIDIA L40S, 0 %, 518 MiB
2025/01/20 05:39:13.673, NVIDIA L40S, 0 %, 1 MiB
2025/01/20 05:39:13.846, NVIDIA L40S, 0 %, 1 MiB
```

# まとめ
* 今回は、AL2023にcuda-toolkit, apexをインストールし、Pythonからcudaが扱えるところまでを試験した
* pyenvや、パッケージインストールにおける依存関係で、わずかにトラブルに出会ったが、すぐに解決できたので、ここまではハードルは低そうだ
* 次回より本題であるLLMモデル構築に進んでいきたいと思う