diff --git a/perun/README.md b/perun/README.md index 133c408..ad56301 100644 --- a/perun/README.md +++ b/perun/README.md @@ -1,150 +1,140 @@ -# Slovenská Aya na Perunovi +# Príprava a spustenie experimentov na Perune -Prvým modelom pre ďalšiu sériu je `ministral/Ministral-3b-instruct`. Po SFT s LoRA v LLaMA-Factory sa pôvodný a dotrénovaný model porovnajú na rovnakých úlohách SK-bench. `configs/ministral.yaml` pripína rovnakú východiskovú revíziu modelu, aká sa stiahla pre baseline. Formát chatu sa odvodí z tokenizéra modelu. Konfigurácie Mistral-SK a OLMo sú v balíku ako ďalšie možnosti. +Postup pre dotrénovanie `ministral/Ministral-3b-instruct` na slovenskej časti Aya Collection metódou SFT s LoRA v LLaMA-Factory a Unslothe. Výsledky dokončeného behu sú v [dokumentácii experimentu](ministral_3b_aya/README.md). Príprava kontajnera aj vyhodnotenie v SK-bench sú v [návode pre SK-bench](SK_BENCH.md). -| Súbor | Úloha | -| --- | --- | -| `scripts/prepare_data.py` | Jednorazová príprava spoločného datasetu. | -| `configs/ministral.yaml` | Model, revízia, LoRA a tréningové nastavenia. | -| `scripts/train.py` | Kontrola dát a spustenie tréningu s Unslothom. | -| `scripts/evaluate.py` | Loss, perplexita, slovný F1 a presná zhoda na spoločnom splite eval alebo test. | +## 1. Pripojenie a cesty -## Jeden pevný dataset - -Príprava dát sa vykoná **raz**. Skript načíta slovenskú Aya, vynechá neplatné a prázdne riadky, odstráni normalizované duplicity a rozdelí dáta so seedom 42. Rovnaké zadania s rôznymi odpoveďami zostávajú v jednej množine. Eval aj test dostanú najviac 1 000 príkladov. Zvyšok patrí do train. - -V `data/aya_slovak_fixed` sa uložia **úplné zadania a referenčné odpovede**. Pri zmene modelu sa použijú tie isté súbory a ten istý manifest. Tokenizácia prebehne až v LLaMA-Factory podľa modelovej konfigurácie. Pri `cutoff_len: 4096` sa prípadné orezanie týka spracovaných tokenov pre daný beh. Dĺžky a počet orezaných príkladov sa môžu medzi modelmi líšiť. - -Manifest zaznamená zdroj, seed, počty a kontrolné súčty. `dataset_info.json` registruje `aya_train`, `aya_eval` a `aya_test`. Súbory `*.metadata.jsonl` uchovávajú pôvod príkladov v rovnakom poradí. Dočasná SQLite databáza zabezpečuje deduplikáciu a rozdelenie bez držania celého datasetu v RAM. - -## 1. Príprava dát na CPU - -Balík sa prenesie do `/mnt/project/perun26012578/perun`. Zdrojový Parquet je už v `/mnt/project/perun26012578/raw/slovak_train.parquet`. Pri príprave stačí Python s PyArrow, ktorý je dostupný aj v pripravenom kontajneri SK-bench. - -Z prihlasovacieho uzla sa príprava odošle do `cpu_short`. Príkaz použije existujúci obraz a Python prostredie v `apphome`. +Po pripojení na VPN Peruna je potrebné prihlásiť sa na uzol `login01` cez SSH. ```bash -cd /mnt/project/perun26012578/perun -mkdir -p logs -export APPHOME=/mnt/home/jascke919/apphome +ssh jascke919@login01.perun.tuke.sk +``` +Príkazy predpokladajú, že priečinok `perun` z repozitára je v projektovom úložisku. Pri prvom prenose sa nasledujúci príkaz spúšťa v PowerShelli z koreňa lokálneho repozitára. Pri ďalších behoch sa používa existujúca kópia priečinka na Perune. + +```powershell +scp -r .\perun jascke919@login01.perun.tuke.sk:/mnt/project/perun26012578/ +``` + +Premenné je potrebné nastaviť na prihlasovacom uzle v relácii, z ktorej sa odosielajú joby. + +```bash +export PROJECT=/mnt/project/perun26012578 +export APPHOME=/mnt/home/jascke919/apphome +export TRAIN_ENV=/mnt/home/jascke919/aya_train_perun_managed +export PY="$TRAIN_ENV/bin/python" +cd "$PROJECT/perun" +mkdir -p logs +``` + +## 2. Príprava prostredia + +Na prípravu dát je potrebný obraz `$APPHOME/cuda.sif` a Python 3.13 s PyArrow v `$APPHOME/venv`. Postup prvotnej inštalácie je v [príprave kontajnera SK-bench](SK_BENCH.md#1-príprava-kontajnera). + +Tréning a Aya test používajú samostatné prostredie s Pythonom 3.11. Na Perune fungovalo prostredie vytvorené cez `uv --managed-python`. Obsahuje hlavičku `Python.h`, ktorú potrebuje Triton. V existujúcom prostredí `aya_train_perun_managed` sa závislosti overia cez `"$PY" -m pip check`. Pri novej inštalácii sa nasledujúce príkazy spúšťajú z priečinka `perun` s dostupným `uv`. + +```bash +uv venv --managed-python --python 3.11 --seed "$TRAIN_ENV" +"$PY" -c 'import pathlib, sysconfig; p = pathlib.Path(sysconfig.get_path("include")) / "Python.h"; assert p.is_file(), p; print(p)' + +"$PY" -m pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 xformers==0.0.29.post3 \ + --index-url https://download.pytorch.org/whl/cu124 +"$PY" -m pip install -r requirements/training.txt +"$PY" -m pip check +``` + +Pred prvým GPU jobom je potrebné stiahnuť základný model do cache Hugging Face. Príkaz vyžaduje prístup na internet a rovnaké nastavenie cache ako tréningový job. + +```bash +"$PY" - <<'PYTHON' +from huggingface_hub import snapshot_download + +snapshot_download( + "ministral/Ministral-3b-instruct", + revision="2c95908929198d6e69af8638f0dbbd9bc6b93f9e", +) +PYTHON +``` + +## 3. Príprava Aya dát na CPU + +Zdrojový súbor je na Perune v `/mnt/project/perun26012578/raw/slovak_train.parquet`. Pripravené dáta sú v `data/aya_slovak_fixed`. Ak priečinok obsahuje pôvodný manifest a rozdelené dáta, pokračuje sa kontrolou tréningovej konfigurácie. Pri príprave od začiatku musí byť výstupný priečinok nový alebo prázdny. + +Ak zdrojový Parquet na Perune chýba, po pripojení na VPN sa prenesie cez `scp`. Príkaz sa spúšťa z lokálneho priečinka so súborom. + +```powershell +scp .\slovak_train.parquet jascke919@login01.perun.tuke.sk:/mnt/project/perun26012578/raw/ +``` + +Kontrolný súčet pôvodného Parquetu sa overí cez `sha256sum "$PROJECT/raw/slovak_train.parquet"`. Očakávaný SHA-256 je `6a21c52b80549cbb8f9d13de981a2d8f65c273a17f651fd09c1efcc952821e82`. + +```bash sbatch --job-name=aya_prepare \ --account=perun26012578 --partition=cpu_short \ --nodes=1 --ntasks=1 --cpus-per-task=4 --mem=24G \ --time=08:00:00 --output=logs/aya_prepare_%j.out \ --wrap='apptainer exec --home "$APPHOME" \ - --bind /mnt/project/perun26012578:/mnt/project/perun26012578 \ - --pwd /mnt/project/perun26012578/perun "$APPHOME/cuda.sif" \ + --bind "$PROJECT:$PROJECT" \ + --pwd "$PROJECT/perun" "$APPHOME/cuda.sif" \ "$APPHOME/venv/bin/python" -u scripts/prepare_data.py \ - --local-parquet /mnt/project/perun26012578/raw/slovak_train.parquet \ - --output-dir data/aya_slovak_fixed' + --local-parquet "$PROJECT/raw/slovak_train.parquet" \ + --output-dir data/aya_slovak_fixed --seed 42 --eval-size 1000 --test-size 1000' ``` -Po dokončení sa skutočné počty a kontrolné súčty nachádzajú v `data/aya_slovak_fixed/manifest.json`. Výstupný priečinok prípravy musí byť nový alebo prázdny. Pri vlastnom lokálnom JSONL slúži `--local-jsonl`. Bez lokálneho vstupu sa použije pripnutá revízia slovenskej Aya na Hugging Face. +Príprava odstráni neplatné riadky a duplicitné páry. Rovnaké normalizované zadania drží v jednej množine. Po pôvodnom jobe 145681 obsahoval [manifest](ministral_3b_aya/training/data_manifest.json) **3 490 041 tréningových, 1 000 validačných a 1 000 testovacích príkladov**. Skript uchováva celé texty a limit 4 096 tokenov sa uplatňuje pri tokenizácii konkrétneho behu. -## Prostredie pre tréning +## 4. Kontrola, pilot a plný tréning -Tréning používa samostatné prostredie s LLaMA-Factory, Unslothom a CUDA pre H200. Balík obsahuje zachytené verzie knižníc. Funkčnosť konkrétnej zostavy sa overí pri prvom pilote na Perunovi. - -### Požiadavky a inštalácia prostredí - -Priečinok `requirements` obsahuje verzie odvodené z exportu Hrušky. - -| Súbor | Prostredie | -| --- | --- | -| `requirements/training.txt` | Python 3.11.16; LLaMA-Factory, Unsloth, PyTorch 2.6.0 s CUDA 12.4 a generatívna evaluácia. | -| `requirements/lm-eval.txt` | Python 3.11.16; lm-evaluation-harness, Transformers 4.48.2 a PyTorch 2.6.0 s CUDA 12.4. | -| `requirements/skbench.txt` | Python 3.13.17; export 308 balíkov SK-bench vrátane vLLM 0.28.0, Transformers 5.12.1 a PyTorch 2.13.0. | -| `requirements/skbench/pyproject.toml` a `uv.lock` | Pôvodné závislosti a uzamknuté verzie SK-bench pre obnovenie cez uv. | - -Git závislosti LLaMA-Factory, lm-eval a lighteval sú pripnuté na pôvodné commity. Neplatná lokálna cesta k pip bola v tréningovom a lm-eval exporte nahradená verziou `pip==26.2.1` z Condy. Výpočtové knižnice zachovávajú pôvodné verzie. - -Do tréningových požiadaviek boli doplnené `jieba==0.42.1`, `rouge-chinese==1.0.3`, `nltk==3.10.3` a jeho závislosti `joblib==1.6.0` a `defusedxml==0.7.1` pre generatívnu evaluáciu. Doplnený je aj `torchao==0.13.0`, pretože ho vyžaduje zachytená verzia Unsloth Zoo a export ho neobsahoval. Táto verzia TorchAO podporuje Python API s PyTorch 2.6; jej natívne C++ rozšírenia sú určené pre PyTorch 2.8. Scenár používa LoRA bez kvantizácie TorchAO. - -Pre tréning je `setuptools` upravený na `80.10.2`, rovnako ako vo funkčnom SK-bench exporte. Knižnica jieba používa `pkg_resources`, ktorý bol zo setuptools 82 odstránený. Tréningové a benchmarkové verzie ostatných zachytených balíkov sa nemenia. - -Ide o požiadavky pripravené z funkčných prostredí Hrušky a uvedených doplnení. Čistá inštalácia a výpočet na H200 sa overia na Perunovi. - -#### Tréning a evaluácia na pripravených Aya dátach - -Nasledujúce príkazy sa zadávajú z koreňa balíka v prostredí s dostupnou Condou a Gitom. PyTorch a xformers sa najprv nainštalujú z rovnakého oficiálneho CUDA 12.4 indexu: +Konfigurácia [`configs/ministral.yaml`](configs/ministral.yaml) používa nastavenia plného behu s learning rate `0.0002`. Skript spustí štyri tréningové procesy cez DDP. Slurm preto dostane jeden task a štyri GPU na jednom uzle. ```bash -conda create -n aya_train_perun python=3.11.16 pip=26.2.1 -y -conda activate aya_train_perun - -python -m pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 xformers==0.0.29.post3 \ - --index-url https://download.pytorch.org/whl/cu124 - -python -m pip install -r requirements/training.txt -python -m pip check +export RUN="$PROJECT/perun/runs/ministral_aya_$(date +%Y%m%d_%H%M%S)" +"$PY" scripts/train.py configs/ministral.yaml --run-dir "$RUN" --dry-run ``` -Na pridelenom GPU uzle sa overia importy, CUDA a BF16: - -```bash -python -c 'import unsloth, torch, jieba, nltk; from rouge_chinese import Rouge; assert torch.cuda.is_available(); assert torch.cuda.is_bf16_supported(); print("GPU:", torch.cuda.get_device_name(0)); print("Viditelne GPU:", torch.cuda.device_count()); print("CUDA:", torch.version.cuda)' -``` - -Tréning aj `scripts/evaluate.py` sa potom spúšťajú v `aya_train_perun` podľa ďalších častí dokumentácie. Overené opravy generovania z Titanu sú súčasťou `scripts/train.py`. - -#### lm-evaluation-harness - -Príkazy sa zadávajú z koreňa balíka: - -```bash -conda create -n aya_lmeval_perun python=3.11.16 pip=26.2.1 -y -conda activate aya_lmeval_perun - -python -m pip install torch==2.6.0 --index-url https://download.pytorch.org/whl/cu124 -python -m pip install -r requirements/lm-eval.txt -python -m pip check -lm_eval --help -``` - -Súbor inštaluje framework pri revízii `6d2abda4fd171e68a8789330c4149e37c1ca0bda`. Vlastné slovenské úlohy, zoznam benchmarkov a evaluačné dáta sa prenášajú samostatne; požiadavky obsahujú Python knižnice. - -## 2. Pilot a tréning - -Z koreňa balíka s aktivovaným tréningovým prostredím sa overí konfigurácia a integrita spoločných dát. - -```bash -python scripts/train.py configs/ministral.yaml --dry-run -``` - -Plný tréning využíva štyri H200 na jednom uzle cez DDP. `train.py` spustí procesy sám. Slurm job preto požaduje jeden task a štyri GPU. Základná konfigurácia používa BF16, LoRA rank 16, alpha 32, batch 8 na GPU a efektívny batch 32. LR 0,0001 je východisková hodnota pre Ministral. - -Prvý pilot overí načítanie modelu, jeho natívny formát chatu, priebeh lossu a uloženie adaptéra. `--pilot-steps 30` obmedzí počet krokov a pri predvolenom splite použije najviac 2 048 tréningových riadkov z pevného poradia. Celý validačný split zostane dostupný. Pilot použije vlastný výstupný priečinok. LLaMA-Factory načíta zdrojový JSONL a obmedzí rozsah pred tokenizáciou. +Po úspešnej kontrole dát sa spúšťa pilot s 30 tréningovými krokmi a samostatným výstupným priečinkom. Rovnaká kontrola GPU a Tritonu sa používa aj pri plnom behu. ```bash sbatch --job-name=ministral_pilot \ --account=perun26012578 --partition=gpu_short \ --nodes=1 --ntasks=1 --gres=gpu:4 --cpus-per-task=32 --mem=128G \ --time=02:00:00 --output=logs/ministral_pilot_%j.out \ - --wrap='python -u scripts/train.py configs/ministral.yaml --run-dir runs/ministral_pilot --pilot-steps 30' + --wrap='set -e + "$PY" -c "import torch; assert torch.cuda.device_count() == 4; assert all(\"H200\" in torch.cuda.get_device_name(i) for i in range(4)); from triton.runtime import driver; print(driver.active.get_current_target())" + "$PY" -u scripts/train.py configs/ministral.yaml --run-dir "${RUN}_pilot" --pilot-steps 30' ``` -Plný tréning sa spustí po kontrole pilotu a spresnení času. Pri dlhom behu sa checkpointy ukladajú každých 1 000 optimizer krokov a vyberá sa podľa najnižšieho validačného lossu. +Pri 300k pilote sa namiesto `--pilot-steps 30` použilo `--pilot-samples 300000 --pilot-steps 9375 --pilot-eval-steps 1875`. Plný beh sa odosiela po úspešnom pilote. ```bash -sbatch --job-name=ministral_train \ +sbatch --job-name=ministral_full_lr020 \ --account=perun26012578 --partition=gpu_long \ --nodes=1 --ntasks=1 --gres=gpu:4 --cpus-per-task=32 --mem=256G \ - --time=2-00:00:00 --output=logs/ministral_train_%j.out \ - --wrap='python -u scripts/train.py configs/ministral.yaml' + --time=2-00:00:00 --output=logs/ministral_full_lr020_%j.out \ + --wrap='set -e + "$PY" -c "import torch; assert torch.cuda.device_count() == 4; assert all(\"H200\" in torch.cuda.get_device_name(i) for i in range(4)); from triton.runtime import driver; print(driver.active.get_current_target())" + "$PY" -u scripts/train.py configs/ministral.yaml --run-dir "$RUN"' ``` -Adaptér sa uloží do `runs/ministral/adapter`. V nadradenom priečinku zostanú `train.log`, `used_config.yaml` a `run.json` s identifikáciou datasetu. Pre iný beh sa zvolí nový `--run-dir`. Pri obnovení z checkpointu sa použije `--resume` s pôvodnými dátami a nastaveniami. +V `$RUN` vzniknú `run.json`, `used_config.yaml`, `train.log` a `adapter/`. Pri pôvodnom jobe 148065 bol posledný checkpoint **109 064**. Export v samotnom `adapter/` zodpovedá validačne najlepšiemu checkpointu **109 000**. -## 3. Vyhodnotenie +## 5. Aya test posledného checkpointu -Po tréningu môže `evaluate.py` na jednej GPU vypočítať validačný loss a perplexitu a vygenerovať odpovede pre F1 a presnú zhodu. Pri generovaní sa ponechá priestor aspoň pre jeden nový token. Pri skórovaní generovaných odpovedí sa používajú úplné referencie zo spoločného JSONL. Výsledky obsahujú aj počty referencií, ktoré sa líšia od exportovaných labelov. Po výbere modelu sa môže použiť `--split test` s novým výstupným priečinkom. +Nasledujúce cesty zodpovedajú testu pôvodného jobu 148065. Pri teste nového tréningu je potrebné nastaviť cestu k novému behu a k jeho poslednému checkpointu. Evaluácia vyžaduje pôvodný manifest dát daného behu. ```bash -python scripts/evaluate.py configs/ministral.yaml --output-dir results/ministral_eval -python scripts/evaluate.py configs/ministral.yaml --split test --output-dir results/ministral_test +export RUN="$PROJECT/perun/models/ministral-3b-instruct/runs/full_aya_lr_0p000200_epoch1" +export CHECKPOINT="$RUN/adapter/checkpoint-109064" +export EVAL_OUT="$RUN/evaluations/aya_test_last_109064_$(date +%Y%m%d_%H%M%S)" + +sbatch --job-name=ministral_aya_test \ + --account=perun26012578 --partition=gpu_short \ + --nodes=1 --ntasks=1 --gres=gpu:1 --cpus-per-task=8 --mem=64G \ + --time=04:00:00 --output=logs/ministral_aya_test_%j.out \ + --wrap='set -e + "$PY" -u scripts/evaluate.py "$RUN/used_config.yaml" \ + --adapter "$CHECKPOINT" --split test \ + --output-dir "$EVAL_OUT" --max-new-tokens 256' ``` -Externý SK-bench sa spúšťa zo samostatného klonu v `/mnt/project/perun26012578/sk-bench`. Na porovnanie s baseline sa použije rovnaká revízia SK-bench, rovnaké nastavenia generovania a rovnaký súbor `tasks_ministral_sk_baseline.txt` s 340 variantmi. Lokálne exportovaný model musí byť viditeľný z kontajnera. [SK-bench na Slurme](https://github.com/slovak-nlp/sk-bench/blob/main/container/slurm/README.md) - -Stav jobu sa sleduje cez `squeue -u "$USER"`. Pri chybách sa kontroluje log v `logs/`. Revízia Gitu sa v prípade potreby zistí pred odoslaním na prihlasovacom uzle. +Súbor `summary.json` obsahuje testovací loss, perplexitu, tokenové F1 a normalizovanú presnú zhodu. Stav jobov sa sleduje cez `squeue -u "$USER"` a výpisy Slurmu sú v `logs/`. diff --git a/perun/SK_BENCH.md b/perun/SK_BENCH.md new file mode 100644 index 0000000..fcc0ba2 --- /dev/null +++ b/perun/SK_BENCH.md @@ -0,0 +1,165 @@ +# SK-bench na Perune + +Oba Ministraly sa hodnotia na rovnakom [zozname 385 slovenských zero-shot variantov](ministral_3b_aya/sk_bench/tasks_sk0_385.txt). Pôvodné výsledky vznikli v dvoch etapách po 340 a 45 variantoch. Nižšie je postup na prípravu prostredia, export modelu a vyhodnotenie celého dostupného zoznamu. + +## 1. Príprava kontajnera + +Cesty sa nastavia na `login01`. Ak sú klon, obraz a prostredie na Perune už pripravené, nasleduje príprava cache. + +```bash +export PROJECT=/mnt/project/perun26012578 +export APPHOME=/mnt/home/jascke919/apphome +export PY=/mnt/home/jascke919/aya_train_perun_managed/bin/python +export BENCH="$PROJECT/sk-bench" +export SKBENCH_REVISION=15823bbcc2d995e69441fcb7d34abb61bd8180c5 +``` + +Pri prvej príprave je potrebné stiahnuť SK-bench v revízii pôvodných jobov. [SK-bench](https://github.com/slovak-nlp/sk-bench/tree/15823bbcc2d995e69441fcb7d34abb61bd8180c5) + +```bash +git clone https://github.com/slovak-nlp/sk-bench.git "$BENCH" +git -C "$BENCH" checkout --detach "$SKBENCH_REVISION" +mkdir -p "$APPHOME" +``` + +Obraz Apptainera sa vytvorí na pridelenom CPU uzle. `container/build.sh` uloží `cuda.sif` do `APPHOME`. + +```bash +srun --account=perun26012578 --partition=cpu_short \ + --nodes=1 --ntasks=1 --cpus-per-task=4 --mem=24G --time=02:00:00 --pty bash +``` + +```bash +cd "$BENCH" +bash container/build.sh +bash container/shell.sh +``` + +V kontajneri je potrebné vytvoriť prostredie s Pythonom 3.13 a nainštalovať závislosti z `uv.lock`. V klone SK-bench sa používa jeho vlastný lockfile. Kópie v [`requirements/skbench/`](requirements/skbench/) majú zhodné kontrolné súčty s klonom na Perune. + +```bash +curl -LsSf https://astral.sh/uv/install.sh | sh +export PATH="$HOME/.local/bin:$PATH" +export UV_PROJECT_ENVIRONMENT="$VIRTUAL_ENV" +uv venv --python 3.13 "$VIRTUAL_ENV" +uv sync --locked +uv run --locked python -m nltk.downloader punkt_tab wordnet omw-1.4 +uv run --locked python --version +``` + +## 2. Cache základného modelu a datasetov + +Zoznam úloh sa skopíruje do klonu SK-bench na hostiteľovi. Na CPU uzle s prístupom na internet sa z toho istého klonu spustí `container/shell.sh`. + +```bash +cp "$PROJECT/perun/ministral_3b_aya/sk_bench/tasks_sk0_385.txt" "$BENCH/" +cd "$BENCH" +bash container/shell.sh +``` + +V kontajneri sa overí účet Hugging Face. Pri prvom prihlásení sa použije `uv run --locked hf auth login`. Pre Slohry, Chiby, Slovak Trivia a Word Analogies treba pod týmto účtom odsúhlasiť podmienky prístupu. + +```bash +export UV_PROJECT_ENVIRONMENT="$VIRTUAL_ENV" +unset HF_DATASETS_OFFLINE HF_HUB_OFFLINE +uv run --locked hf auth whoami + +uv run --locked python - <<'PYTHON' +from pathlib import Path + +from huggingface_hub import snapshot_download +from lighteval.tasks.lighteval_task import LightevalTask +from src.tasks.task_registry import TASK_REGISTRY + +snapshot_download( + "ministral/Ministral-3b-instruct", + revision="2c95908929198d6e69af8638f0dbbd9bc6b93f9e", + local_dir="models/ministral-base", +) +tasks = Path("tasks_sk0_385.txt").read_text(encoding="utf-8").splitlines() +assert len(tasks) == len(set(tasks)) == 385 +for task in tasks: + name, shots = task.rsplit("|", 1) + config = TASK_REGISTRY.get(name) + if config is None or shots != "0": + raise ValueError(f"Neznámy zero-shot variant {task}") + print(f"Pripravujem {name}", flush=True) + LightevalTask(config).eval_docs() +PYTHON +``` + +GPU job používa cache z toho istého `APPHOME`. Po príprave sa príkazom `exit` opustí kontajner a podľa potreby aj CPU uzol. + +## 3. Zlúčenie adaptéra pre SK-bench + +Pôvodný benchmark bežal v klone `sk-bench`. Dotrénovaný model bol vyhodnotený zo samostatného klonu rovnakej revízie. Pre checkpoint z kroku 109 064 sa na `login01` nastavia nasledujúce cesty. + +```bash +export RUN="$PROJECT/perun/models/ministral-3b-instruct/runs/full_aya_lr_0p000200_epoch1" +export CHECKPOINT="$RUN/adapter/checkpoint-109064" +export TUNED_SK="$PROJECT/perun/models/ministral-3b-instruct/benchmarks/sk-bench/finetuned_checkpoint_109064/sk-bench" +export BASE_MODEL="$BENCH/models/ministral-base" +export TUNED_MODEL="$TUNED_SK/models/ministral-last-109064" +``` + +Ak je zlúčený model už uložený, nasleduje evaluácia na GPU. Pri príprave od začiatku je potrebné vytvoriť druhý klon a použiť ten istý [exportný skript](scripts/export_skbench_model.py) ako pri jobe 152979. Skript overí váhy základného modelu proti pripnutej revízii v bežnej Hugging Face cache tréningového účtu. Túto revíziu pripraví krok v [tréningovom návode](README.md#2-príprava-prostredia). + +```bash +mkdir -p "$(dirname "$TUNED_SK")" +git clone --local "$BENCH" "$TUNED_SK" +git -C "$TUNED_SK" checkout --detach "$SKBENCH_REVISION" +mkdir -p "$TUNED_SK/models" "$BENCH/logs" + +sbatch --job-name=ministral_merge \ + --account=perun26012578 --partition=cpu_short \ + --nodes=1 --ntasks=1 --cpus-per-task=8 --mem=64G \ + --time=02:00:00 --output="$BENCH/logs/ministral_merge_%j.out" \ + --wrap='"$PY" -u "$PROJECT/perun/scripts/export_skbench_model.py" \ + "$BASE_MODEL" "$CHECKPOINT" "$TUNED_MODEL"' +``` + +Úspešný job vypíše `MERGED_MODEL_READY` a uloží BF16 váhy so záznamom o použitom checkpointe. + +## 4. Vyhodnotenie na GPU + +Po príprave cache a zlúčení adaptéra sa z `login01` odošle jeden job na každý model. Pri oboch sa zachová rovnaký zoznam úloh, revízia a identifikátory modelov v JSON výstupoch. [Oficiálne spúšťanie SK-bench cez Slurm](https://github.com/slovak-nlp/sk-bench/blob/15823bbcc2d995e69441fcb7d34abb61bd8180c5/container/slurm/README.md) + +```bash +export UV_PROJECT_ENVIRONMENT="$APPHOME/venv" +cp "$BENCH/tasks_sk0_385.txt" "$TUNED_SK/" + +for entry in base:ministral-base tuned:ministral-last-109064; do + label=${entry%%:*} + model=${entry#*:} + if [[ $label == base ]]; then workdir=$BENCH; else workdir=$TUNED_SK; fi + mkdir -p "$workdir/logs" + sbatch --job-name="skbench385_$label" \ + --account=perun26012578 --partition=gpu_short \ + --nodes=1 --ntasks=1 --gres=gpu:1 --cpus-per-task=8 --mem=64G \ + --time=04:00:00 --chdir="$workdir" \ + --output="$workdir/logs/skbench385_${label}_%j.out" \ + --wrap="srun --cpu-bind=none bash container/run-eval.sh models/$model \ + --tensor-parallel-size 1 --tasks-file tasks_sk0_385.txt \ + --output-dir outputs/skbench385_${label}_\$SLURM_JOB_ID" +done +``` + +## 5. Agregácia výsledkov + +Po dokončení jobov sa zadajú ich ID podľa výpisu Slurmu. Pri agregácii pôvodnej dvojice etáp sa do `raw/` vložia všetky štyri súbory JSON. Oficiálny skript ich spojí podľa `config_general.model_name`. + +```bash +cd "$BENCH" +read -r -p "ID jobu pôvodného modelu: " BASE_JOB +read -r -p "ID jobu dotrénovaného modelu: " TUNED_JOB +BASE_JSON=$(find "$BENCH/outputs/skbench385_base_$BASE_JOB" -type f -name 'results_*.json' -print -quit) +TUNED_JSON=$(find "$TUNED_SK/outputs/skbench385_tuned_$TUNED_JOB" -type f -name 'results_*.json' -print -quit) +REPORT=$(mktemp -d "$BENCH/outputs/ministral_leaderboard_XXXXXX") +mkdir "$REPORT/raw" +cp "$BASE_JSON" "$REPORT/raw/base.json" +cp "$TUNED_JSON" "$REPORT/raw/tuned.json" +"$PY" scripts/generate_final_results.py "$REPORT/raw" "$REPORT/aggregated" +"$PY" scripts/generate_leaderboard.py --output-dir "$REPORT" --leaderboard-dir "$REPORT/leaderboard" +``` + +Agregované súbory JSON sa uložia do `$REPORT/aggregated` a HTML prehľad do `$REPORT/leaderboard/index.html`. Porovnanie a Overall podľa metrík verejného SK-bench Space sú v [dokumentácii experimentu](ministral_3b_aya/README.md#čiastočné-výsledky-sk-bench). diff --git a/perun/configs/ministral.yaml b/perun/configs/ministral.yaml index a3324f1..5614842 100644 --- a/perun/configs/ministral.yaml +++ b/perun/configs/ministral.yaml @@ -29,7 +29,7 @@ lora_target: q_proj,k_proj,v_proj,o_proj,gate_proj,up_proj,down_proj per_device_train_batch_size: 8 per_device_eval_batch_size: 8 gradient_accumulation_steps: 1 -learning_rate: 0.0001 +learning_rate: 0.0002 num_train_epochs: 1.0 lr_scheduler_type: cosine warmup_ratio: 0.08 diff --git a/perun/configs/mistral.yaml b/perun/configs/mistral.yaml deleted file mode 100644 index bd648af..0000000 --- a/perun/configs/mistral.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# Jeden trening tohto modelu: 4 H200, DDP, Unsloth a bf16. -model_name_or_path: "slovak-nlp/mistral-sk-7b" -model_revision: "089497ae0d72018e9591895c54f774bf0b4a83a4" -stage: sft -do_train: true -do_eval: true -finetuning_type: lora -template: alpaca -use_unsloth: true -use_unsloth_gc: true -bf16: true -infer_dtype: bfloat16 -flash_attn: sdpa - -dataset_dir: data/aya_slovak_fixed -dataset: aya_train -eval_dataset: aya_eval -cutoff_len: 4096 -packing: false -train_on_prompt: false -preprocessing_num_workers: 8 -dataloader_num_workers: 2 -overwrite_cache: false - -lora_rank: 16 -lora_alpha: 32 -lora_dropout: 0.0 -lora_target: q_proj,k_proj,v_proj,o_proj,gate_proj,up_proj,down_proj - -per_device_train_batch_size: 8 -per_device_eval_batch_size: 8 -gradient_accumulation_steps: 1 -learning_rate: 0.0001 -num_train_epochs: 1.0 -lr_scheduler_type: cosine -warmup_ratio: 0.08 -optim: adamw_torch -weight_decay: 0.0 -max_grad_norm: 0.5 -gradient_checkpointing: true -ddp_find_unused_parameters: false - -output_dir: runs/mistral/adapter -overwrite_output_dir: false -logging_steps: 10 -eval_strategy: steps -eval_steps: 1000 -save_strategy: steps -save_steps: 1000 -save_total_limit: 2 -save_only_model: false -load_best_model_at_end: true -metric_for_best_model: loss -greater_is_better: false -predict_with_generate: false -plot_loss: true -report_to: none -seed: 42 -data_seed: 42 diff --git a/perun/configs/olmo.yaml b/perun/configs/olmo.yaml deleted file mode 100644 index 4595dc1..0000000 --- a/perun/configs/olmo.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# Jeden trening tohto modelu: 4 H200, DDP, Unsloth a bf16. -model_name_or_path: "allenai/OLMo-2-1124-7B" -model_revision: "7df9a82518afdecae4e8c026b27adccc8c1f0032" -stage: sft -do_train: true -do_eval: true -finetuning_type: lora -template: alpaca -use_unsloth: true -use_unsloth_gc: true -bf16: true -infer_dtype: bfloat16 -flash_attn: sdpa - -dataset_dir: data/aya_slovak_fixed -dataset: aya_train -eval_dataset: aya_eval -cutoff_len: 4096 -packing: false -train_on_prompt: false -preprocessing_num_workers: 8 -dataloader_num_workers: 2 -overwrite_cache: false - -lora_rank: 16 -lora_alpha: 32 -lora_dropout: 0.0 -lora_target: q_proj,k_proj,v_proj,o_proj,gate_proj,up_proj,down_proj - -per_device_train_batch_size: 8 -per_device_eval_batch_size: 8 -gradient_accumulation_steps: 1 -learning_rate: 0.0001 -num_train_epochs: 1.0 -lr_scheduler_type: cosine -warmup_ratio: 0.08 -optim: adamw_torch -weight_decay: 0.0 -max_grad_norm: 0.5 -gradient_checkpointing: true -ddp_find_unused_parameters: false - -output_dir: runs/olmo/adapter -overwrite_output_dir: false -logging_steps: 10 -eval_strategy: steps -eval_steps: 1000 -save_strategy: steps -save_steps: 1000 -save_total_limit: 2 -save_only_model: false -load_best_model_at_end: true -metric_for_best_model: loss -greater_is_better: false -predict_with_generate: false -plot_loss: true -report_to: none -seed: 42 -data_seed: 42 diff --git a/perun/ministral_3b_aya/sk_bench/index.html b/perun/ministral_3b_aya/sk_bench/index.html new file mode 100644 index 0000000..7abcea4 --- /dev/null +++ b/perun/ministral_3b_aya/sk_bench/index.html @@ -0,0 +1,409 @@ + + + + + + SK-Bench Leaderboard + + + + + +
+

⚡ SK-Bench Leaderboard

+ 2 models  ·  62 metric columns  ·  generated 2026-11-12 15:24 UTC +
+ +
+
+ Heat-map: + + low → high within each metric column +
+
+ + + + + + + + + + +
Model (2)⌀ AVGlang AVGarcbelebelechibydriving_testsfinancial_examsifeval_skglobalmmlu_litemmlumovie_reviewsmulti_wiki_qasenti_sk:sentiment_3_classsenti_sk:sentiment_binarysklep:hate-speechsklep:nlisklep:question-answeringsklep:rtesklep:sentiment-analysisskj1slovakcopaslovak_triviatoxic_sktruthfulqaanalogies_skslohryscioe_tests_mqae_tests_qaslovak_tests_mqaslovak_tests_qa
ALL|0SK_AVERAGE|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0SK|0
accinst​_level​_loose​_accinst​_level​_strict​_accprompt​_level​_loose​_accprompt​_level​_strict​_accqemqf1rouge1rouge2rougeLaccinst​_level​_loose​_accinst​_level​_strict​_accprompt​_level​_loose​_accprompt​_level​_strict​_accqemqf1rouge1rouge2rougeLaccaccaccaccaccinst​_level​_loose​_accinst​_level​_strict​_accprompt​_level​_loose​_accprompt​_level​_strict​_accaccaccaccqemqf1accaccaccaccqemqf1accaccaccaccaccaccaccaccaccaccaccqemqf1rouge1rouge2rougeLaccqemqf1rouge1rouge2rougeL
models/ministral-base0.00030.17310.17310.07820.07820.00390.00450.00070.00000.00070.00020.17310.17310.07820.07820.00390.00450.00070.00000.00070.24490.25370.00000.13740.08870.17310.17310.07820.07820.21080.22250.00000.00050.00530.00000.00000.00000.00000.00030.00700.00000.00000.59670.05330.19920.00000.06470.00000.00230.23850.15010.00230.00260.00030.00000.00030.17390.01260.00300.00120.00000.0011
models/ministral-last-1090640.26030.21310.21310.10240.10240.03820.07080.01490.00320.01490.24050.21310.21310.10240.10240.03820.07080.01490.00320.01490.24470.26670.14240.27100.18620.21310.21310.10240.10240.27580.24150.14890.07880.12350.38620.49360.21430.02400.05200.14280.31510.11390.00000.27000.27860.19980.31880.00060.01700.25000.18150.01200.00790.01480.00320.01480.26010.00980.00890.01490.00310.0149
+
+ + + \ No newline at end of file diff --git a/perun/requirements/lm-eval.txt b/perun/requirements/lm-eval.txt deleted file mode 100644 index c2abb81..0000000 --- a/perun/requirements/lm-eval.txt +++ /dev/null @@ -1,131 +0,0 @@ -# Python 3.11.16; CUDA 12.4. -absl-py==2.5.0 -accelerate==1.15.0 -aiohappyeyeballs==2.7.1 -aiohttp==3.14.3 -aiosignal==1.4.0 -annotated-doc==0.0.5 -annotated-types==0.8.0 -anyio==4.15.1 -attrs==26.1.0 -certifi==2026.7.22 -chardet==6.0.0.post1 -charset-normalizer==3.5.2 -click==8.5.0 -cloudpickle==3.1.2 -colorama==0.4.6 -contourpy==1.3.3 -cycler==0.12.1 -DataProperty==1.1.1 -datasets==5.0.1 -defusedxml==0.7.1 -dill==0.4.1 -evaluate==0.4.6 -filelock==3.32.3 -fonttools==4.66.1 -frozenlist==1.8.0 -fsspec==2026.6.0 -googleapis-common-protos==1.75.5 -h11==0.16.0 -hf-xet==1.6.0 -httpcore==1.0.9 -httpx==0.28.1 -huggingface_hub==0.36.2 -idna==3.20 -Jinja2==3.1.6 -joblib==1.6.0 -jsonlines==4.0.0 -kiwisolver==1.5.1 -lm_eval @ git+https://github.com/EleutherAI/lm-evaluation-harness.git@6d2abda4fd171e68a8789330c4149e37c1ca0bda -lxml==6.1.3 -markdown-it-py==4.2.0 -MarkupSafe==3.0.3 -matplotlib==3.11.2 -mbstrdecoder==1.1.5 -mdurl==0.1.2 -more-itertools==11.1.0 -mpmath==1.3.0 -multidict==6.9.1 -multiprocess==0.70.19 -narwhals==2.26.0 -networkx==3.6.1 -nltk==3.10.3 -numexpr==2.14.2 -numpy==2.4.6 -nvidia-cublas-cu12==12.4.5.8 -nvidia-cuda-cupti-cu12==12.4.127 -nvidia-cuda-nvrtc-cu12==12.4.127 -nvidia-cuda-runtime-cu12==12.4.127 -nvidia-cudnn-cu12==9.1.0.70 -nvidia-cufft-cu12==11.2.1.3 -nvidia-curand-cu12==10.3.5.147 -nvidia-cusolver-cu12==11.6.1.9 -nvidia-cusparse-cu12==12.3.1.170 -nvidia-cusparselt-cu12==0.6.2 -nvidia-nccl-cu12==2.21.5 -nvidia-nvjitlink-cu12==12.4.127 -nvidia-nvtx-cu12==12.4.127 -opentelemetry-api==1.45.0 -opentelemetry-exporter-http-transport==0.66b0 -opentelemetry-exporter-otlp-common==0.66b0 -opentelemetry-exporter-otlp-proto-common==1.45.0 -opentelemetry-exporter-otlp-proto-http==1.45.0 -opentelemetry-proto==1.45.0 -opentelemetry-sdk==1.45.0 -opentelemetry-semantic-conventions==0.66b0 -packaging==26.3 -pandas==3.0.6 -pathvalidate==3.3.1 -peft==0.14.0 -pillow==12.3.0 -pip==26.2.1 -platformdirs==4.12.2 -portalocker==4.4.0 -propcache==0.5.4 -protobuf==7.36.2 -psutil==7.2.2 -pyarrow==25.0.1 -pybind11==3.1.0 -pydantic==2.13.5 -pydantic_core==2.46.5 -Pygments==2.21.0 -pyparsing==3.3.3 -pytablewriter==1.2.1 -python-dateutil==2.9.0.post0 -pytz==2026.4 -PyYAML==6.0.3 -regex==2026.9.29 -requests==2.34.2 -rich==15.0.0 -rouge_score==0.1.2 -sacrebleu==2.6.0 -safetensors==0.8.0 -scikit-learn==1.9.1 -scipy==1.17.1 -sentencepiece==0.2.2 -setuptools==84.0.0 -shellingham==1.5.4 -six==1.17.0 -sqlitedict==2.1.0 -sympy==1.13.1 -tabledata==1.3.5 -tabulate==0.10.0 -tcolorpy==0.1.7 -threadpoolctl==3.7.0 -tokenizers==0.21.4 -torch==2.6.0+cu124 -tqdm==4.70.1 -tqdm-multiprocess==0.0.11 -transformers==4.48.2 -triton==3.2.0 -typepy==1.3.5 -typer==0.27.2 -typing-inspection==0.4.4 -typing_extensions==4.16.0 -urllib3==2.8.0 -wandb==0.30.0 -wheel==0.48.0 -word2number==1.1 -xxhash==4.0.1 -yarl==1.25.1 -zstandard==0.25.0 diff --git a/perun/requirements/skbench.txt b/perun/requirements/skbench.txt deleted file mode 100644 index ca43f5f..0000000 --- a/perun/requirements/skbench.txt +++ /dev/null @@ -1,309 +0,0 @@ -# Python 3.13.17; -absl-py==2.4.0 -accelerate==1.14.0 -aenum==3.1.15 -aioboto3==15.5.0 -aiobotocore==2.25.1 -aiofiles==25.1.0 -aiohappyeyeballs==2.7.1 -aiohttp==3.14.1 -aioitertools==0.13.0 -aiosignal==1.4.0 -annotated-doc==0.0.4 -annotated-types==0.7.0 -anthropic==0.116.0 -antlr4-python3-runtime==4.13.2 -anyio==4.14.1 -apache-tvm-ffi==0.1.11 -astor==0.8.1 -attrs==26.1.0 -beautifulsoup4==4.15.0 -blake3==1.0.9 -blis==1.3.3 -boto3==1.40.61 -botocore==1.40.61 -cachetools==7.1.4 -catalogue==2.0.10 -cbor2==6.1.2 -certifi==2026.6.17 -cffi==2.0.0 -cfgv==3.5.0 -chardet==6.0.0.post1 -charset-normalizer==3.4.7 -click==8.4.2 -cloudpathlib==0.24.0 -cloudpickle==3.1.2 -colorama==0.4.6 -colorlog==6.10.1 -compressed-tensors==0.17.0 -confection==1.3.3 -cryptography==49.0.0 -cuda-bindings==13.3.1 -cuda-core==1.0.1 -cuda-pathfinder==1.5.6 -cuda-python==13.3.1 -cuda-tile==1.5.0 -cuda-toolkit==13.0.3.0 -cymem==2.0.13 -dataproperty==1.1.1 -datasets==5.0.0 -debugpy==1.8.21 -depyf==0.20.0 -detect-installer==0.1.0 -dill==0.4.1 -diskcache==5.6.3 -distlib==0.4.3 -distro==1.9.0 -dnspython==2.8.0 -docstring-parser==0.18.0 -einops==0.8.2 -email-validator==2.3.0 -emoji==2.15.0 -fastapi==0.136.3 -fastapi-cli==0.0.27 -fastapi-cloud-cli==0.22.1 -fastar==0.11.0 -fastsafetensors==0.3.3 -fastuuid==0.14.0 -filelock==3.29.5 -flashinfer-python==0.6.16.post3 -frozendict==2.4.7 -frozenlist==1.8.0 -fsspec==2025.9.0 -gitdb==4.0.12 -gitpython==3.1.50 -googleapis-common-protos==1.75.0 -grpcio==1.81.1 -h11==0.16.0 -hf==1.29.0 -hf-xet==1.6.0 -httpcore==1.0.9 -httptools==0.8.0 -httpx==0.28.1 -httpx-sse==0.4.3 -huggingface-hub==1.29.0 -humming-kernels==0.1.12 -identify==2.6.19 -idna==3.18 -ijson==3.5.0 -immutabledict==4.3.1 -importlib-metadata==8.9.0 -inspect-ai==0.3.145 -interegular==0.3.3 -jieba==0.42.1 -jinja2==3.1.6 -jiter==0.16.0 -jmespath==1.1.0 -joblib==1.5.3 -jsonlines==4.0.0 -jsonpatch==1.33 -jsonpath-ng==1.8.0 -jsonpointer==3.1.1 -jsonref==1.1.0 -jsonschema==4.26.0 -jsonschema-specifications==2025.9.1 -langcodes==3.5.1 -langdetect==1.0.9 -lark==1.2.2 -latex2sympy2-extended==1.0.6 -lighteval @ git+https://github.com/huggingface/lighteval.git@10b9104eb16778b174e9e240140974a039018981 -linkify-it-py==2.1.0 -litellm==1.90.2 -llguidance==1.7.6 -llvmlite==0.47.0 -lm-format-enforcer==0.11.3 -loguru==0.7.3 -lxml==6.1.1 -markdown-it-py==4.2.0 -markupsafe==3.0.3 -mbstrdecoder==1.1.5 -mcp==1.28.1 -mdit-py-plugins==0.6.1 -mdurl==0.1.2 -mistral-common==1.11.7 -ml-dtypes==0.5.4 -mmh3==5.2.1 -model-hosting-container-standards==0.1.16 -more-itertools==11.1.0 -mpmath==1.3.0 -msgpack==1.2.1 -msgspec==0.21.1 -multidict==6.7.1 -multiprocess==0.70.19 -murmurhash==1.0.15 -narwhals==2.23.0 -natto-py==1.0.1 -nccl4py==0.4.1 -nest-asyncio==1.6.0 -networkx==3.6.1 -ninja==1.13.0 -nltk==3.9.1 -nodeenv==1.10.0 -numba==0.65.0 -numpy==2.4.6 -nvidia-cublas==13.1.1.3 -nvidia-cuda-cccl==13.3.3.4.1 -nvidia-cuda-crt==13.3.73 -nvidia-cuda-cupti==13.0.85 -nvidia-cuda-nvcc==13.2.78 -nvidia-cuda-nvdisasm==13.3.73 -nvidia-cuda-nvrtc==13.0.88 -nvidia-cuda-runtime==13.0.96 -nvidia-cudnn-cu13==9.20.0.48 -nvidia-cudnn-frontend==1.25.0 -nvidia-cufft==12.0.0.61 -nvidia-cufile==1.15.1.6 -nvidia-curand==10.4.0.35 -nvidia-cusolver==12.0.4.66 -nvidia-cusparse==12.6.3.3 -nvidia-cusparselt-cu13==0.8.1 -nvidia-cutlass-dsl==4.6.2 -nvidia-cutlass-dsl-libs-base==4.6.2 -nvidia-cutlass-dsl-libs-core==4.6.2 -nvidia-cutlass-dsl-libs-cu12==4.6.2 -nvidia-cutlass-dsl-libs-cu13==4.6.2 -nvidia-ml-py==13.610.43 -nvidia-nccl-cu13==2.29.7 -nvidia-nvjitlink==13.3.33 -nvidia-nvshmem-cu13==3.4.5 -nvidia-nvtx==13.0.85 -nvidia-nvvm==13.2.78 -nvtx==0.2.15 -openai==2.44.0 -openai-harmony==0.0.8 -opencv-python-headless==5.0.0.93 -opentelemetry-api==1.43.0 -opentelemetry-exporter-otlp==1.43.0 -opentelemetry-exporter-otlp-proto-common==1.43.0 -opentelemetry-exporter-otlp-proto-grpc==1.43.0 -opentelemetry-exporter-otlp-proto-http==1.43.0 -opentelemetry-proto==1.43.0 -opentelemetry-sdk==1.43.0 -opentelemetry-semantic-conventions==0.64b0 -opentelemetry-semantic-conventions-ai==0.5.1 -outlines-core==0.2.14 -packaging==26.2 -pandas==3.0.3 -partial-json-parser==0.2.1.1.post7 -pathlib-abc==0.5.2 -pathvalidate==3.3.1 -pillow==12.3.0 -pip==26.1.2 -platformdirs==4.10.0 -portalocker==3.2.0 -pre-commit==4.6.0 -preshed==3.0.13 -prometheus-client==0.25.0 -prometheus-fastapi-instrumentator==8.0.2 -propcache==0.5.2 -protobuf==6.33.6 -psutil==7.2.2 -py-cpuinfo==9.0.0 -pyarrow==24.0.0 -pybase64==1.4.3 -pycountry==26.2.16 -pycparser==3.0 -pydantic==2.13.4 -pydantic-core==2.46.4 -pydantic-extra-types==2.11.1 -pydantic-settings==2.14.2 -pygments==2.20.0 -pyjwt==2.13.0 -pynvvideocodec==2.0.4 -pytablewriter==1.2.1 -pythainlp==5.3.4 -python-crfsuite==0.9.12 -python-dateutil==2.9.0.post0 -python-discovery==1.4.2 -python-dotenv==1.2.2 -python-json-logger==4.1.0 -python-multipart==0.0.32 -pytz==2026.2 -pyvi==0.1.1 -pyyaml==6.0.3 -pyzmq==27.1.0 -quack-kernels==0.6.4 -ray==2.56.0 -referencing==0.37.0 -regex==2026.6.28 -requests==2.34.2 -rich==15.0.0 -rich-toolkit==0.20.1 -rignore==0.7.6 -rouge-score==0.1.2 -rpds-py==2026.6.3 -s3fs==2025.9.0 -s3transfer==0.14.0 -sacrebleu==2.6.0 -safetensors==0.8.0 -scikit-learn==1.9.0 -scipy==1.18.0 -semver==3.0.4 -sentencepiece==0.2.1 -sentry-sdk==2.64.0 -setproctitle==1.3.7 -setuptools==80.10.2 -shellingham==1.5.4 -shortuuid==1.0.13 -six==1.17.0 -sklearn-crfsuite==0.5.0 -smart-open==8.0.0 -smmap==5.0.3 -sniffio==1.3.1 -soupsieve==2.8.4 -spacy==3.8.14 -spacy-legacy==3.0.12 -spacy-loggers==1.0.5 -srsly==2.5.3 -sse-starlette==3.4.5 -stanza==1.13.0 -starlette==1.3.1 -sudachidict-core==20260428 -sudachipy==0.6.11 -supervisor==4.3.0 -sympy==1.14.0 -tabledata==1.3.5 -tabulate==0.10.0 -tcolorpy==0.1.7 -tenacity==9.1.4 -termcolor==2.3.0 -textual==8.2.8 -thinc==8.3.13 -threadpoolctl==3.6.0 -tiktoken==0.13.0 -tilelang==0.1.12 -tokenizers==0.22.2 -tokenspeed-mla==0.1.8 -tokenspeed-triton==3.7.10.post20260531 -torch==2.13.0 -torch-c-dlpack-ext==0.1.5 -torchaudio==2.11.0 -torchcodec==0.16.0 -torchvision==0.28.0 -tqdm==4.68.3 -transformers==5.12.1 -triton==3.7.1 -typepy==1.3.5 -typer==0.26.8 -typing-extensions==4.16.0 -typing-inspection==0.4.2 -uc-micro-py==2.0.0 -udapi==0.5.2 -udtools==0.2.8 -universal-pathlib==0.3.10 -urllib3==2.7.0 -uvicorn==0.49.0 -uvloop==0.22.1 -virtualenv==21.5.1 -vllm==0.28.0 -wandb==0.28.0 -wasabi==1.1.3 -watchfiles==1.2.0 -weasel==1.0.0 -websockets==16.0 -wrapt==1.17.3 -xgrammar==0.2.3 -xxhash==3.8.0 -yarl==1.24.2 -z3-solver==4.15.4.0 -zipp==4.1.0 diff --git a/perun/scripts/export_skbench_model.py b/perun/scripts/export_skbench_model.py new file mode 100644 index 0000000..85a1bea --- /dev/null +++ b/perun/scripts/export_skbench_model.py @@ -0,0 +1,61 @@ +import hashlib +import json +import os +from pathlib import Path +import shutil +import sys + +import torch +from peft import PeftModel +from safetensors import safe_open +from transformers import AutoModelForCausalLM + +base, checkpoint, target = (Path(x) for x in sys.argv[1:]) +base_revision = '2c95908929198d6e69af8638f0dbbd9bc6b93f9e' +pinned = Path.home()/'.cache/huggingface/hub/models--ministral--Ministral-3b-instruct/snapshots'/base_revision +stage = target.with_name(target.name + '_staging_' + os.environ['SLURM_JOB_ID']) + +def digest(path): + with path.open('rb') as handle: + return hashlib.file_digest(handle, 'sha256').hexdigest() + +if target.exists(): + raise SystemExit('Merged output already exists') +index = 'model.safetensors.index.json' +metadata = ('config.json','generation_config.json','special_tokens_map.json','tokenizer_config.json','tokenizer.json') +original_keys = json.loads((base/index).read_text())['weight_map'] +for name in (*metadata, index, *sorted(set(original_keys.values()))): + if digest(base/name) != digest(pinned/name): + raise RuntimeError(f'Baseline file differs from the pinned training snapshot: {name}') +print('Baseline model matches the pinned training snapshot', base_revision, flush=True) + +torch.set_num_threads(8) +model = AutoModelForCausalLM.from_pretrained(str(base), dtype=torch.float32, local_files_only=True, trust_remote_code=False) +lora = PeftModel.from_pretrained(model, str(checkpoint), is_trainable=False) +with torch.no_grad(): + merged = lora.merge_and_unload(safe_merge=True).to(torch.bfloat16) +stage.mkdir() +merged.save_pretrained(str(stage), safe_serialization=True, max_shard_size='3GB') +for name in metadata: + shutil.copy2(base/name, stage/name) + +saved_keys = json.loads((stage/index).read_text())['weight_map'] +if set(saved_keys) != set(original_keys) or any('lora_' in key for key in saved_keys): + raise RuntimeError('Unexpected merged parameter set') +for key, expected_to_change in ( + ('model.layers.0.self_attn.q_proj.weight', True), + ('model.embed_tokens.weight', False), +): + with safe_open(str(base/original_keys[key]), framework='pt', device='cpu') as left: + with safe_open(str(stage/saved_keys[key]), framework='pt', device='cpu') as right: + original, merged_tensor = left.get_tensor(key), right.get_tensor(key) + changed = not torch.equal(original, merged_tensor) + if merged_tensor.dtype != torch.bfloat16 or changed != expected_to_change: + raise RuntimeError(f'Unexpected merged tensor: {key}') +(stage/'merge_provenance.json').write_text(json.dumps({ + 'checkpoint': str(checkpoint), 'adapter_sha256': digest(checkpoint/'adapter_model.safetensors'), + 'base_revision': base_revision, 'output_dtype': 'bfloat16', + 'tokenizer_and_config': {name: digest(stage/name) for name in metadata}, +}, indent=2) + chr(10)) +stage.rename(target) +print('MERGED_MODEL_READY', target, flush=True) diff --git a/perun/scripts/train.py b/perun/scripts/train.py index 3f2a6ed..37064b2 100644 --- a/perun/scripts/train.py +++ b/perun/scripts/train.py @@ -251,6 +251,8 @@ def main() -> int: parser.add_argument("--pilot-steps", type=int, help="Limit a pilot to this many optimizer steps; requires --run-dir.") parser.add_argument("--pilot-samples", type=int, help="Maximum rows per dataset in a pilot (default: 2048 or all eval rows, whichever is larger).") + parser.add_argument("--pilot-eval-steps", type=int, + help="Evaluate and save at this interval in a pilot (default: at the last step).") parser.add_argument("--dry-run", action="store_true", help="Validate data/config; do not import GPU libraries.") parser.add_argument("--worker", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() @@ -263,6 +265,8 @@ def main() -> int: parser.error("--pilot-steps must be positive and requires --run-dir.") if args.pilot_samples is not None and args.pilot_samples < 1: parser.error("--pilot-samples must be positive.") + if args.pilot_eval_steps is not None and not 1 <= args.pilot_eval_steps <= (args.pilot_steps or 0): + parser.error("--pilot-eval-steps requires --pilot-steps and must be between 1 and that limit.") config = read_config(args.config.resolve()) if config.get("stage") != "sft" or config.get("finetuning_type") != "lora": raise ValueError("Expected an SFT / LoRA training configuration.") @@ -283,9 +287,10 @@ def main() -> int: sample_limit = args.pilot_samples or max(2048, eval_rows) if sample_limit < eval_rows: parser.error(f"--pilot-samples must be at least {eval_rows} to retain the full eval split.") + eval_interval = args.pilot_eval_steps or args.pilot_steps config.update(max_steps=args.pilot_steps, max_samples=sample_limit, eval_strategy="steps", save_strategy="steps", - eval_steps=args.pilot_steps, save_steps=args.pilot_steps) + eval_steps=eval_interval, save_steps=eval_interval) if args.run_dir: config["output_dir"] = str(args.run_dir.resolve() / "adapter") run_dir = Path(config["output_dir"]).parent @@ -305,7 +310,8 @@ def main() -> int: "train_rows": train_rows, "eval_rows": eval_rows, "adapter_output": config["output_dir"]} if args.pilot_steps is not None: - summary.update(pilot_steps=args.pilot_steps, pilot_train_rows=min(train_rows, config["max_samples"])) + summary.update(pilot_steps=args.pilot_steps, pilot_train_rows=min(train_rows, config["max_samples"]), + pilot_eval_steps=config["eval_steps"]) print(json.dumps(summary, ensure_ascii=False, indent=2)) if args.dry_run: print("Configuration/data checks passed. No training started.")