Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

llm-low-resource-domain — адаптация LLM к низкоресурсному языку

Репозиторий собран из моих рабочих Jupyter-ноутбуков, которые я вёл при адаптации большой языковой модели к татарскому языку в контуре Cloud.ru: каждый этап адаптации — это отдельный ноутбук, запускаемый на одной A100 80GB. Пайплайн покрывает перенос словаря (trans-tokenization), continual pretraining, instruction tuning (LoRA SFT) и согласование с предпочтениями (DPO), вместе с evaluation pipeline из шести функциональных метрик. Весь конфиг через один YAML.

Архитектура пайплайна

M0  — Базовая модель (Mistral-NeMo-12B-Base)
 │
 │  E1.1  trans-tokenization     32k BPE (татарский) + alignment fast_align (ru↔tt)
 │                               → перенос эмбеддингов взвешенным средним исходных токенов
 ▼
M1-init — модель с татарским словарём
 │
 │  E1.2  continual pretraining  partial-FT: embed + lm_head + norm + первые/последние K блоков
 ▼
M1  — адаптирована к языку
 │
 │  E2    SFT с LoRA             инструкционные диалоги, assistant-only loss → merge
 ▼
M2  — инструкционная модель
 │
 │  E3    DPO с LoRA             preference-пары, reference = adapter-off база → merge
 ▼
M3  — согласована с предпочтениями носителей
 │
 ▼
[Eval harness]  ← 6 метрик на каждом этапе (baseline / M1 / M2 / M3) → MLflow

Этапы адаптации

  1. E1.1 — перенос словаря (vocabulary/). Обучается собственный 32k BPE-токенайзер на татарском корпусе; эмбеддинги татарских токенов инициализируются взвешенным средним эмбеддингов соответствующих токенов исходной модели по параллельному корпусу предложений ru↔tt.
  2. E1.2 — continual pretraining (pretraining/). Partial fine-tuning на татарском моно-корпусе: размораживаются embed_tokens, lm_head, финальный norm и первые/последние K decoder-блоков. Восстанавливает языковое моделирование под новый словарь.
  3. E2 — SFT с LoRA (sft/). Supervised fine-tuning на инструкционных диалогах с LoRA (r=64, α=32), маскированием loss по ответам ассистента (assistant_only_loss); адаптер мёржится в базу — получается standalone-чекпоинт M2.
  4. E3 — DPO с LoRA (dpo/). Direct Preference Optimization на размеченных preference-парах;
  5. Eval (eval/). Шесть метрик на каждом этапе, результаты логируются в MLflow (необходимо поднять сервис на базе Cloud.ru).

Метрики

Harness замеряет шесть функциональных метрик:

Метрика Что измеряет
PPL_native cross-tokenizer perplexity - качество языкового моделирования
fertility (f_τ) средняя фрагментация: число токенов на 1 слово целевым токенайзером
IFEval-tt prompt-strict доля промптов, где выполнены все verifiable-инструкции (длина, ключевые слова, регистр и т. п.)
DPO margin средняя разность неявного reward между chosen и rejected на held-out preference-парах
DPO accuracy доля пар, где chosen получает больший неявный reward, чем rejected
LCWR length-controlled win rate против SFT-чекпоинта

Сводная динамика по этапам:

Этап PPL_native ↓ f_τ ↓ IFEval-tt ↑ margin ↑ accuracy ↑ LCWR ↑
Базовая модель (M0) 58.4 4.2 2.5 %* — — —
+ trans-tok + CPT (M1) 11.0 1.6 14.0 %* — — —
+ SFT с LoRA (M2) 10.9 1.6 44.5 % 0 0.5 35.8 %**
+ DPO (M3) 10.9 1.6 49.0 % 1.23 0.68 64.2 %**

* IFEval-tt prompt-strict в режиме 5-shot (базовая модель не инструкционная); на M2/M3 — zero-shot. ** LCWR — доля побед в парном сравнении M3 против M2 (length-controlled);

Датасеты

Все входы — локальные JSONL (по одному объекту на строку); пути задаются в configs/default.yaml. Имена полей конфигурируемы, ниже — значения по умолчанию.

Обучающие данные

Моно-татарский корпус — tokenizer_corpus_jsonl (обучение BPE) и cpt_corpus_jsonl (continual pretraining). Источник: CulturaX (tt).

{"text": "Татар теле — тюрки телләре гаиләсенә керүче милли тел."}

Параллельный корпус ru↔tt — parallel_corpus_jsonl (alignment для trans-tokenization). Источник: yasalma/tt-ru-mt (~600k пар).

{"ru": "Доброе утро", "tt": "Хәерле иртә"}

Инструкционные диалоги — sft_corpus_jsonl (E2). Последний ход — обязательно assistant; system допускается только первым сообщением.

{"messages": [{"role": "user", "content": "Казан турында кыскача сөйлә."}, {"role": "assistant", "content": "Казан — Татарстан Республикасының башкаласы, Идел буенда урнашкан."}]}

Preference-пары — dpo_pairs_jsonl (E3). Поля prompt/chosen/rejected — единообразно либо строки, либо списки сообщений (для применения того же [INST]-шаблона, что в SFT).

{"prompt": "Балаларга кыска әкият яз.", "chosen": "Борын-борын заманда бер куян яшәгән...", "rejected": "Әкият."}

Данные для оценки (eval)

PPL / fertility — ppl_eval_jsonl, fertility_eval_jsonl. Источник: FLORES-200 (tat_Cyrl), 997 пар / срез CulturaX.

{"text": "Идел елгасы Татарстан территориясе аша ага."}

IFEval-tt — ifeval_tt_jsonl. Каждый промпт сопровождается машинно-проверяемыми ограничениями (min_words, max_words, keyword_include, bullet_list, json_format и др.).

{"prompt": "Казан турында кимендә 50 сүздән торган текст яз, 'Идел' сүзен кертеп.", "constraints": [{"type": "min_words", "count": 50}, {"type": "keyword_include", "keywords": ["Идел"]}]}

Few-shot примеры для IFEval — ifeval_fewshot_jsonl (используются для 5-shot замера на baseline/M1; держатся отдельно от тестового набора).

{"prompt": "Өч җөмләдән торган җавап яз.", "response": "Беренче җөмлә. Икенче җөмлә. Өченче җөмлә."}

Held-out preference — pref_val_jsonl (DPO margin/accuracy). Схема как у dpo_pairs_jsonl.

{"prompt": "Сәламләү яз.", "chosen": "Исәнмесез! Хәлләрегез ничек?", "rejected": "Привет."}

LCWR-промпты — lcwr_prompts_jsonl (open-ended инструкции для сравнения M3 против M2).

{"prompt": "Балаларга татар телен ничек өйрәтергә? Киңәшләр бир."}

LCWR-метки — lcwr_labels_jsonl (генерируется export_lcwr_pairs, затем носители проставляют win: 1 — победил кандидат M3, 0 — baseline M2).

{"id": 0, "prompt": "...", "candidate_response": "...", "baseline_response": "...", "win": 1}

Стек технологий

Слой Технология
Базовая модель Mistral-NeMo-12B-Base (configurable)
Обучение / inference PyTorch 2.8, Transformers 4.57, TRL 0.24, PEFT 0.17
PEFT-метод LoRA (r=64, α=32)
Перенос словаря SentencePiece (32k BPE), fast_align (FremyCompany fork)
Оптимизатор E1.2 paged AdamW 8-bit (bitsandbytes)
Метрики NumPy, statsmodels (логистическая GLM для LCWR)
Трекинг MLflow
Конфиг pydantic-settings + YAML
Запуск Jupyter-ноутбуки на cloud.ru (по одному на эксперимент)
Инфраструктура Docker (CUDA 12.6) → cloud.ru ML Space, 1× NVIDIA A100 80GB

Структура проекта

mistral-low-resource-domain/
├── src/mistral_lrd/
│   ├── config.py            # pydantic-settings: все пути и гиперпараметры, load_settings()
│   ├── modeling.py          # загрузка модели/токенайзера (bf16, attn, pad=eos)
│   ├── chat_template.py     # [INST]-шаблон + {% generation %} для assistant-only loss
│   ├── freezing.py          # partial-FT: какие слои разморозить (E1.2)
│   ├── peft_utils.py        # merge адаптера в базу → standalone-чекпоинт
│   ├── tracking.py          # MLflow: run на этап, log_params/log_metrics
│   ├── vocabulary/          # E1.1 trans-tokenization
│   │   ├── train_tokenizer.py   # 32k BPE на татарском (SentencePiece)
│   │   ├── parallel_corpus.py   # Moses-корпус ru↔tt + детекция префиксов токенайзера
│   │   ├── align.py             # fast_align (-p) + префлайт бинаря
│   │   ├── token_mapping.py     # token→token mapping из таблицы alignment
│   │   ├── remap.py             # перенос эмбеддингов взвешенным средним
│   │   └── graft.py             # оркестрация E1.1 → M1-init
│   ├── pretraining/         # E1.2 continual pretraining (partial-FT)
│   ├── sft/                 # E2 LoRA SFT
│   ├── dpo/                 # E3 LoRA DPO
│   ├── eval/                # 6 метрик: perplexity, fertility, ifeval_tt, dpo_metrics, lcwr
│   │   ├── lcwr_export.py       # генерация ответов M3/M2 для разметки носителями
│   │   └── run_eval.py          # run_eval(cfg, stage) → метрики этапа в MLflow
│   └── data_prep/           # подготовка публичных датасетов из локальных parquet
├── scripts/data_prep/       # CLI-обёртки: build_culturax / build_parallel / build_flores + README
├── notebooks/               # e1_vocabulary_transfer · e1_continual_pretraining · e2_sft · e3_dpo · eval
├── configs/default.yaml     # единый конфиг (пути + гиперпараметры)
├── Dockerfile               # CUDA 12.6 + сборка fast_align + JupyterLab
├── requirements.txt
└── .env.example

Запуск

Требования

  • cloud.ru ML Space, конфигурация a100.1gpu (1× A100 80GB) — целевая среда запуска; локально подойдёт любая машина с A100 80GB, Docker и поддержкой CUDA
  • Веса базовой модели и публичные датасеты, скачанные файлами (в NFS на cloud.ru или локально)
  • MLflow tracking server

1. Сборка и публикация образа

Образ ставит рабочий стек и поднимает JupyterLab:

docker build -t mistral-lrd .

Для запуска в cloud.ru образ публикуется в реестр воркспейса (суффикс -a100 — для региона с A100):

docker tag  mistral-lrd cr.ai.cloud.ru/<workspace>/mistral-lrd-a100:0.1
docker login cr.ai.cloud.ru
docker push cr.ai.cloud.ru/<workspace>/mistral-lrd-a100:0.1

2. Подготовка данных

python scripts/data_prep/build_culturax.py \
    --input  /data/raw/culturax_tt \
    --out-tokenizer /data/tatar/mono_tokenizer.jsonl \
    --out-cpt       /data/tatar/mono_cpt.jsonl \
    --out-fertility /data/tatar/eval/fertility.jsonl

python scripts/data_prep/build_parallel.py --input /data/raw/tt_ru_mt --out /data/tatar/ru_tt_parallel.jsonl
python scripts/data_prep/build_flores.py   --input /data/raw/flores_tat_Cyrl.parquet --out /data/tatar/eval/ppl.jsonl

Инструкционные диалоги, IFEval-tt, preference-пары и LCWR-промпты подаются готовыми JSONL в схемах выше. Полный контракт схем — в scripts/data_prep/README.md.

3. Настройка

cp .env.example .env

В configs/default.yaml (или через env MLRD_PATHS__*) указать пути к данным, весам базовой модели и fast_align; выставить MLFLOW_TRACKING_URI. Required-пути без значения вызывают ошибку при загрузке конфига (fail-fast).

4. Запуск

На cloud.ru: запустить Jupyter Server на конфигурации a100.1gpu из опубликованного образа (данные примонтированы из NFS, задан MLFLOW_TRACKING_URI). Локально тот же образ поднимается через Docker:

docker run --gpus all -p 8888:8888 -v /data:/data -v /work:/work \
    -e MLFLOW_TRACKING_URI=$MLFLOW_TRACKING_URI mistral-lrd

Ноутбуки запускаются по порядку — по одному на этап адаптации:

  1. e1_vocabulary_transfer.ipynb → M1-init (с префлайтом fast_align и детекции префиксов)
  2. e1_continual_pretraining.ipynb → M1
  3. e2_sft.ipynb → M2
  4. e3_dpo.ipynb → M3
  5. eval.ipynb → 6 метрик по этапам baseline / M1 / M2 / M3 в MLflow

Метрика LCWR замеряется в три шага: export_lcwr_pairs генерирует ответы M3 и M2 → носители проставляют win → run_eval(cfg, "m3", metrics=["lcwr"]) строит length-controlled win rate по разметке.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages