# Thực hành tốt nhất với modelctl

> Hướng dẫn thực chiến để chuẩn bị bundle ONNX đáng tin cậy, tái lập được và an toàn cho biên bằng modelctl — hồ sơ cài đặt, cổng kiểm tra validate, lượng tử hóa, build tái lập, và CI.

Hướng dẫn mang tính chỉ dẫn để đưa một mô hình từ máy trạm lên thiết bị biên
air-gapped một cách **đáng tin cậy**. Về các lệnh từng bước, xem
[Chuẩn bị mô hình với modelctl](/vi/configure/prepare-models/) — trang này nói về
*vì sao* và *nên/không nên* trên nền quy trình đó.

## Chọn hồ sơ cài đặt nhỏ nhất

`modelctl` giữ các framework ML nặng ở dạng **tùy chọn**. Bản cài lõi chạy
`validate` và `package` mà không cần PyTorch hay TensorFlow — nên người vận hành có
thể xác minh một bundle trên máy gọn nhẹ. Chỉ thêm extra mà tác vụ cần.

  
**Validate / package**

    ```bash
    pip install modelctl            # chỉ onnx + onnxruntime + numpy
    ```
  
  
**Convert**

    ```bash
    pip install 'modelctl[pytorch]'      # hoặc [tensorflow] / [all]
    ```
  
  
**Quantize**

    ```bash
    pip install 'modelctl[quantize]'     # kéo ml_dtypes cho INT8
    ```
  

**quantize và tensorflow loại trừ lẫn nhau**

Extra `[quantize]` cần `ml_dtypes` mới hơn mức mà extra `[tensorflow]`/`[all]` cho
phép (TensorFlow giới hạn `ml-dtypes<0.4`). Hãy cài **một hồ sơ hoặc hồ sơ kia** —
convert với TensorFlow ở một môi trường, quantize ở môi trường khác.

## Bàn giao một nguồn tự chứa

Đưa cho máy convert một mô hình mà nó nạp được **mà không cần mã huấn luyện của bạn**:

- **PyTorch → TorchScript `.pt`** (`torch.jit.save`). Một `state_dict` trần bị từ
  chối (thiếu kiến trúc); một `nn.Module` pickle chỉ là phương án dự phòng tốt nhất
  có thể.
- **TensorFlow → thư mục SavedModel** (đường được hỗ trợ tốt; `.keras`/`.h5` cũng
  chạy).

Vì sao: máy trạm chạy `convert` khi đó không cần định nghĩa lớp mô hình — bản export
tái lập được chỉ từ artifact.

## Luôn ghim `--target-ort` theo thiết bị

Opset được kiểm tra theo ONNX Runtime của **biên**, không phải của máy trạm. Ghim
phiên bản của thiết bị để một bản export quá mới fail ngay trên bàn bạn, chứ không
phải ngoài hiện trường:

```bash
modelctl validate --model model.onnx --target-ort 1.18
```

Một mô hình export ở opset mà runtime của thiết bị không chạy được chính là cái bẫy
"chạy ở đây, fail ở kia" phổ biến nhất. Ghim đóng cái bẫy đó trước khi bundle rời đi.

## Coi `validate` là cổng phát hành

Validate là fail-soft — nó chạy **mọi** kiểm tra và báo từng cái, thay vì dừng ở lỗi
đầu tiên. Chặn việc ship nếu exit khác 0. Toàn bộ:

| Kiểm tra | Khẳng định điều gì |
|----------|--------------------|
| ONNX schema valid | `onnx.checker` chấp nhận graph |
| Opset supported | opset ≤ mức tối đa của edge ORT |
| Single input tensor | engine feed đúng một input, theo vị trí |
| Input dtype float32 | engine feed float32 |
| Input rank | khớp shape đã khai (khi có) |
| ONNX Runtime load | runtime khởi tạo được session |
| Test inference | một forward pass điền-zero thành công |

Ánh xạ exit code trong công cụ của bạn:

| Exit | Ý nghĩa |
|------|---------|
| `0` | mọi kiểm tra pass |
| `1` | một kiểm tra validation fail |
| `3` | framework converter (torch/TF) chưa được cài |
| `5` | validation sau convert fail |
| `7` | quantization fail (ví dụ thiếu `ml_dtypes`) |

## Lượng tử hóa có chủ đích, rồi validate lại

Dynamic INT8 (`modelctl quantize`, hoặc `--quantize` trên `convert`/`package`) đánh
đổi độ chính xác lấy kích thước và tốc độ. Hai quy tắc:

1. **Validate lại mô hình đã lượng tử hóa** và xác nhận chất lượng dự đoán trên dữ
   liệu đại diện trước khi ship — lượng tử hóa có thể làm lệch output.
2. **Đừng cho rằng nó co lại.** Với mô hình rất nhỏ, các node scale/zero-point của
   INT8 có thể lớn hơn phần tiết kiệm; lợi ích thể hiện ở mô hình cỡ thật.

**Quantize cần extra**

`quantize` / `package --quantize` cần `pip install 'modelctl[quantize]'`. Thiếu nó,
bạn nhận exit `7` rõ ràng nêu tên module thiếu — cài vào và chạy lại.

## Ship bundle tái lập được, kiểm chứng được

Ghim timestamp để các lần build lại byte-identical — thiết yếu cho audit và rà soát
thay đổi khi đưa lên hộp air-gapped:

```bash
modelctl package --model model.onnx --name pump-anomaly-v1 --timestamp 1700000000
# hoặc: export SOURCE_DATE_EPOCH=1700000000
```

Rồi kiểm tra toàn vẹn **trên thiết bị** trước khi kích hoạt:

```bash
cd pump-anomaly-v1 && sha256sum -c checksums.sha256
```

`checksums.sha256` phủ `model.onnx`, `metadata.json`, và `input_schema.json` — một
thao tác giả mạo hay sao chép thiếu bị bắt ngay tại biên, không phải lúc inference.

## Tôn trọng hợp đồng input của biên

Runtime feed **một tensor float32, theo vị trí**. Một mô hình nhiều input hoặc input
khác float32 *nạp được* nhưng chết lúc inference trên hộp. `validate` bắt việc này
trên máy trạm — hãy thiết kế mô hình export về một input float32 duy nhất, và giữ
**window size** cùng **số lượng đặc trưng** khớp với datasource bạn ghép nó (xem
[Triển khai mô hình](/vi/configure/models/)).

## Đưa `validate` vào CI

Chạy `validate` trên mỗi lần build mô hình và chặn artifact theo exit `0`. Kiểm tra
này không cần torch/TF nên job vẫn gọn:

```bash
pip install modelctl
modelctl validate --model build/model.onnx --target-ort 1.18 || exit 1
```

Bắt một bản export hỏng ngay trong pipeline thay vì phát hiện trên thiết bị bạn
không với tới được.

## Bước tiếp theo

  - [Chuẩn bị mô hình (cách làm)](/vi/configure/prepare-models/) — Các lệnh convert → validate → quantize → package.
  - [Triển khai mô hình](/vi/configure/models/) — Tải ONNX lên, ghép với datasource, kích hoạt.
  - [Thực hành vận hành tốt nhất](/vi/operate/best-practices/) — Vận hành box khỏe mạnh trong production.
