meepsquared 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meepsquared-0.1.0/.github/ISSUE_TEMPLATE/benchmark-result.md +22 -0
- meepsquared-0.1.0/.github/workflows/ci.yml +20 -0
- meepsquared-0.1.0/.github/workflows/publish.yml +26 -0
- meepsquared-0.1.0/.gitignore +10 -0
- meepsquared-0.1.0/Containerfile +27 -0
- meepsquared-0.1.0/LICENSE +201 -0
- meepsquared-0.1.0/NOTICE +20 -0
- meepsquared-0.1.0/PKG-INFO +273 -0
- meepsquared-0.1.0/README.md +246 -0
- meepsquared-0.1.0/assets/epoch-dark.png +0 -0
- meepsquared-0.1.0/assets/epoch-light.png +0 -0
- meepsquared-0.1.0/assets/epoch-loss-dark.png +0 -0
- meepsquared-0.1.0/assets/epoch-loss-light.png +0 -0
- meepsquared-0.1.0/assets/meepsquared-logo-dark.svg +1 -0
- meepsquared-0.1.0/assets/meepsquared-logo-light.svg +1 -0
- meepsquared-0.1.0/assets/meepsquared.svg +1 -0
- meepsquared-0.1.0/benchmarks/README.md +105 -0
- meepsquared-0.1.0/benchmarks/automodel/README.md +26 -0
- meepsquared-0.1.0/benchmarks/automodel/llama3_1_8b_no_robots_lora_spark.yaml +75 -0
- meepsquared-0.1.0/benchmarks/automodel/llama3_1_8b_no_robots_lora_spark_checkpointed.yaml +75 -0
- meepsquared-0.1.0/benchmarks/automodel/llama3_1_8b_no_robots_lora_spark_compile.yaml +84 -0
- meepsquared-0.1.0/benchmarks/common.py +326 -0
- meepsquared-0.1.0/benchmarks/grpo/grpo.py +123 -0
- meepsquared-0.1.0/benchmarks/grpo/zebra_dataset.py +375 -0
- meepsquared-0.1.0/benchmarks/grpo/zebra_task.py +190 -0
- meepsquared-0.1.0/benchmarks/grpo.ipynb +188 -0
- meepsquared-0.1.0/benchmarks/kernels/grouped_lora.py +116 -0
- meepsquared-0.1.0/benchmarks/make_readme_chart.py +226 -0
- meepsquared-0.1.0/benchmarks/moe.ipynb +189 -0
- meepsquared-0.1.0/benchmarks/moe.py +106 -0
- meepsquared-0.1.0/benchmarks/multi_gpu.ipynb +195 -0
- meepsquared-0.1.0/benchmarks/multi_gpu.py +77 -0
- meepsquared-0.1.0/benchmarks/plan.py +45 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/README.md +111 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/as-run/common.py +326 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/as-run/long-run-stream.ipynb +55 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/as-run/long-run.ipynb +1 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/as-run/moe.py +106 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/as-run/plan.py +45 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/as-run/run_env.py +16 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/as-run/sft.py +250 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/epoch-shared-evaluation.jsonl +6 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/epoch-smoke.jsonl +2 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/epoch.jsonl +6 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/moe-gb10.jsonl +6 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/number-formats-llama.jsonl +6 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/parity-eval-only.jsonl +6 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/parity.jsonl +15 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/plans/gemma-epoch.json +416 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/plans/gemma-lora-3090.json +328 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/plans/gemma-lora.json +328 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/plans/gemma-parity.json +416 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/plans/gemma-qlora.json +328 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/plans/llama-lora-3090.json +328 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/plans/llama-lora.json +328 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/plans/llama-parity.json +416 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/plans/llama-qlora.json +328 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/plans/qwen-epoch.json +416 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/plans/qwen-lora.json +328 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/plans/qwen-parity.json +416 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/qlora-gb10.jsonl +24 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/epoch_gb10.log +92 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/epoch_gb10.py +121 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/eval_renamed_gb10.log +7 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/eval_renamed_gb10.py +110 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/ext_gb10.log +158 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/ext_gb10.py +81 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/gb10_queue.log +1 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/gb10_queue.sh +9 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/gb10_queue2.sh +7 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/job_status.py +19 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/parity_gb10.log +105 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/parity_gb10.py +112 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/q3090_queue.log +1 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/q3090_queue.sh +6 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/ref_3090.log +53 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/ref_3090.py +85 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/ref_3090_redo.py +88 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/ref_gb10.log +114 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/ref_gb10c.py +78 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/round2_gb10.log +123 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/queue/round2_gb10c.py +77 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/speed-3090.jsonl +27 -0
- meepsquared-0.1.0/benchmarks/results/2026-10/speed-gb10.jsonl +54 -0
- meepsquared-0.1.0/benchmarks/results/automodel.jsonl +3 -0
- meepsquared-0.1.0/benchmarks/results/grpo.jsonl +4 -0
- meepsquared-0.1.0/benchmarks/results/moe.jsonl +14 -0
- meepsquared-0.1.0/benchmarks/results/multi_gpu.jsonl +6 -0
- meepsquared-0.1.0/benchmarks/results/recorded.jsonl +25 -0
- meepsquared-0.1.0/benchmarks/results/sft.jsonl +82 -0
- meepsquared-0.1.0/benchmarks/run_env.py +16 -0
- meepsquared-0.1.0/benchmarks/sft.ipynb +249 -0
- meepsquared-0.1.0/benchmarks/sft.py +250 -0
- meepsquared-0.1.0/design/api.md +145 -0
- meepsquared-0.1.0/design/grouped-lora-kernel.md +200 -0
- meepsquared-0.1.0/docs/antora.yml +6 -0
- meepsquared-0.1.0/docs/modules/ROOT/nav.adoc +17 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/api.adoc +221 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/benchmarks.adoc +168 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/compatibility.adoc +45 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/from-unsloth.adoc +103 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/get-started.adoc +115 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/grpo.adoc +135 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/index.adoc +57 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/licensing.adoc +46 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/mixture-of-experts.adoc +94 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/saving.adoc +64 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/several-gpus.adoc +102 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/sft.adoc +155 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/trainer-settings.adoc +73 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/training-job.adoc +441 -0
- meepsquared-0.1.0/docs/modules/ROOT/pages/unified-memory.adoc +46 -0
- meepsquared-0.1.0/notebooks/01-lora-sft.ipynb +966 -0
- meepsquared-0.1.0/notebooks/02-fp8-weights.ipynb +966 -0
- meepsquared-0.1.0/notebooks/03-moe-experts.ipynb +933 -0
- meepsquared-0.1.0/notebooks/04-grpo-zebra.ipynb +740 -0
- meepsquared-0.1.0/notebooks/05-several-gpus.ipynb +305 -0
- meepsquared-0.1.0/notebooks/sft_several_gpus.py +96 -0
- meepsquared-0.1.0/pyproject.toml +48 -0
- meepsquared-0.1.0/src/meepsquared/__init__.py +39 -0
- meepsquared-0.1.0/src/meepsquared/__main__.py +10 -0
- meepsquared-0.1.0/src/meepsquared/_allocator.py +91 -0
- meepsquared-0.1.0/src/meepsquared/_banner.py +51 -0
- meepsquared-0.1.0/src/meepsquared/_batches.py +364 -0
- meepsquared-0.1.0/src/meepsquared/_checkpointing.py +605 -0
- meepsquared-0.1.0/src/meepsquared/_compile.py +165 -0
- meepsquared-0.1.0/src/meepsquared/_data_checks.py +211 -0
- meepsquared-0.1.0/src/meepsquared/_fp8.py +289 -0
- meepsquared-0.1.0/src/meepsquared/_fused.py +243 -0
- meepsquared-0.1.0/src/meepsquared/_geglu_tanh.py +93 -0
- meepsquared-0.1.0/src/meepsquared/_grouped.py +444 -0
- meepsquared-0.1.0/src/meepsquared/_liger.py +61 -0
- meepsquared-0.1.0/src/meepsquared/_linear_attention.py +66 -0
- meepsquared-0.1.0/src/meepsquared/_moe.py +228 -0
- meepsquared-0.1.0/src/meepsquared/_save.py +278 -0
- meepsquared-0.1.0/src/meepsquared/_selective.py +238 -0
- meepsquared-0.1.0/src/meepsquared/_sessions.py +453 -0
- meepsquared-0.1.0/src/meepsquared/_split.py +83 -0
- meepsquared-0.1.0/src/meepsquared/_trl.py +305 -0
- meepsquared-0.1.0/src/meepsquared/_unified_memory.py +112 -0
- meepsquared-0.1.0/src/meepsquared/_varlen.py +241 -0
- meepsquared-0.1.0/src/meepsquared/_vendor/__init__.py +0 -0
- meepsquared-0.1.0/src/meepsquared/_vendor/axolotl/LICENSE +202 -0
- meepsquared-0.1.0/src/meepsquared/_vendor/axolotl/README.md +19 -0
- meepsquared-0.1.0/src/meepsquared/_vendor/axolotl/__init__.py +0 -0
- meepsquared-0.1.0/src/meepsquared/_vendor/axolotl/geglu.py +176 -0
- meepsquared-0.1.0/src/meepsquared/_vendor/axolotl/lora.py +2450 -0
- meepsquared-0.1.0/src/meepsquared/_vendor/axolotl/op_registry.py +66 -0
- meepsquared-0.1.0/src/meepsquared/_vendor/axolotl/quantize.py +263 -0
- meepsquared-0.1.0/src/meepsquared/_vendor/axolotl/swiglu.py +179 -0
- meepsquared-0.1.0/src/meepsquared/_vendor/axolotl/utils.py +12 -0
- meepsquared-0.1.0/src/meepsquared/_vllm.py +152 -0
- meepsquared-0.1.0/src/meepsquared/chat_templates.py +224 -0
- meepsquared-0.1.0/src/meepsquared/cli.py +93 -0
- meepsquared-0.1.0/src/meepsquared/job.py +1933 -0
- meepsquared-0.1.0/src/meepsquared/models.py +510 -0
- meepsquared-0.1.0/src/meepsquared/multi_gpu.py +35 -0
- meepsquared-0.1.0/tests/conftest.py +69 -0
- meepsquared-0.1.0/tests/ddp/resume_job_two_processes.py +87 -0
- meepsquared-0.1.0/tests/ddp/train_job_two_processes.py +57 -0
- meepsquared-0.1.0/tests/ddp/train_two_processes.py +37 -0
- meepsquared-0.1.0/tests/test_allocator.py +45 -0
- meepsquared-0.1.0/tests/test_banner.py +29 -0
- meepsquared-0.1.0/tests/test_chat_templates.py +91 -0
- meepsquared-0.1.0/tests/test_checkpointing.py +113 -0
- meepsquared-0.1.0/tests/test_compile.py +54 -0
- meepsquared-0.1.0/tests/test_data_checks.py +179 -0
- meepsquared-0.1.0/tests/test_ddp.py +52 -0
- meepsquared-0.1.0/tests/test_evaluation.py +206 -0
- meepsquared-0.1.0/tests/test_fp8.py +142 -0
- meepsquared-0.1.0/tests/test_fused.py +131 -0
- meepsquared-0.1.0/tests/test_geglu_tanh.py +29 -0
- meepsquared-0.1.0/tests/test_grouped.py +173 -0
- meepsquared-0.1.0/tests/test_job.py +856 -0
- meepsquared-0.1.0/tests/test_liger_budget.py +59 -0
- meepsquared-0.1.0/tests/test_linear_attention.py +101 -0
- meepsquared-0.1.0/tests/test_linear_attention_available.py +18 -0
- meepsquared-0.1.0/tests/test_models.py +185 -0
- meepsquared-0.1.0/tests/test_moe.py +147 -0
- meepsquared-0.1.0/tests/test_multi_gpu.py +18 -0
- meepsquared-0.1.0/tests/test_plan_order.py +24 -0
- meepsquared-0.1.0/tests/test_qlora.py +24 -0
- meepsquared-0.1.0/tests/test_save.py +171 -0
- meepsquared-0.1.0/tests/test_selective.py +340 -0
- meepsquared-0.1.0/tests/test_sessions.py +318 -0
- meepsquared-0.1.0/tests/test_split.py +38 -0
- meepsquared-0.1.0/tests/test_step_check.py +91 -0
- meepsquared-0.1.0/tests/test_trl_patch.py +215 -0
- meepsquared-0.1.0/tests/test_unified_memory.py +32 -0
- meepsquared-0.1.0/tests/test_unified_memory_cap.py +24 -0
- meepsquared-0.1.0/tests/test_varlen.py +277 -0
- meepsquared-0.1.0/tests/test_vllm.py +88 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Benchmark result
|
|
3
|
+
about: A number you measured that differs from ours, or a comparison you think is unfair
|
|
4
|
+
title: "Benchmark: "
|
|
5
|
+
labels: benchmark
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
**GPU and driver:**
|
|
9
|
+
|
|
10
|
+
**Versions** (PyTorch, Transformers, TRL, PEFT, and Unsloth or Meep²):
|
|
11
|
+
|
|
12
|
+
**Command you ran:**
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**The result line it wrote** (from `benchmarks/results/*.jsonl`):
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**What differs from the published table, or what you think is unfair:**
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-24.04
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
# The CPU build of the torch the package is tested with: the tests run a tiny
|
|
17
|
+
# model, and the package does not depend on torch (the environment provides it)
|
|
18
|
+
- run: pip install torch==2.11.0 --index-url https://download.pytorch.org/whl/cpu
|
|
19
|
+
- run: pip install -e ".[test]"
|
|
20
|
+
- run: pytest -q
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# A tag vMAJOR.MINOR.PATCH publishes that version. PyPI accepts the upload by
|
|
4
|
+
# Trusted Publishing: the project names this repository, this workflow and the
|
|
5
|
+
# environment pypi as its publisher, so no token is stored anywhere.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags: ["v*.*.*"]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-24.04
|
|
13
|
+
environment: pypi
|
|
14
|
+
permissions:
|
|
15
|
+
id-token: write
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- name: The tag and the package version are the same
|
|
22
|
+
run: |
|
|
23
|
+
version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
24
|
+
test "v$version" = "$GITHUB_REF_NAME" || { echo "tag $GITHUB_REF_NAME, package version $version"; exit 1; }
|
|
25
|
+
- run: pip install build==1.3.0 && python -m build
|
|
26
|
+
- uses: pypa/gh-action-pypi-publish@v1.12.4
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Copyright Alejandro Martínez Corriá and the Thinkube contributors
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
# meepsquared on NVIDIA's PyTorch container, for the DGX Spark (GB10) and any NVIDIA GPU.
|
|
5
|
+
#
|
|
6
|
+
# docker build -t meepsquared -f Containerfile .
|
|
7
|
+
# docker run --rm -it --gpus all -e HF_TOKEN -v ~/.cache/huggingface:/root/.cache/huggingface \
|
|
8
|
+
# -v $PWD:/work -w /work meepsquared meepsquared train job.yaml
|
|
9
|
+
#
|
|
10
|
+
# podman works the same.
|
|
11
|
+
#
|
|
12
|
+
# The container's PyTorch, FlashAttention and Triton stay as NVIDIA built them
|
|
13
|
+
# for the GPU; pip adds meepsquared at the versions it is tested with: Transformers, PEFT,
|
|
14
|
+
# TRL, Liger Kernel, bitsandbytes, and the linear-attention kernels of Qwen 3.5 and 3.6.
|
|
15
|
+
#
|
|
16
|
+
# PYTORCH_TAG picks the NGC release (26.03 ships PyTorch 2.11). MEEPSQUARED is the pip
|
|
17
|
+
# requirement: a release from PyPI by default, or a commit of the repository,
|
|
18
|
+
# for example "meepsquared @ git+https://github.com/thinkube/meepsquared@<commit>".
|
|
19
|
+
ARG PYTORCH_TAG=26.03-py3
|
|
20
|
+
FROM nvcr.io/nvidia/pytorch:${PYTORCH_TAG}
|
|
21
|
+
ARG MEEPSQUARED=meepsquared
|
|
22
|
+
RUN pip install --no-cache-dir --break-system-packages "${MEEPSQUARED}"
|
|
23
|
+
# VLLM: the URL of a vLLM wheel built for this container's PyTorch (tk-vllm-wheels, release v<vllm>-nv<container>,
|
|
24
|
+
# v0.22.1-nv26.03 for this tag), for GRPO with vLLM inside the trainer; empty leaves vLLM out. vLLM's dependency
|
|
25
|
+
# xgrammar pins Transformers below 5, so Transformers is raised back after it.
|
|
26
|
+
ARG VLLM=
|
|
27
|
+
RUN if [ -n "${VLLM}" ]; then pip install --no-cache-dir --break-system-packages "${VLLM}" && pip install --no-cache-dir --break-system-packages "transformers>=5.17"; fi
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
meepsquared-0.1.0/NOTICE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Meep² (meepsquared)
|
|
2
|
+
Copyright Alejandro Martínez Corriá and the Thinkube contributors
|
|
3
|
+
|
|
4
|
+
This product is licensed under the Apache License 2.0 (see LICENSE).
|
|
5
|
+
|
|
6
|
+
It reproduces the public API of Unsloth (names and arguments of
|
|
7
|
+
FastLanguageModel and FastModel) so that code written for Unsloth runs with
|
|
8
|
+
it. It contains no code from Unsloth. Unsloth is a trademark of its owners;
|
|
9
|
+
meepsquared is not affiliated with or endorsed by them.
|
|
10
|
+
|
|
11
|
+
It includes the LoRA kernels of Axolotl (https://github.com/axolotl-ai-cloud/axolotl),
|
|
12
|
+
v0.21.0, Apache License 2.0, in src/meepsquared/_vendor/axolotl/, with the changes
|
|
13
|
+
listed in the README there. Axolotl's kernel module credits Unsloth for the
|
|
14
|
+
inspiration of these kernels.
|
|
15
|
+
|
|
16
|
+
At run time it uses, without copying their code:
|
|
17
|
+
- Transformers, PEFT, TRL and Accelerate (Hugging Face, Apache License 2.0)
|
|
18
|
+
- Liger Kernel (LinkedIn, BSD 2-Clause License)
|
|
19
|
+
- bitsandbytes (MIT License), for QLoRA
|
|
20
|
+
- FlashAttention (BSD 3-Clause License), when installed
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meepsquared
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fast LoRA and QLoRA fine-tuning with permissively licensed components
|
|
5
|
+
Project-URL: Repository, https://github.com/thinkube/meepsquared
|
|
6
|
+
Author: Alejandro Martínez Corriá and the Thinkube contributors
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
License-File: NOTICE
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
12
|
+
Requires-Python: >=3.12
|
|
13
|
+
Requires-Dist: accelerate==1.15.0
|
|
14
|
+
Requires-Dist: bitsandbytes==0.50.2
|
|
15
|
+
Requires-Dist: datasets==4.8.4
|
|
16
|
+
Requires-Dist: fla-core==0.6.0
|
|
17
|
+
Requires-Dist: flash-linear-attention==0.6.0
|
|
18
|
+
Requires-Dist: liger-kernel==0.8.5
|
|
19
|
+
Requires-Dist: peft==0.21.2
|
|
20
|
+
Requires-Dist: pyyaml==6.0.3
|
|
21
|
+
Requires-Dist: requests==2.32.5
|
|
22
|
+
Requires-Dist: transformers==5.20.2
|
|
23
|
+
Requires-Dist: trl==1.16.0
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest>=8; extra == 'test'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
<h1>
|
|
29
|
+
<picture>
|
|
30
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/thinkube/meepsquared/main/assets/meepsquared-logo-dark.svg">
|
|
31
|
+
<img alt="Meep²" src="https://raw.githubusercontent.com/thinkube/meepsquared/main/assets/meepsquared-logo-light.svg" width="239" height="80">
|
|
32
|
+
</picture>
|
|
33
|
+
</h1>
|
|
34
|
+
|
|
35
|
+
**Fast LoRA and QLoRA fine-tuning on Hugging Face Transformers, PEFT and TRL, under permissive licenses.**
|
|
36
|
+
|
|
37
|
+
[Documentation](https://thinkube.org/meepsquared/) · [Benchmarks](https://thinkube.org/meepsquared/benchmarks.html) · [Licensing](https://thinkube.org/meepsquared/licensing.html) · [Notebooks](notebooks/)
|
|
38
|
+
|
|
39
|
+
## What Meep² is
|
|
40
|
+
|
|
41
|
+
Meep² is an easy-to-use LoRA and QLoRA fine-tuning engine for Hugging Face models: you name the model, the data and the method, and it decides the rest. It measures your model, your data and your GPU, and prints a plan with the reason for each choice. It trains with TRL's own trainers and reaches the same loss as TRL, 2.2 to 2.9 times faster than Unsloth over a full epoch on this year's architectures, with the parameters it sets automatically. It runs on one GPU or several in one machine, including GPUs that share memory with the CPU, such as the DGX Spark.
|
|
42
|
+
|
|
43
|
+
<picture>
|
|
44
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/epoch-dark.png">
|
|
45
|
+
<img alt="Minutes to train one epoch of LoRA on one DGX Spark. Gemma 4 E4B: TRL 141, Unsloth at its fastest 54, Meep² 25. Qwen 3.5 9B: TRL 138, Unsloth 101, Meep² 36. The held-out loss of every adapter, measured by the same evaluation, is within 0.003." src="assets/epoch-light.png">
|
|
46
|
+
</picture>
|
|
47
|
+
|
|
48
|
+
<picture>
|
|
49
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/epoch-loss-dark.png">
|
|
50
|
+
<img alt="Held-out loss over one epoch against minutes of training, for Gemma 4 E4B and Qwen 3.5 9B. Meep² reaches the loss TRL reaches, in a quarter of the time or less; Unsloth's adapter at the end of its epoch is at the same loss." src="assets/epoch-loss-light.png">
|
|
51
|
+
</picture>
|
|
52
|
+
|
|
53
|
+
Every number on this page comes from a script in [`benchmarks/`](benchmarks/), on public models and data, and every run is recorded in [`benchmarks/results/`](benchmarks/results/). If you measure something different, open a [benchmark result](https://github.com/thinkube/meepsquared/issues/new?template=benchmark-result.md) issue.
|
|
54
|
+
|
|
55
|
+
## Install
|
|
56
|
+
|
|
57
|
+
You need:
|
|
58
|
+
|
|
59
|
+
- an NVIDIA GPU of the Ampere generation or later: RTX 30 series or newer, A100, H100, or the DGX Spark. Meep² is tested on the DGX Spark's GB10 and on the RTX 3090; older GPUs, such as the GTX 10 and RTX 20 series, are not supported;
|
|
60
|
+
- Python 3.12;
|
|
61
|
+
- PyTorch 2.11 with CUDA, installed before Meep².
|
|
62
|
+
|
|
63
|
+
**With pip**, on Linux, x86-64 or ARM (the DGX Spark):
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install torch==2.11.0 --index-url https://download.pytorch.org/whl/cu130
|
|
67
|
+
pip install meepsquared
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Meep² installs the versions it is tested with: Transformers 5.20.2, TRL 1.16.0, PEFT 0.21.2 and the rest. FlashAttention is optional: without it, Meep² uses PyTorch's own attention, which is slower on Llama-style models.
|
|
71
|
+
|
|
72
|
+
**With the container**, which has PyTorch 2.11, FlashAttention and Triton built for your GPU by NVIDIA. You need Docker or Podman (replace `docker` with `podman`) and the NVIDIA Container Toolkit, which the DGX Spark already has:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
git clone https://github.com/thinkube/meepsquared
|
|
76
|
+
docker build -t meepsquared -f meepsquared/Containerfile meepsquared
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Then run a training job from the folder that holds it:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
docker run --rm -it --gpus all -e HF_TOKEN \
|
|
83
|
+
-v ~/.cache/huggingface:/root/.cache/huggingface -v "$PWD":/work -w /work \
|
|
84
|
+
meepsquared meepsquared train job.yaml # or: meepsquared python train.py
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`HF_TOKEN` is your Hugging Face token, needed for gated models such as Llama; the cache mount keeps downloaded models between runs; the current folder is the container's `/work`, so the trained adapter appears in it. No prebuilt image is published, because NVIDIA's container comes under NVIDIA's license terms.
|
|
88
|
+
|
|
89
|
+
For GRPO with vLLM inside the trainer, vLLM and PyTorch must be the same pair; the [GRPO page](https://thinkube.org/meepsquared/grpo.html#_installing_vllm) says which vLLM goes with which PyTorch, including the wheels built for NVIDIA's container and the DGX Spark.
|
|
90
|
+
|
|
91
|
+
## Train
|
|
92
|
+
|
|
93
|
+
You describe the training job: the model, the data, and the method. Meep² decides how to train it. Save this as `train.py` and run `python train.py`, or with the container as above:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import meepsquared as m2
|
|
97
|
+
|
|
98
|
+
job = m2.TrainingJob(
|
|
99
|
+
model="Qwen/Qwen3.5-9B",
|
|
100
|
+
data="HuggingFaceH4/no_robots", # a Hugging Face dataset id, a .jsonl, .json or .parquet file, or a datasets.Dataset
|
|
101
|
+
method="lora", # "lora", "qlora" (4-bit weights) or "fp8" (8-bit weights)
|
|
102
|
+
steps=1000, # or epochs=1
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
print(job.plan()) # what meepsquared will do on this machine, and why; nothing is trained yet
|
|
106
|
+
result = job.train() # prints the plan, then trains with TRL
|
|
107
|
+
result.save("out") # the adapter; result.save("out", merged=True) for a 16-bit model
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This is auto mode. Meep² measures the model, the batch and the machine, and decides how to train on it: how the model loads, how the batch is packed, which kernels run, how many layers are recomputed to save memory, the batch size and the learning rate. The plan prints each decision with its reason. You can change any of them with `m2.Recipe` (the training choices) or `m2.Overrides` (the hardware choices); the plan then shows your choice beside Meep²'s.
|
|
111
|
+
|
|
112
|
+
The same job can be written as YAML and run from the command line:
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
115
|
+
# job.yaml
|
|
116
|
+
model: Qwen/Qwen3.5-9B
|
|
117
|
+
data: HuggingFaceH4/no_robots
|
|
118
|
+
method: lora
|
|
119
|
+
steps: 1000
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
meepsquared train job.yaml --plan-only # print the plan and stop
|
|
124
|
+
meepsquared train job.yaml # print the plan, train, and save the adapter
|
|
125
|
+
meepsquared train job.yaml --status-file status.json --stop-file STOP [--resume]
|
|
126
|
+
# train in sessions: stop with a checkpoint when STOP exists
|
|
127
|
+
# (exit code 75), and resume exactly where it stopped
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
On a GPU the plan times a few steps and states how long the training will take.
|
|
131
|
+
|
|
132
|
+
Underneath, Meep² trains with TRL's own trainers. `job.trainer()` gives you TRL's `SFTTrainer` or `GRPOTrainer`, set up as `job.train()` would run it, when you want TRL's full control. The [training jobs page](https://thinkube.org/meepsquared/training-job.html) lists every field and every default, with its source.
|
|
133
|
+
|
|
134
|
+
A training job also puts LoRA on the experts of a mixture of experts (`Recipe(experts=True)`), trains GRPO (`task="grpo"`, with your reward functions in `rewards=[...]` and 8 completions per prompt unless `Recipe(generations=...)` says otherwise), with vLLM generating inside the trainer if you want (`Overrides(vllm=True)`), and trains with DDP on several GPUs under `torchrun`. FSDP2 and a model spread over several GPUs use Meep²'s lower-level calls, which the [documentation](https://thinkube.org/meepsquared/) shows. Code written for Unsloth also runs on those calls, after a change of its import lines ([what differs](https://thinkube.org/meepsquared/from-unsloth.html)).
|
|
135
|
+
|
|
136
|
+
## Numbers
|
|
137
|
+
|
|
138
|
+
One epoch of LoRA on one NVIDIA GB10 (DGX Spark): no_robots' 9,100 training conversations, 1,137 steps of 8, then the held-out loss of every adapter on 400 other conversations, all measured by the same evaluation (TRL's).
|
|
139
|
+
|
|
140
|
+
| Model | Engine | Minutes | Peak GPU memory, GiB | Held-out loss |
|
|
141
|
+
|---|---|---|---|---|
|
|
142
|
+
| Gemma 4 E4B | TRL, default settings | 141.2 | 19.3 | 1.9303 |
|
|
143
|
+
| | Unsloth, fastest | 54.4 | 54.7 | 1.9326 |
|
|
144
|
+
| | **Meep²** | **24.7** | 32.3 | **1.9295** |
|
|
145
|
+
| Qwen 3.5 9B | TRL, default settings | 137.8 | 21.6 | 1.7815 |
|
|
146
|
+
| | Unsloth, fastest | 101.4 | 90.7 | 1.7811 |
|
|
147
|
+
| | **Meep²** | **35.5** | 39.5 | 1.7813 |
|
|
148
|
+
|
|
149
|
+
- **Gemma 4 and Qwen 3.5**, this year's architectures: over a full epoch, Meep² is 2.2 and 2.9 times faster than Unsloth at its fastest setting, and 5.7 and 3.9 times faster than TRL at its defaults, with the same held-out loss. Gemma 4 E4B has 35 sliding-window layers and 7 global layers with attention heads of 512, wider than FlashAttention computes; Qwen 3.5 9B computes 24 of its 32 layers with linear attention. Meep² packs their batches without padding through all of these layers and fuses their LoRA.
|
|
150
|
+
- **Llama 3.1**, the established architecture most benchmarks use: Meep² is slightly slower than Unsloth at its fastest, 1,633 against 1,662 tokens/s (three runs of 30 steps), and reaches the same held-out loss as TRL over 200 steps on two seeds (1.6905 and 1.6914 against 1.6907 and 1.6899).
|
|
151
|
+
- **"Unsloth, fastest"** is Unsloth with gradient checkpointing off. Meep² checkpoints only the layers the free memory requires, which on the GB10 is none, so those two compare like with like. **TRL** runs at its defaults: padded batches, every layer checkpointed; TRL with its own speed options on was not measured.
|
|
152
|
+
- **The held-out loss:** each engine's own evaluation reads differently on Gemma 4 and Qwen 3.5 (Unsloth's reads 0.008 to 0.013 lower, even before training), so every adapter was measured by the same evaluation. Meep²'s and TRL's own evaluations agree with it.
|
|
153
|
+
- All runs use no_robots, whose conversations are short and vary a lot in length. That is where packing without padding gains most, so on data of even lengths the lead is smaller.
|
|
154
|
+
- Measured 25 to 29 October 2026: Meep² at commit `7971242` (transformers 5.20.2, TRL 1.16.0; the short runs at `1375994`), Unsloth 2026.10.7 (transformers 5.17.0, TRL 0.29.1). Unsloth's releases up to 2026.10.10 require transformers 5.17 or older; we will compare again when Unsloth accepts 5.20.
|
|
155
|
+
|
|
156
|
+
<details>
|
|
157
|
+
<summary>Short runs: 30 steps, three runs each, LoRA and QLoRA, Llama included</summary>
|
|
158
|
+
|
|
159
|
+
Training tokens per second, mean of three runs; in brackets the peak GPU memory in GiB as PyTorch allocates it (Unsloth's default moves activations to CPU memory, which this does not count; on the GB10 it is the same memory). Below a value, the range of the three runs.
|
|
160
|
+
|
|
161
|
+
| Model, method | Released | TRL, default settings | Unsloth, default | Unsloth, fastest | Meep² |
|
|
162
|
+
|---|---|---|---|---|---|
|
|
163
|
+
| Gemma 4 E4B, LoRA | Mar 2026 | 291 (19.3) | 577 (18.5) | 769 (54.7)<br>758–776 | **1,903** (29.8)<br>1,898–1,907 |
|
|
164
|
+
| Qwen 3.5 9B, LoRA | Feb 2026 | 278 (21.6) | 278\* (29.4)<br>249–300 | 387\* (90.6)<br>373–395 | **1,253** (36.1)<br>1,252–1,255 |
|
|
165
|
+
| Gemma 4 E4B, QLoRA | Mar 2026 | 304 (12.9) | 553 (13.0) | 733 (49.2) | **1,738** (23.5)<br>1,731–1,748 |
|
|
166
|
+
| Llama 3.1 8B, LoRA | Jul 2024 | 308 (20.0) | 1,125 (16.2)<br>1,026–1,206 | **1,662** (28.7)<br>1,642–1,683 | 1,633 (28.5)<br>1,631–1,636 |
|
|
167
|
+
| Llama 3.1 8B, QLoRA | Jul 2024 | 326 (9.5) | 1,068 (6.7) | **1,494** (19.1)<br>1,473–1,505 | 1,461 (18.7)<br>1,452–1,470 |
|
|
168
|
+
|
|
169
|
+
\* With Unsloth's compiler off (`UNSLOTH_COMPILE_DISABLE=1`), as its Qwen 3.5 runs needed in this environment.
|
|
170
|
+
|
|
171
|
+
NVIDIA NeMo AutoModel, the library of NVIDIA's DGX Spark fine-tuning playbook, trained the Llama LoRA row at 5.68 s per step in 60.0 GiB (one run, with its own optimizer and order of the conversations), against Meep²'s 1.44 s in 27.8 GiB.
|
|
172
|
+
|
|
173
|
+
</details>
|
|
174
|
+
|
|
175
|
+
How each run was made, with the command of every arm, is in [`benchmarks/results/2026-10/`](benchmarks/results/2026-10/README.md). The charts are drawn from those files by [`benchmarks/make_readme_chart.py`](benchmarks/make_readme_chart.py).
|
|
176
|
+
|
|
177
|
+
### More results
|
|
178
|
+
|
|
179
|
+
<details>
|
|
180
|
+
<summary>Frozen weights in FP8, LoRA, 30 steps</summary>
|
|
181
|
+
|
|
182
|
+
With `method="fp8"` (in the lower-level calls, `load_in_fp8=True`), the base model's linear layers are stored in FP8, an 8-bit number format, instead of 16-bit BF16. Their products run on the GPU's FP8 tensor cores. The adapters stay in BF16.
|
|
183
|
+
|
|
184
|
+
| Model | BF16 weights | FP8 weights | Training loss, BF16 → FP8 |
|
|
185
|
+
|---|---|---|---|
|
|
186
|
+
| Llama 3.1 8B | 1,650 (27.8) | **1,908 (21.3)** | 1.857 → 1.862 |
|
|
187
|
+
| Qwen 3.5 9B | 1,263 (35.2) | **1,411 (28.8)** | 1.965 → 1.969 |
|
|
188
|
+
|
|
189
|
+
Single runs, at an earlier commit than the tables above. Over 200 steps on Llama, FP8 weights cost 0.003 in held-out loss and trained 12% faster in 17% less memory ([`number-formats-llama.jsonl`](benchmarks/results/2026-10/number-formats-llama.jsonl), an unfused setup used to compare number formats).
|
|
190
|
+
|
|
191
|
+
</details>
|
|
192
|
+
|
|
193
|
+
<details>
|
|
194
|
+
<summary>Mixture of experts: LoRA on every expert, Qwen3.6-35B-A3B, 30 steps</summary>
|
|
195
|
+
|
|
196
|
+
A mixture-of-experts model has many small MLPs, the experts, and sends each token to a few of them. Training a LoRA adapter on every expert is slow with PEFT alone: at each pass, PEFT builds the full adapted weight of every expert. Meep² instead computes each expert's adapter only on the tokens sent to that expert, in one kernel launch per product. The same kernel lets the expert weights be stored in FP8.
|
|
197
|
+
|
|
198
|
+
| Computation | Training tokens/s | Peak memory, GiB |
|
|
199
|
+
|---|---|---|
|
|
200
|
+
| PEFT's computation of the experts | 571 (569–575) | 80.1 |
|
|
201
|
+
| Meep², routed LoRA, BF16 weights | 777 (773–779) | 78.8 |
|
|
202
|
+
| Meep², routed LoRA, FP8 expert stacks | **857**, single run | **61.2** |
|
|
203
|
+
|
|
204
|
+
</details>
|
|
205
|
+
|
|
206
|
+
<details>
|
|
207
|
+
<summary>One RTX 3090, LoRA, 30 steps</summary>
|
|
208
|
+
|
|
209
|
+
| Model | TRL, default settings | Unsloth, default | Meep² |
|
|
210
|
+
|---|---|---|---|
|
|
211
|
+
| Gemma 4 E4B | 562 (19.3) | 776 (18.5) | **1,674** (18.0) |
|
|
212
|
+
| Llama 3.1 8B | 482 (20.0) | 1,231 (16.3) | **1,361** (21.0) |
|
|
213
|
+
|
|
214
|
+
On this card, checkpointing 16 of Llama's 32 layers by hand gave 1,458 tokens/s in 22.5 GiB, faster than the layout Meep² chose.
|
|
215
|
+
|
|
216
|
+
</details>
|
|
217
|
+
|
|
218
|
+
<details>
|
|
219
|
+
<summary>Two RTX 3090 over PCIe, no NVLink, 20 steps</summary>
|
|
220
|
+
|
|
221
|
+
Single runs. When a model fits on one GPU, DDP (distributed data parallel) trains a copy on each GPU, each on its own examples:
|
|
222
|
+
|
|
223
|
+
| Model, method | 1 GPU | 2 GPUs, DDP |
|
|
224
|
+
|---|---|---|
|
|
225
|
+
| Llama 3.1 8B, LoRA | 1,272 (16.6) | **2,247** (16.8) |
|
|
226
|
+
| Qwen 3.8 27B, QLoRA | 355 (20.2) | **633** (20.6) |
|
|
227
|
+
|
|
228
|
+
When a model does not fit on one GPU, FSDP2 splits every layer's weights across the GPUs, and the layer split puts half of the layers on each. Memory is given for each GPU.
|
|
229
|
+
|
|
230
|
+
| Model, method (29.5 GB of weights: not one 24 GB card) | 2 GPUs, FSDP2 | 2 GPUs, layers split |
|
|
231
|
+
|---|---|---|
|
|
232
|
+
| Qwen3-14B, LoRA, BF16 | 466 (18.2 + 18.1) | **698** (15.2 + 15.2) |
|
|
233
|
+
|
|
234
|
+
</details>
|
|
235
|
+
|
|
236
|
+
## What Meep² does
|
|
237
|
+
|
|
238
|
+
- **Auto mode:** a training job names the model, the data and the method; Meep² measures and decides the rest, and prints a plan with the reason for each decision. A job is also plain data, so a form or a platform can write it.
|
|
239
|
+
- **Fused LoRA kernels** for the attention projections and the MLP (Axolotl's, vendored). **Compiled layers** where compiling pays. **Gradient checkpointing measured for the batch**: Meep² measures one layer on the largest micro-batch the training runs, under three ways of training it (keeping everything, recomputed selectively, recomputed whole), and chooses a layout that fits the free memory. Selective recomputation keeps the outputs of a layer's products and recomputes only the cheap operations around them.
|
|
240
|
+
- **Frozen weights in FP8** on GPUs with FP8 tensor cores (Ada, Hopper, Blackwell). The adapters stay in BF16.
|
|
241
|
+
- **Mixture of experts:** LoRA on every routed expert with Meep²'s own grouped kernel, in BF16 or FP8. This matters on the GPUs where PyTorch has no grouped matrix-multiply kernel (consumer and workstation Blackwell, Ampere, Ada) and loops over the experts instead.
|
|
242
|
+
- **Padding-free batches** that keep the examples apart in the gated DeltaNet layers of Qwen 3.5 and 3.6, and for models whose attention heads are wider than FlashAttention computes (Gemma 4: heads of 512), on PyTorch's memory-efficient kernel. Chat templates are tokenized without stray spaces.
|
|
243
|
+
- **Several GPUs in one machine:** DDP, FSDP2, or the layers spread over the GPUs, with the right settings for each, tested on two RTX 3090 without NVLink. Training across two machines, such as two DGX Sparks, is not tested yet.
|
|
244
|
+
- **GPUs that share the system's memory** (DGX Spark): a checkpoint loads shard by shard and is never held twice in memory. The free memory is read the way the machine reports it.
|
|
245
|
+
- **Training in sessions:** a training stops with a checkpoint when a stop file appears or the process gets SIGTERM, resumes exactly where it stopped, and writes its progress to a status file that a scheduler or a platform can follow.
|
|
246
|
+
- **GRPO** with TRL's own `GRPOTrainer`. Where vLLM is installed, it generates inside the training process through `GRPOConfig`.
|
|
247
|
+
- **Saving** as an adapter, or merged into 16-bit weights. 4-bit and FP8 layers are converted back to 16 bits for the merge.
|
|
248
|
+
|
|
249
|
+
## Notebooks
|
|
250
|
+
|
|
251
|
+
Five tutorials in [`notebooks/`](notebooks/), from the simplest to the most involved. Each one explains the idea it uses. Notebooks 01 to 04 train with a training job; 05 uses the lower-level calls, for FSDP2 and a model spread over several GPUs, which a training job does not do. Each trains on a dataset of conversations that you can replace with your own: a Hugging Face dataset, a Parquet file or a JSONL file, with the number of epochs as a setting. Each loads its model from Hugging Face, or from the cluster's model mirror on [Thinkube](https://thinkube.org). On Thinkube it also records the run in Thinkube Experiments and can register the trained model so the cluster serves it.
|
|
252
|
+
|
|
253
|
+
| Notebook | What it teaches |
|
|
254
|
+
|---|---|
|
|
255
|
+
| [`01-lora-sft.ipynb`](notebooks/01-lora-sft.ipynb) | LoRA and QLoRA on a chat model: the adapter, the data, the loss on the answers only, saving and serving |
|
|
256
|
+
| [`02-fp8-weights.ipynb`](notebooks/02-fp8-weights.ipynb) | The frozen weights held in FP8: what FP8 is, what it costs, what it saves |
|
|
257
|
+
| [`03-moe-experts.ipynb`](notebooks/03-moe-experts.ipynb) | LoRA on every expert of a mixture of experts, with the expert weights in FP8 |
|
|
258
|
+
| [`04-grpo-zebra.ipynb`](notebooks/04-grpo-zebra.ipynb) | GRPO with a reward a program computes: a zebra puzzle graded by a constraint solver |
|
|
259
|
+
| [`05-several-gpus.ipynb`](notebooks/05-several-gpus.ipynb) | Several GPUs without NVLink: DDP when the model fits one, FSDP2 or a layer split when it does not |
|
|
260
|
+
|
|
261
|
+
The scripts that produced the numbers above are in [`benchmarks/`](benchmarks/); a notebook beside each one explains its settings and runs it:
|
|
262
|
+
|
|
263
|
+
| Notebook | What it measures |
|
|
264
|
+
|---|---|
|
|
265
|
+
| [`benchmarks/sft.ipynb`](benchmarks/sft.ipynb) | LoRA, QLoRA and FP8 weights on Llama 3.1 8B, Qwen 3.5 9B and Gemma 4 E4B: Meep², Unsloth and plain TRL on one GPU |
|
|
266
|
+
| [`benchmarks/moe.ipynb`](benchmarks/moe.ipynb) | LoRA on every expert of Qwen3.6-35B-A3B, with BF16 and FP8 expert weights, against PEFT's computation |
|
|
267
|
+
| [`benchmarks/multi_gpu.ipynb`](benchmarks/multi_gpu.ipynb) | Two consumer GPUs without NVLink: DDP for a model that fits one, FSDP2 and the layer split for one that does not |
|
|
268
|
+
|
|
269
|
+
## Built on, and license
|
|
270
|
+
|
|
271
|
+
Meep² is Apache-2.0 ([LICENSE](LICENSE), [NOTICE](NOTICE)). It is built on [Transformers](https://github.com/huggingface/transformers), [PEFT](https://github.com/huggingface/peft), [TRL](https://github.com/huggingface/trl) and [datasets](https://github.com/huggingface/datasets) (Apache-2.0), [Axolotl](https://github.com/axolotl-ai-cloud/axolotl)'s LoRA kernels (vendored, Apache-2.0), [bitsandbytes](https://github.com/bitsandbytes-foundation/bitsandbytes), [Triton](https://github.com/triton-lang/triton) and [flash-linear-attention](https://github.com/fla-org/flash-linear-attention) (MIT), and [Liger Kernel](https://github.com/linkedin/Liger-Kernel) and [FlashAttention](https://github.com/Dao-AILab/flash-attention) (BSD). certifi and tqdm, which requests and Transformers install, are MPL-2.0, which applies file by file. PyTorch and CUDA come from your environment under their own licenses. The [licensing page](https://thinkube.org/meepsquared/licensing.html) links each license file and compares the licenses of other training engines.
|
|
272
|
+
|
|
273
|
+
Meep² is the fine-tuning engine of [Thinkube](https://thinkube.org), a platform for running AI on your own hardware.
|