Skip to content

Hands-on: complete the training script and run it in Docker

Module 5 — Training and deploying to several targets · Slides: slides.md · Module overview · Course page

Fill four points in s12_train.py matching training’s four beats — fit, the representative dataset, int8 I/O and accuracy — then run it in Docker until you get model_int8.tflite and a float32, int8 and confusion-matrix report for a model you trained entirely yourself, and experiment with fewer epochs and a different calibration set.

By the end of this lesson, you will:

  1. Fill the four points in practice/s12_train.py so a Docker run prints float32 test accuracy, int8 test accuracy and a confusion matrix, and writes model_int8.tflite and its .norm.npz.
  2. Run once at 3 epochs and once at 25, record both accuracies, and explain the difference using the word underfitting.
  3. Tell from the symptom which point is still empty (accuracy near 0.33, a ValueError at convert, eval failing to feed int8, or accuracy 0.000).

You’ve been through lesson 5.4, and understand the four beats: build, fit, convert, eval. Copy practice/s12_train.py into shared/training, next to dataset_tools.py, and have data/gestures.csv ready.

The training file already gives you dataset_tools, build_model, and the quantize/dequantize sections in full. Four points remain, matching the four beats: (1) in main(), after compile, fill in model.fit(Xtr, ytr, validation_data=(Xva, yva), epochs=a.epochs, batch_size=32, verbose=2). Forget this, and the model never learns — accuracy sits around 0.33 (guessing among three classes). (2) In to_int8_tflite(), fill in conv.representative_dataset = representative. Forget it, and convert() stops with a ValueError. (3) Fill in conv.inference_input_type = tf.int8 and conv.inference_output_type = tf.int8. Forget it, and the input and output stay float32 (the scale in quantization is 0), so eval_int8(), which feeds int8, fails to run. (4) In eval_int8(), fill in acc = (preds == yte).mean(). Forget it, and it reports 0.000 even though the confusion matrix’s diagonal might be entirely correct.

Run it with docker run --rm -v "$PWD":/work edgeai-train python s12_train.py, and you’ll get float32 test accuracy → int8 test accuracy → a confusion matrix, and the files model_int8.tflite and model_int8.tflite.norm.npz. Success isn’t just watching numbers run — you should be able to say why int8 should be close to float32, and what the representative dataset is for. This file is the artifact the next pair of lessons will carry into a browser and onto the board.

s12_train_full.py synthesizes its own data if data/gestures.csv doesn’t exist yet, prints model.summary() and count_params(), warns when int8’s accuracy drops more than INT8_DROP_WARN = 0.05 below float32, and has an MVP gate that passes when int8 accuracy ≥ MVP_MIN_ACC = 0.80 (returning exit code 0 or 1, usable in CI). It sits in shared/training, alongside the practice file.

File What this file teaches
examples/s12_train_full.py Train + test a hand-gesture model, end to end (full version)

This lesson’s slides also reference files in another lesson and under shared/:

The # TODO: comments are at lines 131 (point 1, model.fit), 53 (point 2, representative_dataset), 60 (point 3, int8 I/O), and 97 (point 4, acc). Fill them in one at a time and run it — the symptom of whichever point is still empty tells you which one’s left.

Practice file Topic
practice/s12_train.py Training our own model with TensorFlow, then testing it on the PC (the fill-in-the-code version)

Open the solution after trying on your own at least once, and read how to use the solutions first.

Solution Pairs with
solution/s12_train.py practice/s12_train.py

The same questions are in quiz.yaml for automated checking.

  1. Every run gives float32 test accuracy: 0.333. Which point is still empty? (single choice · objective 1)

    • a) Point 1, model.fit
    • b) Point 2, representative_dataset
    • c) Point 3, int8 I/O
    • d) Point 4, acc
    Solution

    a — without fitting, the weights stay random, so the model guesses right about one time in three among three classes, and evaluate measures about 0.33.

  2. convert() stops with ValueError: For full integer quantization, a representative_dataset must be specified. Which point needs fixing? (single choice · objective 3)

    • a) Point 1
    • b) Point 2, fill in conv.representative_dataset = representative
    • c) Point 3
    • d) Point 4
    Solution

    b — the message says directly that full-integer compression needs samples to calibrate the value range against.

  3. The report says int8 test accuracy: 0.000, but the confusion matrix’s diagonal is fully populated with correct numbers. Which point is still empty? (single choice · objective 3)

    • a) Point 1
    • b) Point 2
    • c) Point 3
    • d) Point 4, acc is still its starting value, 0.0
    Solution

    d — the confusion matrix is computed from the real preds, but the accuracy number comes from acc, still 0.0. It needs acc = (preds == yte).mean() filled in.

  4. Training for 3 epochs gives both accuracy and val_accuracy clearly lower than at 25 epochs. How would you explain this? (single choice · objective 2)

    • a) Overfitting
    • b) Underfitting, because training was too short — the loss hasn’t finished descending yet
    • c) int8 dropped the accuracy
    • d) Data leakage
    Solution

    b — both train and val being low together is the symptom of underfitting. Giving the model more time to descend the loss’s slope improves it.

The MVP for lessons 5.3–5.5: successfully train a Keras model in Docker, getting a report of float32 accuracy, int8 accuracy and a confusion matrix on a test set the model has never seen, along with the model_int8.tflite and .norm.npz files.

  • All four points in the practice file are filled in, and running it in Docker produces the report and both files.
  • Run with --epochs 3 compared to 25 epochs, note both accuracies in your learning log, and explain the difference.
  • Have representative() feed all-zero windows (np.zeros_like(X_repr[i:i + 1])) instead of real ones, and compare int8 accuracy against the original.
  • Be able to explain where fit, representative_dataset, inference_input_type and accuracy sit, and what each does.

In the next pair of lessons (5.6–5.7), we’ll take this file and run it in a browser, prove its verdict matches the PC’s, and tell the Cortex-A story.

Next lesson: lesson 5.6 — Running a model on the web: LiteRT.js, int8 I/O and parity

  • How far apart are your int8 and float32 accuracies, and how much would you trust that number when the test set only has about twenty windows?
  • If real data gives much lower accuracy than synthetic data, where would you start fixing it?

Review questions

Answer on your own first, then open the answer.

  1. Every run prints float32 test accuracy: 0.333. Which point is still empty? (Objective 1)

    1. ช่อง 1 model.fit
    2. ช่อง 2 representative_dataset
    3. ช่อง 3 int8 I/O
    4. ช่อง 4 acc
    Show answer

    Answer: A. ช่อง 1 model.fit

    ถ้าไม่ fit น้ำหนักยังเป็นค่าสุ่ม โมเดลเดาในสามคลาสได้ราวหนึ่งในสาม evaluate จึงวัดได้ราว 0.33

  2. convert() stops with "ValueError: For full integer quantization, a representative_dataset must be specified." Which point do you fix? (Objective 3)

    1. ช่อง 1
    2. ช่อง 2 เติม conv.representative_dataset = representative
    3. ช่อง 3
    4. ช่อง 4
    Show answer

    Answer: B. ช่อง 2 เติม conv.representative_dataset = representative

    ข้อความบอกตรง ๆ ว่าการบีบแบบ full-integer ต้องมีตัวอย่างไว้ calibrate ช่วงค่า

  3. The report says int8 test accuracy: 0.000 but the confusion matrix is full on the diagonal. Which point is still empty? (Objective 3)

    1. ช่อง 1
    2. ช่อง 2
    3. ช่อง 3
    4. ช่อง 4 acc ยังเป็นค่าเริ่มต้น 0.0
    Show answer

    Answer: D. ช่อง 4 acc ยังเป็นค่าเริ่มต้น 0.0

    confusion matrix คำนวณจาก preds จริง แต่ตัวเลขความแม่นมาจาก acc ที่ยังเป็น 0.0 ต้องเติม acc = (preds == yte).mean()

  4. With 3 epochs both accuracy and val_accuracy are clearly lower than with 25. How do you explain it? (Objective 2)

    1. overfit
    2. underfit เพราะฝึกสั้นเกินไป loss ยังลงไม่สุด
    3. int8 ทำให้แม่นตก
    4. ข้อมูลรั่ว
    Show answer

    Answer: B. underfit เพราะฝึกสั้นเกินไป loss ยังลงไม่สุด

    ทั้ง train และ val ต่ำด้วยกันคืออาการ underfit ให้เวลาโมเดลไถลลงเนิน loss มากขึ้นก็ดีขึ้น

Cite this lesson

If you teach from this lesson or reuse it in slides or documents, credit it with the text below. If you changed it, add (adapted) after the title.

"Hands-on: complete the training script and run it in Docker" from TESA Open Knowledge by the Thai Embedded Systems Association (TESA), https://github.com/tesaiot/tesa-qualification-program, licensed under CC BY-NC 4.0

Thai attribution: "ลงมือทำ: เติมสคริปต์ฝึกแล้วรันใน Docker" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0

Lesson link: https://tesaiot.github.io/tesa-qualification-program/en/courses/edge-ai-developer/m05-training/l05-train-lab/

Full guide: how to cite TESA

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0

Content is licensed CC BY-NC 4.0. Reuse it non-commercially and credit the Thai Embedded Systems Association (TESA) every time. · How to cite TESA