Retour aux guides
Architecture·17 juillet 2026·Mis à jour le 30 septembre 2026·13 min de lecture

Extraction de données de factures : deep learning vs machine learning vs LLM (guide 2026)

L’extraction de factures est passée par quatre générations de techniques — modèles, machine learning classique, deep learning ajusté finement, et désormais un appel unique à un vision-LLM. Voici leur comparaison, ainsi que le schéma LLM complet et opérationnel : code, sortie structurée, garde-fous et économie par facture.

Dernière vérification le .

Il y a cinq ans, extraire des données structurées de factures signifiait mettre en place un pipeline de deep learning : une étape OCR, un modèle tenant compte de la mise en page (LayoutLM, Donut) ajusté sur des milliers de documents annotés manuellement, et une charge de maintenance chaque fois qu’un fournisseur modifiait son modèle. En 2026, l’ensemble du pipeline se réduit à un appel API à un LLM de vision : image en entrée, JSON validé par schéma en sortie — aucune donnée d’entraînement, aucun modèle hébergé et nouvelles mises en page traitées en zero-shot. Ce guide de Kunavo — une passerelle indépendante compatible OpenAI vers les modèles de vision utilisés par cette approche — présente le fonctionnement complet, son coût par facture calculé à partir de nos tarifs publiés et la manière de maintenir une précision adaptée à la production.

Comparaison des approches d’extraction de factures : règles, apprentissage automatique, deep learning, LLM

Quatre générations de techniques ont été utilisées pour extraire des données structurées d’une facture. Il est utile de les comparer côte à côte, car le compromis qui a déterminé leur choix pendant deux décennies — une précision obtenue grâce aux données annotées — est précisément celui qui ne s’applique plus.

ApprocheFonctionnementCoût de mise en placeGère une nouvelle mise en page fournisseur
Modèles / règles (années 1990–)OCR, puis zones de coordonnées et expressions régulières propres à chaque fournisseur (« le total est le nombre à droite de “TOTAL” »)Des heures par fournisseur, pour toujoursNon — nécessite un nouveau modèle
Apprentissage automatique classique (années 2010)OCR, puis caractéristiques conçues manuellement (position, taille de police, mots voisins) transmises à un CRF ou un SVM qui étiquette chaque tokenIngénierie des caractéristiques + quelques milliers d’annotationsPartiellement — se dégrade sur les mises en page inédites
Deep learning (2020–)Transformers tenant compte de la mise en page (LayoutLM, Donut, DocTR) ajustés pour lire conjointement le texte, la position et les pixels3 000–50 000 factures annotées + service GPUSouvent non — nécessite généralement une réannotation et un réentraînement
LLM de vision (2024–)Un appel API : image de la facture plus schéma JSON, sortie structurée contrainte par ce schémaAucun — un schéma et un prompt ; $0.00248–$0.00497 par factureOui, en zero-shot

Les trois premières approches partagent une propriété : la précision s’achète avec des données annotées. Chaque modèle fournisseur, chaque caractéristique CRF et chaque fine-tuning LayoutLM constitue un projet d’annotation. Un LLM de vision a déjà payé ce coût pendant son préentraînement ; le coût marginal d’une nouvelle mise en page est donc nul. C’est tout le changement — les anciennes méthodes n’ont pas cessé de fonctionner, mais ce pour quoi elles dépensaient leur budget est devenu gratuit.

Deep learning ajusté ou LLM de vision : comparaison détaillée

Modèle de mise en page ajusté (2021)LLM de vision (2026)
Données d’entraînement3 000–50 000 factures annotéesAucune (zero-shot) — quelques exemples aident pour les cas limites
Nouvelle mise en page fournisseurNécessite souvent une réannotation et un réentraînementGérée en zero-shot
InfrastructureService GPU + étape OCRUn appel HTTPS
Format de sortieÉtiquettes de tokens → décodage personnaliséJSON contraint par un schéma
Coût par factureLe temps d’ingénierie domine$0.00248–$0.00497

Un extracteur ajusté peut encore gagner sur une mise en page unique, figée et à gros volume, mais pour la longue traîne des factures réelles — fournisseurs, langues et qualités de scan différents — le LLM de vision zero-shot est plus précis en pratique et radicalement moins coûteux à exploiter. D’autres modèles pour cette catégorie de charge figurent dans le cas d’usage d’extraction de données.

Le modèle complet : image → JSON validé par schéma

Tout ce qui suit utilise le point de terminaison compatible OpenAI de Kunavo — faites pointer base_url vers https://api.kunavo.com/v1 et le même code peut appeler Claude ou GPT en modifiant une seule chaîne :

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"])

Trois détails font l’essentiel du travail : le schéma JSON dans response_format (avec Claude, le modèle ne peut rien émettre d’autre — consultez la note sur GPT dans la référence liée ci-dessous), la règle du prompt système « copier les valeurs exactement ; null en cas d’absence ; ne jamais inventer » (qui élimine les numéros fiscaux hallucinés) et le garde-fou arithmétique — les lignes plus la taxe doivent égaler le total, sinon le document est envoyé à un modèle plus puissant ou à un humain. Consultez la référence des chat completions pour savoir ce que response_format impose à chaque famille de modèles.

Le même schéma sous forme de modèle Pydantic typé

Un dictionnaire de schéma JSON brut convient pour un seul point de terminaison, mais devient pénible pour dix. En production, la plupart des équipes définissent le schéma une seule fois comme modèle Pydantic et en dérivent à la fois la contrainte et l’objet analysé ; l’extracteur et le reste de la base de code ne peuvent ainsi jamais diverger sur la structure :

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)

Deux éléments méritent d’être repris ici. Les champs typés comme float | None plutôt que float offrent au modèle un moyen légal d’indiquer « absent » — sans cela, une ligne de taxe manquante pousse fortement à inventer un zéro. Et le champ confidence auto-déclaré coûte trois tokens de sortie et assure étonnamment bien le routage ; il n’est pas calibré, mais « faible » est un signal fiable indiquant qu’un humain devrait examiner le document.

Routage à deux niveaux : ce qui fonctionne réellement en production

Utiliser un seul modèle pour chaque document est une mauvaise architecture. Les factures imprimées propres — l’immense majorité — sont traitées par le modèle de vision le moins cher disponible ; les cas difficiles (écriture manuscrite, mauvais scans, mises en page inhabituelles) justifient un modèle plus puissant. Faites remonter les documents en fonction d’un échec de validation, et non d’une supposition :

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

Le contrôle arithmétique fait le véritable travail : c’est le seul signal indépendant de l’opinion que le modèle a de lui-même. Un document dont les lignes correspondent au total indiqué est presque toujours correct sur les points importants ; un document qui échoue mérite le modèle plus puissant ou un humain, quelle que soit la confiance annoncée.

Coût par facture, par modèle

Une facture d’une page ≈ 1 800 tokens d’entrée (facture envoyée sous forme d’image) + ~350 tokens de sortie en JSON :

ModèlePar facturePour 10 000 facturesÀ utiliser pour
claude-haiku-4-5$0.00248$24.80Par défaut : factures imprimées propres et traitements en volume les moins chers
claude-sonnet-5$0.00497$49.70Niveau d’escalade : scans, écriture manuscrite, échecs

Les tarifs sont actualisés sur la page des tarifs (environ 30 % sous les tarifs catalogue des fournisseurs), et les requêtes échouées ne sont pas facturées. Avec un routage à deux niveaux — tout traiter via Haiku, puis escalader vers Sonnet les échecs du contrôle arithmétique — 10 000 factures par mois reviennent généralement à moins de $27.29.

Précision : la liste de contrôle pour atteindre la production

  1. Commencez par le schéma. Marquez les champs réellement obligatoires comme required, et autorisez null partout ailleurs — forcer une valeur force une hallucination.
  2. Envoyez l’image, pas le texte OCR. La mise en page porte du sens (colonnes, cases de total) ; les modèles de vision la lisent directement. Ne revenez au texte que pour les PDF nativement numériques dont vous pouvez extraire une couche de texte propre.
  3. Validez l’arithmétique dans le code. Lignes + taxe = total détecte gratuitement la plupart des erreurs d’extraction.
  4. Escaladez, ne réessayez pas aveuglément. Les échecs sont envoyés à Sonnet avec l’erreur de validation incluse dans le prompt ; les échecs persistants sont transmis à une file de traitement humain.
  5. Utilisez le few-shot pour les vrais cas limites. Deux ou trois exemples de vos mises en page les plus difficiles (avoirs, multidevises) dans le prompt système valent mieux que toute quantité de réglage des instructions.

Au-delà des factures

Le même modèle contraint par schéma couvre les reçus, bons de commande, bons de livraison, formulaires douaniers et relevés bancaires — remplacez le schéma, gardez les garde-fous. Consultez le cas d’usage plus large de l’extraction de données structurées pour les modèles proches du RAG, ou le guide d’optimisation des coûts pour les stratégies de routage lorsque le volume augmente. Une seule clé Kunavo couvre tous les niveaux de modèles utilisés ici — inscrivez-vous gratuitement et créez une clé pour exécuter l’extrait de code tel quel.

Questions fréquentes

Ai-je besoin d’un modèle de deep learning pour extraire des données de factures ?

Plus maintenant. Le pipeline classique — OCR, puis modèle de mise en page comme LayoutLM ajusté sur des milliers de factures annotées — a été remplacé pour la plupart des équipes par un unique appel à un LLM de vision : envoyez l’image de la facture avec un schéma JSON et récupérez des données structurées validées. Il n’y a aucun jeu de données à annoter, aucun modèle à héberger, et les changements de mise en page qui faisaient échouer les modèles ajustés sont gérés en zero-shot.

Comment l’extraction de factures par apprentissage automatique se compare-t-elle à l’utilisation d’un LLM ?

Les extracteurs classiques d’apprentissage automatique (CRF ou SVM sur une sortie OCR, avec des caractéristiques de position et de police conçues manuellement) et leurs successeurs en deep learning (LayoutLM, Donut) achètent tous deux la précision avec des données annotées — des milliers de factures annotées par déploiement, à réannoter lorsque les mises en page changent. Un LLM de vision a payé ce coût pendant son préentraînement ; il lit donc en zero-shot une mise en page fournisseur jamais vue et renvoie un JSON contraint par le schéma en un seul appel API. L’apprentissage automatique reste gagnant pour une mise en page unique, figée et à très gros volume, lorsque le coût d’inférence domine ; pour la longue traîne des factures réelles, le LLM est à la fois plus précis en pratique et bien moins coûteux à exploiter.

Puis-je encore utiliser LayoutLM ou Donut pour l’extraction de factures ?

Oui, et ces modèles restent pertinents lorsque vous traitez des millions de documents avec une mise en page stable et pouvez amortir l’annotation ainsi que le service GPU. Ce qui a changé, c’est le choix par défaut : pour un nouveau projet, un LLM de vision atteint une précision de production en un après-midi sans jeu d’entraînement, et le coût par document est suffisamment faible pour que l’économie du fine-tuning soit rarement rentable. Une approche intermédiaire courante consiste à exécuter d’abord le LLM, puis à n’effectuer le fine-tuning que plus tard, en utilisant les sorties acceptées du LLM comme corpus annoté.

Combien coûte l’extraction de données d’une facture par document ?

Une facture d’une page représente environ 1 800 tokens d’entrée (sous forme d’image) plus ~350 tokens de sortie en JSON. Sur Kunavo, cela revient à environ $0.00248 par facture avec claude-haiku-4-5 et $0.00497 avec claude-sonnet-5 — même avec la qualité Sonnet, mille factures coûtent donc quelques dollars. Les requêtes échouées ne sont pas facturées.

Quel modèle utiliser pour extraire les données de factures ?

Commencez par claude-haiku-4-5 : il traite les factures imprimées propres avec une précision quasi parfaite au prix le plus bas. Acheminez uniquement les échecs — écriture manuscrite, scans basse résolution, mises en page inhabituelles ou documents qui échouent à votre contrôle arithmétique — vers claude-sonnet-5. Ce routage à deux niveaux maintient généralement plus de 90 % du volume sur le niveau économique.

Comment garantir que la sortie est un JSON valide ?

Utilisez les sorties structurées : transmettez response_format avec un schéma JSON et un modèle Claude est contraint de produire exactement cette structure — sans post-traitement par expressions régulières. Ajoutez par-dessus une garde-fou applicative peu coûteuse : vérifiez que les lignes plus la taxe correspondent au total, puis envoyez les écarts au modèle plus puissant ou à une file de traitement humain.

Puis-je extraire des données de factures PDF, et pas seulement d’images ?

Oui — convertissez chaque page PDF en PNG (par exemple avec pdf2image) et envoyez-la comme ci-dessus. Pour les PDF nativement numériques, vous pouvez aussi extraire la couche de texte et l’envoyer en texte brut, ce qui coûte moins cher ; conservez le chemin image pour les scans.

Quelle est la précision de l’extraction de factures par LLM ?

Sur des factures imprimées propres, l’extraction visuelle contrainte par schéma, combinée au garde-fou arithmétique, dépasse régulièrement 98 % de précision au niveau des champs — et, contrairement à un modèle ajusté, la précision se maintient sur des mises en page jamais vues. Évaluez la précision sur vos propres documents : 100 factures annotées constituent un solide jeu d’évaluation.

Mes données de facturation sont-elles utilisées pour l’entraînement ?

Non — les requêtes envoyées via Kunavo sont transmises aux fournisseurs de modèles dans le cadre de conditions d’utilisation API (et non des conditions d’applications grand public) et ne servent pas à entraîner les modèles. Consultez la documentation sur la facturation et les données pour les détails de conservation.