HW1: Byte-level BPE и Transformer для русских анекдотов
Домашняя работа по курсу «Большие языковые модели»: реализация собственного Byte-level BPE, причинного Transformer и цикла обучения. Модели обучались с нуля; сторонняя предобученная языковая модель не использовалась.
Итоговая модель — small, 79 448 832 параметра, validation loss 2.410. Выбранные веса доступны в ревизии 332b41f3a1742e38713111081be4d61e6978aebf.
Данные и токенизатор
Источник: IgorVolochay/russian_jokes, колонка text; использована Parquet-версия датасета.
Разбиение train_test_split(test_size=0.1, seed=0xC0FFEE) даёт 135 497 обучающих и 15 056 проверочных текстов. Проверочная часть называется test в ноутбуке и используется для валидации и выбора настроек; отдельного нетронутого тестового набора нет.
Byte-level BPE обучен на тренировочной части. Словарь — 1024 токена; [EOS] имеет ID 1023. Словарь и слияния находятся в vocabulary.json и merges.json. Паддинг использует тот же ID, но исключается из loss по маске; настоящий EOS участвует в обучении.
Архитектура small
| Параметр | Значение |
|---|---|
| Слоёв Transformer | 12 |
| Размер скрытого состояния | 768 |
| Головы запросов / головы ключей и значений | 12 / 6, Grouped-Query Attention |
| Позиционное смещение | ALiBi |
| Feed-forward | SwiGLU, промежуточный размер 2048 |
| Нормализация | RMSNorm перед attention и FFN |
| Dropout | 0.1 |
| Контекст | 256 токенов |
| Размер словаря | 1024 |
Attention использует причинную маску и маску паддингов. Длинные обучающие тексты обрезаются до 256 токенов. Веса находятся в model.safetensors, конфигурация — в config.json.
Обучение и эксперименты
Для mini и small использовались batch size 32 и контекст 256. Small обучалась на Tesla T4 с FP16 и GradScaler. Оптимизатор — AdamW, weight decay 0.01, ограничение нормы градиента 1.0. Первые 10% шагов — линейный warmup, затем линейное снижение LR до нуля.
| Эксперимент | Параметров | Шаги; пиковый LR | Validation loss |
|---|---|---|---|
| mini, 5 эпох | 10 523 520 | 21 170; 3e-4 | 2.651 |
| mini, ещё 2 эпохи | 10 523 520 | +8 468; 1e-4 | 2.619 |
| small, 5 эпох — итоговая версия | 79 448 832 | 21 170; 3e-4 | 2.410 |
| small, ещё 1 эпоха | 79 448 832 | +4 234; 1e-4 | 2.415 |
При дополнительных запусках обученные веса сохранялись, а оптимизатор и расписание LR создавались заново. Это продолжение обучения весов, а не точное восстановление состояния оптимизатора. Эксперименты отличаются числом параметров и объёмом обучения, поэтому таблица не изолирует влияние одной архитектуры.
Loss — cross entropy для следующего токена со сдвигом целей и исключением паддингов. Validation loss усредняется по батчам. Значения выше получены на всей проверочной части.
Контрольные точки small также сравнивались на одинаковых 100 проверочных батчах:
| Шаг основного обучения | Loss на 100 батчах |
|---|---|
| 4 000 | 2.9628 |
| 8 000 | 2.6706 |
| 12 000 | 2.5272 |
| 16 000 | 2.4493 |
| 20 000 | 2.4204 |
| 21 170 | 2.4141 |
2.4141 относится к подвыборке, 2.410 — ко всей валидации. Дополнительная эпоха не улучшила результат; для итоговой модели выбрана версия после первых пяти эпох.
Качество и ограничения
Модель усвоила русские слова, характерные завязки и форму диалогов. Однако устойчиво связных анекдотов с осмысленной развязкой получить не удалось. Встречаются повторы, ошибки согласования и потеря связи между репликами. Улучшение validation loss не обеспечило надёжной связности или юмора.
При жадном декодировании наблюдались зацикливания. Дополнительные проверки со штрафом за повторение 1.1 и запретом повторяющихся последовательностей из 6 токенов уменьшили зацикливание, но не обеспечили связных анекдотов.
Примеры генерации
Это реальные результаты выбранной версии из Colab на T4, без ручного исправления грамматики или смысла. EOS не добавлялся в промпт. Настройки: do_sample=True, temperature=0.7, top_k=20, max_new_tokens=150. Состояние генератора случайных чисел для этих трёх примеров отдельно не фиксировалось.
Промпт: «Заходит в бар»
Заходит в бар в бар и заказывает кружку пива. Бармен:- Мне, пожалуйста, стакан водки!- А мне стаканчик на полметра, пожалуйста!- Да, мне только пива и водки...
Промпт: «Штирлиц»
Штирлиц шел по лесу и увидел, как он волк на лодке сидит. Штирлиц шел, а потом сказал: - Штирлиц, ты что, совсем тупой? - Штирлиц, ну я же и волк, а ты какой!
Промпт: «Муж приходит домой»
Муж приходит домой, а жена его спрашивает:- Слушай, почему ты вся такая довольная?- Ой, ну ты же знаешь, что сегодня вечером я обратил внимание на работу, а ты сегодня вечером не можешь вставать!
Загрузка и запуск
Сначала выполните определения собственных классов ByteLevelBPETokenizer, TransformerConfig, TransformerForCausalLM и вспомогательных функций из ноутбука HW1. Затем:
import torch
from huggingface_hub import snapshot_download
folder = snapshot_download(
repo_id="kamacops/llm-course-hw1",
revision="332b41f3a1742e38713111081be4d61e6978aebf",
allow_patterns=["config.json", "model.safetensors",
"vocabulary.json", "merges.json"],
)
tokenizer = ByteLevelBPETokenizer.from_pretrained(folder)
device = "cuda" if torch.cuda.is_available() else "cpu"
model = TransformerForCausalLM.from_pretrained(folder).to(device).eval()
torch.manual_seed(42)
ids = torch.tensor(
[tokenizer.encode("Заходит в бар", add_eos_token=False)], device=device
)
with torch.no_grad(), torch.autocast(
"cuda", dtype=torch.float16, enabled=device == "cuda"
):
output = model.generate(
ids,
max_new_tokens=150,
eos_token_id=tokenizer.eos_token_id,
do_sample=True,
temperature=0.7,
top_k=20,
)
print(tokenizer.decode(output[0].tolist()))
Этот код задаёт seed для нового запуска и не предназначен для точного повторения приведённых выше случайных примеров. Результаты могут зависеть от устройства и версии PyTorch. Повторное обучение для загрузки сохранённых весов не требуется.
- Downloads last month
- 221