canifinetune 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.
- canifinetune-0.1.0/.gitignore +62 -0
- canifinetune-0.1.0/LICENSE +21 -0
- canifinetune-0.1.0/PKG-INFO +251 -0
- canifinetune-0.1.0/README.md +201 -0
- canifinetune-0.1.0/examples/README.md +16 -0
- canifinetune-0.1.0/examples/qwen_1_5b_qlora_4080/README.md +56 -0
- canifinetune-0.1.0/examples/tiny_gpt2_lora_smoke/README.md +56 -0
- canifinetune-0.1.0/pyproject.toml +127 -0
- canifinetune-0.1.0/src/canifinetune/__init__.py +6 -0
- canifinetune-0.1.0/src/canifinetune/bench/__init__.py +5 -0
- canifinetune-0.1.0/src/canifinetune/bench/memory_trace.py +69 -0
- canifinetune-0.1.0/src/canifinetune/bench/oom.py +34 -0
- canifinetune-0.1.0/src/canifinetune/bench/runner.py +450 -0
- canifinetune-0.1.0/src/canifinetune/bench/synthetic_data.py +75 -0
- canifinetune-0.1.0/src/canifinetune/cli.py +512 -0
- canifinetune-0.1.0/src/canifinetune/doctor.py +165 -0
- canifinetune-0.1.0/src/canifinetune/estimator/__init__.py +29 -0
- canifinetune-0.1.0/src/canifinetune/estimator/calibration.py +191 -0
- canifinetune-0.1.0/src/canifinetune/estimator/formulas.py +315 -0
- canifinetune-0.1.0/src/canifinetune/estimator/memory.py +250 -0
- canifinetune-0.1.0/src/canifinetune/estimator/model_metadata.py +342 -0
- canifinetune-0.1.0/src/canifinetune/estimator/recommender.py +228 -0
- canifinetune-0.1.0/src/canifinetune/recipes/__init__.py +5 -0
- canifinetune-0.1.0/src/canifinetune/recipes/generator.py +147 -0
- canifinetune-0.1.0/src/canifinetune/recipes/templates/README.md.j2 +56 -0
- canifinetune-0.1.0/src/canifinetune/recipes/templates/config.yaml.j2 +38 -0
- canifinetune-0.1.0/src/canifinetune/recipes/templates/dataset_format.md.j2 +35 -0
- canifinetune-0.1.0/src/canifinetune/recipes/templates/eval_smoke.py.j2 +56 -0
- canifinetune-0.1.0/src/canifinetune/recipes/templates/expected_vram.md.j2 +51 -0
- canifinetune-0.1.0/src/canifinetune/recipes/templates/requirements.txt.j2 +11 -0
- canifinetune-0.1.0/src/canifinetune/recipes/templates/run.sh.j2 +9 -0
- canifinetune-0.1.0/src/canifinetune/recipes/templates/sample_dataset.jsonl.j2 +8 -0
- canifinetune-0.1.0/src/canifinetune/recipes/templates/train.py.j2 +300 -0
- canifinetune-0.1.0/src/canifinetune/recipes/templates/troubleshooting.md.j2 +69 -0
- canifinetune-0.1.0/src/canifinetune/reports/__init__.py +11 -0
- canifinetune-0.1.0/src/canifinetune/reports/html.py +97 -0
- canifinetune-0.1.0/src/canifinetune/reports/markdown.py +167 -0
- canifinetune-0.1.0/src/canifinetune/utils/__init__.py +1 -0
- canifinetune-0.1.0/src/canifinetune/utils/gpu.py +145 -0
- canifinetune-0.1.0/src/canifinetune/utils/hf.py +60 -0
- canifinetune-0.1.0/src/canifinetune/utils/logging.py +48 -0
- canifinetune-0.1.0/src/canifinetune/utils/subprocess.py +30 -0
- canifinetune-0.1.0/src/canifinetune/utils/units.py +96 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.eggs/
|
|
11
|
+
*.egg
|
|
12
|
+
|
|
13
|
+
# Virtual envs
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
env/
|
|
17
|
+
.python-version-local
|
|
18
|
+
|
|
19
|
+
# Tooling
|
|
20
|
+
.ruff_cache/
|
|
21
|
+
.mypy_cache/
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
htmlcov/
|
|
25
|
+
.tox/
|
|
26
|
+
.nox/
|
|
27
|
+
|
|
28
|
+
# IDE
|
|
29
|
+
.vscode/
|
|
30
|
+
.idea/
|
|
31
|
+
*.swp
|
|
32
|
+
*~
|
|
33
|
+
|
|
34
|
+
# OS
|
|
35
|
+
.DS_Store
|
|
36
|
+
Thumbs.db
|
|
37
|
+
desktop.ini
|
|
38
|
+
|
|
39
|
+
# Project local artifacts (small results are tracked under benchmarks/results)
|
|
40
|
+
.cache/
|
|
41
|
+
hf_cache/
|
|
42
|
+
runs/
|
|
43
|
+
output*/
|
|
44
|
+
checkpoints/
|
|
45
|
+
adapters/
|
|
46
|
+
*.log
|
|
47
|
+
|
|
48
|
+
# Models we never want to commit
|
|
49
|
+
*.bin
|
|
50
|
+
*.safetensors
|
|
51
|
+
*.gguf
|
|
52
|
+
*.pt
|
|
53
|
+
*.pth
|
|
54
|
+
*.onnx
|
|
55
|
+
|
|
56
|
+
# Large datasets
|
|
57
|
+
*.parquet
|
|
58
|
+
*.arrow
|
|
59
|
+
|
|
60
|
+
# OneDrive cruft
|
|
61
|
+
*.tmp
|
|
62
|
+
*.lock
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daoyuan Li
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: canifinetune
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Estimate, benchmark, and generate fine-tuning recipes for LLMs on consumer GPUs.
|
|
5
|
+
Project-URL: Homepage, https://github.com/DaoyuanLi2816/can-i-finetune-this
|
|
6
|
+
Project-URL: Issues, https://github.com/DaoyuanLi2816/can-i-finetune-this/issues
|
|
7
|
+
Project-URL: Repository, https://github.com/DaoyuanLi2816/can-i-finetune-this
|
|
8
|
+
Author-email: Daoyuan Li <lidaoyuan2816@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: fine-tuning,gpu,huggingface,llm,lora,memory,peft,qlora,transformers
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: huggingface-hub>=0.25
|
|
24
|
+
Requires-Dist: jinja2>=3.1
|
|
25
|
+
Requires-Dist: platformdirs>=4.2
|
|
26
|
+
Requires-Dist: pydantic>=2.6
|
|
27
|
+
Requires-Dist: pyyaml>=6.0
|
|
28
|
+
Requires-Dist: rich>=13.7
|
|
29
|
+
Requires-Dist: typer>=0.12
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
35
|
+
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
|
|
36
|
+
Provides-Extra: report
|
|
37
|
+
Requires-Dist: pandas>=2.2; extra == 'report'
|
|
38
|
+
Requires-Dist: tabulate>=0.9; extra == 'report'
|
|
39
|
+
Provides-Extra: train
|
|
40
|
+
Requires-Dist: accelerate>=0.33; extra == 'train'
|
|
41
|
+
Requires-Dist: bitsandbytes>=0.43; (platform_system != 'Darwin') and extra == 'train'
|
|
42
|
+
Requires-Dist: datasets>=2.20; extra == 'train'
|
|
43
|
+
Requires-Dist: peft>=0.12; extra == 'train'
|
|
44
|
+
Requires-Dist: protobuf>=4.25; extra == 'train'
|
|
45
|
+
Requires-Dist: sentencepiece>=0.2; extra == 'train'
|
|
46
|
+
Requires-Dist: torch>=2.3; extra == 'train'
|
|
47
|
+
Requires-Dist: transformers>=4.43; extra == 'train'
|
|
48
|
+
Requires-Dist: trl>=0.9; extra == 'train'
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
|
|
51
|
+
# can-i-finetune-this
|
|
52
|
+
|
|
53
|
+
[](https://github.com/DaoyuanLi2816/can-i-finetune-this/actions/workflows/ci.yml)
|
|
54
|
+
[](https://www.python.org)
|
|
55
|
+
[](LICENSE)
|
|
56
|
+
|
|
57
|
+
**Estimate, benchmark, and generate fine-tuning recipes for LLMs on consumer GPUs.**
|
|
58
|
+
|
|
59
|
+

|
|
60
|
+
|
|
61
|
+
You have one consumer-grade NVIDIA GPU. You want to fine-tune an open-weight LLM
|
|
62
|
+
with LoRA or QLoRA, but you do not want to download 14 GB of weights just to
|
|
63
|
+
discover that your 12 GB / 16 GB / 24 GB card OOMs on step 1.
|
|
64
|
+
|
|
65
|
+
`canifinetune` answers, before you spend the disk and the time:
|
|
66
|
+
|
|
67
|
+
1. Can I fine-tune this model?
|
|
68
|
+
2. About how much VRAM will it use?
|
|
69
|
+
3. What batch size / sequence length / LoRA rank / quantization should I use?
|
|
70
|
+
4. If I can't, how should I downsize?
|
|
71
|
+
5. Is there local benchmark evidence for that answer?
|
|
72
|
+
6. Can I get a ready-to-run Hugging Face + PEFT + TRL training script for that config?
|
|
73
|
+
|
|
74
|
+
It is a single Python package with a CLI:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
canifinetune doctor
|
|
78
|
+
canifinetune estimate --model Qwen/Qwen2.5-1.5B-Instruct --method qlora --gpu-vram-gb 16 --seq-len 2048 --micro-batch-size 1 --lora-rank 16
|
|
79
|
+
canifinetune recommend --model Qwen/Qwen2.5-1.5B-Instruct --gpu-vram-gb 16
|
|
80
|
+
canifinetune bench --model sshleifer/tiny-gpt2 --method lora --steps 3
|
|
81
|
+
canifinetune calibrate --benchmarks benchmarks/results
|
|
82
|
+
canifinetune recipe --model Qwen/Qwen2.5-1.5B-Instruct --method qlora --output recipes/qwen2.5-1.5b-qlora-4080
|
|
83
|
+
canifinetune report --benchmarks benchmarks/results --out report.md
|
|
84
|
+
canifinetune compare --benchmarks benchmarks/results --out compare.md
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
What `canifinetune estimate` actually prints:
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
+-------- Qwen/Qwen2.5-1.5B-Instruct (qlora) --------+
|
|
91
|
+
| feasible: YES ratio = 0.20 confidence = medium |
|
|
92
|
+
+------------------------------------------------------+
|
|
93
|
+
Memory breakdown (GB)
|
|
94
|
+
+---------------------------------+
|
|
95
|
+
| Component | Value |
|
|
96
|
+
|-----------------------+---------|
|
|
97
|
+
| static model | 0.737 |
|
|
98
|
+
| quantization overhead | 0.018 |
|
|
99
|
+
| trainable params | 4.4 MB |
|
|
100
|
+
| gradients | 0.008 |
|
|
101
|
+
| optimizer states | 0.010 |
|
|
102
|
+
| activations | 0.328 |
|
|
103
|
+
| CUDA / fragmentation | 1.280 |
|
|
104
|
+
| safety margin | 0.800 |
|
|
105
|
+
| total | 3.163 |
|
|
106
|
+
+---------------------------------+
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Static estimate says 3.16 GB; on a real RTX 4080 the same config measures
|
|
110
|
+
7.10 GB (heavy bitsandbytes unpacking buffers at seq_len=2048). `canifinetune
|
|
111
|
+
bench` and `canifinetune calibrate` close that gap on your machine —
|
|
112
|
+
that is the *point* of the project.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Install
|
|
117
|
+
|
|
118
|
+
`canifinetune` runs in two layers:
|
|
119
|
+
|
|
120
|
+
| Layer | Install | What you get |
|
|
121
|
+
| --- | --- | --- |
|
|
122
|
+
| Core (estimate / recommend / recipe / report) | `pip install canifinetune` | All CLI commands. No PyTorch required. |
|
|
123
|
+
| Training (bench / real fine-tuning) | `pip install canifinetune[train]` | Adds `torch`, `transformers`, `peft`, `bitsandbytes`, `trl`, `datasets`. |
|
|
124
|
+
| Reporting extras | `pip install canifinetune[report]` | Pandas/tabulate for prettier tables. |
|
|
125
|
+
| Development | `pip install canifinetune[dev]` | pytest, ruff, mypy. |
|
|
126
|
+
|
|
127
|
+
If you use `uv`:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
uv venv
|
|
131
|
+
uv pip install -e ".[dev,report]"
|
|
132
|
+
# Add training deps when you want to run benchmarks:
|
|
133
|
+
uv pip install -e ".[dev,train,report]"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
PyTorch should generally be installed with the CUDA wheel that matches your driver,
|
|
137
|
+
e.g.
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
uv pip install torch --index-url https://download.pytorch.org/whl/cu121
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
See `docs/troubleshooting.md` for Windows / WSL / bitsandbytes specifics.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Quickstart
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# 1. See what your machine looks like
|
|
151
|
+
canifinetune doctor
|
|
152
|
+
|
|
153
|
+
# 2. Ask if a model fits on your card
|
|
154
|
+
canifinetune estimate \
|
|
155
|
+
--model Qwen/Qwen2.5-1.5B-Instruct \
|
|
156
|
+
--method qlora \
|
|
157
|
+
--gpu-vram-gb 16 \
|
|
158
|
+
--seq-len 2048 \
|
|
159
|
+
--micro-batch-size 1 \
|
|
160
|
+
--lora-rank 16
|
|
161
|
+
|
|
162
|
+
# 3. Have it search for a feasible config
|
|
163
|
+
canifinetune recommend --model Qwen/Qwen2.5-1.5B-Instruct --gpu-vram-gb 16
|
|
164
|
+
|
|
165
|
+
# 4. Run a tiny real benchmark (downloads sshleifer/tiny-gpt2, ~5 MB)
|
|
166
|
+
canifinetune bench --model sshleifer/tiny-gpt2 --method lora --steps 3
|
|
167
|
+
|
|
168
|
+
# 5. Generate a ready-to-run training recipe
|
|
169
|
+
canifinetune recipe \
|
|
170
|
+
--model Qwen/Qwen2.5-1.5B-Instruct \
|
|
171
|
+
--method qlora \
|
|
172
|
+
--seq-len 2048 \
|
|
173
|
+
--output recipes/qwen2.5-1.5b-qlora-4080
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## What's different from `accelerate estimate-memory`?
|
|
179
|
+
|
|
180
|
+
`accelerate estimate-memory` tells you how much memory **loading** a model takes.
|
|
181
|
+
That is not enough to know whether you can **train** it.
|
|
182
|
+
|
|
183
|
+
This project tries to answer the harder question. It models:
|
|
184
|
+
|
|
185
|
+
- Model weights, in fp32 / fp16 / bf16 / int8 / NF4 + double-quant
|
|
186
|
+
- LoRA / QLoRA trainable parameter count for typical `target_modules`
|
|
187
|
+
- Gradients only for trainable parameters
|
|
188
|
+
- AdamW vs 8-bit / paged AdamW optimizer states
|
|
189
|
+
- Activations as a function of `seq_len`, `batch_size`, `hidden_size`, `num_layers`,
|
|
190
|
+
with and without gradient checkpointing
|
|
191
|
+
- A fragmentation / CUDA / buffer safety margin
|
|
192
|
+
- A feasibility decision against your actual GPU
|
|
193
|
+
- Concrete degradation suggestions when not feasible
|
|
194
|
+
|
|
195
|
+
Estimates are **always** marked with an `assumptions` block and a `confidence`
|
|
196
|
+
level, because activation memory in particular is hard to predict statically.
|
|
197
|
+
Run `canifinetune bench` and `canifinetune calibrate` to ground them in real
|
|
198
|
+
measurements on your machine.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## RTX 4080 baselines
|
|
203
|
+
|
|
204
|
+
`docs/rtx4080_baselines.md` contains real measurements collected on a single
|
|
205
|
+
RTX 4080 (16 GB). These are not synthetic. If a configuration was not run, the
|
|
206
|
+
table says "not run", not a guessed number.
|
|
207
|
+
|
|
208
|
+
Highlights (more in the doc):
|
|
209
|
+
|
|
210
|
+
| model | method | seq_len | measured peak | tok/sec |
|
|
211
|
+
| --- | --- | --- | --- | --- |
|
|
212
|
+
| `Qwen/Qwen2.5-0.5B-Instruct` | qlora | 1024 | 3.30 GB | 1995 |
|
|
213
|
+
| `Qwen/Qwen2.5-1.5B-Instruct` | qlora | 1024 | 4.36 GB | 1352 |
|
|
214
|
+
| `Qwen/Qwen2.5-1.5B-Instruct` | qlora | 2048 | 7.10 GB | 1470 |
|
|
215
|
+
| `Qwen/Qwen2.5-3B-Instruct` | qlora | 1024 | 5.54 GB | 1158 |
|
|
216
|
+
| `sshleifer/tiny-gpt2` (smoke) | lora | 128 | 0.12 GB | 1735 |
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Repository layout
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
src/canifinetune/ # package code (estimator, bench, recipes, reports, cli)
|
|
224
|
+
benchmarks/ # configs/, results/ (JSON), calibration/
|
|
225
|
+
docs/ # design, memory model, troubleshooting
|
|
226
|
+
examples/ # end-to-end recipe folders
|
|
227
|
+
tests/ # pytest tests (CPU-only, no large downloads)
|
|
228
|
+
scripts/ # helper scripts for collecting baselines
|
|
229
|
+
.github/workflows/ # CI (ruff + pytest on CPU)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Roadmap
|
|
235
|
+
|
|
236
|
+
The current scope is "single consumer GPU, single node, LoRA / QLoRA, causal LM,
|
|
237
|
+
Hugging Face stack". Possible directions, none committed:
|
|
238
|
+
|
|
239
|
+
- DeepSpeed ZeRO and FSDP estimation for multi-GPU setups
|
|
240
|
+
- Heuristics for sequence-classification / encoder-decoder training
|
|
241
|
+
- Throughput modeling (tokens / sec), not just feasibility
|
|
242
|
+
- Auto-tuning of `gradient_accumulation_steps` for a target effective batch size
|
|
243
|
+
- A web UI on top of the CLI
|
|
244
|
+
|
|
245
|
+
Contributions welcome.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## License
|
|
250
|
+
|
|
251
|
+
MIT. See `LICENSE`.
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# can-i-finetune-this
|
|
2
|
+
|
|
3
|
+
[](https://github.com/DaoyuanLi2816/can-i-finetune-this/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.python.org)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
**Estimate, benchmark, and generate fine-tuning recipes for LLMs on consumer GPUs.**
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
You have one consumer-grade NVIDIA GPU. You want to fine-tune an open-weight LLM
|
|
12
|
+
with LoRA or QLoRA, but you do not want to download 14 GB of weights just to
|
|
13
|
+
discover that your 12 GB / 16 GB / 24 GB card OOMs on step 1.
|
|
14
|
+
|
|
15
|
+
`canifinetune` answers, before you spend the disk and the time:
|
|
16
|
+
|
|
17
|
+
1. Can I fine-tune this model?
|
|
18
|
+
2. About how much VRAM will it use?
|
|
19
|
+
3. What batch size / sequence length / LoRA rank / quantization should I use?
|
|
20
|
+
4. If I can't, how should I downsize?
|
|
21
|
+
5. Is there local benchmark evidence for that answer?
|
|
22
|
+
6. Can I get a ready-to-run Hugging Face + PEFT + TRL training script for that config?
|
|
23
|
+
|
|
24
|
+
It is a single Python package with a CLI:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
canifinetune doctor
|
|
28
|
+
canifinetune estimate --model Qwen/Qwen2.5-1.5B-Instruct --method qlora --gpu-vram-gb 16 --seq-len 2048 --micro-batch-size 1 --lora-rank 16
|
|
29
|
+
canifinetune recommend --model Qwen/Qwen2.5-1.5B-Instruct --gpu-vram-gb 16
|
|
30
|
+
canifinetune bench --model sshleifer/tiny-gpt2 --method lora --steps 3
|
|
31
|
+
canifinetune calibrate --benchmarks benchmarks/results
|
|
32
|
+
canifinetune recipe --model Qwen/Qwen2.5-1.5B-Instruct --method qlora --output recipes/qwen2.5-1.5b-qlora-4080
|
|
33
|
+
canifinetune report --benchmarks benchmarks/results --out report.md
|
|
34
|
+
canifinetune compare --benchmarks benchmarks/results --out compare.md
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
What `canifinetune estimate` actually prints:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
+-------- Qwen/Qwen2.5-1.5B-Instruct (qlora) --------+
|
|
41
|
+
| feasible: YES ratio = 0.20 confidence = medium |
|
|
42
|
+
+------------------------------------------------------+
|
|
43
|
+
Memory breakdown (GB)
|
|
44
|
+
+---------------------------------+
|
|
45
|
+
| Component | Value |
|
|
46
|
+
|-----------------------+---------|
|
|
47
|
+
| static model | 0.737 |
|
|
48
|
+
| quantization overhead | 0.018 |
|
|
49
|
+
| trainable params | 4.4 MB |
|
|
50
|
+
| gradients | 0.008 |
|
|
51
|
+
| optimizer states | 0.010 |
|
|
52
|
+
| activations | 0.328 |
|
|
53
|
+
| CUDA / fragmentation | 1.280 |
|
|
54
|
+
| safety margin | 0.800 |
|
|
55
|
+
| total | 3.163 |
|
|
56
|
+
+---------------------------------+
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Static estimate says 3.16 GB; on a real RTX 4080 the same config measures
|
|
60
|
+
7.10 GB (heavy bitsandbytes unpacking buffers at seq_len=2048). `canifinetune
|
|
61
|
+
bench` and `canifinetune calibrate` close that gap on your machine —
|
|
62
|
+
that is the *point* of the project.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Install
|
|
67
|
+
|
|
68
|
+
`canifinetune` runs in two layers:
|
|
69
|
+
|
|
70
|
+
| Layer | Install | What you get |
|
|
71
|
+
| --- | --- | --- |
|
|
72
|
+
| Core (estimate / recommend / recipe / report) | `pip install canifinetune` | All CLI commands. No PyTorch required. |
|
|
73
|
+
| Training (bench / real fine-tuning) | `pip install canifinetune[train]` | Adds `torch`, `transformers`, `peft`, `bitsandbytes`, `trl`, `datasets`. |
|
|
74
|
+
| Reporting extras | `pip install canifinetune[report]` | Pandas/tabulate for prettier tables. |
|
|
75
|
+
| Development | `pip install canifinetune[dev]` | pytest, ruff, mypy. |
|
|
76
|
+
|
|
77
|
+
If you use `uv`:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
uv venv
|
|
81
|
+
uv pip install -e ".[dev,report]"
|
|
82
|
+
# Add training deps when you want to run benchmarks:
|
|
83
|
+
uv pip install -e ".[dev,train,report]"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
PyTorch should generally be installed with the CUDA wheel that matches your driver,
|
|
87
|
+
e.g.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
uv pip install torch --index-url https://download.pytorch.org/whl/cu121
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
See `docs/troubleshooting.md` for Windows / WSL / bitsandbytes specifics.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Quickstart
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# 1. See what your machine looks like
|
|
101
|
+
canifinetune doctor
|
|
102
|
+
|
|
103
|
+
# 2. Ask if a model fits on your card
|
|
104
|
+
canifinetune estimate \
|
|
105
|
+
--model Qwen/Qwen2.5-1.5B-Instruct \
|
|
106
|
+
--method qlora \
|
|
107
|
+
--gpu-vram-gb 16 \
|
|
108
|
+
--seq-len 2048 \
|
|
109
|
+
--micro-batch-size 1 \
|
|
110
|
+
--lora-rank 16
|
|
111
|
+
|
|
112
|
+
# 3. Have it search for a feasible config
|
|
113
|
+
canifinetune recommend --model Qwen/Qwen2.5-1.5B-Instruct --gpu-vram-gb 16
|
|
114
|
+
|
|
115
|
+
# 4. Run a tiny real benchmark (downloads sshleifer/tiny-gpt2, ~5 MB)
|
|
116
|
+
canifinetune bench --model sshleifer/tiny-gpt2 --method lora --steps 3
|
|
117
|
+
|
|
118
|
+
# 5. Generate a ready-to-run training recipe
|
|
119
|
+
canifinetune recipe \
|
|
120
|
+
--model Qwen/Qwen2.5-1.5B-Instruct \
|
|
121
|
+
--method qlora \
|
|
122
|
+
--seq-len 2048 \
|
|
123
|
+
--output recipes/qwen2.5-1.5b-qlora-4080
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## What's different from `accelerate estimate-memory`?
|
|
129
|
+
|
|
130
|
+
`accelerate estimate-memory` tells you how much memory **loading** a model takes.
|
|
131
|
+
That is not enough to know whether you can **train** it.
|
|
132
|
+
|
|
133
|
+
This project tries to answer the harder question. It models:
|
|
134
|
+
|
|
135
|
+
- Model weights, in fp32 / fp16 / bf16 / int8 / NF4 + double-quant
|
|
136
|
+
- LoRA / QLoRA trainable parameter count for typical `target_modules`
|
|
137
|
+
- Gradients only for trainable parameters
|
|
138
|
+
- AdamW vs 8-bit / paged AdamW optimizer states
|
|
139
|
+
- Activations as a function of `seq_len`, `batch_size`, `hidden_size`, `num_layers`,
|
|
140
|
+
with and without gradient checkpointing
|
|
141
|
+
- A fragmentation / CUDA / buffer safety margin
|
|
142
|
+
- A feasibility decision against your actual GPU
|
|
143
|
+
- Concrete degradation suggestions when not feasible
|
|
144
|
+
|
|
145
|
+
Estimates are **always** marked with an `assumptions` block and a `confidence`
|
|
146
|
+
level, because activation memory in particular is hard to predict statically.
|
|
147
|
+
Run `canifinetune bench` and `canifinetune calibrate` to ground them in real
|
|
148
|
+
measurements on your machine.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## RTX 4080 baselines
|
|
153
|
+
|
|
154
|
+
`docs/rtx4080_baselines.md` contains real measurements collected on a single
|
|
155
|
+
RTX 4080 (16 GB). These are not synthetic. If a configuration was not run, the
|
|
156
|
+
table says "not run", not a guessed number.
|
|
157
|
+
|
|
158
|
+
Highlights (more in the doc):
|
|
159
|
+
|
|
160
|
+
| model | method | seq_len | measured peak | tok/sec |
|
|
161
|
+
| --- | --- | --- | --- | --- |
|
|
162
|
+
| `Qwen/Qwen2.5-0.5B-Instruct` | qlora | 1024 | 3.30 GB | 1995 |
|
|
163
|
+
| `Qwen/Qwen2.5-1.5B-Instruct` | qlora | 1024 | 4.36 GB | 1352 |
|
|
164
|
+
| `Qwen/Qwen2.5-1.5B-Instruct` | qlora | 2048 | 7.10 GB | 1470 |
|
|
165
|
+
| `Qwen/Qwen2.5-3B-Instruct` | qlora | 1024 | 5.54 GB | 1158 |
|
|
166
|
+
| `sshleifer/tiny-gpt2` (smoke) | lora | 128 | 0.12 GB | 1735 |
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Repository layout
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
src/canifinetune/ # package code (estimator, bench, recipes, reports, cli)
|
|
174
|
+
benchmarks/ # configs/, results/ (JSON), calibration/
|
|
175
|
+
docs/ # design, memory model, troubleshooting
|
|
176
|
+
examples/ # end-to-end recipe folders
|
|
177
|
+
tests/ # pytest tests (CPU-only, no large downloads)
|
|
178
|
+
scripts/ # helper scripts for collecting baselines
|
|
179
|
+
.github/workflows/ # CI (ruff + pytest on CPU)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Roadmap
|
|
185
|
+
|
|
186
|
+
The current scope is "single consumer GPU, single node, LoRA / QLoRA, causal LM,
|
|
187
|
+
Hugging Face stack". Possible directions, none committed:
|
|
188
|
+
|
|
189
|
+
- DeepSpeed ZeRO and FSDP estimation for multi-GPU setups
|
|
190
|
+
- Heuristics for sequence-classification / encoder-decoder training
|
|
191
|
+
- Throughput modeling (tokens / sec), not just feasibility
|
|
192
|
+
- Auto-tuning of `gradient_accumulation_steps` for a target effective batch size
|
|
193
|
+
- A web UI on top of the CLI
|
|
194
|
+
|
|
195
|
+
Contributions welcome.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT. See `LICENSE`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
This folder will collect end-to-end recipes generated by `canifinetune recipe`.
|
|
4
|
+
We commit them so that someone browsing the repo can read the generated
|
|
5
|
+
`train.py` and `config.yaml` without having to install the package first.
|
|
6
|
+
|
|
7
|
+
To regenerate any of these from scratch:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
canifinetune recipe \
|
|
11
|
+
--model Qwen/Qwen2.5-1.5B-Instruct \
|
|
12
|
+
--method qlora \
|
|
13
|
+
--seq-len 2048 \
|
|
14
|
+
--lora-rank 16 \
|
|
15
|
+
--output examples/qwen_1_5b_qlora_4080
|
|
16
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# canifinetune-recipe
|
|
2
|
+
|
|
3
|
+
This folder was generated by `canifinetune recipe`. It is a self-contained,
|
|
4
|
+
single-GPU QLORA fine-tuning script for **Qwen/Qwen2.5-1.5B-Instruct**.
|
|
5
|
+
|
|
6
|
+
## Files
|
|
7
|
+
|
|
8
|
+
- `train.py` — main training entrypoint.
|
|
9
|
+
- `config.yaml` — all hyperparameters; pass with `--config`.
|
|
10
|
+
- `eval_smoke.py` — load the adapter, generate one completion as a smoke test.
|
|
11
|
+
- `data/sample.jsonl` — tiny synthetic instruction dataset (not for serious training).
|
|
12
|
+
- `requirements.txt` — Python deps. Match CUDA wheels to your driver.
|
|
13
|
+
- `expected_vram.md` — static estimate for this exact config.
|
|
14
|
+
- `dataset_format.md` — JSONL schema your real dataset should follow.
|
|
15
|
+
- `troubleshooting.md` — OOM / bitsandbytes / Windows tips.
|
|
16
|
+
- `run.sh` — convenience wrapper.
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# 1. Install deps in a fresh venv.
|
|
22
|
+
python -m venv .venv && source .venv/bin/activate # or .\.venv\Scripts\activate on Windows
|
|
23
|
+
pip install -r requirements.txt
|
|
24
|
+
# For CUDA wheels: e.g. `pip install torch --index-url https://download.pytorch.org/whl/cu121`
|
|
25
|
+
|
|
26
|
+
# 2. Smoke run with the bundled tiny dataset.
|
|
27
|
+
python train.py --config config.yaml --max-steps 5
|
|
28
|
+
|
|
29
|
+
# 3. Confirm the adapter loads.
|
|
30
|
+
python eval_smoke.py --adapter output/adapter
|
|
31
|
+
|
|
32
|
+
# 4. Replace data/sample.jsonl with your own JSONL and run again.
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Memory estimate (from `canifinetune estimate`)
|
|
36
|
+
|
|
37
|
+
- Method: **qlora**, base dtype **bf16**, quantization **nf4_double_quant**
|
|
38
|
+
- LoRA rank: **16**, target modules: `q_proj, k_proj, v_proj, o_proj`
|
|
39
|
+
- seq_len: **1024**, micro_batch_size: **1**
|
|
40
|
+
- Gradient checkpointing: **on**
|
|
41
|
+
- Optimizer: **paged_adamw_8bit**
|
|
42
|
+
- Attention impl: **sdpa**
|
|
43
|
+
|
|
44
|
+
Static feasibility on a 16.0 GB GPU: **yes**
|
|
45
|
+
(confidence: medium).
|
|
46
|
+
|
|
47
|
+
See `expected_vram.md` for the full breakdown. Run
|
|
48
|
+
`canifinetune bench --model Qwen/Qwen2.5-1.5B-Instruct --method qlora --steps 3` to
|
|
49
|
+
measure real VRAM on your machine.
|
|
50
|
+
|
|
51
|
+
## Caveats
|
|
52
|
+
|
|
53
|
+
- The bundled `data/sample.jsonl` exists so the smoke run completes; it is
|
|
54
|
+
far too small for any real fine-tune. Bring your own data.
|
|
55
|
+
- This recipe assumes a single GPU. For multi-GPU, regenerate with `accelerate`
|
|
56
|
+
/ DeepSpeed integration (not yet in `canifinetune`).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# canifinetune-recipe
|
|
2
|
+
|
|
3
|
+
This folder was generated by `canifinetune recipe`. It is a self-contained,
|
|
4
|
+
single-GPU LORA fine-tuning script for **sshleifer/tiny-gpt2**.
|
|
5
|
+
|
|
6
|
+
## Files
|
|
7
|
+
|
|
8
|
+
- `train.py` — main training entrypoint.
|
|
9
|
+
- `config.yaml` — all hyperparameters; pass with `--config`.
|
|
10
|
+
- `eval_smoke.py` — load the adapter, generate one completion as a smoke test.
|
|
11
|
+
- `data/sample.jsonl` — tiny synthetic instruction dataset (not for serious training).
|
|
12
|
+
- `requirements.txt` — Python deps. Match CUDA wheels to your driver.
|
|
13
|
+
- `expected_vram.md` — static estimate for this exact config.
|
|
14
|
+
- `dataset_format.md` — JSONL schema your real dataset should follow.
|
|
15
|
+
- `troubleshooting.md` — OOM / bitsandbytes / Windows tips.
|
|
16
|
+
- `run.sh` — convenience wrapper.
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# 1. Install deps in a fresh venv.
|
|
22
|
+
python -m venv .venv && source .venv/bin/activate # or .\.venv\Scripts\activate on Windows
|
|
23
|
+
pip install -r requirements.txt
|
|
24
|
+
# For CUDA wheels: e.g. `pip install torch --index-url https://download.pytorch.org/whl/cu121`
|
|
25
|
+
|
|
26
|
+
# 2. Smoke run with the bundled tiny dataset.
|
|
27
|
+
python train.py --config config.yaml --max-steps 5
|
|
28
|
+
|
|
29
|
+
# 3. Confirm the adapter loads.
|
|
30
|
+
python eval_smoke.py --adapter output/adapter
|
|
31
|
+
|
|
32
|
+
# 4. Replace data/sample.jsonl with your own JSONL and run again.
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Memory estimate (from `canifinetune estimate`)
|
|
36
|
+
|
|
37
|
+
- Method: **lora**, base dtype **fp32**, quantization **bf16**
|
|
38
|
+
- LoRA rank: **8**, target modules: `c_attn, c_proj`
|
|
39
|
+
- seq_len: **128**, micro_batch_size: **1**
|
|
40
|
+
- Gradient checkpointing: **off**
|
|
41
|
+
- Optimizer: **adamw_torch**
|
|
42
|
+
- Attention impl: **eager**
|
|
43
|
+
|
|
44
|
+
Static feasibility on a 16.0 GB GPU: **yes**
|
|
45
|
+
(confidence: medium).
|
|
46
|
+
|
|
47
|
+
See `expected_vram.md` for the full breakdown. Run
|
|
48
|
+
`canifinetune bench --model sshleifer/tiny-gpt2 --method lora --steps 3` to
|
|
49
|
+
measure real VRAM on your machine.
|
|
50
|
+
|
|
51
|
+
## Caveats
|
|
52
|
+
|
|
53
|
+
- The bundled `data/sample.jsonl` exists so the smoke run completes; it is
|
|
54
|
+
far too small for any real fine-tune. Bring your own data.
|
|
55
|
+
- This recipe assumes a single GPU. For multi-GPU, regenerate with `accelerate`
|
|
56
|
+
/ DeepSpeed integration (not yet in `canifinetune`).
|