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
Safetensors
Model size
80.2M params
Tensor type
F32
·
BOOL
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Dataset used to train kamacops/llm-course-hw1