LLM на простом чистом C/CUDA без необходимости использования 245 МБ PyTorch или 107 МБ cPython. В настоящее время основное внимание уделяется предварительному обучению, в частности воспроизведению мини-сериалов GPT-2 и GPT-3, а также параллельной эталонной реализации PyTorch в train_gpt2.py. Вы узнаете в этом файле слегка измененный nanoGPT, мой более ранний проект. На данный момент llm.c немного быстрее PyTorch Nightly (примерно на 7%). В дополнение к новейшему основному коду в train_gpt2.cu у нас есть простая эталонная реализация fp32 для ЦП, состоящая примерно из 1000 строк чистого кода в одном файле train_gpt2.c. Я бы хотел, чтобы это репо поддерживало только код C и CUDA. Порты на другие языки или репозитории очень приветствуются, но их следует делать в отдельных репозиториях, и я рад дать ссылку на них ниже в разделе «примечательные форки». Координация действий разработчиков происходит в обсуждениях и в Discord: либо на канале #llmc
на канале Zero to Hero, либо на канале #llmdotc
на Discord в режиме GPU.
Лучшее знакомство с репозиторием 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
Раздел «Я настолько беден графическим процессором, что у меня нет даже одного графического процессора». Вы все еще можете наслаждаться поездом 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 ), оценить потери при проверке и взять образец текста. Честно говоря, если у вас нет мощного процессора (и вы не можете увеличить количество потоков OMP в команде запуска), вы не продвинетесь так далеко в 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
отвечают за загрузку, токенизацию и сохранение токенов в файлы .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 содержат короткий заголовок (1024 байта), а затем поток токенов в uint16, указывающий идентификаторы токенов с помощью токенизатора GPT-2. Дополнительные наборы данных доступны в /dev/data
.
Я также прилагаю простой модульный тест, позволяющий убедиться, что наш код C согласуется с кодом PyTorch. Например, на процессоре скомпилируйте и запустите с помощью:
make test_gpt2
./test_gpt2
Теперь он загружает файл gpt2_124M_debug_state.bin
, который записывается train_gpt2.py, запускает прямой проход, сравнивает логиты и потери с эталонной реализацией PyTorch, затем выполняет 10 итераций обучения с Адамом и проверяет, соответствуют ли потери PyTorch. Чтобы проверить версию графического процессора, мы запускаем:
# 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.
вспышка внимания . С 1 мая 2024 г. мы используем Flash Attention от cuDNN. Поскольку cuDNN увеличивает время компиляции с нескольких секунд до ~минуты, а этот путь кода сейчас очень новый, по умолчанию он отключен. Вы можете включить его, скомпилировав следующим образом:
make train_gpt2cu USE_CUDNN=1
Это попытается скомпилировать с помощью cudnn и запустить его. В вашей системе должен быть установлен cuDNN. Инструкции по установке cuDNN с помощью apt-get получат набор пакетов cuDNN по умолчанию. Для минимальной настройки достаточно пакета разработчика cuDNN, например, в 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, но это всего лишь файлы заголовков. Просто клонируйте репозиторий на свой диск. В настоящее время 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
, следуя инструкциям из раздела «Мульти-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).Ни один из этих трех методов не является лучшим, мы просто предлагаем вам варианты, которые вы сможете использовать в своей конкретной среде.
Просто в качестве примера процесса оценки скорости обучения на машине с 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.
Еще несколько слов о том, каким я хочу видеть это репо:
Во-первых, я хочу, чтобы llm.c
стал местом обучения. Например, наша папка dev/cuda
— это место для библиотеки ядер для всех слоев, которые написаны вручную и очень хорошо документированы, начиная от очень простых ядер и заканчивая более сложными/быстрыми ядрами. Если у вас есть новое ядро с различными компромиссами, пожалуйста, поделитесь им здесь.
Тем не менее, я также хочу, чтобы llm.c
был очень быстрым и даже практически полезным для обучения сетей. Например, для начала мы должны быть в состоянии воспроизвести большой тренировочный прогон GPT-2 (1.6B). Это требует, чтобы мы включили все возможные самые быстрые ядра, включая использование таких библиотек, как cuBLAS, cuBLASLt, CUTLASS, cuDNN и т. д. Я также думаю, что это служит образовательной цели, чтобы установить экспертную верхнюю границу и единицу измерения. например, вы можете сказать, что ваши ядра, написанные вручную, составляют 80% скорости cuBLAS и т. д. Затем вы можете выполнить сверхбыстрый запуск или выбрать «перетаскивание» любых ручных ядер, которые вы хотите использовать, и запустить с те.
Однако в качестве ограничения я хочу, чтобы основная строка llm.c
в корневой папке была простой и читабельной. Если есть PR, который, например, повышает производительность на 2%, но «стоит» 500 строк сложного кода C и, возможно, экзотической сторонней зависимости, я могу отклонить PR, потому что сложность того не стоит. В качестве конкретного примера: сделать cuBLAS для matmuls по умолчанию в корневом цикле обучения не составляет труда: это делает основной код намного быстрее, это одна строка интерпретируемого кода, и это очень распространенная зависимость. Кроме того, у нас могут быть ручные реализации, которые могут конкурировать с cuBLAS в dev/cuda
.
Наконец, я буду гораздо более чувствителен к сложности корневой папки проекта, которая содержит основные файлы проекта/файлы по умолчанию. Для сравнения, папка dev/
— это немного больше места, где мы можем разработать библиотеку ядер или классов и поделиться полезным, связанным или образовательным кодом, и часть этого кода может быть (локально) сложной.
Поддержка AMD
С#
КУДА С++
С++/CUDA
Веб-GPU С++
С++
Идти
Ява
Металл
Моджо
OpenCL
Ржавчина
Быстрый
Зиг
Гавана Гауди2
Ним
Способы организации разработки:
#llmc
на своем канале Zero to Hero Discord. Массачусетский технологический институт