En studie av Majd Jamal

Jag letade och hittade guld

— En studie i anomalidetektion

Min modell gick igenom 2,4 miljoner offentliga fakturor från Örebro kommun för att hitta ovanliga mönster. En av de mest avvikande fakturorna gällde en beställning på omkring en miljon kronor från en guldsmed. Jag begärde ut originalhandlingarna och hittade 169 gåvor till personalen, bland annat klockor, diamantringar och guldkedjor i 18 karat, samt ett faktureringsfel.

Skrolla för att läsa studien

Ska skattepengar gå till guldkedjor i 18 karat?

Jag använde anomalidetektion på offentliga fakturor. Det här hittade min algoritm …

Om datan

Örebro kommun publicerar en lista över de leverantörsfakturor kommunen har betalat.

Datan uppdateras varje månad och visar vem som fått betalt, hur mycket, vilket datum och av vilken kommunal förvaltning. Den är fri att använda, men visar inte vad som köptes.

I Sverige kan vem som helst begära ut handlingar från en myndighet utan att uppge sitt namn eller förklara varför. Jag använde den rätten för att få ut de tre handlingarna bakom den här studien.

2 446 856 fakturor,

till ett värde av cirka 40 miljarder kronor,

betalda från januari 2018 till juli 2026.

Scanning av fakturorna för räkenskapsåret 2023Scanning av fakturorna för räkenskapsåret 2023

Jag tränade en autoencoder på data från räkenskapsåret 2023. Den lär sig hur en vanlig faktura ser ut och markerar sedan fakturor som avviker från det vanliga. Ett fall från 2023 väckte särskilt min uppmärksamhet vid flera olika inställningar och träningskörningar.

Autoencoderns karta över samtliga 291 292 fakturor som betalades under räkenskapsåret 2023. Liknande fakturor ligger nära varandra. Punkten i guld visar kreditfakturan från guldbutiken, rankad som nummer 5.

Jag följde upp avvikelsen

Jag följde kreditfakturan genom betalningsuppgifterna och hittade tre handlingar som gällde samma beställning.

Beställningens tre handlingar, ordnade efter betalningsår. Beloppen anges i kronor exklusive moms.
Räkenskaps­årHandlingNummerFakturadatumBetalnings­datumBelopp, kr exkl. moms
2023Ursprunglig faktura 24849251Rankad nr 37 652 av 291 292Rankad nr 37 652 av 291 2922484925115 nov 202315 dec 2023882 960
2023Kreditfaktura 24862111Flaggad av modellen · rankad nr 5 av 291 2922486211130 nov 202315 dec 2023−882 960
2024Ny faktura 24867828248678281 dec 20232 jan 20241 014 000≈ 1 miljon131 040 kr mer än den första

Tre handlingar, en beställning. Kreditfakturan, som visas i guld, är den handling modellen flaggade. Den krediterar hela den första fakturan, som sedan ersätts av den nya.

Så jag mejlade kommunen

Jag bad om de tre handlingarna och frågade varför den första fakturan hade krediterats och om gåvorna gällde 2023 eller 2024. Registratorn skickade alla tre och besvarade båda frågorna.

Majd Jamaltill Örebro kommun

Kopior av tre leverantörsfakturor

Hej!

Jag heter Majd Jamal och analyserar kommunens öppna data över leverantörsfakturor. Tack för att ni publicerar den!

Jag skulle vilja få kopior av tre fakturor från Jarl Sandin Guld och Silver AB (org.nr 556285-0700), konto 71110 Personaluppvakt o gåvor. Fakturanummer enligt öppna data:

  • 24849251, betald 2023-12-15,
    882 960 kr
  • 24862111, betald 2023-12-15,
    –882 960 kr (kreditfaktura)
  • 24867828, betald 2024-01-02,
    1 014 000 kr
  • 24849251, betald 2023-12-15, 882 960 kr
  • 24862111, betald 2023-12-15, –882 960 kr (kreditfaktura)
  • 24867828, betald 2024-01-02, 1 014 000 kr

Personuppgifter kan gärna maskas.

Om det går skulle jag också vilja veta varför den första fakturan krediterades, och om gåvorna avsåg 2023 eller 2024.

Tack på förhand!

Med vänliga hälsningar
Majd Jamal

Örebro kommun · Registratortill Majd Jamal

Sv: Kopior av tre leverantörsfakturor

Hej!

Bifogar efterfrågade handlingar. Den första fakturan avsåg 24 årsgåvor 2023 och den krediterades för att fakturan hade fel belopp på två poster.

Med vänliga hälsningar,
Registrator
Informationshantering och Stadsarkiv, Kommunledningsförvaltningen

  • 24849251.pdf
  • 24862111.pdf (kreditfakturan som modellen flaggade)
  • 24867828.pdf

Källa: Mejlen mellan Majd Jamal och Örebro kommun den 27 och 30 september 2026. Här återges de svenska originalen. E-postadresserna är utelämnade.

Skickat

Copies of three supplier invoices

Majd Jamalto Örebro kommun

Hi!

My name is Majd Jamal and I’m analysing the municipality’s open data on supplier invoices. Thank you for publishing it!

I would like copies of three invoices from Jarl Sandin Guld och Silver AB (org. no. 556285-0700), account 71110 Staff recognition and gifts. Invoice numbers according to the open data:

  • 24849251, paid 2023-12-15,
    882,960 kr
  • 24862111, paid 2023-12-15,
    −882,960 kr (credit note)
  • 24867828, paid 2024-01-02,
    1,014,000 kr
  • 24849251, paid 2023-12-15, 882,960 kr
  • 24862111, paid 2023-12-15, −882,960 kr (credit note)
  • 24867828, paid 2024-01-02, 1,014,000 kr

Personal data may of course be masked.

If possible, I would also like to know why the first invoice was credited, and whether the gifts were for 2023 or 2024.

Thanks in advance!

Kind regards
Majd Jamal

English translation of the Swedish original. Email addresses are left out.

Kopior av tre leverantörsfakturor

Majd Jamaltill Örebro kommun

Hej!

Jag heter Majd Jamal och analyserar kommunens öppna data över leverantörsfakturor. Tack för att ni publicerar den!

Jag skulle vilja få kopior av tre fakturor från Jarl Sandin Guld och Silver AB (org.nr 556285-0700), konto 71110 Personaluppvakt o gåvor. Fakturanummer enligt öppna data:

  • 24849251, betald 2023-12-15,
    882 960 kr
  • 24862111, betald 2023-12-15,
    –882 960 kr (kreditfaktura)
  • 24867828, betald 2024-01-02,
    1 014 000 kr
  • 24849251, betald 2023-12-15, 882 960 kr
  • 24862111, betald 2023-12-15, –882 960 kr (kreditfaktura)
  • 24867828, betald 2024-01-02, 1 014 000 kr

Personuppgifter kan gärna maskas.

Om det går skulle jag också vilja veta varför den första fakturan krediterades, och om gåvorna avsåg 2023 eller 2024.

Tack på förhand!

Med vänliga hälsningar
Majd Jamal

Det svenska originalet, så som det skickades. E-postadresserna är utelämnade.

Svar

Re: Copies of three supplier invoices

Örebro kommun · Registratorto Majd Jamal

Hi!

Attached are the requested documents. The first invoice concerned the 24-year gifts for 2023, and it was credited because the invoice had the wrong amount on two lines.

Kind regards,
Registrar
Information Management and City Archives, Örebro kommun

  • 24849251.pdf
  • 24862111.pdf (the credit note the model flagged)
  • 24867828.pdf

English translation of the Swedish original. Email addresses are left out.

Sv: Kopior av tre leverantörsfakturor

Örebro kommun · Registratortill Majd Jamal

Hej!

Bifogar efterfrågade handlingar. Den första fakturan avsåg 24 årsgåvor 2023 och den krediterades för att fakturan hade fel belopp på två poster.

Med vänliga hälsningar,
Registrator
Informationshantering och Stadsarkiv, Kommunledningsförvaltningen

  • 24849251.pdf
  • 24862111.pdf
  • 24867828.pdf

Det svenska originalet, så som det mottogs. E-postadresserna är utelämnade.

Vad fanns på fakturan?

Vad

169 gåvor till anställda med lång anställningstid

Allt på den nya fakturan kostade 1 014 000 kr exklusive moms. I illustrationen motsvarar varje punkt en gåva.

  • 42 guldkedjor
    i 18 karat
  • 38 klockor
  • 34 ringar
  • 29 set med
    designer­smycken
  • 19 diamant­hängen19 diamant-
    hängen
  • 6 par
    diamant­örhängen
    6 par
    diamant-
    örhängen
  • 1 diamant­armband1 diamant-
    armband

När

Fakturerat 2023, betalt 2024

Den ursprungliga fakturan och dess fullständiga kreditering har samma betalningsdatum, den 15 december 2023, och tar ut varandra. Den nya fakturan betalades den 2 januari 2024.

Flyttade den här rättelsen kostnaden från 2023 till ett annat år?

Varför beloppet ändrades

Samma beställning, högre faktura

På den ursprungliga fakturan kostade de 42 guldkedjorna 2 880 kr styck. På den nya fakturan var priset 6 000 kr styck. Det ökade beloppet med 131 040 kr exklusive moms. Kommunen uppgav att de ursprungliga beloppen var felaktiga.

Varför steg priset på samma kedja på fakturan?

Förtydligande

Med hjälp av anomalidetektion hittade jag ett kommunalt inköp av gåvor till anställda med lång anställningstid, bland annat guldkedjor i 18 karat. Kommunen förklarade varför fakturan hade rättats. Om skattepengar ska gå till sådana gåvor är en fråga värd att diskutera. Modellen flaggade kreditfakturan som fick mig att ställa frågorna.

— Majd Jamal

Den här studien visar hur anomalidetektion fungerar och är ingen anklagelse mot Örebro kommun. Modellen hjälpte mig att välja en post att undersöka. Handlingarna förklarade ett faktureringsfel som redan hade rättats. En flaggning är ett skäl att titta närmare, inte ett bevis på oegentligheter.

Metod och begränsningar

Från öppna data till kartan i sex små steg.

Metoden som ett flöde: samla in den öppna datan, slå samman och städa den, räkna fakturorna per år, gör om varje faktura till 8 tal, komprimera dem med en autoencoder till 2 tal och återskapa dem sedan till 8, använd de 2 talen för att placera fakturorna på kartan och rangordna fakturorna efter högst rekonstruktionsfel.

Samla in data

orebro.se · 24 filer
2 446 856 fakturor
7 294 483 konteringsrader

Slå samman och städa

två kolumnuppsättningar
→ en gemensam tabell
allt behålls, även kreditfakturor

Fakturor per år

= 1 000 fakturor

Fakturorna grupperas efter det år då de först betalades. År 2026 omfattar januari till juli.

Feature engineering

8 tal per fakturahur ovanlig · hur stor ·
vilket tecken · hur många rader

Autoencoder

8 → 2 → 8 · utan etiketter

8 in

återskapade

latent representation

Visualisera

varje 2023-fakturas 2 latenta tal placerar den på kartan

Kartbilden visar de 291 292 fakturor som betalades under 2023. Kreditfakturan markeras med en punkt i guld.

Rangordna avvikelser

efter högst rekonstruktionsfel
topp 5 av 291 292 · 2023

  1. 1Lokalhyra290,85
  2. 2Lägenhetshyra70,05
  3. 3Inhyrda sjuksköterskor32,43
  4. 4Fordonsbesiktning21,75
  5. 5Kreditfaktura, juvelerare18,73
Metoden som ett flöde: samla in den öppna datan, slå samman och städa den, räkna fakturorna per år, gör om varje faktura till 8 tal, komprimera dem med en autoencoder till 2 tal och återskapa dem sedan till 8, använd de 2 talen för att placera fakturorna på kartan och rangordna fakturorna efter högst rekonstruktionsfel.

Samla in data

orebro.se · 24 filer
2 446 856 fakturor · 7 294 483 rader

Slå samman och städa

två kolumnuppsättningar → en tabell
allt behålls, även kreditfakturor

Fakturor per år

= 1 000 fakturor

Efter år för första betalning. 2026: januari–juli.

Feature engineering

8 talper faktura

Autoencoder

8 → 2 → 8 · utan etiketter

8 in

8 ut

latent representation

Visualisera

varje 2023-fakturas
2 tal blir kartan

Kartbilden visar de 291 292 fakturor som betalades under 2023. Kreditfakturan markeras med en punkt i guld.

Rangordna

efter högst
rekonstruktionsfel
topp 5 · 2023

  1. 1Lokalhyra
  2. 2Lägenhetshyra
  3. 3Hyrsjuksköterskor
  4. 4Fordonsbesiktning
  5. 5Kreditfaktura

Utvärdering av metoden.

Modellen använder rekonstruktionsfel för att rangordna ovanliga fakturor, men det visar inte om de innehåller fel. Utan märkta utfall kan jag inte redovisa F1 eller ROC AUC. Syntetiska tester skulle kunna pröva specifika beteenden, men optimering mot konstruerade fall kan gynna mönster som jag redan förväntar mig.

Jag gick igenom de 20 högst rankade fakturorna från varje år och följde upp juvelerarens kreditfaktura. Handlingarna förklarade ett faktureringsfel som redan hade rättats. Det visar hur en flaggning kan vägleda en undersökning. Modellens samlade förmåga att upptäcka avvikelser har ännu inte mätts.

Fakturaunderlag.

Den öppna datan visar inte vad som köptes. Jag måste begära ut originalfakturorna, vilket gör undersökningarna långsammare. Ett samarbete med kommunen skulle kunna göra granskningarna snabbare och hjälpa till att förbättra modellen.

Varje faktura blir 8 tal, till exempel P(konto | nämnd). Autoencodern komprimerar dem till 2, en punkt på kartan, och återskapar alla 8; ju sämre återskapandet är, desto mörkare blir punkten.

  1. 7 294 483konteringsrader i bokföringen
  2. städaen rad per faktura
  3. 8 variablertal per faktura
  4. X 291 292 × 8alla fakturor från 2023
  5. 8 → 2 → 8autoencodern; de 2 talen är kartan

P(konto | nämnd): hur sannolikt är det att den här nämnden betalar för den här typen av kostnad?

”Det här systemet bygger på oövervakad inlärning, utan etiketter, så det är svårt att mäta exakt prestanda. Och med injicerade avvikelser skulle det optimeras mot något vi vet att vi letar efter.”

Om utvärdering

Data: Örebro kommuns leverantörsfakturor, januari 2018 – juli 2026: 7 294 483 konteringsrader, 2 446 856 fakturor, cirka 40 miljarder kronor. Fakturadetaljer (CSV) · Mejlen (TXT)

Bilaga

Alla formler och inställningar bakom variablerna, X och autoencodern.

Feature engineering

8 per faktura

Varje konteringsrad från 2023 får ett värde i förhållande till liknande rader. En faktura behåller sina raders mest extrema värden och sitt antal rader: åtta tal per faktura.

Vilket konto
  • 1 Konto givet nämnd

    p_konto_namnd_max

    Högt när minst en rad på fakturan bokförs på ett konto som nämnden sällan använder.

    x_{v,1} = \max_{i \in v} \left[ -\ln \hat{P}(k_i \mid c_i, e_i) \right]
    \hat{P}(k \mid c, e) = \frac{n(e, c, k)}{n(e, c)}
  • 2 Konto givet leverantör

    p_konto_leverantor_max

    Högt när minst en rad bokförs på ett konto som leverantören sällan bokförs på.

    x_{v,2} = \max_{i \in v} \left[ -\ln \hat{P}(k_i \mid s_i, e_i) \right]
    \hat{P}(k \mid s, e) = \frac{n(e, s, k)}{n(e, s)}
Hur stort belopp
  • 3 Belopp givet konto (max)

    p_amount_account_max

    Högt när minst en rad är mycket större (utan hänsyn till tecken) än vad som är typiskt för kontot: r är ett robust z‑värde, mätt i spridningar från medianen.

    x_{v,3} = \max_{i \in v} r_i^{(e,k)}
    r_i^{(e,k)} = \frac{a_i - m_{(e_i,k_i)}}{\hat{\sigma}_{(e_i,k_i)}}
  • 4 Belopp givet konto (min)

    p_amount_account_min

    Kraftigt negativt när minst en rad är mycket mindre (utan hänsyn till tecken) än vad som är typiskt för kontot; högt bara när varje rad ligger över det typiska.

    x_{v,4} = \min_{i \in v} r_i^{(e,k)}
  • 5 Belopp givet leverantör (max)

    p_amount_reskontra_max

    Högt när minst en rad är mycket större (utan hänsyn till tecken) än en typisk rad för den här leverantören.

    x_{v,5} = \max_{i \in v} r_i^{(s)}
    r_i^{(s)} = \frac{a_i - m_{s_i}}{\hat{\sigma}_{s_i}}
  • 6 Belopp givet leverantör (min)

    p_amount_reskontra_min

    Kraftigt negativt när minst en rad är mycket mindre (utan hänsyn till tecken) än en typisk rad för den här leverantören.

    x_{v,6} = \min_{i \in v} r_i^{(s)}
Vilket tecken
  • 7 Tecken givet konto

    s_sign_account_max

    Högt när en rad har ett ovanligt tecken för sitt konto, t.ex. en kreditering på ett konto som nästan alltid debiteras (b = 0 för krediteringar och nollbelopp).

    x_{v,7} = \max_{i \in v} \left[ -\ln \hat{P}(b_i \mid k_i, e_i) \right]
    b_i = \mathbf{1}\{y_i > 0\}
Hur många rader
  • 8 Antal rader

    n_lines

    Högt när fakturan är uppdelad på många konteringsrader (2023 är medianen 1 och det högsta värdet 266).

    x_{v,8} = |v| = \#\{\, i : i \in v \,\}
Notation och gemensamma formler
i
en konteringsrad
v
en faktura, v = 1, \dots, N, och mängden av dess rader, i \in v
e_i
era: kontoplanen, som skiljs åt utifrån kontokodens längd (5 eller 4 siffror)
c_i
nämnd
s_i
leverantör (organisationsnummer)
k_i
kontokod
y_i
belopp, kronor exkl. moms
  • Andel och sällsynthet

    n(\cdot) räknar konteringsraderna från 2023 med de värdena: g en grupp (en nämnd, en leverantör, ett konto, var och en inom sin era), u ett värde inom gruppen. Sällsyntheten är 0 när alla rader i gruppen har värdet och växer ju mer sällsynt det är.

    \hat{P}(u \mid g) = \frac{n(g,\, u)}{n(g)}
    -\ln \hat{P}(u_i \mid g_i)
  • Logaritmerat belopp

    Storleken på en logaritmisk skala, utan hänsyn till tecken: tecknet är variabel 7.

    a_i = \ln\!\left(1 + |y_i|\right)
  • Typiskt belopp

    Medianen av det logaritmerade beloppet i radens grupp g: radens konto inom samma era, (e_i, k_i), för variabel 3 och 4; radens leverantör, s_i, för 5 och 6.

    m_g = \operatorname*{median}_{j \in g} a_j
  • Robust spridning

    Medianen av de absoluta avvikelserna M_g, skalad (×1,4826) för att motsvara en normalfördelnings \sigma; när M_g är 0 används medelvärdet av de absoluta avvikelserna A_g, skalat (×1,2533) för att också motsvara en normalfördelnings \sigma; aldrig under 0,05.

    M_g = \operatorname*{median}_{j \in g} \left| a_j - m_g \right|
    A_g = \operatorname*{mean}_{j \in g} \left| a_j - m_g \right|
    \tilde{\sigma}_g = \begin{cases} 1.4826\, M_g & M_g > 0 \\ 1.2533\, A_g & M_g = 0 \end{cases}
    \hat{\sigma}_g = \max\!\left(0.05,\; \tilde{\sigma}_g\right)

Bygg X

StandardScaler

En rad per faktura och en kolumn per variabel. Saknade eller oändliga värden sätts först till 0 (för 2023 finns inga sådana värden). Varje kolumn centreras sedan kring sitt medelvärde och divideras med sin standardavvikelse, så att alla åtta räknas på samma skala.

X = \begin{bmatrix} x_{1,1} & \cdots & x_{1,8} \\ \vdots & \ddots & \vdots \\ x_{N,1} & \cdots & x_{N,8} \end{bmatrix} \in \mathbb{R}^{N \times 8}
N = 291\,292
\tilde{x}_{v,j} = \frac{x_{v,j} - \mu_j}{\sigma_j}
\mu_j = \frac{1}{N}\sum_{v} x_{v,j}
\sigma_j = \sqrt{\frac{1}{N}\sum_{v} \left(x_{v,j} - \mu_j\right)^2}

Autoencoder

PyTorch

Ett litet neuralt nätverk komprimerar varje fakturas åtta tal till två och återskapar sedan alla åtta från dessa två. Nätverket tränas på alla fakturor från 2023 och lär sig hur vanliga fakturor ser ut: en faktura som det återskapar dåligt är ovanlig.

Illustrationen: fakturans 8 standardiserade tal går in i en encoder med 128, 64 och 16 enheter och kommer ut som z, 2 tal; en decoder med 16, 64 och 128 enheter återskapar 8 tal från z. Ett återskapat tal missar sitt original och är markerat i guld.
x̂ — rekonstruktion, används för avvikelsepoängen
\varepsilon(v) = \frac{1}{8} \sum_{j=1}^{8} \left( \hat{x}_{v,j} - \tilde{x}_{v,j} \right)^2

Hur dåligt faktura v återskapas, i genomsnitt över dess åtta tal. Guldbutikens kreditfaktura rankades som nummer 5 av 291 292.

z — latent representation, används för visualisering

Varje fakturas två tal placerar den på kartan. Liknande fakturor hamnar nära varandra; poängen syns i nyansen, inte i positionen.

  • Encoder

    ReLU efter varje dolt lager; lagret med två tal, z, är linjärt.

    z_v = E(\tilde{\mathbf{x}}_v) \in \mathbb{R}^{2}
    E:\; 8 \to 128 \to 64 \to 16 \to 2
    h_0 = \tilde{\mathbf{x}}
    h_\ell = \mathrm{ReLU}(W_\ell\, h_{\ell-1} + b_\ell),\; \ell \le 3
    E(\tilde{\mathbf{x}}) = W_4\, h_3 + b_4
  • Decoder

    Samma form i omvänd ordning, med egna vikter; de åtta utgångarna är linjära.

    \hat{\mathbf{x}}_v = D(z_v) \in \mathbb{R}^{8}
    D:\; 2 \to 16 \to 64 \to 128 \to 8
  • Träning

    Medelvärdet av \varepsilon över varje batch B, minimerat under 20 epoker. Den publicerade körningen tränades med AE.train_stream, som blandar inom fönster om fyra batchar som läses i filordning; AE.train (som används av examples/run_analysis.py) blandar alla fakturor varje epok, så dess karta skiljer sig i detaljer.

    \mathcal{L}(B) = \frac{1}{|B|} \sum_{v \in B} \varepsilon(v)
  • optimerare Adam
  • inlärningshastighet 10⁻³
  • epoker 20
  • batchstorlek 4 096
  • slumpfrö 0

Se och granska koden på GitHub