-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
175 lines (154 loc) · 8.13 KB
/
Copy pathMakefile
File metadata and controls
175 lines (154 loc) · 8.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# =============================================================================
# Local SLM Data Cleaner: beginner-friendly pipeline.
#
# New here? Run `make help` to list every command, then follow the numbered
# steps in the README. Each command below prints what it is doing.
#
# You override any setting on the command line, for example:
# make data N=2000 # make 2000 examples instead of 1000
# make train ITERS=1500 # train for more steps
# =============================================================================
# ---- settings you can override on the command line -----------------------
# Keep values on their own lines with no inline comments. A trailing-space
# comment would become part of the value and break commands like `-hf repo:quant`.
#
# MODEL_PRESET model family shortcut: qwen3-0.6b, qwen3.5-0.8b, minicpm5-1b
# MODEL base model to fine-tune (auto-downloads)
# GGUF_HF stock GGUF repo for the zero-shot baseline
# GGUF_QUANT quant level to download/build (Q8_0, Q4_K_M, ...)
# N number of synthetic examples
# SEED random seed (same seed = same data)
# ITERS training steps
# BATCH training batch size
# LAYERS how many layers LoRA touches
# PORT local server port
# ALIAS model name the eval/clean scripts look for
# LLAMA_CPP path to a llama.cpp source checkout (only for `make gguf`)
MODEL_PRESET ?= qwen3-0.6b
# Model presets — set MODEL_PRESET to quickly switch between supported models.
# Each preset sets MODEL, GGUF_HF, GGUF_QUANT, and ALIAS. Override any
# individually on the command line (e.g. `make train MODEL=...`).
# Switching presets? Run `make clean` first so adapters/GGUF from the old model
# are not reused.
ifeq ($(MODEL_PRESET),qwen3-0.6b)
MODEL ?= Qwen/Qwen3-0.6B
GGUF_HF ?= Qwen/Qwen3-0.6B-GGUF
GGUF_QUANT ?= Q8_0
ALIAS ?= qwen3-0.6b-cleaner
else ifeq ($(MODEL_PRESET),qwen3.5-0.8b)
MODEL ?= Qwen/Qwen3.5-0.8B
GGUF_HF ?= unsloth/Qwen3.5-0.8B-GGUF
GGUF_QUANT ?= Q8_0
ALIAS ?= qwen3.5-0.8b-cleaner
else ifeq ($(MODEL_PRESET),minicpm5-1b)
MODEL ?= openbmb/MiniCPM5-1B
GGUF_HF ?= openbmb/MiniCPM5-1B-GGUF
GGUF_QUANT ?= Q4_K_M
ALIAS ?= minicpm5-1b-cleaner
else
$(error Unknown MODEL_PRESET '$(MODEL_PRESET)'. Run 'make list-models'.)
endif
N ?= 1000
SEED ?= 0
ITERS ?= 1000
BATCH ?= 4
LAYERS ?= 8
PORT ?= 8080
DATA ?= data
ADAPTERS ?= adapters
FUSED ?= fused
GGUF ?= $(ALIAS).gguf
# GGUF quant file uses lowercase version of GGUF_QUANT in its name.
# E.g. Q8_0 -> -q8_0, Q4_K_M -> -q4_k_m.
QGGUF_SUFFIX = $(shell echo $(GGUF_QUANT) | tr 'A-Z' 'a-z')
QGGUF ?= $(ALIAS)-$(QGGUF_SUFFIX).gguf
LLAMA_CPP ?= ../llama.cpp
PY ?= python3
.DEFAULT_GOAL := help
.PHONY: help list-models setup model data sanity baseline-serve baseline train fuse gguf \
serve eval demo all clean distclean
help: ## show this list of commands
@echo "Local SLM Data Cleaner: commands (run them in this order):"
@echo ""
@echo "Model preset: $(MODEL_PRESET) (run 'make list-models' for options)"
@echo ""
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
| awk 'BEGIN{FS=":.*?## "}{printf " \033[36m%-14s\033[0m %s\n", $$1, $$2}'
list-models: ## show available model presets
@echo "Available MODEL_PRESET values:"
@echo ""
@echo " qwen3-0.6b Qwen3-0.6B (default) — 600M params, ~1 GB, Apache-2.0"
@echo " qwen3.5-0.8b Qwen3.5-0.8B — 800M params, ~1.5 GB, Apache-2.0"
@echo " minicpm5-1b MiniCPM5-1B — 1B params, SOTA in class, Apache-2.0"
@echo ""
@echo "Usage: make MODEL_PRESET=minicpm5-1b <command>"
@echo "Set it once: export MODEL_PRESET=minicpm5-1b"
@echo "Switch preset: make clean (drops adapters/GGUF from the previous model)"
# --- STEP 3: install the tools --------------------------------------------- #
setup: ## STEP 3: install Python libraries + the trainer (mlx-lm)
@echo ">> Installing Python packages (requests)..."
$(PY) -m pip install -r requirements.txt
@echo ">> Installing mlx-lm (the fine-tuning engine for Apple Silicon)..."
$(PY) -m pip install mlx-lm
@echo ">> Installing llama.cpp (runs the model) via Homebrew, if available..."
@command -v brew >/dev/null 2>&1 && brew install llama.cpp \
|| echo " !! Homebrew not found. Install it from https://brew.sh then run: brew install llama.cpp"
@echo ">> Done. Next: make model"
# --- STEP 4: download the model FRESH -------------------------------------- #
model: ## STEP 4: download the base model from Hugging Face
@echo ">> Model preset: $(MODEL_PRESET)"
@echo ">> Downloading $(MODEL) (first time only, no login needed)..."
@echo ">> It caches in ~/.cache/huggingface so later steps are instant."
$(PY) -c "from mlx_lm import load; load('$(MODEL)'); print('model ready')"
@echo ">> Done. Next: make data"
# --- STEP 5: make the training data ---------------------------------------- #
data: ## STEP 5a: generate synthetic train/valid/test data (N, SEED)
@echo ">> Generating $(N) synthetic messy->clean examples..."
$(PY) synth/generate.py --n $(N) --out $(DATA) --seed $(SEED)
@echo ">> Done. Next: make sanity"
sanity: ## STEP 5b: check the data is correct (should say 100%)
@echo ">> Checking the held-out test split against the rule-based algorithm..."
$(PY) eval/evaluate.py --data $(DATA)/test.jsonl --algorithm
@echo ">> If the numbers are ~100%, the data is good. Next: make baseline-serve"
# --- STEP 6: measure the model BEFORE training (the 'before' number) ------- #
baseline-serve: ## STEP 6a: serve the STOCK model (downloads it fresh), keep running
@echo ">> Downloading + serving the untrained stock model on port $(PORT)."
@echo ">> Leave this running and open a SECOND terminal for 'make baseline'."
llama-server -hf $(GGUF_HF):$(GGUF_QUANT) --port $(PORT) --alias $(ALIAS)
baseline: ## STEP 6b: score the stock model (run in the 2nd terminal)
@echo ">> Scoring the untrained model (this is your 'before' score)..."
$(PY) eval/evaluate.py --data $(DATA)/test.jsonl --live --port $(PORT) --model-name $(ALIAS)
# --- STEP 7: fine-tune ------------------------------------------------------ #
train: ## STEP 7: fine-tune the model on your data (takes a while)
@echo ">> Fine-tuning $(MODEL) with LoRA for $(ITERS) steps..."
@echo ">> Stop the baseline-serve terminal first to free memory."
mlx_lm.lora --model $(MODEL) --train --data ./$(DATA) \
--iters $(ITERS) --batch-size $(BATCH) --num-layers $(LAYERS) \
--adapter-path $(ADAPTERS)
@echo ">> Done. Next: make fuse"
fuse: ## STEP 8a: merge the training result back into the model
@echo ">> Merging the LoRA adapter into full model weights..."
mlx_lm.fuse --model $(MODEL) --adapter-path $(ADAPTERS) --save-path $(FUSED)
@echo ">> Done. Next: make gguf"
gguf: ## STEP 8b: convert the model to a runnable file (needs llama.cpp source)
@echo ">> Converting to GGUF (the format llama.cpp runs)..."
$(PY) $(LLAMA_CPP)/convert_hf_to_gguf.py $(FUSED) --outfile $(GGUF)
@echo ">> Compressing to $(GGUF_QUANT)..."
llama-quantize $(GGUF) $(QGGUF) $(GGUF_QUANT)
@echo ">> Done. Next: make serve"
# --- STEP 9: measure the model AFTER training (the 'after' number) --------- #
serve: ## STEP 9a: serve YOUR fine-tuned model, keep running
@echo ">> Serving your fine-tuned model on port $(PORT)."
@echo ">> Leave this running and open a SECOND terminal for 'make eval'."
llama-server -m $(QGGUF) --port $(PORT) --alias $(ALIAS)
eval: ## STEP 9b: score your fine-tuned model (compare to the baseline)
@echo ">> Scoring your fine-tuned model (this is your 'after' score)..."
$(PY) eval/evaluate.py --data $(DATA)/test.jsonl --live --port $(PORT) --model-name $(ALIAS)
demo: ## STEP 10: clean one messy record with your model
$(PY) clean.py --live --port $(PORT) --model-name $(ALIAS)
all: data sanity train fuse gguf ## do steps 5, 7 and 8 in one go (no serving)
@echo ">> Built $(QGGUF). Now run 'make serve', then 'make eval' in a 2nd terminal."
clean: ## delete training artifacts (keeps your data)
rm -rf $(ADAPTERS) $(FUSED) *.gguf __pycache__ */__pycache__
distclean: clean ## delete training artifacts AND generated data
rm -f $(DATA)/*.jsonl