SNA Hackathon 2019

I februar-marts 2019 blev der afholdt en konkurrence for at rangere det sociale netværks feed SNA Hackathon 2019, hvor vores hold tog førstepladsen. I artiklen vil jeg fortælle om tilrettelæggelsen af ​​konkurrencen, de metoder, vi prøvede, og catboost-indstillingerne for træning på big data.

SNA Hackathon 2019

SNA Hackathon

Det er tredje gang, der afholdes et hackathon under dette navn. Det er organiseret af det sociale netværk ok.ru, henholdsvis opgaven og dataene er direkte relateret til dette sociale netværk.
SNA (social network analysis) er i dette tilfælde mere korrekt ikke forstået som en analyse af en social graf, men snarere som en analyse af et socialt netværk.

  • I 2014 var opgaven at forudsige antallet af likes et opslag ville få.
  • I 2016 - VVZ-opgaven (måske du er bekendt), tættere på analysen af ​​den sociale graf.
  • I 2019, rangering af brugerens feed baseret på sandsynligheden for, at brugeren vil kunne lide indlægget.

Jeg kan ikke sige noget om 2014, men i 2016 og 2019 var der udover dataanalyseevner også påkrævet færdigheder i at arbejde med big data. Jeg tror, ​​det var kombinationen af ​​maskinlæring og problemer med big databehandling, der tiltrak mig til disse konkurrencer, og min erfaring på disse områder hjalp mig med at vinde.

mlbootcamp

I 2019 blev konkurrencen arrangeret på platformen https://mlbootcamp.ru.

Konkurrencen begyndte online den 7. februar og bestod af 3 opgaver. Alle kunne registrere sig på siden, downloade baseline og læs din bil i et par timer. Ved afslutningen af ​​online-scenen den 15. marts blev de 15 bedste fra hver springbegivenhed inviteret til Mail.ru-kontoret til offline-scenen, som fandt sted fra 30. marts til 1. april.

Opgave

Kildedataene giver bruger-id'er (userId) og post-id'er (objectId). Hvis brugeren fik vist et indlæg, så indeholder dataene en linje, der indeholder userId, objectId, brugerreaktioner på dette indlæg (feedback) og et sæt forskellige funktioner eller links til billeder og tekster.

bruger ID objekt-id ejer-id tilbagemeldinger billeder
3555 22 5677 [synes godt om, klikkede] [hash1]
12842 55 32144 [ikke lide] [hash2, hash3]
13145 35 5677 [klikket, delt] [hash2]

Testdatasættet indeholder en lignende struktur, men feedbackfeltet mangler. Opgaven er at forudsige tilstedeværelsen af ​​den 'like'-reaktion i feedbackfeltet.
Indsendelsesfilen har følgende struktur:

bruger ID SortedList[objectId]
123 78,13,54,22
128 35,61,55
131 35,68,129,11

Metrikken er den gennemsnitlige ROC AUC for brugere.

En mere detaljeret beskrivelse af data kan findes på rådets hjemmeside. Du kan også downloade data der, inklusive test og billeder.

Online scene

På online-stadiet var opgaven delt op i 3 dele

Offline scene

På offlinestadiet indeholdt dataene alle funktioner, mens tekster og billeder var sparsomme. Der var 1,5 gange flere rækker i datasættet, som der allerede var mange af.

Løsningen af ​​problemet

Da jeg laver CV på jobbet, startede jeg min rejse i denne konkurrence med opgaven "Billeder". De data, der blev leveret, var userId, objectId, ownerId (den gruppe, hvori indlægget blev offentliggjort), tidsstempler for oprettelse og visning af indlægget, og selvfølgelig billedet for dette indlæg.
Efter at have genereret adskillige funktioner baseret på tidsstempler, var den næste idé at tage det næstsidste lag af neuronen, der var forudtrænet på imagenet, og sende disse indlejringer til boosting.

SNA Hackathon 2019

Resultaterne var ikke imponerende. Indlejringer fra imagenet-neuronen er irrelevante, tænkte jeg, jeg skal lave min egen autoencoder.

SNA Hackathon 2019

Det tog meget tid, og resultatet blev ikke bedre.

Feature generation

At arbejde med billeder tager meget tid, så jeg besluttede at gøre noget enklere.
Som du umiddelbart kan se, er der flere kategoriske træk i datasættet, og for ikke at genere for meget, tog jeg bare catboost. Løsningen var fremragende, uden nogen indstillinger kom jeg straks til den første linje på ranglisten.

Der er ret mange data, og det er lagt ud i parketformat, så uden at tænke mig om, tog jeg scala og begyndte at skrive alt i gnist.

De enkleste funktioner, der gav mere vækst end billedindlejringer:

  • hvor mange gange objectId, userId og ownerId optrådte i dataene (skal korrelere med popularitet);
  • hvor mange indlæg userId har set fra ownerId (skal korrelere med brugerens interesse i gruppen);
  • hvor mange unikke userId'er, der har set indlæg fra ownerId (afspejler størrelsen på gruppens publikum).

Fra tidsstempler var det muligt at få det tidspunkt på dagen, hvor brugeren så feedet (morgen/eftermiddag/aften/nat). Ved at kombinere disse kategorier kan du fortsætte med at generere funktioner:

  • hvor mange gange userId loggede ind om aftenen;
  • på hvilket tidspunkt dette indlæg oftest vises (objectId) og så videre.

Alt dette forbedrede gradvist metrikken. Men størrelsen af ​​træningsdatasættet er omkring 20 millioner rekorder, så tilføjelse af funktioner bremsede træningen betydeligt.

Jeg har gentænket min tilgang til brug af data. Selvom dataene er tidsafhængige, så jeg ikke nogen åbenlyse informationslækage "i fremtiden", ikke desto mindre, for en sikkerheds skyld, opdelte jeg det sådan:

SNA Hackathon 2019

Det træningssæt, vi fik (februar og 2 uger i marts) var opdelt i 2 dele.
Modellen blev trænet på data fra de sidste N dage. De ovenfor beskrevne sammenlægninger blev bygget på alle data, inklusive testen. Samtidig er der dukket data op, hvorpå det er muligt at bygge forskellige indkodninger af målvariablen. Den enkleste tilgang er at genbruge kode, der allerede skaber nye funktioner, og simpelthen fodre den med data, som den ikke vil blive trænet på og mål = 1.

Således har vi lignende funktioner:

  • Hvor mange gange har userId set et indlæg i gruppens ownerId;
  • Hvor mange gange userId kunne lide indlægget i group ownerId;
  • Procentdelen af ​​indlæg, som userId kunne lide fra ownerId.

Det vil sige, viste det sig gennemsnitlig målkodning på en del af datasættet for forskellige kombinationer af kategoriske træk. I princippet bygger catboost også target-encoding og set fra dette synspunkt er der ingen fordel, men det blev for eksempel muligt at tælle antallet af unikke brugere, der kunne lide indlæg i denne gruppe. Samtidig blev hovedmålet nået - mit datasæt blev reduceret flere gange, og det var muligt at fortsætte med at generere funktioner.

Mens catboost kun kan bygge kodning baseret på den reaktion, der kunne lides, har feedback andre reaktioner: videredelt, ikke kunne lide, ikke kunne lide, klikket, ignoreret, kodninger, som kan udføres manuelt. Jeg genberegnet alle slags aggregater og eliminerede funktioner med lav betydning for ikke at puste datasættet op.

På det tidspunkt var jeg på førstepladsen med stor margin. Det eneste, der var forvirrende, var, at billedindlejringer næsten ikke viste nogen vækst. Ideen kom til at give alt til catboost. Vi grupperer Kmeans-billeder og får en ny kategorisk funktion imageCat.

Her er nogle klasser efter manuel filtrering og sammenlægning af klynger opnået fra KMeans.

SNA Hackathon 2019

Baseret på imageCat genererer vi:

  • Nye kategoriske funktioner:
    • Hvilken imageCat blev oftest set af userId;
    • Hvilken imageCat viser oftest ejer-id;
    • Hvilken imageCat blev oftest kunne lide af userId;
  • Forskellige tællere:
    • Hvor mange unikke imageCat kiggede på bruger-id;
    • Omkring 15 lignende funktioner plus målkodning som beskrevet ovenfor.

tekster

Resultaterne i billedkonkurrencen passede mig, og jeg besluttede mig for at prøve mig frem med tekster. Jeg har ikke arbejdet meget med tekster før, og tåbeligt nok slog jeg dagen ihjel på tf-idf og svd. Så så jeg baseline med doc2vec, som gør præcis hvad jeg har brug for. Efter at have justeret doc2vec-parametrene lidt, fik jeg tekstindlejringer.

Og så genbrugte jeg simpelthen koden til billederne, hvor jeg erstattede billedindlejringerne med tekstindlejringer. Det resulterede i, at jeg fik en 2. plads i tekstkonkurrencen.

Samarbejdssystem

Der var en konkurrence tilbage, som jeg endnu ikke havde "prikket" med en pind, og at dømme efter AUC'en på ranglisten, burde resultaterne af netop denne konkurrence have haft den største indflydelse på offline-stadiet.
Jeg tog alle de funktioner, der var i kildedataene, udvalgte kategoriske og beregnede de samme aggregater som for billeder, bortset fra funktioner baseret på selve billederne. Bare det at sætte dette i catboost fik mig til en 2. plads.

Første trin i catboost-optimering

En første- og to andenpladser glædede mig, men der var en forståelse for, at jeg ikke havde gjort noget særligt, hvilket betyder, at jeg kunne forvente et tab af stillinger.

Konkurrencens opgave er at rangordne indlæg inden for brugeren, og hele denne tid løste jeg klassifikationsproblemet, det vil sige at optimere den forkerte metrik.

Lad mig give dig et simpelt eksempel:

bruger ID objekt-id forudsigelse grundsandhed
1 10 0.9 1
1 11 0.8 1
1 12 0.7 1
1 13 0.6 1
1 14 0.5 0
2 15 0.4 0
2 16 0.3 1

Lad os lave en lille omlægning

bruger ID objekt-id forudsigelse grundsandhed
1 10 0.9 1
1 11 0.8 1
1 12 0.7 1
1 13 0.6 0
2 16 0.5 1
2 15 0.4 0
1 14 0.3 1

Vi får følgende resultater:

Model AUC Bruger1 AUC Bruger2 AUC betyder AUC
Mulighed 1 0,8 1,0 0,0 0,5
Mulighed 2 0,7 0,75 1,0 0,875

Som du kan se, betyder en forbedring af den overordnede AUC-metrik ikke en forbedring af den gennemsnitlige AUC-metrik inden for en bruger.

Catboost ved, hvordan man optimerer rangeringsmetrics fra kassen. Jeg læste om rangeringsmålinger, succeshistorier når du bruger catboost og indstil YetiRankPairwise til at træne natten over. Resultatet var ikke imponerende. Da jeg besluttede, at jeg var undertrænet, ændrede jeg fejlfunktionen til QueryRMSE, som efter catboost-dokumentationen at dømme konvergerer hurtigere. I sidste ende fik jeg de samme resultater, som når jeg trænede til klassifikation, men ensemblerne af disse to modeller gav en god stigning, hvilket bragte mig på førstepladsen i alle tre konkurrencer.

5 minutter før afslutningen af ​​onlinefasen af ​​konkurrencen "Collaborative Systems" flyttede Sergey Shalnov mig til andenpladsen. Vi gik den videre vej sammen.

Forberedelse til offline-stadiet

Vi var sikret sejr i online-fasen med et RTX 2080 TI-videokort, men hovedpræmien på 300 rubler og, højst sandsynligt, selv den endelige førsteplads tvang os til at arbejde i disse 000 uger.

Som det viste sig, brugte Sergey også catboost. Vi udvekslede ideer og funktioner, og jeg lærte om rapport af Anna Veronica Dorogush som indeholdt svar på mange af mine spørgsmål, og endda dem, som jeg endnu ikke havde fået på det tidspunkt.

At se rapporten førte mig til ideen om, at vi skal returnere alle parametre til standardværdien og foretage indstillingerne meget omhyggeligt og først efter at have rettet et sæt funktioner. Nu tog en træning omkring 15 timer, men en model formåede at opnå en bedre hastighed end den opnåede i ensemblet med rangordning.

Feature generation

I konkurrencen Collaborative Systems vurderes en lang række funktioner som vigtige for modellen. For eksempel, auditweights_spark_svd - det vigtigste tegn, men der er ingen oplysninger om, hvad det betyder. Jeg tænkte, at det ville være umagen værd at tælle de forskellige aggregater baseret på vigtige funktioner. For eksempel gennemsnitlig auditweights_spark_svd efter bruger, efter gruppe, efter objekt. Det samme kan beregnes ved hjælp af data, som der ikke trænes på og mål = 1, det vil sige gennemsnit auditweights_spark_svd af bruger efter objekter, han kunne lide. Vigtige tegn foruden auditweights_spark_svd, der var flere. Her er nogle af dem:

  • auditweightsCtrKøn
  • auditweightsCtrHigh
  • userOwnerCounterCreateLikes

For eksempel gennemsnittet auditweightsCtrKøn ifølge userId viste det sig at være en vigtig funktion, ligesom gennemsnitsværdien userOwnerCounterCreateLikes af userId+ownerId. Dette burde allerede få dig til at tænke, at du skal forstå betydningen af ​​felterne.

Også vigtige funktioner var revisionsvægteLikesTæl и revisionsvægteShowsCount. Ved at dele den ene med den anden, blev der opnået et endnu vigtigere træk.

Datalækker

Konkurrence- og produktionsmodellering er meget forskellige opgaver. Når man forbereder data, er det meget svært at tage højde for alle detaljer og ikke formidle nogle ikke-trivielle oplysninger om målvariablen i testen. Hvis vi laver en produktionsløsning, vil vi forsøge at undgå at bruge datalæk, når vi træner modellen. Men hvis vi vil vinde konkurrencen, så er datalæk de bedste funktioner.

Når du har studeret dataene, kan du se det ifølge objectId-værdierne revisionsvægteLikesTæl и revisionsvægteShowsCount ændring, hvilket betyder, at forholdet mellem de maksimale værdier af disse funktioner vil afspejle efterkonverteringen meget bedre end forholdet på visningstidspunktet.

Den første lækage vi fandt er auditweightsLikesCountMax/auditweightsShowsCountMax.
Men hvad hvis vi ser nærmere på dataene? Lad os sortere efter udstillingsdato og få:

objekt-id bruger ID revisionsvægteShowsCount revisionsvægteLikesTæl mål (kan lide)
1 1 12 3 sikkert ikke
1 2 15 3 måske ja
1 3 16 4

Det var overraskende, da jeg fandt det første sådan eksempel, og det viste sig, at min forudsigelse ikke gik i opfyldelse. Men under hensyntagen til det faktum, at de maksimale værdier af disse egenskaber i objektet gav en stigning, var vi ikke dovne og besluttede at finde revisionsvægteShowsCountNæste и revisionsvægteLikesTalNæste, det vil sige værdierne i det næste tidspunkt. Ved at tilføje en funktion
(auditweightsShowsCountNext-auditweightsShowsCount)/(auditweightsLikesCount-auditweightsLikesCountNext) vi lavede et skarpt spring hurtigt.
Lignende lækager kan bruges ved at finde følgende værdier for userOwnerCounterCreateLikes indenfor userId+ownerId og f.eks. auditweightsCtrKøn inden for objectId+userGender. Vi fandt 6 lignende felter med lækager og udtog så meget information som muligt fra dem.

På det tidspunkt havde vi presset så meget information ud som muligt fra samarbejdsfunktioner, men vendte ikke tilbage til billed- og tekstkonkurrencer. Jeg havde en god idé at tjekke: hvor meget giver funktioner direkte baseret på billeder eller tekster i relevante konkurrencer?

Der var ingen lækager i billed- og tekstkonkurrencer, men på det tidspunkt havde jeg returneret standard catboost-parametrene, ryddet op i koden og tilføjet et par funktioner. Den samlede var:

beslutning snart
Maksimalt med billeder 0.6411
Maksimalt ingen billeder 0.6297
Resultatet på andenpladsen 0.6295

beslutning snart
Maksimalt med tekster 0.666
Maksimalt uden tekster 0.660
Resultatet på andenpladsen 0.656

beslutning snart
Maksimalt i samarbejde 0.745
Resultatet på andenpladsen 0.723

Det blev tydeligt, at vi næppe ville være i stand til at presse meget ud af tekster og billeder, og efter at have prøvet et par af de mest interessante ideer, holdt vi op med at arbejde med dem.

Yderligere generering af funktioner i kollaborative systemer gav ikke en stigning, og vi begyndte at rangere. På online-stadiet gav klassifikations- og rangeringsensemblet mig en lille stigning, da det viste sig, fordi jeg undertrænede klassifikationen. Ingen af ​​fejlfunktionerne, inklusive YetiRanlPairwise, producerede i nærheden af ​​det resultat, som LogLoss gjorde (0,745 vs. 0,725). Der var stadig håb for QueryCrossEntropy, som ikke kunne lanceres.

Offline scene

På offlinestadiet forblev datastrukturen den samme, men der var mindre ændringer:

  • identifikatorer userId, objectId, ownerId blev omrandomiseret;
  • flere skilte blev fjernet og flere blev omdøbt;
  • dataene er steget ca. 1,5 gange.

Ud over de anførte vanskeligheder var der et stort plus: holdet fik tildelt en stor server med en RTX 2080TI. Jeg har nydt htop i lang tid.
SNA Hackathon 2019

Der var kun én idé – simpelthen at reproducere det, der allerede eksisterer. Efter at have brugt et par timer på at sætte miljøet op på serveren, begyndte vi så småt at verificere, at resultaterne var reproducerbare. Det største problem, vi står over for, er stigningen i datamængden. Vi besluttede at reducere belastningen lidt og indstille catboost-parameteren ctr_complexity=1. Dette sænker hastigheden lidt, men min model begyndte at virke, resultatet var godt - 0,733. Sergey, i modsætning til mig, delte ikke dataene op i 2 dele og trænede på alle dataene, selvom dette gav de bedste resultater på online-stadiet, på offline-stadiet var der mange vanskeligheder. Hvis vi tog alle de funktioner, vi genererede, og prøvede at skubbe dem ind i catboost, så ville intet fungere på online-stadiet. Sergey lavede typeoptimering, for eksempel ved at konvertere float64-typer til float32. I denne artikel, Du kan finde information om hukommelsesoptimering i pandaer. Som et resultat trænede Sergey på CPU'en ved at bruge alle data og fik omkring 0,735.

Disse resultater var nok til at vinde, men vi skjulte vores sande fart og kunne ikke være sikre på, at andre hold ikke gjorde det samme.

Kæmp til det sidste

Catboost tuning

Vores løsning blev fuldt gengivet, vi tilføjede funktionerne i tekstdata og billeder, så der var kun tilbage at justere catboost-parametrene. Sergey trænede på CPU'en med et lille antal iterationer, og jeg trænede på den med ctr_complexity=1. Der var en dag tilbage, og hvis du bare tilføjede iterationer eller øgede ctr_complexity, så kunne du om morgenen få en endnu bedre fart og gå hele dagen.

På offlinestadiet kunne hastigheder meget nemt skjules ved blot at vælge ikke den bedste løsning på webstedet. Vi forventede drastiske ændringer i leaderboardet i de sidste minutter før indsendelserne lukkede og besluttede ikke at stoppe.

Fra Annas video lærte jeg, at for at forbedre kvaliteten af ​​modellen er det bedst at vælge følgende parametre:

  • learning_rate — Standardværdien beregnes ud fra størrelsen af ​​datasættet. Forøgelse af learning_rate kræver at øge antallet af iterationer.
  • l2_blad_reg — Regulariseringskoefficient, standardværdi 3, vælg helst fra 2 til 30. Reduktion af værdien fører til en stigning i overfit.
  • bagging_temperatur — tilføjer randomisering til vægten af ​​objekter i stikprøven. Standardværdien er 1, hvor vægtene er trukket fra en eksponentiel fordeling. Reduktion af værdien fører til en stigning i overfit.
  • tilfældig_styrke — Påvirker valget af opdelinger ved en specifik iteration. Jo højere tilfældig_styrke, desto større er chancen for, at en opdeling med lav betydning bliver valgt. Ved hver efterfølgende iteration falder tilfældigheden. Reduktion af værdien fører til en stigning i overfit.

Andre parametre har en meget mindre effekt på det endelige resultat, så jeg forsøgte ikke at vælge dem. Én iteration af træning på mit GPU-datasæt med ctr_complexity=1 tog 20 minutter, og de valgte parametre på det reducerede datasæt var lidt anderledes end de optimale på det fulde datasæt. Til sidst lavede jeg omkring 30 iterationer på 10 % af dataene og derefter omkring 10 flere iterationer på alle dataene. Det viste sig noget som dette:

  • learning_rate Jeg steg med 40% fra standard;
  • l2_blad_reg forlod det det samme;
  • bagging_temperatur и tilfældig_styrke reduceret til 0,8.

Vi kan konkludere, at modellen var undertrænet med standardparametrene.

Jeg blev meget overrasket, da jeg så resultatet på ranglisten:

Model model 1 model 2 model 3 ensemble
Uden tuning 0.7403 0.7404 0.7404 0.7407
Med tuning 0.7406 0.7405 0.7406 0.7408

Jeg konkluderede for mig selv, at hvis hurtig anvendelse af modellen ikke er nødvendig, så er det bedre at erstatte valget af parametre med et ensemble af flere modeller ved hjælp af ikke-optimerede parametre.

Sergey optimerede størrelsen af ​​datasættet for at køre det på GPU'en. Den enkleste mulighed er at afskære en del af dataene, men det kan gøres på flere måder:

  • fjern gradvist de ældste data (begyndelsen af ​​februar), indtil datasættet begynder at passe ind i hukommelsen;
  • fjern funktioner med den laveste betydning;
  • fjerne bruger-id'er, for hvilke der kun er én post;
  • efterlad kun de bruger-id'er, der er i testen.

Og i sidste ende lav et ensemble ud af alle mulighederne.

Det sidste ensemble

Sen aften den sidste dag havde vi lagt et ensemble af vores modeller, der gav 0,742. I løbet af natten lancerede jeg min model med ctr_complexity=2 og i stedet for 30 minutter trænede den i 5 timer. Først klokken 4 blev det talt, og jeg lavede det sidste ensemble, som gav 0,7433 på den offentlige rangliste.

På grund af forskellige tilgange til at løse problemet var vores forudsigelser ikke stærkt korrelerede, hvilket gav en god stigning i ensemblet. For at få et godt ensemble er det bedre at bruge råmodellens forudsigelser forudsigelse(prediction_type='RawFormulaVal') og indstille scale_pos_weight=neg_count/pos_count.

SNA Hackathon 2019

På hjemmesiden kan du se endelige resultater på den private rangliste.

Andre løsninger

Mange hold fulgte kanonerne for anbefalingssystemalgoritmer. Jeg, der ikke er ekspert på dette område, kan ikke vurdere dem, men jeg husker 2 interessante løsninger.

  • Nikolay Anokhins løsning. Nikolay, der er ansat hos Mail.ru, ansøgte ikke om præmier, så hans mål var ikke at opnå maksimal hastighed, men at opnå en let skalerbar løsning.
  • Juryprisvindende holds beslutning baseret på denne artikel fra facebook, gav mulighed for meget god billedklynger uden manuelt arbejde.

Konklusion

Hvad satte sig mest i min hukommelse:

  • Hvis der er kategoriske funktioner i dataene, og du ved, hvordan du laver målkodning korrekt, er det stadig bedre at prøve catboost.
  • Hvis du deltager i en konkurrence, bør du ikke spilde tid på at vælge andre parametre end learning_rate og iterations. En hurtigere løsning er at lave et ensemble af flere modeller.
  • Boosting kan lære på GPU'en. Catboost kan lære meget hurtigt på GPU'en, men det æder meget hukommelse.
  • Under udvikling og test af ideer er det bedre at indstille en lille rsm~=0.2 (kun CPU) og ctr_complexity=1.
  • I modsætning til andre teams gav ensemblet af vores modeller en stor stigning. Vi udvekslede kun ideer og skrev på forskellige sprog. Vi havde en anden tilgang til at opdele dataene, og jeg tror, ​​at hver havde sine egne fejl.
  • Det er ikke klart, hvorfor rangeringsoptimering klarede sig dårligere end klassifikationsoptimering.
  • Jeg fik en del erfaring med at arbejde med tekster og en forståelse for, hvordan anbefalingssystemer laves.

SNA Hackathon 2019

Tak til arrangørerne for de modtagne følelser, viden og præmier.

Kilde: www.habr.com

Tilføj en kommentar