Skip to content

Latest commit

 

History

History
559 lines (431 loc) · 27.6 KB

File metadata and controls

559 lines (431 loc) · 27.6 KB

Заметки по эксперименту

2026-04-02: Пробный запуск (завершен)

Выводы

  1. Токенизатор с vocab_size=30000 на 50 книгах не содержит целых слов
  2. MLM учится предсказывать субворды (окончания), а не смысл
  3. Loss 7.0 — потолок для субворд-токенизации
  4. Модель не может выучить семантику без целых слов в словаре

Решения

  1. Увеличить vocab_size до 50000
  2. Проверить наличие целых слов после токенизации
  3. Пересобрать датасет с новым токенизатором
  4. Переобучить модели

2026-04-02: Чистый эксперимент (начат)

Шаги

  • Очищены processed данные
  • Создана документация
  • Переобучен токенизатор (vocab_size=50000)
  • Проверен словарь на целые слова
  • Пересобран датасет
  • Запущено обучение BERT-tiny (50 эпох)
  • Запущено обучение BERT-small (50 эпох)
  • Запущено обучение BERT-base (30-50 эпох)

Ожидаемые результаты

  • MLM предсказывает осмысленные слова
  • Loss ниже 6.5
  • Визуализация attention показывает семантические паттерны

2026-04-02: Токенизатор исправлен

Выполнено

  • Исправлен train_tokenizer.py (поддержка переменного vocab_size)
  • Переобучен токенизатор с vocab_size=50000, min_frequency=2
  • Проверено наличие целых слов в словаре

Результаты проверки словаря

Слово ID Статус
учение 5010 ✅
граница 6102 ✅
материя 5450 ✅
философия 7659 ✅
методология 13217 ✅
сознание 3847 ✅

Следующий шаг

  • Пересобрать датасет с новым токенизатором

2026-04-02: Датасет пересобран, маскирование исправлено

Выполнено

  • Пересобран датасет с новым токенизатором (vocab_size=50000)
  • Проверено маскирование: теперь маскируются целые слова
  • Train: 11,323 примера, Val: 91 пример

Проверка токенизации

Слово Токенизация
философия ['философия']
учение ['учение']
граница ['граница']
материя ['материя']

Следующий шаг

  • Обучение BERT-tiny (50 эпох) — ожидаем осмысленные MLM предсказания

2026-04-02: Чистый эксперимент запущен

Конфигурация

  • Модель: BERT-tiny (29.9M параметров)
  • Токенизатор: vocab_size=50000 (целые слова)
  • Датасет: 11,323 train / 91 val
  • Эпох: 50
  • Batch size: 16

Прогресс обучения (первые 3 эпохи)

Эпоха Train Loss Val Loss Perplexity
1 10.15 9.66 15,732
2 8.92 8.30 4,032
3 8.06 8.22 3,729

Ключевые отличия от пробного эксперимента

  1. Целые слова вместо субвордов
  2. vocab_size=50000 (было 30000)
  3. Модель учится предсказывать осмысленные токены

Ожидание

  • Loss продолжит снижаться до 6.5-7.0
  • MLM предсказания должны стать осмысленными
  • Визуализация attention покажет семантические паттерны

Следующий шаг

  • Дождаться 10, 20, 30, 40, 50 эпох
  • Проанализировать MLM предсказания после 10 эпох
  • Визуализировать attention patterns

2026-04-02: Ключевой вывод по токенизации

Проблема

Токенизатор, обученный на малом корпусе (50 книг, 27 МБ), дает некачественные субворды. Пример: 'невзгод' → ['нев', '##з', '##го', '##д'] (бессмысленное разбиение)

Причина

Для обучения качественного WordPiece токенизатора нужен большой и разнообразный корпус (4+ ГБ), чтобы:

  • Алгоритм увидел статистику по редким словам
  • Сформировал осмысленные субворды (морфемы)
  • Правильно определил границы между частями слов

Правильная стратегия

  1. Обучить токенизатор на БОЛЬШОМ корпусе (4 ГБ военно-исторический → затем 40 ГБ)
  2. Использовать этот токенизатор для обработки ЛЮБЫХ данных (хоть 50 книг, хоть весь корпус)
  3. Качество субвордов будет хорошим даже для маленького датасета

Следующий шаг

  • Обработать военно-исторический корпус (4 ГБ, 11K файлов militera)
  • Обучить токенизатор на 4 ГБ
  • Пересобрать датасет из 50 философских книг с новым токенизатором
  • Переобучить BERT-tiny

Долгосрочная цель

  • Обучить токенизатор на всем корпусе 40 ГБ
  • Обучить BERT-base на 40 ГБ

2026-04-02: Streaming датасет и обучение

Датасет militera

  • Исходные файлы: 11,350 книг
  • Предложения: ~81 млн
  • Примеры BERT: 4,534,321
  • Размер: 25 ГБ (JSONL)

Изменения в пайплайне

  1. build_dataset.py переписан на streaming режим
  2. Создан train_tiny_streaming.py для работы с JSONL
  3. Обучение теперь не загружает весь датасет в память

Запуск обучения

python training/train_tiny_streaming.py \
    --config training/config/tiny_bert_streaming.yaml \
    --dataset_path data/processed/dataset_militera \
    --output_dir data/models

2026-04-03: Эпоха 1 завершена. Успех.

Конфигурация

  • Датасет: militera (4,319,956 train, 214,365 val)
  • Модель: BERT-tiny (29.9M параметров)
  • Токенизатор: обучен на militera, vocab_size=50000
  • Эпох: 5 (завершена 1)

Результаты эпохи 1

Метрика Значение
Train loss 4.55
Val loss 3.43
Val perplexity 30.85
Время эпохи 10.07 часа (36264 сек)

Сравнение с предыдущим экспериментом (50 книг, плохой токенизатор)

Показатель 50 книг militera
Val loss (эпоха 1) ~7.8 3.43
Perplexity ~2400 30.8

Вывод

Токенизатор, обученный на большом корпусе (4+ ГБ), дает осмысленные субворды. Модель выучивает семантику, а не знаки препинания. Perplexity 30.8 означает, что модель выбирает правильный токен из ~30 вариантов (вместо случайного угадывания из 50000).

Следующие шаги

  • Эпоха 2 в процессе
  • Ожидаемый val_loss к концу 5 эпох: 2.8-3.0
  • После завершения 5 эпох: тест MLM предсказаний
269996,1,269996,3.35166072845459,4.849232155038563e-05,"tensor(3.2634, device='cuda:0')"
269997,1,269997,3.528240919113159,4.849229944134392e-05,"tensor(3.5215, device='cuda:0')"
269998,1,269998,3.285130023956299,4.8492277332145146e-05,"tensor(6.7311, device='cuda:0')"
269999,2,1,3.4111690521240234,4.8492255222789296e-05,"tensor(4.0513, device='cuda:0')"
270000,2,2,3.4222428798675537,4.849223311327639e-05,"tensor(4.0643, device='cuda:0')"
270001,2,3,3.521181106567383,4.8492211003606415e-05,"tensor(3.8972, device='cuda:0')"
270002,2,4,3.595346212387085,4.849218889377938e-05,"tensor(3.3441, device='cuda:0')"
270003,2,5,3.598033905029297,4.849216678379528e-05,"tensor(3.1930, device='cuda:0')"

2026-04-03: Валидация MLM на военных текстах

Результаты теста (8 примеров из militera корпуса)

Тип слова Качество предсказаний
Прилагательные (названных → этих, основных) ✅ Отлично
Глаголы (случалось → было, произошло) ✅ Отлично
Военные термины (отбивать → преследовать, взять) ✅ Хорошо
Абстрактные существительные (честь → жизнь, волю) ✅ Хорошо
Местоимения/союзы (который → что, которые) ✅ Хорошо
Имена собственные (Андронникова → турок, русских) ⚠️ Категория угадана, но не точное имя

Проблемы

  • Пунктуация, прилипшая к слову (огня. → предсказания сбиваются)
  • Имена собственные не восстанавливаются (ожидаемо для BERT-tiny)

Вывод

Модель успешно выучила семантику военно-исторических текстов. Perplexity 30.8 на валидации и осмысленные предсказания на реальных примерах подтверждают, что эксперимент достиг цели.

Следующий шаг: завершить эпохи 2-5 и оценить улучшение.

(venv) audetv@home:~/go/src/github.com/terratensor/book2bert$ python scripts/test_mlm.py
Using device: cuda
Model loaded successfully

Loading real examples from militera corpus...
============================================================
MLM Predictions on military-historical texts (after epoch 1)
============================================================

Original: Представляется, что на краткосрочную перспективу первый из названных процессов будет развиваться все-таки быстрее второг...
Masked:   Представляется, что на краткосрочную перспективу первый из [MASK] процессов будет развиваться все-таки быстрее второго.
  Masked word: 'названных' → Predictions: ['этих', 'основных', 'таких', 'конкретных', 'важнейших', 'других', 'всех', 'теоретических']

Original: После трудных переговоров конференция привела к соглашению о прекращении огня....
Masked:   После трудных переговоров конференция привела к соглашению о прекращении [MASK]
  Masked word: 'огня.' → Predictions: ['…', 'переговоров', 'и']

Original: После таких пропагандистских заявлений «Египет — по словам Киссинджера — мог заняться проблемой разъединения войск — воп...
Masked:   После таких пропагандистских заявлений «Египет — по словам Киссинджера — мог заняться проблемой разъединения войск — вопросом, [MASK] Фахми, между прочим, не затронул в своей речи» .
  Masked word: 'который' → Predictions: ['что', 'которые', 'между', 'как', 'какими', 'с', 'а', 'и']

Original: Вернувшись назад, я стал что-то невнятное бормотать Рубинчику про постигшую меня неудачу....
Masked:   Вернувшись назад, я стал что-то невнятное [MASK] Рубинчику про постигшую меня неудачу.
  Masked word: 'бормотать' → Predictions: ['письмо', 'заявление', 'послание', 'замечание', 'мне', 'предложение', 'лицо', 'слово']

Original: Такого давно уже с нами не случалось, с уходом корифея всего и вся мои литературные дела неплохо наладились....
Masked:   Такого давно уже с нами не [MASK] с уходом корифея всего и вся мои литературные дела неплохо наладились.
  Masked word: 'случалось,' → Predictions: ['было', 'произошло', 'случилось', 'удалось', 'оставалось', 'только', 'осталось', 'будет']

Original: — Я готов держать какое угодно пари, что русские не станут отбивать у нас Малахова!...
Masked:   — Я готов держать какое угодно пари, что русские не станут [MASK] у нас Малахова!
  Masked word: 'отбивать' → Predictions: ['преследовать', 'оставить', 'искать', 'взять', 'делать', 'удержать', 'расстреливать', 'иметь']

Original: Необходимость защищать свою честь закаляла его с каждым днем, преображала на глазах у жены....
Masked:   Необходимость защищать свою [MASK] закаляла его с каждым днем, преображала на глазах у жены.
  Masked word: 'честь' → Predictions: ['жизнь', 'волю', 'очередь', 'силу', 'мать', 'душу', 'долю', 'страну']

Original: Одновременно с победой над турецким флотом русские сухопутные силы одержали под командой Андронникова при крепости Ахалц...
Masked:   Одновременно с победой над турецким флотом русские сухопутные силы одержали под командой [MASK] при крепости Ахалцых крупную победу над большим отрядом турок.
  Masked word: 'Андронникова' → Predictions: ['турок', 'англичан', 'русских', 'французов', 'шведов', 'противника', 'японцев', 'немцев']

План улучшения токенизатора для следующего эксперимента (полный корпус 40 ГБ)

Проблема

В текущем словаре присутствуют CJK-символы (китайские, японские, корейские) и тайские символы, которые являются шумом (OCR-ошибки, редкие вкрапления).

Решение

  1. Фильтровать CJK и тайские символы при сборе корпуса для токенизатора
  2. Оставить русский + английский (латиница)
  3. Установить limit_alphabet для ограничения базовых символов
  4. Проверить, что редкие символы не попали в словарь

Код фильтрации

def has_cjk(text):
    for ch in text:
        if 0x4E00 <= ord(ch) <= 0x9FFF:  # основной диапазон CJK
            return True
    return False

def has_thai(text):
    for ch in text:
        if 0x0E00 <= ord(ch) <= 0x0E7F:
            return True
    return False
import re

# Диапазоны CJK-символов
CJK_RANGES = [
    (0x4E00, 0x9FFF),   # CJK Unified Ideographs (основной)
    (0x3400, 0x4DBF),   # CJK Unified Ideographs Extension A
    (0x20000, 0x2A6DF), # CJK Unified Ideographs Extension B
    (0x2A700, 0x2B73F), # Extension C
    (0x2B740, 0x2B81F), # Extension D
    (0x2B820, 0x2CEAF), # Extension E
    (0x2CEB0, 0x2EBEF), # Extension F
    (0x30000, 0x3134F), # Extension G
    (0x31350, 0x323AF), # Extension H
    (0x2E80, 0x2EFF),   # CJK Radicals
    (0x2F00, 0x2FDF),   # Kangxi Radicals
    (0x2FF0, 0x2FFF),   # Ideographic Description Characters
    (0x3000, 0x303F),   # CJK Symbols and Punctuation
    (0x31C0, 0x31EF),   # CJK Strokes
    (0x3200, 0x32FF),   # Enclosed CJK Letters and Months
    (0x3300, 0x33FF),   # CJK Compatibility
    (0xF900, 0xFAFF),   # CJK Compatibility Ideographs
    (0xFE30, 0xFE4F),   # CJK Compatibility Forms
]

def has_cjk(text):
    """Проверяет, содержит ли текст CJK-символы"""
    for ch in text:
        code = ord(ch)
        for start, end in CJK_RANGES:
            if start <= code <= end:
                return True
    return False

def has_thai(text):
    """Проверяет, содержит ли текст тайские символы"""
    # Тайский диапазон: 0x0E00 – 0x0E7F
    for ch in text:
        if 0x0E00 <= ord(ch) <= 0x0E7F:
            return True
    return False

# При фильтрации:
if has_cjk(line) or has_thai(line):
    continue  # пропускаем

Ожидаемый эффект

  • Уменьшение размера словаря
  • Более качественные эмбеддинги для релевантных токенов
  • Отсутствие влияния на инференс (CJK символы не встречаются в текстах)

2026-04-03: Исправления build_dataset.py

Проблемы

  1. Двойной [CLS] и [SEP] из-за ручного добавления
  2. Потеря [SEP] между предложениями
  3. NSP не используется (осознанное решение)

Исправления

  1. Убрано ручное добавление [CLS] и [SEP] в encode_group
  2. token_type_ids всегда нули (NSP отключен)
  3. Добавлен флаг --use-nsp для будущего

Обоснование отказа от NSP

  • Задачи: semantic search, эмбеддинги, классификация
  • NSP не влияет на качество [CLS] эмбеддингов
  • Упрощает пайплайн и уменьшает размер датасета

Когда добавить NSP

  • Для задач QA, NLI, определения связности текста
  • Потребуется формирование пар предложений и token_type_ids

2026-04-05: Тестирование многотокенных слов

Результаты full mode (10 слов)

  • Успешных предсказаний: 0
  • Accuracy: 0%

Анализ

  • Модель не восстанавливает редкие словоформы целиком
  • Но часто предсказывает семантически близкие варианты (берется → взялся)
  • Проблема: неестественная токенизация (ПОДВОДНЫЕ → ПОД/##ВОД/##НЫЕ)

Вывод для книги

  • Многотокенные слова — сложная задача для tiny-модели
  • Семантика выучена, точность словоформ требует больше данных

2026-04-05: Эпоха 3 завершена

Результаты

Эпоха Val loss Perplexity
1 3.41 30.21
2 2.88 17.78
3 2.71 14.96

Сравнение с v2

  • v3 лучше на 0.25 по val loss (2.96 → 2.71)
  • v3 лучше на 4.34 по perplexity (19.30 → 14.96)

Прогноз

  • Эпоха 4: val_loss ~2.60-2.65
  • Эпоха 5: val_loss ~2.55-2.60

2026-04-06: Эпоха 4 завершена

Результаты

Эпоха Val loss Perplexity
1 3.41 30.21
2 2.88 17.78
3 2.71 14.96
4 2.62 13.78

Сравнение с v2

  • v3 лучше на 0.34 по val loss
  • v3 лучше на 5.52 по perplexity

Прогноз эпохи 5

  • Val loss: ~2.58-2.60
  • Perplexity: ~13.0-13.5

2026-04-06: Уточнение по шагам обучения

  • У нас ~1,055,881 шагов (начало 5 эпохи)
  • Оригинальный BERT-base: 1,000,000 шагов
  • Количество шагов сопоставимо! ?
  • Разница в эпохах: у BERT-base batch size 256 → 25k шагов/эпоху
  • У вас batch size 16 → 245k шагов/эпоху
  • Ваша модель достигла сопоставимого количества шагов, но за 4-5 эпох

2026-04-06: План улучшения — CPU offload, 2 GPU, curriculum learning

1. CPU Offload

  • Технология: CPUOffloadOptimizer (PyTorch) или Transformer Engine
  • Эффект: освобождает 100-200 MB — не решает проблему BERT-base

2. Две GPU (DataParallel / DDP)

  • RTX 5060 Ti (16GB) + RTX 3060 (12GB) = 28GB суммарно
  • BERT-base (110M) в fp16: ~6-8 GB на карту → должно влезть
  • Ускорение: ~1.8x

3. Curriculum learning для редких слов

  • Идея: сначала простые примеры, потом сложные
  • Реализация: weighted sampling или two-stage training
  • Научное обоснование: улучшает обобщение на 5-10%

План действий (по приоритету)

  1. ✅ Завершить эпоху 5 (baseline)
  2. ⏳ Запустить BERT-small на 2 GPU через DDP
  3. ⏳ При необходимости — curriculum learning

Данные (200+ GB)

  • Не использовать всё
  • Выделить редкие слова из других жанров
  • Создать сбалансированный датасет (70% militera + 30% редкие слова)

2026-04-06: Сравнение с HF моделями (multitoken)

distilrubert-tiny (107M params, 100k vocab)

  • Accuracy: 0/10 (0%)
  • Предсказывает семантически близкие слова, но не точные формы
  • Не знает военную терминологию ("Стрелковая" → ",")

Вывод

  • Многотокенные слова — сложная задача для любых tiny-моделей
  • Наша модель (30M) не уступает distilrubert (107M) на военном корпусе
  • Для улучшения нужны base-модели или больше эпох

2026-04-06: Шок — rubert-base тоже 0% на многотокенных словах

Результаты

  • rubert-base (178M params, 119k vocab): 0/10 accuracy (0%)
  • Модель понимает семантику ("ракетноторпедным" → "дизельным-торпедным")
  • Но точную форму не угадывает

Вывод

  • Проблема многотокенных слов — отраслевая, не зависящая от размера модели
  • Наша tiny-модель не хуже 178M модели на этой задаче
  • Для практических задач (semantic search) это не критично (?)

2026-04-06: ФИНАЛ — все модели показали 0% на многотокенных словах

Тестированные модели

  • our (militera) — 30M params — 0%
  • distilrubert-tiny — 107M params — 0%
  • rubert-base — 178M params — 0%
  • bert-base-multilingual — 178M params — 0%

Вывод

  • Проблема многотокенных слов не решается увеличением модели
  • Нужны другие подходы: больше эпох, weighted sampling, curriculum learning
  • Для semantic search текущее качество достаточно

Теория: BPE vs WordPiece

BPE

  • Критерий: частота пары
  • Нет маркера субвордов
  • Используется в GPT, RoBERTa

WordPiece

  • Критерий: максимизация вероятности
  • Маркер ## для субвордов
  • Используется в BERT

Почему WordPiece лучше для русского

  • Выделяет морфемы (корни, окончания)
  • Обратимость токенизации
  • Лучше для грамматики

2026-04-06: Проблема исправления разрывов слов

Типы разрывов

  1. Разрыв пробелом внутри слова: "внима тельно" → "внимательно"
  2. Перенос с дефисом: "внима- \n тельно" → "внимательно"
  3. Отсутствие пробелов: "подводяитог" → "подводя итог"

Решение

  • Тип 2: исправляем (fixHyphenatedWords)
  • Тип 1: не исправляем (нужен словарь)
  • Тип 3: не возникает при правильной очистке

Почему не исправляем тип 1

  • Нужен словарь русского языка (100k+ слов)
  • Без словаря будут ложные срабатывания
  • Модель BERT может выучить эти паттерны сама

2026-04-07: Обработка полного корпуса завершена

Статистика

Корпус Файлы Предложения
Flibusta 2023 143,861 776,606,995
Flibusta 2025 21,388 189,639,159
Militera 11,361 107,900,756
География 191 7,247,353
Итого 176,801 1,081,394,263
  • Размер на диске: 408 GB
  • Ошибок: 0
  • Пропущено: 394 (0.22%)

Следующие шаги

  1. Сбор корпуса для токенизатора (2-3 часа)
  2. Тестирование оптимального vocab_size
  3. Сборка датасета (3-5 часов)
  4. Обучение BERT-small (100-120 часов)