LLM ในรูปแบบ C/CUDA ที่เรียบง่ายและบริสุทธิ์ โดยไม่จำเป็นต้องใช้ PyTorch 245MB หรือ cPython 107MB โฟกัสในปัจจุบันอยู่ที่การฝึกล่วงหน้า โดยเฉพาะอย่างยิ่งการสร้างมินิซีรีส์ GPT-2 และ GPT-3 ขึ้นมาใหม่ พร้อมกับการใช้งานอ้างอิง PyTorch แบบขนานใน train_gpt2.py คุณจะรับรู้ว่าไฟล์นี้เป็น nanoGPT ที่ได้รับการปรับแต่งเล็กน้อย ซึ่งเป็นโปรเจ็กต์ก่อนหน้าของฉัน ปัจจุบัน llm.c เร็วกว่า PyTorch Nightly เล็กน้อย (ประมาณ 7%) นอกเหนือจากโค้ดหลัก Bleeding Edge ใน train_gpt2.cu แล้ว เรายังมีการใช้งาน CPU fp32 อ้างอิงอย่างง่ายในโค้ดประมาณ 1,000 บรรทัดในไฟล์เดียว train_gpt2.c ฉันต้องการให้ repo นี้รักษารหัส C และ CUDA เท่านั้น ยินดีเป็นอย่างยิ่งที่จะย้ายไปยังภาษาอื่นหรือ repos แต่ควรทำใน repos แยกกัน และฉันยินดีที่จะลิงก์ไปที่ด้านล่างในส่วน "forks ที่โดดเด่น" การประสานงานของนักพัฒนาเกิดขึ้นในการสนทนาและบน Discord ไม่ว่าจะเป็นช่อง #llmc
บนช่อง Zero to Hero หรือบน #llmdotc
บน GPU MODE Discord
การแนะนำที่ดีที่สุดสำหรับ repo 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) โมเดล GPT-2 124M ที่บันทึกไว้ใน fp32 ใน bfloat16 2) "สถานะการแก้ไขข้อบกพร่อง" ที่ใช้ในการทดสอบหน่วย (ชุดข้อมูลขนาดเล็ก และการเปิดใช้งานเป้าหมายและการไล่ระดับสี) 3) โทเค็นไนเซอร์ GPT-2 และ 3) ชุดข้อมูล Tinyshakespeare ที่ได้รับโทเค็น อีกทางหนึ่ง แทนที่จะเรียกใช้สคริปต์ .sh คุณสามารถสร้างส่วนเหล่านี้ใหม่ได้ด้วยตนเองดังนี้:
pip install -r requirements.txt
python dev/data/tinyshakespeare.py
python train_gpt2.py
ส่วน "ฉันใช้ GPU แย่มากจนฉันไม่มี GPU ตัวเดียว" คุณยังสามารถเพลิดเพลินกับการชมรถไฟ 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
หากคุณต้องการหลีกเลี่ยงการเรียกใช้สคริปต์แพ็กเริ่มต้น ตามที่กล่าวไว้ในส่วนก่อนหน้า คุณสามารถสร้างไฟล์ .bin และอาร์ติแฟกต์ที่เหมือนกันทุกประการได้โดยการเรียกใช้ python dev/data/tinyshakespeare.py
แล้วตามด้วย python train_gpt2.py
บรรทัดด้านบน (1) ดาวน์โหลดชุดข้อมูล Tinyshakespeare ที่ได้รับโทเค็นแล้ว และดาวน์โหลดน้ำหนัก GPT-2 (124M) (3) เริ่มต้นจากพวกเขาใน C และฝึกฝน 40 ขั้นตอนบน tineshakespeare ด้วย AdamW (ใช้ขนาดแบตช์ 4 ความยาวบริบทเพียง 64 ) ประเมินการสูญเสียการตรวจสอบ และสุ่มตัวอย่างข้อความบางส่วน จริงๆ แล้ว เว้นแต่คุณจะมี CPU ที่แข็งแกร่ง (และสามารถเพิ่มจำนวนเธรด OMP ในคำสั่งเรียกใช้ได้) คุณจะไม่ได้ไปได้ไกลขนาดนั้นในการฝึกอบรม LLM ของ CPU แต่อาจเป็นการสาธิต/ข้อมูลอ้างอิงที่ดี ผลลัพธ์จะมีลักษณะเช่นนี้บน 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
มีหน้าที่ในการดาวน์โหลด โทเค็น และบันทึกโทเค็นเป็นไฟล์ .bin ซึ่งสามารถอ่านได้ง่ายจาก C ดังนั้น ตัวอย่างเช่น เมื่อคุณเรียกใช้:
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 มีส่วนหัวแบบสั้น (1,024 ไบต์) และสตรีมโทเค็นใน uint16 ซึ่งระบุรหัสโทเค็นด้วยโทเค็น GPT-2 ชุดข้อมูลเพิ่มเติมมีอยู่ใน /dev/data
ฉันยังแนบการทดสอบหน่วยอย่างง่ายเพื่อให้แน่ใจว่ารหัส C ของเราสอดคล้องกับรหัส PyTorch บน CPU เป็นตัวอย่าง ให้คอมไพล์และรันด้วย:
make test_gpt2
./test_gpt2
ตอนนี้จะโหลดไฟล์ gpt2_124M_debug_state.bin
ที่ถูกเขียนโดย train_gpt2.py รันการส่งต่อ เปรียบเทียบบันทึกและการสูญเสียกับการใช้งานอ้างอิง PyTorch จากนั้นจะทำการฝึกฝนซ้ำ 10 ครั้งกับ Adam และตรวจสอบให้แน่ใจว่าการสูญเสียนั้นตรงกับ 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 หรือเลเยอร์ Norm ของเลเยอร์ นี่เป็นจุดเริ่มต้นที่ดีในการทำความเข้าใจวิธีการนำเลเยอร์ต่างๆ ไปใช้ในภาษา C
ความสนใจแบบแฟลช ตั้งแต่วันที่ 1 พฤษภาคม 2024 เป็นต้นไป เราใช้ Flash Attention จาก cuDNN เนื่องจาก cuDNN ขยายเวลาคอมไพล์จากไม่กี่วินาทีเป็น ~นาที และเส้นทางโค้ดนี้ใหม่มากในขณะนี้ สิ่งนี้จึงถูกปิดใช้งานตามค่าเริ่มต้น คุณสามารถเปิดใช้งานได้โดยการรวบรวมดังนี้:
make train_gpt2cu USE_CUDNN=1
สิ่งนี้จะพยายามคอมไพล์ด้วย cudnn และรันมัน คุณต้องติดตั้ง cuDNN บนระบบของคุณ คำแนะนำในการติดตั้ง cuDNN พร้อม apt-get จะดึงชุดแพ็คเกจ cuDNN เริ่มต้น สำหรับการตั้งค่าขั้นต่ำ แพ็คเกจ cuDNN dev ก็เพียงพอแล้ว เช่น บน Ubuntu 22.04 สำหรับ 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
ยิ่งไปกว่านั้น คุณต้องมีส่วนหน้า cuDNN แต่นี่เป็นเพียงไฟล์ส่วนหัว เพียงโคลน repo ไปยังดิสก์ของคุณ ขณะนี้ Makefile ค้นหาในโฮมไดเร็กตอรี่ของคุณหรือในไดเร็กทอรีปัจจุบัน หากคุณวางไว้ที่อื่น ให้เพิ่ม CUDNN_FRONTEND_PATH=/path/to/your/cudnn-frontend/include
ลงในบรรทัดคำสั่ง make
ตรวจสอบให้แน่ใจว่าคุณติดตั้ง MPI และ NCCL เช่นบน Linux:
sudo apt install openmpi-bin openmpi-doc libopenmpi-dev
สำหรับ NCCL ให้ปฏิบัติตามคำแนะนำจากเว็บไซต์อย่างเป็นทางการ (เช่น โปรแกรมติดตั้งเครือข่าย)
แล้ว:
make train_gpt2cu
mpirun -np < number of GPUs > ./train_gpt2cu
หรือเรียกใช้สคริปต์ตัวใดตัวหนึ่งของเราภายใต้ ./scripts/
/
ตรวจสอบให้แน่ใจว่าคุณได้ติดตั้ง NCCL
ตามคำแนะนำจากส่วน multi-GPU
ขณะนี้มี 3 วิธีที่เรารองรับซึ่งช่วยให้คุณเรียกใช้การฝึกอบรมแบบหลายโหนดได้:
./scripts/multi_node/run_gpt2_124M_mpi.sh
สคริปต์เพื่อดูรายละเอียด./scripts/multi_node/run_gpt2_124M_fs.sbatch
สำหรับรายละเอียด./scripts/multi_node/run_gpt2_124M_tcp.sbatch
สำหรับรายละเอียดบันทึก:
slurm-wlm
ลดการรองรับ PMIx) คุณจะต้องใช้วิธี FS (2) หรือ TCP (3) . หากต้องการทดสอบว่า slurm ของคุณรองรับการรัน PMIx หรือไม่: srun --mpi=list
และดูว่าคุณได้รับ pmix
ในเอาต์พุตหรือไม่mpirun
- MPI (1)ไม่มีวิธีใดใน 3 วิธีนี้ที่เหนือกว่า เราเพียงเสนอทางเลือกให้คุณเพื่อให้คุณสามารถทำงานในสภาพแวดล้อมเฉพาะของคุณได้
เช่นเดียวกับกระบวนการตัวอย่างในการกวาดอัตราการเรียนรู้บนเครื่องที่มี GPU 4 ตัวบน TinyStories รันเชลล์สคริปต์ 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 ที่แตกต่างกัน สิ่งนี้จะเขียนไฟล์บันทึก stories$i.log
พร้อมการสูญเสียทั้งหมด ซึ่งคุณสามารถลงจุดได้ตามที่คุณต้องการใน Python ตัวอย่างโดยย่อของวิธีแยกวิเคราะห์และพล็อตไฟล์บันทึกเหล่านี้อยู่ใน dev/vislog.ipynb
อีกสองสามคำเกี่ยวกับสิ่งที่ฉันต้องการให้ repo นี้เป็น:
ก่อนอื่นฉันต้องการให้ llm.c
เป็นสถานที่ทางการศึกษา เช่น โฟลเดอร์ dev/cuda
ของเราเป็นสถานที่สำหรับไลบรารีเคอร์เนลสำหรับเลเยอร์ทั้งหมดที่ถูกเขียนด้วยมือและมีเอกสารอย่างดี ตั้งแต่เคอร์เนลธรรมดาๆ ไปจนถึงเคอร์เนลที่ซับซ้อน/เร็วกว่า หากคุณมีเคอร์เนลใหม่ที่มีข้อดีหลายอย่าง โปรดอย่าลังเลที่จะมีส่วนร่วมที่นี่
ที่กล่าวว่า ฉันต้องการให้ llm.c
ทำงานเร็วมากเช่นกัน แม้จะมีประโยชน์ในทางปฏิบัติในการฝึกอบรมเครือข่ายก็ตาม เช่น ในการเริ่มต้น เราควรสามารถสร้างการวิ่งฝึกซ้อมขนาดใหญ่ GPT-2 (1.6B) ได้ สิ่งนี้กำหนดให้เราต้องรวมเคอร์เนลที่เร็วที่สุดที่มีอยู่ รวมถึงการใช้ไลบรารีเช่น cuBLAS, cuBLASLt, CUTLASS, cuDNN เป็นต้น นอกจากนี้ ฉันยังคิดว่าการทำเช่นนั้นมีวัตถุประสงค์ทางการศึกษาเพื่อสร้างขอบเขตบนของผู้เชี่ยวชาญ และหน่วยการวัด เช่น คุณสามารถพูดได้ว่าเคอร์เนลที่คุณเขียนเองนั้นมีความเร็ว cuBLAS 80% เป็นต้น จากนั้นคุณสามารถเลือกที่จะรันแบบเร็วสุด ๆ หรือคุณสามารถเลือกที่จะ "ลากและวาง" อะไรก็ได้ เคอร์เนลแบบแมนนวลที่คุณต้องการใช้และรันกับเคอร์เนลเหล่านั้น
อย่างไรก็ตาม ตามข้อจำกัด ฉันต้องการให้ mainline llm.c
ในโฟลเดอร์รูทเรียบง่ายและสามารถอ่านได้ หากมี PR เช่น ปรับปรุงประสิทธิภาพ 2% แต่ "ต้นทุน" ของโค้ด C ที่ซับซ้อน 500 บรรทัด และอาจเป็นการพึ่งพาบุคคลที่สามที่แปลกใหม่ ฉันอาจปฏิเสธ PR เนื่องจากความซับซ้อนไม่คุ้มค่า ตามตัวอย่างที่เป็นรูปธรรม - การทำให้ cuBLAS สำหรับ matmuls เป็นค่าเริ่มต้นในลูปการฝึกรูทนั้นไม่ใช่เรื่องง่าย: มันทำให้โค้ด mainline เร็วขึ้นมาก มันเป็นโค้ดบรรทัดเดียวที่ตีความได้ และเป็นการพึ่งพากันทั่วไปมาก ด้านข้างของสิ่งนี้ เราสามารถมีการใช้งานแบบแมนนวลที่สามารถแข่งขันกับ cuBLAS ใน dev/cuda
ได้
สุดท้ายนี้ ฉันจะไวต่อความซับซ้อนมากขึ้นในโฟลเดอร์รูทของโปรเจ็กต์ ซึ่งมีไฟล์หลัก/ไฟล์ดีฟอลต์ของโปรเจ็กต์ ในการเปรียบเทียบ โฟลเดอร์ dev/
นั้นเป็นพื้นที่เริ่มต้นเล็กน้อยสำหรับเราในการพัฒนาไลบรารีของเคอร์เนลหรือคลาส และแบ่งปันโค้ดที่มีประโยชน์หรือเกี่ยวข้องหรือเพื่อการศึกษา และโค้ดบางส่วนนี้อาจเป็นเรื่องปกติที่จะซับซ้อน (ในเครื่อง)
เอเอ็มดีสนับสนุน
ค#
CUDA C++
C++/CUDA
เว็บ GPU C++
ซี++
ไป
ชวา
โลหะ
โมโจ
โอเพ่นซีแอล
สนิม
สวิฟท์
ซิก
ฮาบาน่า เกาดี้2
นิม
วิธีจัดระเบียบการพัฒนา:
#llmc
ใหม่บนช่อง Zero to Hero Discord ของฉัน เอ็มไอที