Skip to content
Selected work

Case study

docverify — Payslip Tampering Detection

Find the edit, show where it is, and explain it to a human.

Every credit or rental file comes with payslips, and editing one takes two minutes with a free PDF editor. I built a system that checks a payslip with five independent kinds of evidence and fuses them into one risk level with reasons in plain French. All data is synthetic: generated payslips, fake people.

Role
Design, implementation, evaluation
Domain
Banking · document fraud · forensics
Core stack
PyTorch · ONNX Runtime · FastAPI · Streamlit · Ollama
95%
forgeries caught, image + sums
1.7%
false alarms
0.902
tamper model ROC-AUC
540/h
pages on 4 CPU threads

How one document is checked

A router (SigLIP with a linear probe) first checks the document is a payslip at all. A field detector (YOLO) finds the ten fields — net pay, gross, IBAN, SIRET — so an edit is pinned to a name, not to pixels. A forensic network I designed for the job (ConvNeXt on the image, SRM noise residuals and the JPEG 8×8 grid) paints the edited pixels. Seven payslip rules check that the sums hold: hours × rate = base, gross − contributions = net. And for a PDF, the file itself is questioned: an old amount still hiding under a white box, a different font, a second revision, an online editor as producer.

Each check catches what the others miss. Fusion keeps them honest: any strong signal makes the risk high, weak signals are only noted, and an unreadable value is "unknown", never "fraud" — a blurry photo cannot create an alert.

A payslip with a rewritten gross salary, the model output and the ground truth
A rewritten gross salary. The image model scores it as clean; two of the seven payslip sums fail, so the verdict is still high risk. Synthetic document.
Pipeline: router, field detector, tamper network, arithmetic rules, PDF forensics, fusion, analyst note
Each check catches what the others miss; fusion turns them into one risk level with reasons.
StepCheckWhat it does
1 · RouterSigLIPFrozen vision-language model + a linear probe: payslip, RIB or invoice. 100% on the test documents (zero-shot: 68%).
2 · FieldsYOLOFinds the ten fields so an edit is pinned to net pay, not to pixels. mAP50-95 0.975.
3 · PixelsTamperNetConvNeXt on the image, SRM noise residuals and the JPEG 8×8 grid: copy-move, splice and re-typed digits leave traces there.
4 · SumsSeven payslip rulesHours × rate = base, base + extras = gross, gross − contributions = net… on OCR, or exactly on a PDF's text layer.
5 · FilePDF forensicsAn old amount under a white box, a different font, a second revision, an online editor as producer.
6 · WordsGranite noteA local IBM Granite model writes the analyst's note; it chooses facts and wording, every figure comes from templates.
The six steps, in the order a page goes through them.

Real output, side by side with the truth

Each example shows the submitted page, what docverify returns (red = edited pixels, boxes = detected fields) and the ground-truth mask the model never saw. All documents are synthetic.

Rewritten amount caught by both checks
Net before tax rewritten: the pixel map lands on it and three sum rules fail.
Copy-move on net pay
Copy-move: digits copied from elsewhere on the same page onto net pay.
Splice on a phone photo
Splice from another payslip, then photographed with a phone (perspective, blur, JPEG) — still found.
Genuine payslip
A genuine payslip: low risk, every checked sum consistent.

Measured, not claimed

On a frozen test split of 793 payslips (390 genuine), the tamper network reaches 0.902 ROC-AUC and catches 72% of forgeries at 5% false alarms, with a pixel F1 of 81% on where the edit is. Rewritten amounts are its hard case — 50% — and that is exactly where the sums help: image plus arithmetic catches 95% of forgeries at 1.7% false alarms, and the rules alone raised no false alarm at all.

Recall per forgery type, image model alone versus image plus arithmetic
The arithmetic rescues rewritten amounts — the case the image model finds hardest.
v1 versus v2 recall per segment
v2 beats v1 on every segment, on the same frozen test set.
ROC curve and score distribution
The threshold is set for 5% false alarms on genuine payslips.
Pixel F1 per forged payslip, by forgery type
It says where — the overlay an analyst sees lands on the edited amount.
Arithmetic recall by edited field
The rules raised zero false alarms. A bonus edit breaks no checked total.
Which PDF signal fires for each kind of PDF
A re-save alone is only ever a weak note — never fraud.
YOLO per-field mAP and SigLIP routing accuracy
The helper models: fields and document type.
Risk levels for genuine and forged payslips
What an analyst's queue looks like: 1% of genuine payslips rated high.
CheckResult
Tamper network, recall at 5% false alarms72% (v1: 66%)
Image + arithmetic, recall95% at 1.7% false alarms
Arithmetic, false alarms0%
Edited PDFs flagged by a strong file signal100%, 0% of genuine PDFs
YOLO fields, mAP50-950.975
SigLIP router accuracy100% (zero-shot 68%)
Synthetic test set; the combined row is the 240-document subset read by OCR.

An analyst note that cannot invent a number

An optional local LLM — IBM Granite, through Ollama — writes the analyst's note. The first version let it quote figures and checked they existed in the evidence; it passed while swapping "expected" and "found". So the design changed: the model now chooses which facts to cite and how to phrase them, and every number comes from templates. The system flags; an analyst decides.

Granite note acceptance by the guard and latency
The LLM picks facts and wording; every figure comes from templates.

The same machine, other documents

PDF forensics and the tamper network detect edit traces, not payslips: they apply to any document as-is. The router needs one more class per new document type; fields and rules are written per document type.

Insurance: repair quotes and garage invoices (hours × rate = labour, Σ parts = total), medical and pharmacy bills (price × quantity, act codes vs tariff), accident reports (copy-move and splice on form zones), damage photos (splice, copy-move, near-duplicate search across claims), proofs of purchase and certificates. Elsewhere: whole tenant files for rental agencies, diplomas and transcripts in HR, benefit applications in the public sector, supplier invoices and bank-detail change letters in accounts payable, invoices and certificates of origin in logistics, seller KYC on marketplaces.

DocumentTypical fraudWhat catches it
Payslip (built)inflated net pay, gross, base salarypixel edits + seven sum rules + PDF traces
Tax noticeincome raised, household size changedpixel edits; tax = scale(income, parts); the 2D-DOC barcode
Bank statementbalance raised, salary lines addedopening + credits − debits = closing, line by line
RIB / IBAN proofIBAN swapped to a mule accountIBAN checksum, bank code vs name, font on the IBAN line
Proof of addressname or address pasted insplice detection, PDF producer, consumption × tariff
Company accountsrevenue or equity editedassets = liabilities + equity; totals = sums of lines
Invoices (factoring, leasing)amount or VAT edited, fake supplierexcl. VAT × (1 + VAT) = incl. VAT; SIRET / VAT checksums; duplicate hashing
Banking: the documents that come with a credit file, and what would catch an edit on each.

From one CPU to bank volumes

Workers are stateless, so capacity is pages per worker times workers. Measured: 540 pages an hour on four CPU threads for the full pipeline, and about 8,200 an hour for the tamper network on a single T4. OCR, not the neural networks, is the bottleneck: native PDFs skip it entirely because their text layer is exact.

Measured pages per hour on CPU and on one T4
Measured throughput, one worker.
VolumePeak hourCPU only (4-vCPU pods)CPU + GPU
10k documents / month — a regional lender~70 / h1 podnot needed
300k / month — a mid-size bank~2,100 / h~9 pods at peak, 1–2 at night1 T4 + ~6 CPU pods
3M / month — a national group~21,000 / h~86 pods at peak3 T4 + ~57 CPU pods
Peak hour = five times the average; everything beyond the measured throughput is arithmetic.

Project and weights

Project README · tamper model on Hugging Face · YOLO field detector.

Limits

Synthetic data only: no number here was measured on real payslips. A forger who edits every amount consistently escapes the arithmetic, and an edited bonus breaks no checked total. The PDF forgeries were made by me, so their 100% is optimistic. The analyst note's summaries can be slightly off; the facts it cites, and every number, are always the exact ones.

More projects

The rest of the work.

02

One Framework, Four Medical Datasets

How I built one framework that matches or beats published results on medical classification problems it was never tuned for.

While building the leukocyte classifier I built the pipeline around it as a reusable framework: deterministic patient-grouped splits, CNN and ViT backbones behind one interface, one fixed training recipe. Applied unchanged to four public medical datasets, it beats the dataset papers on bone-marrow cytology and colorectal histology — and on retinal OCT it found that a third of the official training images come from test-set patients.

  • PyTorch
  • YOLO
  • SigLIP · DINOv2 · C-RADIOv4
  • Medical imaging
Read the case study
+8.4 pts
balanced accuracy over the bone-marrow dataset paper
03

Automated Leukocyte Classification

I worked with the R&D arm of one of the largest medical-biology groups in Alsace — more than 40 labs — building an in-house model that classifies white blood cells from blood-smear images. Trained on 39,037 clinician-validated cells across 12 classes, it outperformed a re-implemented top-3 competition solution on the same test split, and beat vision backbones ten times its size while running several times faster.

  • PyTorch
  • YOLO
  • SigLIP · DINOv2
  • Medical imaging
Read the case study
95.8%
macro F1 across 12 cell classes
04

DaT Parkinson's Challenge

Classifying dopamine-transporter SPECT brain scans as normal or abnormal, in a code-execution competition run on DrivenData for the French Society of Nuclear Medicine. I built four tabular views of every scan and a 3D convolutional network, fused without a single fitted weight, and learned the hard way how far a public leaderboard can mislead.

  • Medical imaging
  • 3D CNN
  • TabICL
  • PyTorch
Read the case study
6th
of 1,009 entrants, private leaderboard
05

The Link

I worked with one of the largest residential landlords in Alsace, where every department held its own data, used it for its own tasks, and could not see past its own boundary — with basic reporting rented from outside vendors. Before any modelling was possible, I spent weeks mapping what each department held and what it needed, and joining those sources into one base covering the whole estate.

  • Python
  • pandas
  • SQL
  • Data integration
Read the case study
13
variables catalogued into one base
06

The Value

Chapter two of the same engagement: with the data finally joined, modelling what actually sets rent per square metre.

  • scikit-learn
  • SciPy
  • Web scraping
  • Forecasting
Project complete — write-up in progress
60%
of predictive weight in one financing rule
07

AI Assistant for Cross-Continental Freight

An AI chatbot for a large startup moving cargo between Europe and the Gulf states, built around the way freight actually gets booked, tracked and chased across two regions and several time zones.

  • LLMs
  • Conversational AI
  • Logistics
In progress
Report coming soon — the work is still running
Back to the homepage