Hoppa till innehållet
AI-grafen
F· AI engineeringmultimodala-modeller· ca 90 min· volatil — kontrolleras ofta· verifierad 2026-09-20

Dokumentförståelse: OCR, layout, tabeller

Kunna extrahera strukturerad information ur dokumentbilder och mäta kvalitet.

Förkunskaper

Intuition

Ett dokument är inte en textsträng. Layouten bär betydelse: vad som står i vilken kolumn, vilken rad i en tabell, vilket fält en siffra hör till. Läser du bara av texten uppifrån och ner tappar du just det som gör dokumentet begripligt.

Två vägar, och de är verkligen olika:

OCR-pipelineVLM direkt
Stegavbild → OCR → layoutanalys → fältmatchningavbild → modell → JSON
Gertextkoordinater, spårbarhet till pixeldirekt strukturerat svar
Bra påhöga volymer, fasta mallar, revisionvarierande layouter, handskrift, få dokument
Fel uppståri OCR:en och i regelverkettyst, som påhittade fältvärden
Kostnadlåg per sidahög per sida

Den avgörande skillnaden är var felen syns. En OCR-pipeline som misslyckas ger tomt eller uppenbart skräp. En VLM som misslyckas ger ett trovärdigt men fel organisationsnummer.

Formellt

Mät fältvis, aldrig som en helhet. Ett dokumentextraktionssystem med «94 % träffsäkerhet» är en meningslös siffra. Fälten skiljer sig enormt:

FältTypisk exakt träffVarför
Fakturanummer0,98välavgränsat, tecken lätt lästa
Datum0,95format varierar men går att normalisera
Totalbelopp0,93förväxlas med delsummor
Leverantörsnamn0,85logotyper, förkortningar, juridiskt namn ≠ varumärke
Radposter (tabell)0,70flersidiga tabeller, sammanslagna celler

Normalisera före jämförelse: «1 234,50 kr», «1234.50» och «1 234,50» är samma belopp. Annars mäter du formatering.

Konfidens och mänsklig granskning. Nyttan ligger i att skicka rätt dokument till människa. Det kräver en kalibrerad konfidens:

  1. Låt modellen ange konfidens per fält — eller använd OCR-konfidens och överensstämmelse mellan två metoder.
  2. Kalibrera mot en märkt mängd: när modellen säger 0,9, hur ofta har den rätt?
  3. Sätt tröskeln utifrån vad ett fel kostar jämfört med vad granskningen kostar.
  4. Mät täckningen — andelen dokument som går igenom automatiskt. Det är det tal som avgör affärsnyttan.

Kryssvalidering är billig och effektiv: kör både OCR-pipeline och VLM, och flagga varje fält där de är oense. Två oberoende fel som sammanfaller är sällsynta, och det du får är en konfidenssignal som inte kommer från modellen själv.

Två saker att hantera från början: flersidiga dokument (radposter som fortsätter över sidbrytning) och personuppgifter (dokumentbilder innehåller nästan alltid sådana — samma gallringsregler gäller som för all annan persondata).

Kod

import json, re
from pydantic import BaseModel, Field

class Radpost(BaseModel):
    beskrivning: str
    antal: float
    styckpris: float
    belopp: float

class Faktura(BaseModel):
    fakturanummer: str | None = None
    fakturadatum: str | None = Field(default=None, description="ÅÅÅÅ-MM-DD")
    leverantor: str | None = None
    organisationsnummer: str | None = None
    totalbelopp: float | None = None
    valuta: str = "SEK"
    radposter: list[Radpost] = []
    osakra_falt: list[str] = []

PROMPT = ("Extrahera fakturan som JSON enligt schemat. Läs ENDAST det som står i bilden. "
          "Sätt null för fält du inte kan läsa och lista dem i osakra_falt. Hitta aldrig på värden.")

def belopp(s):
    if s is None:
        return None
    return float(re.sub(r"[^\d,.-]", "", str(s)).replace(" ", "").replace(",", "."))

def faltvis_matt(facit: list[dict], pred: list[dict], falt: list[str]):
    ut = {}
    for f in falt:
        traff = sum(1 for a, b in zip(facit, pred)
                    if (belopp(a[f]) if f.endswith("belopp") else a[f]) ==
                       (belopp(b.get(f)) if f.endswith("belopp") else b.get(f)))
        ut[f] = round(traff / len(facit), 3)
    return ut

# Kryssvalidering: två oberoende vägar, flagga oenighet
def kryss(ocr_resultat: dict, vlm_resultat: dict, falt: list[str]):
    return [f for f in falt if ocr_resultat.get(f) != vlm_resultat.get(f)]

print(faltvis_matt(facit, pred, ["fakturanummer", "fakturadatum", "totalbelopp", "leverantor"]))
# {'fakturanummer': 0.98, 'fakturadatum': 0.95, 'totalbelopp': 0.93, 'leverantor': 0.85}

osakra_falt i schemat är billigt och underskattat: det ger modellen ett sätt att säga «jag vet inte» som är strukturerat nog att agera på i koden.

Behärskning innebär

  • Väljer mellan OCR-pipeline och VLM
  • Extraherar fält och tabeller med schema
  • Mäter fältvis träffsäkerhet och kalibrerar konfidens

Logga in för att göra övningarna och bygga upp din behärskning.

Källor

Alla källor och licenser