シンプルで純粋な C/CUDA の LLM は、245MB の PyTorch または 107MB の cPython を必要としません。現在の焦点は、事前トレーニング、特に GPT-2 および GPT-3 ミニシリーズの再現と、train_gpt2.py での並列 PyTorch リファレンス実装にあります。このファイルは、私の以前のプロジェクトである nanoGPT に少し調整を加えたものであることがわかります。現在、llm.c は PyTorch Nightly よりもわずかに高速です (約 7%)。 train_gpt2.cu の最先端のメインライン コードに加えて、1 つのファイル train_gpt2.c に、約 1,000 行のクリーンなコードで構成される単純なリファレンス CPU fp32 実装があります。このリポジトリでは C と CUDA コードのみを維持したいと考えています。他の言語やリポジトリへの移植は大歓迎ですが、別のリポジトリで行う必要があり、以下の「注目すべきフォーク」セクションに喜んでリンクさせていただきます。開発者の調整はディスカッションと Discord (Zero to Hero チャンネルの#llmc
チャンネル、または GPU MODE Discord の#llmdotc
で行われます。
今日の llm.c リポジトリへの最良の入門は、GPT-2 (124M) モデルを再現することです。ディスカッション #481 では、これについて詳しく説明します。 GPT-2 および GPT-3 シリーズの他のモデルは、llm.c と PyTorch の並列実装の両方で再現できます。スクリプトの README を参照してください。
デバッグのヒント: make
コマンドを実行してバイナリをビルドするときは、 -O3
-g
に置き換えてバイナリを変更し、お気に入りの IDE (vscode など) でコードをステップ実行できるようにします。
複数のノードでトレーニングを行わず、混合精度に興味がなく、CUDA の学習に興味がある場合は、fp32 (レガシー) ファイルが興味深いかもしれません。これらは、llm.c の歴史の初期に「チェックポイント」が設定され、時間の経過とともに凍結されたファイルです。これらはよりシンプルで移植性が高く、おそらく理解しやすいものです。次のように 1 GPU、fp32 コードを実行します。
chmod u+x ./dev/download_starter_pack.sh
./dev/download_starter_pack.sh
make train_gpt2fp32cu
./train_gpt2fp32cu
download_starter_pack.sh スクリプトは、素早く簡単に開始できる方法で、開始に役立つ多数の .bin ファイルをダウンロードします。これらには次のものが含まれます: 1) bfloat16 の fp32 に保存された GPT-2 124M モデル、2) 単体テストで使用される「デバッグ状態」 (データの小さなバッチ、ターゲットのアクティベーションと勾配)、3) GPT-2 トークナイザー、3) トークン化された tinyshakespeare データセット。または、.sh スクリプトを実行する代わりに、次のようにこれらのアーティファクトを手動で再作成することもできます。
pip install -r requirements.txt
python dev/data/tinyshakespeare.py
python train_gpt2.py
「私は GPU が非常に貧弱なので、GPU を 1 つも持っていません」セクション。 llm.cの列車を見ることはまだ楽しめます!しかし、あまり遠くには行きません。上記の fp32 バージョンと同様に、CPU バージョンは、llm.c の歴史においてさらに初期のチェックポイントであり、当時は C での単なる参照実装でした。たとえば、最初からトレーニングする代わりに、GPT を微調整することができます。例として、シェイクスピアのようなテキストを出力する場合は 2 小さい (124M):
chmod u+x ./dev/download_starter_pack.sh
./dev/download_starter_pack.sh
make train_gpt2
OMP_NUM_THREADS=8 ./train_gpt2
スターター パック スクリプトの実行を避けたい場合は、前のセクションで説明したように、 python dev/data/tinyshakespeare.py
を実行してからpython train_gpt2.py
実行することで、まったく同じ .bin ファイルとアーティファクトを再現できます。
上記の行は、(1) すでにトークン化された tinyshakespeare データセットをダウンロードし、GPT-2 (124M) 重みをダウンロードします。(3) C でそれらを初期化し、AdamW で Tineshakespeare の 40 ステップをトレーニングします (バッチ サイズ 4、コンテキスト長のみ 64 を使用) )、検証損失を評価し、いくつかのテキストをサンプリングします。正直なところ、強力な CPU を持っていない限り (起動コマンドで OMP スレッドの数を増やすことができる場合)、CPU トレーニング LLM でそこまで到達することはできませんが、良いデモ/リファレンスになる可能性があります。私の MacBook Pro (Apple Silicon M3 Max) では、出力は次のようになります。
[GPT-2]
max_seq_len: 1024
vocab_size: 50257
num_layers: 12
num_heads: 12
channels: 768
num_parameters: 124439808
train dataset num_batches: 1192
val dataset num_batches: 128
num_activations: 73323776
val loss 5.252026
step 0: train loss 5.356189 (took 1452.121000 ms)
step 1: train loss 4.301069 (took 1288.673000 ms)
step 2: train loss 4.623322 (took 1369.394000 ms)
step 3: train loss 4.600470 (took 1290.761000 ms)
... (trunctated) ...
step 39: train loss 3.970751 (took 1323.779000 ms)
val loss 4.107781
generating:
---
Come Running Away,
Greater conquer
With the Imperial blood
the heaviest host of the gods
into this wondrous world beyond.
I will not back thee, for how sweet after birth
Netflix against repounder,
will not
flourish against the earlocks of
Allay
---
/dev/data/(dataset).py
内のデータ ファイルは、ダウンロード、トークン化、および C から簡単に読み取り可能な .bin ファイルへのトークンの保存を担当します。つまり、たとえば次を実行すると、次のようになります。
python dev/data/tinyshakespeare.py
tinyshakespeare データセットをダウンロードしてトークン化します。この出力は次のようになります。
writing 32,768 tokens to ./dev/data/tinyshakespeare/tiny_shakespeare_val.bin
writing 305,260 tokens to ./dev/data/tinyshakespeare/tiny_shakespeare_train.bin
.bin ファイルには、短いヘッダー (1024 バイト) と、GPT-2 トークナイザーでのトークン ID を示す uint16 のトークン ストリームが含まれています。 /dev/data
ではさらに多くのデータセットが利用可能です。
C コードが PyTorch コードと一致していることを確認するための簡単な単体テストも添付します。例として CPU 上で、次のようにコンパイルして実行します。
make test_gpt2
./test_gpt2
これにより、train_gpt2.py によって書き込まれたgpt2_124M_debug_state.bin
ファイルがロードされ、フォワード パスが実行され、ロジットと損失が PyTorch リファレンス実装と比較され、次に Adam でトレーニングを 10 回反復して、損失が PyTorch と一致するかどうかが確認されます。 GPU のバージョンをテストするには、次のコマンドを実行します。
# fp32 test (cudnn not supported)
make test_gpt2cu PRECISION=FP32 && ./test_gpt2cu
# mixed precision cudnn test
make test_gpt2cu USE_CUDNN=1 && ./test_gpt2cu
これは、fp32 パスと混合精度パスの両方をテストします。テストは合格し、 overall okay: 1
。
非常に小さなチュートリアルを doc/layernorm/layernorm.md に添付しました。これは、GPT-2 モデルの単一層であるlayernorm 層を実装するための簡単なステップバイステップのガイドです。これは、C でレイヤーがどのように実装されるかを理解するための良い出発点となります。
フラッシュ注意。 2024 年 5 月 1 日以降、cuDNN の Flash アテンションを使用します。 cuDNN はコンパイル時間を数秒から数分にまで膨張させ、このコード パスは現時点では非常に新しいため、これはデフォルトで無効になっています。次のようにコンパイルすることで有効にできます。
make train_gpt2cu USE_CUDNN=1
これにより、cudnn でコンパイルして実行しようとします。システムに cuDNN がインストールされている必要があります。 apt-get を使用した cuDNN のインストール手順では、cuDNN パッケージのデフォルトのセットが取得されます。最小限のセットアップの場合は、cuDNN dev パッケージで十分です。たとえば、CUDA 12.x 用の Ubuntu 22.04 では次のようになります。
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt-get update
sudo apt-get -y install libcudnn9-dev-cuda-12
これに加えて cuDNN フロントエンドが必要ですが、これは単なるヘッダー ファイルです。リポジトリのクローンをディスクに作成するだけです。 Makefile は現在、ホーム ディレクトリまたは現在のディレクトリのいずれかを検索します。別の場所に配置した場合は、 CUDNN_FRONTEND_PATH=/path/to/your/cudnn-frontend/include
make
コマンドラインに追加します。
MPI と NCCL を Linux などに必ずインストールしてください。
sudo apt install openmpi-bin openmpi-doc libopenmpi-dev
NCCL の場合は、公式 Web サイトの指示に従ってください (ネットワーク インストーラーなど)
その後:
make train_gpt2cu
mpirun -np < number of GPUs > ./train_gpt2cu
または、 ./scripts/
でスクリプトの 1 つを実行するだけです。
マルチ GPU セクションの手順に従ってNCCL
がインストールされていることを確認してください。
マルチノード トレーニングを実行できるように現在サポートされている方法は 3 つあります。
./scripts/multi_node/run_gpt2_124M_mpi.sh
/run_gpt2_124M_mpi.sh スクリプトなどを参照してください。./scripts/multi_node/run_gpt2_124M_fs.sbatch
run_gpt2_124M_fs.sbatch スクリプトを参照してください。./scripts/multi_node/run_gpt2_124M_tcp.sbatch
/run_gpt2_124M_tcp.sbatch スクリプトを参照してください。注記:
slurm-wlm
PMIx サポートを廃止したことを考えると、これは一般的な状況であると想定されます)、FS (2) または TCP (3) アプローチを使用する必要があります。 。 slurm が PMIx をサポートしているかどうかをテストするには、 srun --mpi=list
実行し、出力にpmix
含まれるかどうかを確認します。mpirun
- MPI (1) を使用してマルチノードの実行を開始できます。これら 3 つの方法のいずれも優れているわけではありません。特定の環境で実行できるようにオプションを提供しているだけです。
TinyStories 上で 4 つの GPU を備えたマシンで学習率をスイープするプロセスの例として。シェル スクリプトsweep.sh
を実行します (もちろんchmod u+x sweep.sh
実行した後)。
#! /bin/bash
learning_rates=(3e-5 1e-4 3e-4 1e-3)
for i in {0..3} ; do
export CUDA_VISIBLE_DEVICES= $i
screen -dmS " tr $i " bash -c " ./train_gpt2cu -i data/TinyStories -v 250 -s 250 -g 144 -l ${learning_rates[$i]} -o stories $i .log "
done
# you can bring these down with
# screen -ls | grep -E "tr[0-3]" | cut -d. -f1 | xargs -I {} screen -X -S {} quit
この例では、4 つの画面セッションを開き、異なる LR を使用して 4 つのコマンドを実行します。これにより、すべての損失がログ ファイルstories$i.log
に書き込まれ、Python で必要に応じてプロットできます。これらのログファイルを解析してプロットする方法の簡単な例は、dev/vislog.ipynb にあります。
このリポジトリをどのようなものにしたいかについてもう少し説明します。
まずはllm.c
教育の場にしたいと思っています。たとえば、 dev/cuda
フォルダーは、非常に単純なカーネルからより複雑で高速なカーネルまで、すべてのレイヤーのカーネルのライブラリの場所であり、手動で手書きされ、非常によく文書化されています。さまざまなトレードオフを伴う新しいカーネルをお持ちの場合は、お気軽にここに投稿してください。
そうは言っても、私はllm.c
も非常に高速であり、ネットワークをトレーニングするのに実用的に役立つことも望んでいます。たとえば、まず、大規模な GPT-2 (1.6B) トレーニング実行を再現できる必要があります。これには、cuBLAS、cuBLASLt、CUTLASS、cuDNN などのライブラリの使用を含め、存在する最速のカーネルをすべて組み込む必要があります。そうすることは、専門家の上限と測定単位を確立するという教育的な目的にも役立つと思います。たとえば、手動で作成したカーネルが cuBLAS の速度の 80% であると言うことができます。その後、超高速の実行を選択することも、「ドラッグ アンド ドロップ」を選択することもできます。使用したい手動カーネルを選択し、それらを使用して実行します。
ただし、制約として、ルート フォルダー内のメインラインllm.c
シンプルで読みやすいものに保ちたいと考えています。たとえば、パフォーマンスを 2% 向上させる PR があるが、500 行の複雑な C コードと、場合によっては珍しいサードパーティへの依存関係が「コスト」かかる場合、その複雑さには価値がないため、私はその PR を拒否するかもしれません。具体的な例として、matmuls の cuBLAS をルート トレーニング ループのデフォルトにするのは簡単です。これにより、メインライン コードが大幅に高速化され、解釈可能なコードが 1 行になり、非常に一般的な依存関係になります。これに加えて、 dev/cuda
で cuBLAS と競合できる手動実装を行うことができます。
最後に、プロジェクトのメイン/デフォルト ファイルが含まれるプロジェクトのルート フォルダー内の複雑さに対して、より敏感になります。それに比べて、 dev/
フォルダーは、カーネルやクラスのライブラリを開発し、有用なコードや関連コード、教育的なコードを共有するためのスクラッチ スペースのようなもので、このコードの一部は (ローカルで) 複雑になっても問題ありません。
AMDのサポート
C#
CUDA C++
C++/CUDA
WebGPU C++
C++
行く
ジャワ
金属
モジョ
OpenCL
さび
迅速
ジグ
ハバナ ガウディ 2
ニム
開発を組織する方法:
#llmc
チャンネルを作成しました。 マサチューセッツ工科大学