Vaner, uvaner og abonnement

Inge Johnsen
Dec 20, 2019 · 7 min read

Hvor mange har ikke inngått et abonnement, bare for innse at det etter kort tid gir deg dårlig økonomi og samvittighet? Selv den mest viljesterke har vel inngått et treningsabonnement i januar for så å bli et passivt støttemedlem, som sørger for at de få som holder ut gjennom året, opplever blanke speil, skinnende vekter og dundrende musikk. Dette er historien om hvordan vi i SpareBank 1 bruker maskinlæring for å hjelpe kundene våre til å få oversikt over abonnementene sine.

Men la oss starte fra begynnelsen. I SpareBank 1 bruker vi maskinlæring for å klassifisere banktransaksjoner. Vi ser på om du f.eks har vært i matbutikken, eller om du har vært på kafé. Tanken er at du med et filter kan få oversikt over hvor mye du bruker på f.eks mat i måneden, eller hvor mye du bruker på moro og fjas. Det vi derimot manglet, var et filter for faste utgifter og abonnement. Mange kunder har større nytte av å kunne kategorisere hva som er faste utgifter og ikke, eller rett og slett få oversikt over hvilke abonnement en har. I tillegg inngikk vi en avtale med en tredjepartsleverandør om abonnementshåndtering. Denne tredjeparten tilbyr kansellering av abonnement og endring av abonnement (f.eks endring til et billigere mobilabonnement med de samme vilkårene).

Som så mange andre modeller, fikk vår første modell en høy bias (partiskhet). Den fungerte veldig godt for de som lagde modellen. Kort fortalt ble det en regex lignende modell som sa at Netflix-, HBO- og Spotify-transaksjonene dine er abonnement. Vi fikk forsåvidt gode tilbakemeldinger fra våre kolleger da de også stort sett kun har en eller flere av disse 3. Alt så rosenrødt ut og vi kunne sole oss i glansen og flotte oss med vanskelige maskinlæringutrykk for å fremheve vår egen fortreffelighet.

Alle elsker Netflix

Hvor mange forskjellige abonnement tror du kundene til SpareBank 1 har? Jeg kan gi deg et hint: vi har rundt en million kunder. Og jeg skal gi deg enda et hint: svaret er større enn 3. For det viste seg ganske fort at det finnes mange abonnement. Mange, mange abonnement…

Det var bare en ting å gjøre, brette opp ermene.

For å forstå løsningen må man skjønne problemet. Klientene våre sitter på transaksjoner som spenner over flere måneder og år. Transaksjonene har begrenset informasjon, men vi valgte å benytte oss av dato, beløp, transaksjonskode (unik kode som spesifiserer hva slags type transaksjon det er) og transaksjonstekst.

Eksempel på transaksjon

Maskinlæringsalgoritmene trenger tall for å fungere, så det første steget er vektorisering. Som så mange andre bruker vi Pandas for å lese og manipulere store datasett.

Dato

Det første vi gjør, er å endre dato til dag i måneden (i eksemplet blir 22.12.2019 til 22) for så å bruke sci-kit learns StandardScaler på den. Rammeverket beregner gjennomsnittet og variansen for oss, basert på hele treningsdatasettet. Da kan vi senere (når vi skal beregne nye abonnement) vektorisere ved å bruke ligningen under.

Standardscalerens ligning

Beløp

Den neste featuren (egenskapen) er beløp, som vi bruker sci-kit learns RobustScaler på. Grunnen er at vi har noen veldig store beløp som gjør at denne fungerer bedre enn Standardscaleren.

Men hvorfor gjør vi om tall til tall, jeg sa jo nettopp at maskinlæringsalgoritmene trenger tall for å fungere? Forklaringen ligger i at tallene bør være i noenlunde samme størrelsesorden og at de bør ligge over og under 0 for å fungere optimalt. Tenk på dag i måneden. Her har vi: Min(dag_i_mnd) = 1 og Max(dag_i_mnd) = 31.

Hva med beløpet? Siden vi har alt fra lønnstransaksjoner og boligsalg til store innkjøp kan Min(beløp)=mange hundre tusen i minus og Max(beløp)=mange hundre tusen i pluss. Dag i måneden og beløp er altså på helt forskjellige skalaer når det gjelder nominelle tall. Ved å normalisere de, får vi begge featurene rundt null og på samme skala.

Transkasjonskode

Transaksjonskodene er av type kategori (categorical features) og bør one-hot-encodes. I praksis har vi noen hundre transaksjonstyper i bruk og hver transaksjon vektoriseres da til ca. 200 dimensjoner, der 1 dimensjon har lengde 1 og de øvrige har lengde 0.

La meg forklare med et litt enklere eksempel. La oss si at vi har 3 transaksjonskoder (i stedet for 200): R_013 (Lønn), R_714 (Visakjøp) og R_718 (Minibank). En transaksjon har en av disse 3, men aldri flere. “Vanlig” normalisering gir ingen mening her. R_718 er på ingen måte “større enn” R_714, på samme måte som at R_710 ikke er “mindre enn” R_714. Det er bare forskjellige transaksjonskoder som er tilfeldig nummerert og som har forskjellig mening.

Eksempel på one-hot-encoding

Transaksjonstekst

Transaksjonsteksten er den siste og kanskje vanskeligste. Det finnes mange måter å represente tekst på, men vi har valgt å gå for word embeddinger ved bruk av fastText. fastText er et rammeverk fra Facebook som gir oss 100-dimensjonale word embeddings. Hvert unike ord er altså representert ved 100 floats og vi vektoriserer opp til 7 ord per transaksjonstekst. Dette kan virke merkelig, men disse embeddingene har visse egenskaper som vi benytter oss av. Syntaktisk og semantisk like ord vil f.eks ligge nære hverandre i vektorrommet. Vi benytter transfer learning når vi trener våre egne word embeddinger (vi baserer oss på pre-trente embeddinger fra Language Technology Group).

Til sammen gir dette oss en 902 dimensjonal feature vector:

Når disse featurene er vektorisert, clustrer vi de ved hjelp av dbscan. dbscan er en unsupervised clustringsalgoritme som er flink til å finne clustre med lik tetthet og tilfeldig form. Det viktigste argumentet til dbscan er epsilon og min_samples. min_samples bestemmer hvor mange punkter som minst må være innenfor epsilon avstand fra hverandre for å kalles kjerneelementer. Oppfyller man dette kriteriet har man et cluster. Når man har dette minimumet av kjerneelementer, kan man se på grenseelementer som må være innenfor epsilon avstand fra minst et kjernepunkt. Disse vil også bli medlemmer av clusteret. Vi hadde størst suksess med en epsilon rundt 0.3 og min_samples ≥ 3 når vi skulle finne de faste utgiftene. Altså, minst 3 punkter som er innenfor 0.3 avstand fra hverandre + et tilfeldig antall grenseelementer som er innenfor 0.3 avstand fra minst et av disse kjernepunktene.

1,2 og 3 er mindre enn epsilon avstand fra hverandre og er derfor kjerneelementer. 4 er kun mindre enn epsilon avstand fra 2 og kalles da et grenseelement. 5 er mer enn epsilon avstand fra alle og kalles støy.

Et par observasjoner (hopp over disse hvis du synes dette avsnittet var vanskelig).

  • Vi benytter euclidian distance i clustringen. Det finnes flere måter å måle avstand på (se scipy spatial distance), men det viste seg at euclidian distance ga best resultat. Med word embeddings benytter vi oss ofte av cosine similarity, for å finne likheter mellom ord. Hvordan passer dette inn med euclidian clustring? Løsningen ligger blant annet i det som kalles monotonic transformations. Når cosine similarity øker, minsker euclidian distance (når unit vector length er lik), altså akkurat det vi ønsker. Den komplette forklaringen er mer kompleks og krever en egen bloggpost.
  • Et annet punkt er at dag i måneden gir månedlige abonnement. Vi har også kvartalsvise, halvårs og årsabonnement, løst ved å lage en ekstra feature basert på modulusfunksjoner.
  • Du kan også oppleve noen problemer ved å bruke dag i måneden i månedslutten hvis du får helg/helligdag på en sen dato.
  • Den siste utfordringen er at du vil ha markert Netflix abonnementet ditt allerede den første måneden du får opp transaksjonen, ikke vente til du har 3 Netflix transaksjoner. Hvordan dette ble løst ligger utenfor scopet til denne bloggen, men ta gjerne kontakt så deler vi gjerne.

Resultater

Her er et typisk mobilabonnement. Kunden har variable summer (brukt mer mobildata f.eks) og variable datoer (lørdag-/søndag-/helligdag-problematikk, bankene prosesserer ikke betalingen til abonnementet før første bankdag). Ved å ha en passe epsilon under clustringen vil disse variasjonene være små nok til å bli naboer i det 902 dimensjonale vektorrommet.

Et annet problem vi hadde med vår første regex versjon, er de firmaene som både har abonnement og selger enkeltprodukter. Et eksempel er Viaplay, en strømmetjeneste som har både abonnement og muligheten for å leie enkeltfilmer. I dette tilfellet markerer clustringen kun de som faktisk er abonnementer (markert som grønne).

Ikke alle abonnement kan finnes med regex’er. Mange bedrifter bruker kundenummer og faktureringsnummer som transaksjonstekst.

Noen bedrifter endrer transaksjonsteksten fra måned til måned.

Noen ganger er navneendringene store. Hvordan er det mulig for algoritmen å skjønne at CanalDigital plutselig heter Telenor? I vanlig språk er god, bra og fantastisk semantisk like ord. På samme måte viser våre word embeddinger at CanalDigital er det ordet som er mest likt Telenor.

Disse eksemplene er noen få eksempler på hvordan algoritmen fungerer. De viser dog ikke det store volumet vi fant. Hos kundene våre finner vi hundrevis, om ikke tusenvis av abonnement. Det er alt fra aviser og treningssentre til datingtjenester og japanske tegneserier. Det er norske, amerikanske, polske og rumenske abonnement. Abonnement som du nå kan filtrere og håndtere i SpareBank 1 sin mobilbank.

SpareBank 1 Utvikling

Vi jobber med digitale løsninger hos SpareBank 1. Vi liker å skrive om det vi brenner for

Thanks to Anders Gjendem, Vidar Moe, Jonas Nordstrand, and Stian Conradsen

Inge Johnsen

Written by

Machine learning enthusiast @SpareBank1

SpareBank 1 Utvikling

Vi jobber med digitale løsninger hos SpareBank 1. Vi liker å skrive om det vi brenner for

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade