LLM dalam C/CUDA murni sederhana tanpa memerlukan PyTorch 245MB atau cPython 107MB. Fokus saat ini adalah pada pra-pelatihan, khususnya reproduksi miniseri GPT-2 dan GPT-3, serta implementasi referensi PyTorch paralel di train_gpt2.py. Anda akan mengenali file ini sebagai nanoGPT yang sedikit diubah, proyek saya sebelumnya. Saat ini, llm.c sedikit lebih cepat daripada PyTorch Nightly (sekitar 7%). Selain kode jalur utama terbaru di train_gpt2.cu, kami memiliki implementasi CPU fp32 referensi sederhana dalam ~1.000 baris kode bersih dalam satu file train_gpt2.c. Saya ingin repo ini hanya mempertahankan kode C dan CUDA. Port ke bahasa atau repo lain sangat diterima, tetapi harus dilakukan dalam repo terpisah, dan saya dengan senang hati akan menautkannya di bawah di bagian "garpu terkenal". Koordinasi pengembang terjadi di Diskusi dan Discord, baik di saluran #llmc
di saluran Zero to Hero, atau di #llmdotc
di GPU MODE Discord.
Pengenalan terbaik untuk repo llm.c saat ini adalah mereproduksi model GPT-2 (124M). Diskusi #481 membahasnya secara mendetail. Kami dapat mereproduksi model lain dari seri GPT-2 dan GPT-3 di llm.c dan implementasi paralel PyTorch. Silakan lihat skrip README.
tip debugging: ketika Anda menjalankan perintah make
untuk membangun biner, modifikasilah dengan mengganti -O3
dengan -g
sehingga Anda dapat menelusuri kode di IDE favorit Anda (misalnya vscode).
Jika Anda tidak akan berlatih pada banyak node, tidak tertarik pada presisi campuran, dan tertarik mempelajari CUDA, file fp32 (lama) mungkin menarik bagi Anda. Ini adalah file yang "diperiksa" di awal sejarah llm.c dan dibekukan dalam waktu. Mereka lebih sederhana, lebih portabel, dan mungkin lebih mudah dipahami. Jalankan 1 GPU, kode fp32 seperti ini:
chmod u+x ./dev/download_starter_pack.sh
./dev/download_starter_pack.sh
make train_gpt2fp32cu
./train_gpt2fp32cu
Skrip download_starter_pack.sh adalah cara cepat & mudah untuk memulai dan mengunduh banyak file .bin yang membantu Anda memulai. Ini berisi: 1) model GPT-2 124M yang disimpan di fp32, di bfloat16, 2) "status debug" yang digunakan dalam pengujian unit (sekelompok kecil data, serta aktivasi dan gradien target), 3) tokenizer GPT-2 , dan 3) kumpulan data tinyshakespeare yang diberi token. Alternatifnya, alih-alih menjalankan skrip .sh, Anda dapat membuat ulang artefak ini secara manual sebagai berikut:
pip install -r requirements.txt
python dev/data/tinyshakespeare.py
python train_gpt2.py
Bagian "Saya sangat miskin GPU sehingga saya bahkan tidak memiliki satu pun GPU". Anda masih bisa menikmati melihat kereta llm.c! Tapi Anda tidak akan melangkah terlalu jauh. Sama seperti versi fp32 di atas, versi CPU adalah checkpoint yang lebih awal dalam sejarah llm.c, saat itu hanya implementasi referensi sederhana di C. Misalnya, daripada berlatih dari awal, Anda dapat menyempurnakan GPT- 2 kecil (124M) untuk menampilkan teks mirip Shakespeare, sebagai contoh:
chmod u+x ./dev/download_starter_pack.sh
./dev/download_starter_pack.sh
make train_gpt2
OMP_NUM_THREADS=8 ./train_gpt2
Jika Anda memilih untuk tidak menjalankan skrip paket perdana, maka seperti yang disebutkan di bagian sebelumnya, Anda dapat mereproduksi file .bin dan artefak yang sama persis dengan menjalankan python dev/data/tinyshakespeare.py
lalu python train_gpt2.py
.
Baris di atas (1) mengunduh kumpulan data tinyshakespeare yang sudah diberi token dan mengunduh bobot GPT-2 (124M), (3) init darinya di C dan berlatih selama 40 langkah di tineshakespeare dengan AdamW (menggunakan ukuran batch 4, panjang konteks hanya 64 ), mengevaluasi kehilangan validasi, dan mengambil sampel beberapa teks. Sejujurnya, kecuali Anda memiliki CPU yang besar (dan dapat meningkatkan jumlah thread OMP dalam perintah peluncuran), Anda tidak akan sampai sejauh itu dalam LLM pelatihan CPU, tetapi ini mungkin merupakan demo/referensi yang bagus. Outputnya terlihat seperti ini di MacBook Pro saya (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
---
File data di dalam /dev/data/(dataset).py
bertanggung jawab untuk mengunduh, memberi token, dan menyimpan token ke file .bin, dapat dibaca dengan mudah dari C. Jadi misalnya saat Anda menjalankan:
python dev/data/tinyshakespeare.py
Kami mengunduh dan memberi token pada kumpulan data tinyshakespeare. Outputnya terlihat seperti ini:
writing 32,768 tokens to ./dev/data/tinyshakespeare/tiny_shakespeare_val.bin
writing 305,260 tokens to ./dev/data/tinyshakespeare/tiny_shakespeare_train.bin
File .bin berisi header pendek (1024 byte) dan kemudian aliran token di uint16, yang menunjukkan id token dengan tokenizer GPT-2. Kumpulan data lainnya tersedia di /dev/data
.
Saya juga melampirkan pengujian unit sederhana untuk memastikan kode C kita sesuai dengan kode PyTorch. Pada CPU sebagai contoh, kompilasi dan jalankan dengan:
make test_gpt2
./test_gpt2
Ini sekarang memuat file gpt2_124M_debug_state.bin
yang ditulis oleh train_gpt2.py, menjalankan forward pass, membandingkan logit dan kerugian dengan implementasi referensi PyTorch, lalu melakukan 10 iterasi pelatihan dengan Adam dan memastikan kerugiannya cocok dengan PyTorch. Untuk menguji versi GPU kami menjalankan:
# fp32 test (cudnn not supported)
make test_gpt2cu PRECISION=FP32 && ./test_gpt2cu
# mixed precision cudnn test
make test_gpt2cu USE_CUDNN=1 && ./test_gpt2cu
Ini menguji jalur fp32 dan jalur presisi campuran. Tes harus lulus dan mencetak overall okay: 1
.
Saya melampirkan tutorial yang sangat kecil di sini, di doc/layernorm/layernorm.md. Ini adalah panduan sederhana langkah demi langkah untuk mengimplementasikan satu lapisan model GPT-2, yaitu lapisan layernorm. Ini adalah titik awal yang baik untuk memahami bagaimana lapisan diimplementasikan di C.
perhatian kilat . Mulai 1 Mei 2024 kami menggunakan Flash Attention dari cuDNN. Karena cuDNN menambah waktu kompilasi dari beberapa detik menjadi ~menit dan jalur kode ini saat ini sangat baru, jalur ini dinonaktifkan secara default. Anda dapat mengaktifkannya dengan mengkompilasi seperti ini:
make train_gpt2cu USE_CUDNN=1
Ini akan mencoba mengkompilasi dengan cudnn dan menjalankannya. Anda harus menginstal cuDNN di sistem Anda. Petunjuk instalasi cuDNN dengan apt-get akan mengambil kumpulan paket cuDNN default. Untuk setup minimal, paket cuDNN dev sudah cukup, misalnya di Ubuntu 22.04 untuk CUDA 12.x:
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
Selain itu, Anda memerlukan frontend cuDNN, tetapi ini hanya file header. Cukup kloning repo ke disk Anda. Makefile saat ini mencarinya di direktori home Anda atau direktori saat ini. Jika Anda meletakkannya di tempat lain, tambahkan CUDNN_FRONTEND_PATH=/path/to/your/cudnn-frontend/include
ke baris perintah make
.
Pastikan Anda menginstal MPI dan NCCL, misalnya di Linux:
sudo apt install openmpi-bin openmpi-doc libopenmpi-dev
Untuk NCCL ikuti instruksi dari situs resminya (misal installer jaringan)
kemudian:
make train_gpt2cu
mpirun -np < number of GPUs > ./train_gpt2cu
atau cukup jalankan salah satu skrip kami di bawah ./scripts/
.
Pastikan Anda telah menginstal NCCL
dengan mengikuti petunjuk dari bagian multi-GPU.
Ada 3 cara yang saat ini kami dukung yang memungkinkan Anda menjalankan pelatihan multi-node:
./scripts/multi_node/run_gpt2_124M_mpi.sh
untuk detailnya../scripts/multi_node/run_gpt2_124M_fs.sbatch
untuk detailnya../scripts/multi_node/run_gpt2_124M_tcp.sbatch
untuk detailnya.Catatan:
slurm-wlm
menghentikan dukungan PMIx), Anda harus menggunakan pendekatan FS (2) atau TCP (3) . Untuk menguji apakah slurm Anda mendukung PMIx run: srun --mpi=list
dan lihat apakah Anda mendapatkan pmix
pada outputnya.mpirun
- MPI (1).Tidak satu pun dari 3 metode ini yang lebih unggul, kami hanya menawarkan opsi sehingga Anda dapat menjalankannya di lingkungan spesifik Anda.
Seperti contoh proses menyapu learning rate pada mesin dengan 4 GPU di TinyStories. Jalankan skrip shell sweep.sh
(tentu saja setelah Anda 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
Contoh ini membuka 4 sesi layar dan menjalankan empat perintah dengan LR berbeda. Ini menulis file log stories$i.log
dengan semua kerugiannya, yang dapat Anda plot sesuai keinginan dengan Python. Contoh singkat cara mengurai dan memplot file log ini ada di dev/vislog.ipynb.
Beberapa kata lagi tentang apa yang saya inginkan dari repo ini:
Pertama, saya ingin llm.c
menjadi tempat pendidikan. Misalnya folder dev/cuda
kami adalah tempat perpustakaan kernel untuk semua lapisan yang ditulis tangan secara manual dan didokumentasikan dengan sangat baik, mulai dari kernel yang sangat sederhana hingga kernel yang lebih kompleks/cepat. Jika Anda memiliki kernel baru dengan berbagai pengorbanan yang berbeda, silakan berkontribusi di sini.
Meskipun demikian, saya juga ingin llm.c
menjadi sangat cepat, bahkan berguna secara praktis untuk melatih jaringan. Misalnya, untuk memulai, kita harus dapat mereproduksi rangkaian pelatihan GPT-2 (1,6B) yang besar. Hal ini mengharuskan kita memasukkan kernel tercepat apa pun yang ada, termasuk penggunaan perpustakaan seperti cuBLAS, cuBLASLt, CUTLASS, cuDNN, dll. Menurut saya, hal ini juga memiliki tujuan pendidikan untuk menetapkan batas atas pakar, dan unit pengukuran, misalnya Anda dapat mengatakan bahwa kernel yang Anda tulis secara manual adalah 80% dari kecepatan cuBLAS, dll. Kemudian Anda dapat memilih untuk menjalankan super cepat, atau Anda dapat memilih untuk "drag and drop" kernel manual apa pun yang ingin Anda gunakan, dan jalankan dengan itu.
Namun, sebagai kendala, saya ingin menjaga llm.c
jalur utama di folder root tetap sederhana dan mudah dibaca. Jika ada PR yang misalnya meningkatkan kinerja sebesar 2% tetapi "menghabiskan" 500 baris kode C yang kompleks, dan mungkin ketergantungan pihak ketiga yang eksotis, saya mungkin menolak PR tersebut karena kompleksitasnya tidak sepadan. Sebagai contoh nyata - menjadikan cuBLAS untuk matmuls sebagai default dalam loop pelatihan root adalah hal yang mudah: ini membuat kode jalur utama lebih cepat, ini adalah satu baris kode yang dapat ditafsirkan, dan ini adalah ketergantungan yang sangat umum. Selain itu, kita dapat memiliki implementasi manual yang dapat bersaing dengan cuBLAS di dev/cuda
.
Terakhir, saya akan lebih peka terhadap kompleksitas di folder root proyek, yang berisi file utama/default proyek. Sebagai perbandingan, folder dev/
lebih merupakan ruang awal bagi kita untuk mengembangkan perpustakaan kernel atau kelas dan berbagi kode yang berguna atau terkait atau mendidik, dan beberapa dari kode ini mungkin boleh saja menjadi kompleks (secara lokal).
dukungan AMD
C#
CUDA C++
C++/CUDA
WebGPU C++
C++
Pergi
Jawa
Logam
Mojo
OpenCL
Karat
Cepat
Zig
Habana Gaudi2
Nim
Cara-cara pengorganisasian pembangunan:
#llmc
baru di saluran Zero to Hero Discord saya. MIT