Fine-Tune a Local SLM to Clean Master Data on Your Mac

⬅️ Back to Tutorials

By the end you have a ~600 MB model on disk that takes messy vendor, customer, or material JSON and returns normalized fields (ISO country codes, trimmed text, canonical IBANs, fixed dates and amounts) with a rule-based safety net behind it. Plan on 30–45 minutes on an Apple Silicon Mac; most of that is downloads and training, not typing.

This tutorial follows the free demo repo, Local-SLM-Data-Cleaner. The same approach ships in production form as Enterprise-SLM-Data-Cleaner—client-specific YAML rulebooks, an append-only audit log, air-gapped containers, CI quality gates, and swappable base models (including European options). The steps below are the demo; the enterprise section at the end is where you go when a laptop proof-of-concept is not enough.

TLDR:

  • Clone Local-SLM-Data-Cleaner, run make setup then make model.
  • make data builds 1,000 synthetic messy→clean pairs from deterministic rules in convention_spec.py (no real client data).
  • make baseline-serve + make baseline scores the stock Qwen3-0.6B before training; write down field accuracy.
  • make train fine-tunes with MLX LoRA; make fuse and make gguf produce qwen3-0.6b-cleaner-q8_0.gguf.
  • make serve + make eval + make demo prove the after score beats baseline and clean one live record.
  • For production: Enterprise-SLM-Data-Cleaner adds per-client conventions/*.yaml, make review, pinned container delivery, and make eval-gate adversarial CI.

Prerequisites: Mac with Apple Silicon (M1 or later), 8 GB RAM, ~5 GB disk, Homebrew. Intel Macs cannot run the MLX training step.

Step 1: Install tools and clone the repo

brew install python git llama.cpp
git clone https://github.com/TMFNK/Local-SLM-Data-Cleaner.git
cd Local-SLM-Data-Cleaner
make setup

make setup installs Python deps and mlx-lm. Success looks like >> Done. Next: make model with no red ERROR lines above it.

Step 2: Download the base model

make model

Pulls Qwen3-0.6B (~1.2 GB) from Hugging Face into your cache. No account needed. Done when it prints model ready.

Step 3: Generate synthetic training data

make data
make sanity

make data writes data/train.jsonl, valid.jsonl, and test.jsonl (default 800/100/100). The generator invents clean records, corrupts them like real messy master data, then labels each pair with the same deterministic algorithm the runtime uses later.

make sanity should report 100% field accuracy on the test split. That is the answer key checking itself, not a model score yet.

Want more examples? make data N=2000.

Step 4: Score the model before training

Terminal 1:

make baseline-serve

Wait for listening on http://127.0.0.1:8080. First run downloads ~600 MB.

Terminal 2 (same project folder):

make baseline

Note the field accuracy line. That is your before number. Stop the server in Terminal 1 with Ctrl+C before training.

Step 5: Fine-tune with MLX LoRA

make train

Loss should trend down over a few minutes. Output lands in adapters/. If the Mac runs out of memory, close browser tabs and retry with make train BATCH=2.

Step 6: Fuse and export to GGUF

make fuse
make gguf

If make gguf cannot find llama.cpp sources:

cd .. && git clone https://github.com/ggml-org/llama.cpp && cd Local-SLM-Data-Cleaner

You should see qwen3-0.6b-cleaner-q8_0.gguf (~600 MB) when ls *.gguf runs clean.

Step 7: Serve, evaluate, and demo

Terminal 1:

make serve

Terminal 2:

make eval
make demo

make eval should beat your Step 4 baseline on field accuracy. make demo sends one messy JSON record through the server and prints cleaned output plus a changes audit list.

Pro tip: Steps that call make baseline-serve, make serve, or make eval need two Terminal windows. The server holds port 8080 until you Ctrl+C it. If the port is busy, use make serve PORT=8081 and make eval PORT=8081.

Enterprise version: what changes for production

The demo hard-codes your house standard in convention_spec.py and optimizes for “clone it on a Mac and see it work.” Enterprise-SLM-Data-Cleaner keeps the same synthetic-training + LoRA + GGUF + llama.cpp pipeline but splits the repo for teams that need governance before they trust an SLM with master data.

Demo (this tutorial)Enterprise
One Python convention fileEditable YAML per client in conventions/
make eval on your laptopmake eval-gate + pinned adversarial suite in CI
Serve locally on port 8080deploy/ container with --network none and vendored weights pinned by hash
Qwen3-0.6B defaultSame default, or swap base model (MODEL=utter-project/EuroLLM-1.7B-Instruct, Teuken-7B, Phi-4-mini, …) and re-run the same eval bar
Demo output + changes listAppend-only audit log: input, output, every change, confidence, model and convention version hashes
make review lists uncertain records for manual sign-off; resolutions append new log entries, never edits

Layout in the enterprise repo: core/ (convention engine), conventions/ (client specs), synth/ (synthetic data), eval/ (harness + adversarial cases), runtime/ (model → validate → rule safety net), deploy/ (offline container). The enterprise README walks through each layer; deploy/README.md covers air-gapped operation.

Quick start once you have finished the demo and want the production Makefile:

git clone https://github.com/TMFNK/Enterprise-SLM-Data-Cleaner.git
cd Enterprise-SLM-Data-Cleaner
make setup
make data CONVENTION=conventions/default.yaml
make sanity
make eval-gate          # sanity + adversarial suites must pass before you trust a change
make train fuse gguf
make pin-model          # vendor weights + pin hash for the container
make serve
make eval
make review             # records waiting for human review

Swap the base model without changing the rest of the stack:

make model train fuse gguf MODEL=utter-project/EuroLLM-1.7B-Instruct
make eval

Enterprise is AGPL-3.0. Commercial licensing and help applying this to a real master-data migration are via mbitai.com.

Cleanup

# stop the model server in Terminal 1 with Ctrl+C
# optional: remove cloned llama.cpp sibling if you only needed it for gguf

If it breaks: Cannot reach the model server means the serve step is not listening yet. Address already in use means an old server is still on 8080. Training killed mid-run? Re-open Terminal, cd back into the repo, and continue; finished downloads and data/ files are still there. Demo troubleshooting: Local-SLM-Data-Cleaner README. Enterprise deploy and security notes: deploy/README.md.

Related TMFNK Content

Crepi il lupo! 🐺