量化感知訓練
量化感知訓練(QAT)會在模擬 INT8 推論數值效果的同時微調 PyTorch 模型。 當訓練後量化造成無法接受的準確度損失,而且您可以使用具代表性的資料重新訓練模型時, 請使用 QAT。
將 SiMa QAT 加入現有的 PyTorch 訓練專案。保留資料集、前處理、資料增強、損失函數、 最佳化器設定與驗證指標。
盡可能從預先訓練的浮點檢查點開始。系統也支援從隨機初始化開始訓練,但通常需要 更多時間與資料。
QAT 的運作方式
準備程序會將觀察器與假量化運算加入模型。觀察器 會測量啟用值範圍,而假量化會在 正向傳遞期間對數值進行捨入與限幅,以近似 INT8 執行。張量、梯度和最佳化器更新仍 保持浮點格式,因此訓練可以調整模型權重以適應量化效果。
工作流程如下:
- 為 QAT 準備 eager PyTorch 模型。
- 使用一般訓練迴圈預熱觀察器。
- 凍結啟用值範圍與 SiMa 相容的權重縮放因子。
- 使用鎖定的縮放因子繼續訓練,以恢復準確度。
- 完成用於推論的模型。
- 匯出包含
QuantizeLinear與DequantizeLinear(QDQ)節點的標準 opset-17 ONNX 模型。
QDQ 節點描述匯出 ONNX 圖中浮點值與 INT8 值之間的轉換。
安裝
QAT wheel 需要 Python 3.10 或更新版本以及 PyTorch 2.8.x。請將它安裝到已包含 模型訓練相依套件的環境中。
使用 sima-cli 下載 QAT 套件:
sima-cli neat install qat
此命令會下載 wheel,並安裝或更新供 Codex 與 Claude 使用的 QAT 程式碼 Agent 技能。 它不會變更目前的 Python 環境。請啟用訓練環境,然後安裝下載的 wheel:
python -m pip install ./sima_qat-*.whl
python -c "import torch, sima_qat; print(torch.__version__, sima_qat.__file__)"
將 QAT 加入訓練專案
下列步驟組成一個連續的工作流程。請依現有訓練專案調整模型、資料、最佳化器、 損失函數與驗證呼叫。
1. 準備模型
請在建立最佳化器之前準備模型。準備程序會傳回獨立的 QAT 圖,不會修改或移動
來源模型或範例輸入。輸入 tuple 必須符合模型的位置輸入、資料類型與形狀。
device 引數會選擇傳回 QAT 圖的訓練裝置。訓練與驗證的批次資料也應位於該裝置。
import torch
from sima_qat import (
sima_export_onnx,
sima_finalize_qat_model,
sima_freeze_qat,
sima_prepare_qat_model,
)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Recreate the model and load the floating-point checkpoint.
source_model = MyModel()
source_model.load_state_dict(torch.load("model-fp32.pt", map_location="cpu"))
source_model.train()
example_inputs = (torch.randn(1, 3, 224, 224),)
qat_model = sima_prepare_qat_model(
source_model,
example_inputs,
device=device,
)
optimizer = torch.optim.AdamW(qat_model.parameters(), lr=1e-5)
criterion = torch.nn.CrossEntropyLoss()
2. 訓練、凍結與恢復
預熱時,觀察器會測量啟用值範圍,而假量化也已開始模擬 INT8。凍結會停止範圍與 縮放因子的更新,不會停止權重訓練。之後請繼續訓練,讓權重在固定設定下恢復準確度。
驗證時必須保留假量化並停用觀察器,避免驗證資料改變測量範圍。僅使用 eval()
不會停止觀察器。這個分類輔助函式會還原先前的模式與觀察器狀態,包括凍結時已停用
的觀察器。偵測或其他任務請調整指標。
from torch.ao.quantization import disable_observer
from torch.ao.quantization.fake_quantize import FakeQuantizeBase
def validate(model, loader, device):
was_training = model.training
observer_states = [
(module, module.observer_enabled.clone())
for module in model.modules()
if isinstance(module, FakeQuantizeBase)
]
correct = total = 0
try:
model.eval()
model.apply(disable_observer)
with torch.inference_mode():
for images, labels in loader:
images, labels = images.to(device), labels.to(device)
predictions = model(images).argmax(dim=1)
correct += (predictions == labels).sum().item()
total += labels.numel()
finally:
model.train(was_training)
for module, enabled in observer_states:
module.observer_enabled.copy_(enabled)
return correct / total
freeze_epoch = 2
num_epochs = 4
for epoch in range(num_epochs):
qat_model.train()
# Reserve one or more later epochs for recovery training.
if epoch == freeze_epoch:
sima_freeze_qat(qat_model)
for images, labels in train_loader:
images = images.to(device)
labels = labels.to(device)
optimizer.zero_grad(set_to_none=True)
predictions = qat_model(images)
loss = criterion(predictions, labels)
loss.backward()
optimizer.step()
accuracy = validate(qat_model, validation_loader, device)
print(f"Epoch {epoch}: validation accuracy {accuracy:.2%}")
此範例使用兩個預熱 epoch 與兩個恢復 epoch。請將它視為起點,依驗證結果選擇 凍結的 epoch 與恢復訓練的時間。
3. 儲存檢查點
請在完成模型前儲存準備後的模型,以便繼續訓練。檢查點應同時包含 QAT 模型與 最佳化器狀態。
from pathlib import Path
checkpoint_dir = Path("checkpoints")
checkpoint_dir.mkdir(parents=True, exist_ok=True)
torch.save(
{
"epoch": epoch,
"model": qat_model.state_dict(),
"optimizer": optimizer.state_dict(),
},
checkpoint_dir / f"qat-{epoch:02d}.pt",
)
若要稍後繼續訓練(選用),請使用相同的範例輸入與批次規格重新建立並準備相同模型,再載入 儲存的狀態:
checkpoint = torch.load("checkpoints/qat-03.pt", map_location="cpu")
source_model = MyModel()
source_model.load_state_dict(torch.load("model-fp32.pt", map_location="cpu"))
qat_model = sima_prepare_qat_model(
source_model,
example_inputs,
device=device,
)
optimizer = torch.optim.AdamW(qat_model.parameters(), lr=1e-5)
qat_model.load_state_dict(checkpoint["model"])
optimizer.load_state_dict(checkpoint["optimizer"])
start_epoch = checkpoint["epoch"] + 1
檢查點會保留觀察器狀態、凍結狀態,以及稍後用於完成模型與 ONNX 匯出的精確 量化參數。
4. 完成與匯出
完成程序會將訓練後的 QAT 模型轉換成僅供推論的圖;匯出則會將該圖寫入 ONNX。 完成後的模型無法繼續訓練。
final_model = sima_finalize_qat_model(qat_model)
accuracy = validate(final_model, validation_loader, device)
sima_export_onnx(
final_model,
example_inputs,
"model.qdq.onnx",
input_names=["images"],
output_names=["predictions"],
)
| 操作 | 裝置行為 |
|---|---|
| 準備 | 使用所選的訓練裝置。 |
| 完成 | 維持 QAT 模型的裝置。 |
| 匯出 | 暫時使用 CPU,之後將模型移回原本裝置,即使失敗也會還原。輸入維持不變。 |
省略匯出的 device 引數即可維持模型的裝置。明確指定值會在匯出成功後移動
PyTorch 模型,而非指定 ONNX 的執行裝置。
批次大小
準備程序預設會將張量的首個維度(即 Batch 維度)保持為動態。因此,同一個準備後的圖可以使用
一般資料載入器的批次大小進行訓練、處理最後一個較短的批次,並使用傳給
sima_export_onnx 的具體批次大小匯出。
大多數模型不需要批次選項。只有模型刻意要求與範例完全相同的批次大小時,才設定
dynamic_batch=False:
qat_model = sima_prepare_qat_model(
source_model,
example_inputs,
device=device,
dynamic_batch=False,
)
當模型對批次大小進行斷言或依其分支、使用固定大小的循環狀態,或將批次合併到方向、 通道或其他版面配置幾何時,適合使用固定批次擷取。動態擷取會在提供的範例上比較 擷取結果與原始模型輸出;若行為改變,則會失敗並提示停用動態批次。