← All posts

Feeding Data to a Primary-School Math App: A Multimodal Fine-Tuning Log

5/28/2026 #AI Training #Fine-Tuning #Multimodal #Flutter #Case Study

Sample-material notice: the model outputs in the before/after comparison boxes below are desensitized sample data, used to demonstrate how the “before vs. after fine-tuning” difference is presented; the real evaluation text and accuracy metrics are to be filled in and replaced by the site owner later (see the “TODO” at the end).

The Problem: Why Photo-Grading Keeps Getting It Wrong

I’m building a math-grading app for primary schoolers (math_app, Flutter + Volcano Engine’s Doubao-Seed-1.6-flash multimodal API). The initial flow was naive: snap a photo of the homework, send it to the model with one generic prompt, and have it read the problem, compute the answer, and judge right or wrong.

Sounds simple; in practice it was full of pitfalls:

The core issue wasn’t “the model isn’t smart enough,” but that a general-purpose model hadn’t been aligned to the specific task of “grading.”

Data Prep: Breaking “Grading” into Annotatable Samples

I didn’t do parameter-level fine-tuning (small project, not cost-effective); instead I took the route of multimodal prompt engineering + few-shot alignment, first nailing down the task standard:

  1. Collect real homework photos (problem screenshot + student’s handwritten answer), about 200 after desensitization.
  2. Write structured annotations for each image: problem text / student answer / correct answer / error-cause label.
  3. Compile the recurring failure modes in the annotations (missed carry digits, misaligned answers) into negative-example prompts that explicitly tell the model “don’t do this.”

The data-prep stage is the most time-consuming, but it’s the foundation for every improvement that follows — the ceiling of the model is set by how clearly you define the task.

The Approach: Writing Prompts Like Programs

There were three key changes:

Evaluation: Quantifying with the Same Blind Test Set

I held back 50 blind-test problems that never entered the prompt, and ran each through “before fine-tuning (generic prompt)” and “after fine-tuning (aligned prompt),” then checked by hand.

The table below shows placeholder sample metrics; the real numbers are to be backfilled by the site owner using the live blind-test set.

MetricBefore (sample)After (sample)
Handwriting recognition accuracy~62%~91%
Grading-judgment accuracy~55%~88%
Output parse-ability~70%~100%

Before / After: The Same Long-Form Problem

Below is the same “23 × 7” long-form homework photo, with a real style comparison of the model before and after fine-tuning (desensitized):

Before
Recognized expression: 23 x 1 = 161
(Note: student's "7" misread as "1")
Judgment: the student got this wrong,
the correct answer should be 23×1=23…
but the image shows 161, can't be sure.
Output: a variable-length explanation
in prose, with no fixed fields.
After
{
  "problem": "23 × 7",
  "student_answer": "161",
  "correct_answer": "161",
  "is_correct": true,
  "error_cause": null,
  "comment": "Long-form layout is proper, carrying is correct."
}

Before fine-tuning: it read “7” as “1,” contradicted itself, and had an uncontrollable format. After: it transcribed character by character correctly, its judgment matched the answer, and it returned strict JSON. That’s the gap “aligning the task” brings — the model didn’t change; the task definition and constraints did.

Takeaways