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.
Jag letade ochhittade 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.
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.
scanningScanning 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.
undersökning
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år
Handling
Nummer
Fakturadatum
Betalningsdatum
Belopp, kr exkl. moms
2023
Ursprunglig faktura 24849251Rankad nr 37 652 av 291 292Rankad nr 37 652 av 291 292
24849251
Daterad 15 nov 2023
Betald 15 dec 2023
882 960
2023
Kreditfaktura 24862111Flaggad av modellen · rankad nr 5 av 291 292
24862111
Daterad 30 nov 2023
Betald 15 dec 2023
−882 960
2024
Ny faktura 24867828
24867828
Daterad 1 dec 2023
Betald 2 jan 2024
1 014 000≈ 1 miljon131 040 kr mer än den första
131 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.
MJ
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:
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
3 dagar senare
ÖK
Ö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.
i handlingarna
Vad fanns på fakturan?
1
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 designersmycken
19 diamanthängen19 diamant- hängen
6 par diamantörhängen6 par diamant- örhängen
1 diamantarmband1 diamant- armband
2
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.
202320241 dec 202310 jan 202415 dec2 janFaktura +882 960Kreditfaktura −882 960= 0 krNy faktura betald1 014 000 kr
Flyttade den här rättelsenkostnaden från 2023till ett annat år?
3
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.
20232024202320241 dec 202315 dec2 jan10 jan 20242 880 kr / kedja6 000 kr / kedjaFaktura betaldNy faktura betald
Varför steg prisetpå samma kedjapå 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
Förtydligande
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.
två kolumnuppsättningar → en gemensam tabell allt behålls, även kreditfakturor
Fakturor per år
= 1 000 fakturor
2018277 410
2019287 252
2020274 081
2021264 546
2022288 748
2023291 292
2024298 241
2025292 256
2026173 030
Fakturorna grupperas efter det år då de först betalades. År 2026 omfattar januari till juli.
Feature engineering
Matematiken
Varje konteringsrad jämförs med andra rader för samma leverantör, nämnd eller konto. Till slut representeras varje faktura av åtta tal. Funktionen −log gör en andel till ett mått på ovanlighet: 0 om något alltid förekommer, högre ju mer sällsynt det är.
P(konto | leverantör)Hur sannolikt är det att den här leverantören bokförs på det här kontot?
−log(antalet av leverantörens rader på kontot delat med leverantörens samtliga rader)
P(konto | nämnd)Hur sannolikt är det att den här nämnden bokför på det här kontot?
−log(antalet av nämndens rader på kontot delat med nämndens samtliga rader)
z(belopp | konto)Hur långt ligger beloppet från det som vanligtvis betalas på det här kontot?
logaritmerat belopp − kontots median, allt delat med kontots spridningfakturan behåller sitt högsta och sitt lägsta värde
z(belopp | leverantör)Hur långt ligger beloppet från det som den här leverantören vanligtvis får betalt?
logaritmerat belopp − leverantörens median, allt delat med leverantörens spridningäven här behåller fakturan sitt högsta och sitt lägsta värde
P(tecken | konto)Hur sannolik är en debet- respektive kreditpost på det här kontot?
−log(antalet rader på kontot med detta tecken delat med kontots samtliga rader)
antal raderHur många konteringsrader har fakturan?
antalet rader som hör till fakturan
logaritmerat belopp = log(1 + |belopp|), en gemensam skala för 10 kr och 10 miljoner kr. median = värdet i mitten. spridning = hur långt beloppen vanligtvis ligger från medianen. En faktura med flera rader behåller det högsta värdet. För beloppsmåtten behålls både det högsta och det lägsta. Vart och ett av de åtta talen standardiseras innan det matas in i autoencodern.
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
5
Rangordna avvikelser
efter högst rekonstruktionsfel topp 5 av 291 292 · 2023
1Lokalhyra290,85
2Lägenhetshyra70,05
3Inhyrda sjuksköterskor32,43
4Fordonsbesiktning21,75
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.
två kolumnuppsättningar → en tabell allt behålls, även kreditfakturor
Fakturor per år
= 1 000 fakturor
’18
’19
’20
’21
’22
’23
’24
’25
’26
Efter år för första betalning. 2026: januari–juli.
Feature engineering
Matematiken
Varje konteringsrad jämförs med andra rader för samma leverantör, nämnd eller konto. Till slut representeras varje faktura av åtta tal. Funktionen −log gör en andel till ett mått på ovanlighet: 0 om något alltid förekommer, högre ju mer sällsynt det är.
P(konto | leverantör)Hur sannolikt är det att den här leverantören bokförs på det här kontot?
−log(antalet av leverantörens rader på kontot delat med leverantörens samtliga rader)
P(konto | nämnd)Hur sannolikt är det att den här nämnden bokför på det här kontot?
−log(antalet av nämndens rader på kontot delat med nämndens samtliga rader)
z(belopp | konto)Hur långt ligger beloppet från det som vanligtvis betalas på det här kontot?
logaritmerat belopp − kontots median, allt delat med kontots spridningfakturan behåller sitt högsta och sitt lägsta värde
z(belopp | leverantör)Hur långt ligger beloppet från det som den här leverantören vanligtvis får betalt?
logaritmerat belopp − leverantörens median, allt delat med leverantörens spridningäven här behåller fakturan sitt högsta och sitt lägsta värde
P(tecken | konto)Hur sannolik är en debet- respektive kreditpost på det här kontot?
−log(antalet rader på kontot med detta tecken delat med kontots samtliga rader)
antal raderHur många konteringsrader har fakturan?
antalet rader som hör till fakturan
logaritmerat belopp = log(1 + |belopp|), en gemensam skala för 10 kr och 10 miljoner kr. median = värdet i mitten. spridning = hur långt beloppen vanligtvis ligger från medianen. En faktura med flera rader behåller det högsta värdet. För beloppsmåtten behålls både det högsta och det lägsta. Vart och ett av de åtta talen standardiseras innan det matas in i autoencodern.
8 talper faktura
Autoencoder
8 → 2 → 8 · utan etiketter
8 in
8 ut
latent representation
Visualisera
varje 2023-fakturas 2 tal blir kartan
5
Rangordna
efter högst rekonstruktionsfel topp 5 · 2023
1Lokalhyra
2Lägenhetshyra
3Hyrsjuksköterskor
4Fordonsbesiktning
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.
7 294 483konteringsrader i bokföringen
städaen rad per faktura
8 variablertal per faktura
X 291 292 × 8alla fakturor från 2023
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.
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.
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.
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).
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.
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.
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.
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.