# Lab Specification — Deep-Dive FTDD-01: Steer MiniCPM5-1B with SWIFT

**Course**: Course 3 — LLM Fine-Tuning Masterclass
**Deep-Dive**: FTDD-01 — MiniCPM Family (OpenBMB)
**Duration**: 30–40 minutes (a single-GPU hands-on lab)
**Environment**: A machine with a CUDA GPU (≥8GB VRAM recommended; the lab is designed to fit on an RTX 3060/4060 or better). Apple Silicon users should follow the MLX sidebar in Phase 5. Python 3.10+.

---

## Learning objectives

By the end of this lab you will have:

1. **Loaded MiniCPM5-1B** and generated a baseline response to a format-sensitive prompt — the "before" snapshot.
2. **Applied a format-steering LoRA** via SWIFT (OpenBMB's first-party framework) on a small slice of UltraChat — the "steer."
3. **Measured the before/after behavior shift** — felt, not just asserted, the FT00 thesis that fine-tuning steers behavior.
4. **Justified the framework choice** (SWIFT over LLaMA-Factory) and observed why the MiniCPM ecosystem reward first-party tooling.

This lab is deliberately small. The point is not to produce a production adapter — it is to make the steering thesis (FT00) tangible on the course's default base, in the time it takes to drink a coffee.

---

## Phase 0 — Set up (5 min)

Install SWIFT and pull the base.

```bash
# Recommended: a fresh venv
pip install 'ms-swift[llm]' --upgrade

# Confirm MiniCPM5-1B is reachable
swift infer --model openbmb/MiniCPM5-1B --max_length 64 \
  --prompt "List three planets. Respond as JSON."
```

If the `swift` command is not found, SWIFT installs as `ms-swift` and exposes the `swift` CLI. Confirm the model downloads (first run pulls ~2GB).

> **Why SWIFT and not LLaMA-Factory?** Per the deep-dive decision rule: even for the text model, SWIFT is the first-party option with the most reliable MiniCPM chat templates. This lab uses it to give you exposure to the OpenBMB-flavored path. For a multi-model pipeline, LLaMA-Factory is a valid alternative (FTDD-05 covers Axolotl for the general case).

---

## Phase 1 — Baseline: the "before" snapshot (5 min)

Run a format-sensitive prompt against the un-steered base. We use a JSON-output prompt because format is the canonical steering problem (FT00, FT12).

```bash
swift infer --model openbmb/MiniCPM5-1B --max_length 128 \
  --prompt "List three planets and their primary color. Respond as a JSON array of objects with keys 'planet' and 'color'."
```

**Record the output.** Note specifically:
- Did it produce valid JSON? (Often: no, or only approximately.)
- Did it include prose before/after the JSON? (Common: "Sure, here are...")
- Did it follow the exact key names (`planet`, `color`)?

This is the **before** snapshot. Save it — you will compare the after.

---

## Phase 2 — The steer: format LoRA on UltraChat (15 min)

SWIFT provides a self-contained SFT entrypoint. We use a tiny slice of UltraChat (the open SFT dataset from the Ultra* family) and a LoRA configuration.

```bash
swift sft \
  --model openbmb/MiniCPM5-1B \
  --dataset AI-ModelScope/UltraChat-200k \
  --dataset_sample 2000 \
  --lora_target_modules ALL \
  --lora_rank 8 \
  --max_length 1024 \
  --num_train_epochs 1 \
  --per_device_train_batch_size 4 \
  --gradient_accumulation_steps 2 \
  --learning_rate 1e-4 \
  --output_dir ./ftdd01-minicpm-format-lora
```

> **What this configuration does.** `--lora_target_modules ALL` applies LoRA to all linear layers (the modern default; FT09). `--lora_rank 8` keeps the adapter tiny. `--dataset_sample 2000` caps the run to keep it under 15 minutes on a single GPU. The point is a demonstrable behavior shift, not a converged adapter.

If VRAM is tight, drop `--per_device_train_batch_size` to 2 and increase `--gradient_accumulation_steps` to 4 (FT01 — the VRAM-lever tradeoff).

**Note:** the UltraChat slice is a general instruction dataset, not a JSON-specific one. The format shift you will observe is partly from instruction-tuning sharpening generally. For a JSON-specific steer, you would curate a small JSON-formatted dataset (FT04) — but for a 30-minute lab, UltraChat is enough to feel the effect.

---

## Phase 3 — Inference: the "after" snapshot (5 min)

Run the SAME prompt against the steered (LoRA-merged) model.

```bash
swift infer \
  --adapters ./ftdd01-minicpm-format-lora \
  --model openbmb/MiniCPM5-1B \
  --max_length 128 \
  --prompt "List three planets and their primary color. Respond as a JSON array of objects with keys 'planet' and 'color'."
```

**Record the output.** Compare to Phase 1:
- Is the JSON better-formed?
- Is the prose padding reduced?
- Are the key names closer to the specification?

---

## Phase 4 — The reflection (5 min)

Write 3–5 sentences answering: **Did the steer change the model's behavior? Did it change the model's knowledge?**

The expected finding: the steer changed the model's *behavior* (better instruction-following, cleaner format) without changing its *knowledge* (the planets and colors were already in the base — MiniCPM5-1B knew them). This is the FT00 thesis, felt on the course's default base. The LoRA did not teach the model astronomy; it steered the model toward the requested format.

If the shift was small, note it — a 2000-sample, 1-epoch LoRA is a weak steer by design. The point is direction, not magnitude.

---

## Phase 5 — Apple Silicon sidebar (optional, 5 min)

If you are on Apple Silicon (no CUDA), SWIFT's CUDA-optimized path is unavailable. Two options:

1. **Use the MLX backend** (FT20 covers MLX serving). MiniCPM5-1B runs in 4-bit on an M-series Mac with 16GB+ unified memory. The fine-tune step (Phase 2) may not be supported; in that case, run Phase 1 and Phase 3 against the un-steered base and the pre-trained instruct variant to observe the format difference.
2. **Use a hosted GPU** (Colab, a cloud instance) for the full lab. The lab is designed to fit on a free-tier Colab T4.

---

## Deliverables

Submit `ftdd01-steer-report.md` containing:

- [ ] The Phase 1 **before** output (verbatim).
- [ ] The Phase 3 **after** output (verbatim).
- [ ] The Phase 4 reflection (3–5 sentences on behavior vs knowledge).
- [ ] One sentence on why you used SWIFT rather than LLaMA-Factory for this lab.

---

## Solution key

The expected finding is a measurable format shift (cleaner JSON, less prose padding, better key-name adherence) in the "after" output, WITHOUT any change to the underlying knowledge (the planets and colors are unchanged from base). The reflection should name this as the FT00 steering thesis: the LoRA redirected probability mass toward the requested format; it did not inject astronomy.

If the before/after are nearly identical, the most likely causes are: (a) the UltraChat slice was too small or too general to shift format specifically — try a JSON-curated slice (FT04); (b) the LoRA rank was too low — try `--lora_rank 16`; (c) the learning rate was too low — try `5e-5` to `2e-4`. These are the same FT08/FT12 levers the course develops formally.

The SWIFT justification: first-party MiniCPM support, reliable chat templates, and (for this lab) exposure to the OpenBMB-flavored path. For a multi-model production pipeline, LLaMA-Factory would be defensible — but not for a single-model MiniCPM lab.

---

## Stretch goals

1. **Swap the adapter.** Train a second LoRA on UltraFeedback (the DPO dataset, arXiv:2310.01377) using SWIFT's DPO entrypoint. Hot-swap between the SFT and DPO adapters on the same base and observe the preference shift. This is the Layer-2 modularity (FT00) felt directly — swap the steer without touching the base.
2. **Try MiniCPM3-4B.** Repeat the lab on the denser text base. Observe how the same LoRA produces a clearer behavior shift on a more capable model — the gap between "steering a weak model" and "steering a capable model" (referenced in the deep-dive).
3. **Export to GGUF.** Use SWIFT's export path (or llama.cpp's convert script, FTDD-09) to produce a quantized GGUF of your steered model and serve it via Ollama. This closes the loop from Layer 3 (steer) through Layer 4 (export) — the full stack, on the course's default base.
