Quay lại hướng dẫn
Kiến trúc·17 tháng 7, 2026·Cập nhật 30 tháng 9, 2026·Đọc 13 phút

Trích xuất dữ liệu hóa đơn: deep learning so với machine learning so với LLM (hướng dẫn 2026)

Trích xuất hóa đơn đã trải qua bốn thế hệ kỹ thuật — mẫu, machine learning cổ điển, deep learning tinh chỉnh và hiện nay là một lần gọi vision-LLM duy nhất. Dưới đây là cách chúng so sánh, cùng mô hình LLM hoàn chỉnh có thể chạy: mã, đầu ra có cấu trúc, guardrail và kinh tế trên mỗi hóa đơn.

Được xem xét lần cuối vào .

Năm năm trước, trích xuất dữ liệu có cấu trúc từ hóa đơn đồng nghĩa với một quy trình deep learning: một bước OCR, một mô hình nhận biết bố cục (LayoutLM, Donut) được tinh chỉnh trên hàng nghìn tài liệu gắn nhãn thủ công, cùng gánh nặng bảo trì mỗi khi nhà cung cấp thay đổi mẫu. Năm 2026, toàn bộ quy trình thu gọn thành một lần gọi API vision-LLM: đưa ảnh vào, nhận JSON đã xác thực theo schema — không dữ liệu huấn luyện, không mô hình lưu trữ, và bố cục mới được xử lý zero-shot. Hướng dẫn này từ Kunavo — một gateway độc lập, tương thích với OpenAI, đến các vision model mà mô hình này sử dụng — trình bày toàn bộ cách triển khai, chi phí trên mỗi hóa đơn được đo theo bảng giá công bố của chúng tôi, và cách duy trì độ chính xác ở cấp sản xuất.

So sánh các phương pháp trích xuất hóa đơn: quy tắc, machine learning, deep learning, LLM

Bốn thế hệ kỹ thuật đã được dùng để lấy dữ liệu có cấu trúc từ hóa đơn. Việc đặt chúng cạnh nhau là đáng giá, vì sự đánh đổi từng quyết định lựa chọn giữa chúng trong hai thập kỷ — mua độ chính xác bằng dữ liệu đã gắn nhãn — là điều không còn áp dụng.

Phương phápCách hoạt độngChi phí thiết lậpXử lý bố cục nhà cung cấp mới
Mẫu / quy tắc (từ thập niên 1990)OCR, sau đó là các vùng tọa độ và biểu thức regex theo từng nhà cung cấp (“tổng cộng là con số bên phải ‘TOTAL’”)Vài giờ cho mỗi nhà cung cấp, vĩnh viễnKhông — cần mẫu mới
Học máy cổ điển (thập niên 2010)OCR, sau đó đưa các đặc trưng được thiết kế thủ công (vị trí, cỡ chữ, các từ lân cận) vào CRF hoặc SVM để gắn thẻ từng tokenThiết kế đặc trưng + vài nghìn nhãnMột phần — suy giảm trên bố cục chưa từng gặp
Deep learning (từ năm 2020)Các transformer nhận biết bố cục (LayoutLM, Donut, DocTR) được tinh chỉnh để đọc đồng thời văn bản, vị trí và pixel3.000–50.000 hóa đơn đã gắn nhãn + chạy suy luận trên GPUThường không — thường cần gắn nhãn lại và huấn luyện lại
Vision LLM (từ năm 2024)Một lần gọi API: ảnh hóa đơn cộng JSON schema, đầu ra có cấu trúc bị ràng buộc theo schema đóKhông — một schema và một prompt; $0,00248–$0,00497 cho mỗi hóa đơnCó, zero-shot

Ba phương pháp đầu tiên đều có chung một đặc điểm: độ chính xác được mua bằng dữ liệu đã gắn nhãn. Mỗi mẫu nhà cung cấp, mỗi đặc trưng CRF, mỗi lần tinh chỉnh LayoutLM đều là một dự án chú thích. Vision LLM đã trả chi phí đó trong quá trình tiền huấn luyện, nên chi phí biên cho một bố cục mới bằng không. Đó là toàn bộ sự chuyển dịch — không phải các phương pháp cũ ngừng hoạt động, mà là thứ chúng từng tiêu ngân sách đã trở nên miễn phí.

Deep learning tinh chỉnh so với vision LLM, chi tiết

Mô hình bố cục tinh chỉnh (2021)LLM thị giác (2026)
Dữ liệu huấn luyện3.000–50.000 hóa đơn đã gắn nhãnKhông (zero-shot) — một vài ví dụ giúp xử lý các trường hợp biên
Bố cục nhà cung cấp mớiThường cần gắn nhãn lại + huấn luyện lạiXử lý zero-shot
Hạ tầngPhục vụ GPU + bước OCRMột cuộc gọi HTTPS
Định dạng đầu raThẻ token → giải mã tùy chỉnhJSON bị ràng buộc bởi schema
Chi phí trên mỗi hóa đơnThời gian kỹ thuật chiếm phần lớn$0,00248–$0,00497

Bộ trích xuất tinh chỉnh vẫn có thể thắng trên một bố cục cố định duy nhất có khối lượng lớn, nhưng với phần lớn hóa đơn trong thực tế — nhiều nhà cung cấp, ngôn ngữ và chất lượng bản quét khác nhau — vision LLM zero-shot chính xác hơn trong thực tế và rẻ hơn rất nhiều để vận hành. Xem thêm các mô hình cho loại khối lượng công việc này trong trường hợp sử dụng trích xuất dữ liệu.

Mô hình hoàn chỉnh: ảnh → JSON đã xác thực theo schema

Mọi thứ bên dưới đều chạy qua endpoint tương thích với OpenAI của Kunavo — trỏ base_url đến https://api.kunavo.com/v1 và cùng một đoạn mã có thể gọi Claude hoặc GPT chỉ bằng cách thay đổi một chuỗi:

extract_invoice.py
from openai import OpenAI
import base64, json

client = OpenAI(
    base_url="https://api.kunavo.com/v1",
    api_key="sk-kn-...",
)

SCHEMA = {
    "name": "invoice",
    "schema": {
        "type": "object",
        "properties": {
            "vendor_name":    {"type": "string"},
            "vendor_tax_id":  {"type": ["string", "null"]},
            "invoice_number": {"type": "string"},
            "invoice_date":   {"type": "string", "description": "ISO 8601"},
            "due_date":       {"type": ["string", "null"]},
            "currency":       {"type": "string", "description": "ISO 4217"},
            "line_items": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "description": {"type": "string"},
                        "quantity":    {"type": "number"},
                        "unit_price":  {"type": "number"},
                        "amount":      {"type": "number"},
                    },
                    "required": ["description", "amount"],
                },
            },
            "subtotal":  {"type": ["number", "null"]},
            "tax":       {"type": ["number", "null"]},
            "total":     {"type": "number"},
        },
        "required": ["vendor_name", "invoice_number", "invoice_date",
                     "currency", "line_items", "total"],
    },
}

def extract(path: str) -> dict:
    image_b64 = base64.b64encode(open(path, "rb").read()).decode()
    resp = client.chat.completions.create(
        model="claude-haiku-4-5",   # the cheapest capable model for clean invoices
        response_format={"type": "json_schema", "json_schema": SCHEMA},
        messages=[
            {"role": "system", "content":
                "Extract the invoice into the schema. Copy values exactly as "
                "printed; use null when a field is absent. Never invent data."},
            {"role": "user", "content": [
                {"type": "image_url",
                 "image_url": {"url": f"data:image/png;base64,{image_b64}"}},
            ]},
        ],
    )
    return json.loads(resp.choices[0].message.content)

inv = extract("invoice_0231.png")

# Cheap arithmetic guardrail: reject when the line items don't add up.
delta = abs(sum(li["amount"] for li in inv["line_items"])
            + (inv.get("tax") or 0) - inv["total"])
if delta > 0.02:
    raise ValueError(f"line items disagree with total by {delta:.2f}")

print(inv["vendor_name"], inv["total"], inv["currency"])

Ba chi tiết đảm nhiệm phần lớn công việc: JSON schema trong response_format (trên Claude, mô hình không thể phát ra bất kỳ thứ gì khác — xem ghi chú về GPT trong tài liệu tham khảo được liên kết bên dưới), quy tắc system prompt “sao chép giá trị chính xác; null khi không có; không bao giờ tự bịa” (loại bỏ mã số thuế bị bịa), và guardrail số học — các mục hàng + thuế phải bằng tổng cộng, nếu không tài liệu được chuyển sang mô hình mạnh hơn hoặc con người. Xem tài liệu tham khảo chat completions để biết response_format áp dụng điều gì trên từng họ mô hình.

Cùng schema dưới dạng mô hình Pydantic có kiểu

Một dict JSON-schema thô phù hợp với một endpoint nhưng trở nên khó chịu khi có mười endpoint. Trong sản xuất, hầu hết đội ngũ định nghĩa schema một lần dưới dạng mô hình Pydantic rồi suy ra cả ràng buộc lẫn đối tượng đã phân tích từ đó, để bộ trích xuất và phần còn lại của codebase không bao giờ bất đồng về cấu trúc:

schema_pydantic.py
from pydantic import BaseModel, Field
from typing import Literal
from openai import OpenAI

client = OpenAI(base_url="https://api.kunavo.com/v1", api_key=KEY)

class LineItem(BaseModel):
    description: str
    quantity: float | None = None
    unit_price: float | None = None
    amount: float

class Invoice(BaseModel):
    vendor_name: str
    vendor_tax_id: str | None = None
    invoice_number: str
    invoice_date: str = Field(description="ISO 8601")
    currency: str = Field(description="ISO 4217")
    line_items: list[LineItem]
    tax: float | None = None
    total: float
    # A confidence field the model fills in itself is a cheap, surprisingly
    # reliable router: low self-reported confidence correlates well with the
    # documents a human should see.
    confidence: Literal["high", "medium", "low"]

resp = client.chat.completions.create(
    model="claude-haiku-4-5",
    response_format={
        "type": "json_schema",
        "json_schema": {"name": "invoice", "schema": Invoice.model_json_schema()},
    },
    messages=[...],
)

invoice = Invoice.model_validate_json(resp.choices[0].message.content)

Có hai điều đáng sao chép ở đây. Các trường được định kiểu là float | None thay vì float cho mô hình một cách hợp lệ để nói “không có” — nếu không, một dòng thuế bị thiếu sẽ kéo mạnh mô hình về phía việc bịa ra số không. Và trường confidence tự báo cáo chỉ tốn ba token đầu ra nhưng định tuyến hiệu quả đáng ngạc nhiên; trường này không được hiệu chuẩn, nhưng “thấp” là tín hiệu đáng tin rằng con người nên xem xét.

Định tuyến hai tầng: hệ thống thực tế chạy gì

Dùng một mô hình cho mọi tài liệu là sai về mặt cấu trúc. Hóa đơn in rõ ràng — chiếm đại đa số — được xử lý bằng vision model rẻ nhất hiện có; phần khó (chữ viết tay, bản quét kém, bố cục bất thường) là nơi mô hình mạnh hơn xứng đáng với mức giá của nó. Hãy nâng cấp dựa trên lỗi xác thực, không dựa trên phỏng đoán:

two_tier_routing.py
CHEAP, STRONG = "claude-haiku-4-5", "claude-sonnet-5"

def valid(inv: dict) -> bool:
    """Arithmetic is the guardrail no confidence score replaces."""
    items = sum(li["amount"] for li in inv["line_items"])
    return abs(items + (inv.get("tax") or 0) - inv["total"]) <= 0.02

def extract_routed(path: str) -> tuple[dict, str]:
    inv = extract(path, model=CHEAP)
    if valid(inv) and inv.get("confidence") != "low":
        return inv, CHEAP

    # Escalate: same prompt, same schema, stronger model. Only the failures
    # pay the higher rate, which is what keeps the blended cost near the
    # cheap tier's.
    inv = extract(path, model=STRONG)
    if not valid(inv):
        raise NeedsHumanReview(path, inv)
    return inv, STRONG

# Blended cost at a 90/10 split, per 10,000 invoices:
#   9,000 x $0.00248 + 1,000 x $0.00497
#   = $27.29

Kiểm tra số học mới là phần thực sự tạo ra hiệu quả: đây là tín hiệu duy nhất độc lập với ý kiến tự đánh giá của mô hình. Một tài liệu có các mục hàng cộng lại bằng tổng đã nêu gần như không bao giờ sai theo cách quan trọng; tài liệu không vượt qua kiểm tra đáng để chuyển sang mô hình mạnh hơn hoặc con người, bất kể trường độ tin cậy nói gì.

Chi phí trên mỗi hóa đơn theo mô hình

Một hóa đơn một trang ≈ 1.800 token đầu vào (gửi dưới dạng ảnh) + ~350 token đầu ra JSON:

Mô hìnhMỗi hóa đơnMỗi 10.000 hóa đơnDùng cho
claude-haiku-4-5$0,00248$24,80Mặc định: hóa đơn in rõ ràng và các đợt chạy số lượng lớn rẻ nhất
claude-sonnet-5$0,00497$49,70Tầng nâng cấp: bản quét, chữ viết tay, trường hợp thất bại

Mức giá được cập nhật trực tiếp từ trang giá (thấp hơn khoảng 30% so với giá niêm yết của nhà cung cấp), và các yêu cầu thất bại không bị tính phí. Với định tuyến hai tầng — mọi thứ chạy qua Haiku, các trường hợp thất bại kiểm tra số học được nâng cấp lên Sonnet — 10.000 hóa đơn/tháng thường có chi phí dưới $27,29.

Độ chính xác: danh sách kiểm tra đưa bạn lên sản xuất

  1. Schema trước. Đánh dấu các trường thực sự bắt buộc là required, cho phép null ở mọi nơi khác — ép phải có giá trị đồng nghĩa với ép mô hình bịa.
  2. Gửi ảnh, không gửi văn bản OCR. Bố cục mang ý nghĩa (cột, ô tổng); vision model đọc trực tiếp bố cục. Chỉ chuyển sang văn bản với PDF kỹ thuật số khi bạn có thể trích xuất một lớp văn bản sạch.
  3. Xác thực số học trong code. Các mục hàng + thuế = tổng cộng phát hiện phần lớn lỗi trích xuất miễn phí.
  4. Nâng cấp, đừng mù quáng thử lại. Các trường hợp thất bại được gửi đến Sonnet cùng lỗi xác thực trong prompt; các trường hợp vẫn thất bại được chuyển đến hàng đợi xử lý thủ công.
  5. Few-shot các trường hợp biên thực sự. Hai hoặc ba ví dụ về bố cục tệ nhất của bạn (phiếu ghi có, đa tiền tệ) trong system prompt hiệu quả hơn mọi mức instruction tuning.

Ngoài hóa đơn

Mô hình bị ràng buộc bởi schema tương tự bao phủ biên lai, đơn mua hàng, phiếu giao hàng, biểu mẫu hải quan và sao kê ngân hàng — thay schema, giữ nguyên guardrail. Xem trường hợp sử dụng trích xuất dữ liệu có cấu trúc rộng hơn để biết các mô hình gần với RAG, hoặc hướng dẫn tối ưu hóa chi phí để biết chiến lược định tuyến khi khối lượng tăng. Một khóa Kunavo bao phủ mọi tầng mô hình được dùng ở đây — đăng ký miễn phí và tạo khóa để chạy nguyên đoạn mã.

Câu hỏi thường gặp

Tôi có cần mô hình học sâu để trích xuất dữ liệu từ hóa đơn không?

Không còn cần nữa. Quy trình truyền thống — OCR, sau đó là mô hình bố cục như LayoutLM được tinh chỉnh trên hàng nghìn hóa đơn đã gắn nhãn — đã được hầu hết đội ngũ thay thế bằng một lần gọi vision LLM duy nhất: gửi ảnh hóa đơn cùng một JSON schema và nhận lại dữ liệu có cấu trúc đã được xác thực. Không cần tập dữ liệu để gắn nhãn, không cần lưu trữ mô hình, và các thay đổi bố cục từng làm hỏng mô hình tinh chỉnh giờ được xử lý zero-shot.

Trích xuất hóa đơn bằng machine learning so với dùng LLM khác nhau thế nào?

Các bộ trích xuất machine learning cổ điển (CRF hoặc SVM trên đầu ra OCR cùng các đặc trưng vị trí và phông chữ được thiết kế thủ công) và các thế hệ deep learning kế nhiệm (LayoutLM, Donut) đều đánh đổi độ chính xác lấy dữ liệu đã gắn nhãn — hàng nghìn hóa đơn được chú thích cho mỗi lần triển khai, rồi gắn nhãn lại khi bố cục thay đổi. Vision LLM đã trả chi phí đó trong quá trình tiền huấn luyện, nên có thể đọc bố cục của nhà cung cấp chưa từng gặp theo cách zero-shot và trả về JSON bị ràng buộc bởi schema chỉ qua một lần gọi API. Machine learning vẫn thắng trên một bố cục duy nhất, cố định và có khối lượng cực lớn, nơi chi phí suy luận là yếu tố quyết định; với phần lớn hóa đơn thực tế, LLM vừa chính xác hơn trong thực tế vừa rẻ hơn nhiều để vận hành.

Tôi vẫn có thể dùng LayoutLM hoặc Donut để trích xuất hóa đơn không?

Có, và chúng vẫn hợp lý khi bạn xử lý hàng triệu tài liệu theo một bố cục ổn định duy nhất, đồng thời có thể phân bổ chi phí gắn nhãn và phục vụ GPU. Điều đã thay đổi là lựa chọn mặc định: với một dự án mới, vision LLM đạt độ chính xác sản xuất chỉ trong một buổi chiều mà không cần tập dữ liệu huấn luyện, và chi phí trên mỗi tài liệu đủ thấp để hiệu quả kinh tế của việc tinh chỉnh hiếm khi hoàn vốn. Một hướng trung gian phổ biến là chạy LLM trước rồi chỉ tinh chỉnh sau, dùng chính các đầu ra đã được LLM chấp nhận làm kho dữ liệu đã gắn nhãn.

Chi phí trích xuất hóa đơn trên mỗi tài liệu là bao nhiêu?

Một hóa đơn một trang có khoảng 1.800 token đầu vào (dưới dạng ảnh) cộng với ~350 token đầu ra JSON. Trên Kunavo, chi phí là khoảng $0,00248 cho mỗi hóa đơn với claude-haiku-4-5 và $0,00497 với claude-sonnet-5 — nghĩa là ngay cả ở chất lượng Sonnet, một nghìn hóa đơn cũng chỉ tốn vài đô la. Các yêu cầu thất bại không bị tính phí.

Tôi nên dùng mô hình nào để trích xuất dữ liệu hóa đơn?

Hãy bắt đầu với claude-haiku-4-5: mô hình này xử lý các hóa đơn in rõ ràng gần như hoàn hảo với mức giá thấp nhất. Chỉ chuyển các trường hợp thất bại — chữ viết tay, bản quét độ phân giải thấp, bố cục lạ hoặc tài liệu không vượt qua kiểm tra số học — sang claude-sonnet-5. Cách định tuyến hai tầng này thường giữ hơn 90% lưu lượng ở tầng giá rẻ.

Làm thế nào để đảm bảo đầu ra là JSON hợp lệ?

Hãy dùng structured outputs: truyền response_format cùng JSON schema và mô hình Claude sẽ bị ràng buộc phải phát ra chính xác cấu trúc đó — không cần hậu xử lý bằng regex. Thêm một guardrail rẻ ở cấp ứng dụng: kiểm tra tổng các mục hàng cộng thuế có bằng tổng cộng hay không, rồi chuyển các trường hợp không khớp sang mô hình mạnh hơn hoặc hàng đợi xử lý thủ công.

Tôi có thể trích xuất dữ liệu từ hóa đơn PDF chứ không chỉ ảnh không?

Có — hãy kết xuất từng trang PDF thành PNG (ví dụ bằng pdf2image) và gửi như trên. Với PDF kỹ thuật số, bạn cũng có thể trích xuất lớp văn bản và gửi dưới dạng văn bản thuần, tiết kiệm hơn; hãy giữ đường dẫn ảnh cho các bản quét.

Trích xuất hóa đơn bằng LLM chính xác đến mức nào?

Với hóa đơn in rõ ràng, việc trích xuất bằng vision bị ràng buộc bởi schema cùng guardrail số học thường đạt hơn 98% độ chính xác ở cấp trường — và không giống mô hình tinh chỉnh, độ chính xác vẫn giữ được trên các bố cục chưa từng thấy. Hãy đo trên tài liệu của chính bạn: 100 hóa đơn đã gắn nhãn là một tập đánh giá vững chắc.

Dữ liệu hóa đơn của tôi có được dùng để huấn luyện không?

Không — các yêu cầu qua Kunavo được chuyển đến nhà cung cấp mô hình theo điều khoản sử dụng API (không phải điều khoản ứng dụng dành cho người tiêu dùng) và không được dùng để huấn luyện mô hình. Xem tài liệu về thanh toán & dữ liệu để biết chi tiết lưu giữ.