從圖片抽取結構化資料:發票與表單的實作模式
單據辨識不要用自由問答,用 schema 約束加上欄位驗證。
發票、收據、申請表這類單據的辨識,重點不是「看懂」,而是穩定地填滿指定欄位。做法與一般圖片問答很不一樣。
用 schema 定義輸出
{
"type": "object",
"required": ["invoice_no", "date", "total", "items"],
"properties": {
"invoice_no": {"type": "string", "description": "發票號碼,兩碼英文加八碼數字"},
"date": {"type": "string", "description": "YYYY-MM-DD"},
"total": {"type": "number", "description": "含稅總金額"},
"tax": {"type": ["number", "null"]},
"items": {"type": "array", "items": {"type": "object", "properties": {
"name": {"type": "string"}, "qty": {"type": "number"}, "price": {"type": "number"}
}}}
}
}用業務規則驗證,不要只信模型
def validate(inv):
errs = []
if not re.fullmatch(r'[A-Z]{2}\d{8}', inv['invoice_no']):
errs.append('發票號碼格式不符')
calc = sum(i['qty'] * i['price'] for i in inv['items'])
if abs(calc - inv['total']) > max(1, inv['total'] * 0.01):
errs.append(f"明細加總 {calc} 與總額 {inv['total']} 不符")
return errs加總驗證是最有價值的一條規則:它能抓出「某一列明細被漏讀」這種模型自己不會發現的錯誤。
低信心就轉人工
設計上必須有人工複核的出口。驗證失敗、關鍵欄位為空、或模型信心低時,把案件送進人工佇列,而不是硬吞一個可能錯誤的結果進系統。
保留原圖與座標
人工複核時要能看到原圖並直接對照欄位位置。若模型能回傳每個欄位的來源座標,複核效率會差很多。
把人工修正的結果回收成評測集。單據辨識是少數能自然累積高品質標註資料的場景,別浪費。