SNA Hackathon 2019

U veljači i ožujku 2019. održano je natjecanje za rangiranje feedova društvenih mreža SNA Hackathon 2019, u kojem je naša ekipa zauzela prvo mjesto. U članku ću govoriti o organizaciji natjecanja, metodama koje smo isprobali i catboost postavkama za trening na velikim podacima.

SNA Hackathon 2019

SNA Hackathon

Ovo je treći put da se održava hackathon pod ovim nazivom. Organizira ga društvena mreža ok.ru, odnosno zadatak i podaci izravno su povezani s ovom društvenom mrežom.
SNA (analiza društvenih mreža) u ovom slučaju ispravnije je shvatiti ne kao analizu društvenog grafikona, već kao analizu društvene mreže.

  • U 2014. zadatak je bio predvidjeti broj lajkova koji će objava dobiti.
  • 2016. - VVZ zadatak (možda vam je poznat), bliže analizi socijalnog grafikona.
  • U 2019., rangiranje korisničkog feeda na temelju vjerojatnosti da će se korisniku objava svidjeti.

Ne mogu reći za 2014., ali u 2016. i 2019., osim sposobnosti analize podataka, bile su potrebne i vještine rada s velikim podacima. Mislim da me kombinacija strojnog učenja i velikih problema s obradom podataka privukla ovim natjecanjima, a moje iskustvo u tim područjima pomoglo mi je da pobijedim.

mlbootcamp

U 2019. godini natjecanje je organizirano na platformi https://mlbootcamp.ru.

Natjecanje je započelo online 7. veljače i sastojalo se od 3 zadatka. Svatko se mogao registrirati na web stranici, preuzeti osnovica i napuniti svoj auto nekoliko sati. Na kraju online etape 15. ožujka, 15 najboljih iz svake natjecanja u preponskom jahanju pozvano je u ured Mail.ru za offline etapu, koja se održala od 30. ožujka do 1. travnja.

Zadatak

Izvorni podaci pružaju korisničke ID-ove (userId) i ID-ove objave (objectId). Ako je korisniku prikazana objava, tada podaci sadrže redak koji sadrži userId, objectId, reakcije korisnika na tu objavu (feedback) i skup raznih značajki ili poveznica na slike i tekstove.

ID korisnika objectId ownerId povratna veza slika
3555 22 5677 [lajkao, kliknuo] [hash1]
12842 55 32144 [ne sviđa mi se] [hash2,hash3]
13145 35 5677 [kliknuto, podijeljeno] [hash2]

Testni skup podataka sadrži sličnu strukturu, ali nedostaje polje povratne informacije. Zadatak je predvidjeti prisutnost reakcije 'sviđa mi se' u polju povratne informacije.
Datoteka za podnošenje ima sljedeću strukturu:

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

Mjerni podatak je prosječni ROC AUC za korisnike.

Detaljniji opis podataka nalazi se na web stranica vijeća. Tamo također možete preuzeti podatke, uključujući testove i slike.

Online faza

U online fazi zadatak je bio podijeljen u 3 dijela

  • Suradnički sustav — uključuje sve značajke osim slika i tekstova;
  • slika — uključuje samo informacije o slikama;
  • tekstovi — uključuje informacije samo o tekstovima.

Offline faza

U izvanmrežnoj fazi podaci su uključivali sve značajke, dok su tekstovi i slike bili rijetki. Bilo je 1,5 puta više redaka u skupu podataka, kojih je već bilo puno.

Rješenje problema

Budući da životopis radim na poslu, svoj sam put u ovom natječaju započeo sa zadatkom “Slike”. Podaci koji su navedeni su userId, objectId, ownerId (grupa u kojoj je objava objavljena), vremenske oznake za kreiranje i prikaz objave i, naravno, slika za ovu objavu.
Nakon generiranja nekoliko značajki na temelju vremenskih oznaka, sljedeća ideja bila je uzeti pretposljednji sloj neurona koji je prethodno treniran na imagenetu i poslati ta ugrađivanja na pojačavanje.

SNA Hackathon 2019

Rezultati nisu bili impresivni. Ugradnje iz imagenet neurona su nebitne, pomislio sam, moram napraviti vlastiti autokoder.

SNA Hackathon 2019

Trebalo je puno vremena, a rezultat se nije popravio.

Generiranje značajki

Rad sa slikama oduzima puno vremena, pa sam odlučio napraviti nešto jednostavnije.
Kao što odmah možete vidjeti, postoji nekoliko kategoričkih značajki u skupu podataka, a kako ne bih previše smetao, uzeo sam samo catboost. Rješenje je bilo izvrsno, bez ikakvih postavki sam odmah došao u prvu liniju ljestvice.

Podataka ima dosta i raspoređeni su u parket formatu, pa sam bez razmišljanja uzeo scalu i počeo sve pisati u iskri.

Najjednostavnije značajke koje su dale veći rast od ugrađivanja slika:

  • koliko su se puta objectId, userId i ownerId pojavili u podacima (trebalo bi biti u korelaciji s popularnošću);
  • koliko je postova userId vidio od ownerId-a (trebalo bi biti u korelaciji s korisnikovim interesom za grupu);
  • koliko je jedinstvenih userId-ova pregledalo postove s ownerId-a (odražava veličinu publike grupe).

Iz vremenskih oznaka bilo je moguće dobiti doba dana u kojem je korisnik gledao feed (jutro/poslijepodne/večer/noć). Kombiniranjem ovih kategorija možete nastaviti generirati značajke:

  • koliko puta se userId prijavio navečer;
  • u koje vrijeme se ta objava najčešće prikazuje (objectId) i tako dalje.

Sve je to postupno poboljšalo metriku. Ali veličina skupa podataka za obuku je oko 20 milijuna zapisa, pa je dodavanje značajki uvelike usporilo obuku.

Ponovno sam razmislio o svom pristupu korištenju podataka. Iako su podaci ovisni o vremenu, nisam vidio nikakvo očito curenje informacija "u budućnosti", ipak, za svaki slučaj, raščlanio sam ih ovako:

SNA Hackathon 2019

Skup treninga koji smo dobili (veljača i 2 tjedna ožujka) podijeljen je u 2 dijela.
Model je treniran na podacima iz zadnjih N dana. Gore opisane agregacije izgrađene su na svim podacima, uključujući test. Istodobno su se pojavili podaci na temelju kojih je moguće graditi različita kodiranja ciljne varijable. Najjednostavniji pristup je ponovno upotrijebiti kod koji već stvara nove značajke i jednostavno mu dati podatke na kojima se neće obučavati i ciljati = 1.

Dakle, dobili smo slične karakteristike:

  • Koliko je puta userId vidio objavu u grupi ownerId;
  • Koliko je puta userId lajkao objavu u grupi ownerId;
  • Postotak objava koje je userId lajkao od ownerId-a.

Odnosno, pokazalo se srednje ciljno kodiranje na dijelu skupa podataka za različite kombinacije kategoričkih obilježja. U principu, catboost također gradi ciljno kodiranje i s ove točke gledišta nema koristi, ali, na primjer, postalo je moguće brojati broj jedinstvenih korisnika koji su lajkali objave u ovoj grupi. U isto vrijeme, glavni cilj je postignut - moj skup podataka je smanjen nekoliko puta, i bilo je moguće nastaviti generirati značajke.

Dok catboost može izgraditi kodiranje samo na temelju reakcije koja im se sviđa, povratne informacije imaju i druge reakcije: ponovno dijeljenje, nesviđanje, nesviđanje, klik, ignoriranje, kodiranje za koje se može napraviti ručno. Ponovno sam izračunao sve vrste agregata i eliminirao značajke niske važnosti kako ne bih povećao skup podataka.

Do tada sam bio na prvom mjestu s velikom razlikom. Jedina stvar koja je bila zbunjujuća je da ugradnje slika nisu pokazivale gotovo nikakav rast. Došla je ideja da sve dam catboostu. Grupiramo Kmeans slike i dobivamo novu kategoričku značajku imageCat.

Evo nekoliko klasa nakon ručnog filtriranja i spajanja klastera dobivenih iz KMeans-a.

SNA Hackathon 2019

Na temelju imageCat generiramo:

  • Nove kategoričke značajke:
    • Koji imageCat je najčešće pregledavan prema userId;
    • Koji imageCat najčešće pokazuje ownerId;
    • Koji imageCat je najčešće lajkao userId;
  • Razni brojači:
    • Koliko je jedinstvenih imageCat pogledalo userId;
    • Oko 15 sličnih značajki plus ciljano kodiranje kao što je gore opisano.

tekstovi

Rezultati na slikovnom natječaju su mi odgovarali i odlučila sam se okušati u tekstovima. Nisam prije puno radio s tekstovima i, budalasto, ubio sam dan na tf-idf i svd. Zatim sam vidio baseline s doc2vecom, koji radi točno ono što mi treba. Malo prilagodivši doc2vec parametre, dobio sam tekstualne umetke.

Zatim sam jednostavno ponovno upotrijebio kôd za slike, u kojem sam umetnute slike zamijenio umetnutim tekstom. Time sam zauzeo 2. mjesto na natjecanju tekstova.

Suradnički sustav

Ostalo je još jedno natjecanje koje još nisam “bocnuo” palicom, a sudeći po AUC-u na ljestvici, upravo su rezultati ovog natjecanja trebali najviše utjecati na offline pozornicu.
Uzeo sam sva obilježja koja su bila u izvornim podacima, odabrao kategorička i izračunao iste agregate kao za slike, osim obilježja koja se temelje na samim slikama. Samo stavljanje ovoga u catboost dovelo me do 2. mjesta.

Prvi koraci catboost optimizacije

Radovali su me jedno prvo i dva druga mjesta, ali bilo je razumijevanja da nisam napravio ništa posebno, što znači da mogu očekivati ​​gubitak pozicije.

Zadatak natjecanja je rangiranje objava unutar korisnika, a ja sam cijelo to vrijeme rješavao problem klasifikacije, odnosno optimizacije krive metrike.

Dat ću vam jednostavan primjer:

ID korisnika objectId proricanje temeljna istina
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

Napravimo mali preustroj

ID korisnika objectId proricanje temeljna istina
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

Dobivamo sljedeće rezultate:

Model AUC Korisnik1 AUC Korisnik2 AUC srednji AUC
Opcija 1 0,8 1,0 0,0 0,5
Opcija 2 0,7 0,75 1,0 0,875

Kao što vidite, poboljšanje ukupne metrike AUC-a ne znači poboljšanje prosječne metrike AUC-a unutar korisnika.

Catboost zna kako optimizirati metriku rangiranja iz kutije. Čitao sam o metrici rangiranja, uspješne priče kada koristite catboost i postavite YetiRankPairwise da trenira preko noći. Rezultat nije bio impresivan. Odlučivši da sam nedovoljno obučen, promijenio sam funkciju pogreške u QueryRMSE, koja, sudeći prema dokumentaciji catboosta, brže konvergira. Na kraju sam dobio iste rezultate kao i kod treninga za klasifikaciju, ali su ansambli ova dva modela dali dobar porast, što me dovelo do prvog mjesta u sva tri natjecanja.

Pet minuta prije zatvaranja online faze natjecanja "Suradnički sustavi", Sergey Shalnov me pomaknuo na drugo mjesto. Zajedno smo prošli dalji put.

Priprema za izvanmrežnu fazu

Zajamčena nam je pobjeda u mrežnoj fazi s video karticom RTX 2080 TI, ali glavna nagrada od 300 000 rubalja i, najvjerojatnije, čak i konačno prvo mjesto natjerali su nas da radimo ova 2 tjedna.

Kako se ispostavilo, Sergej je također koristio catboost. Razmijenili smo ideje i značajke, a ja sam naučio o tome izvješće Anna Veronica Dorogush koji je sadržavao odgovore na mnoga moja pitanja, pa čak i ona koja do tada još nisam imao.

Pregledavanje izvješća dovelo me do ideje da sve parametre trebamo vratiti na zadane vrijednosti, a postavke obaviti vrlo pažljivo i tek nakon popravljanja skupa značajki. Sada je jedan trening trajao oko 15 sati, ali je jedan model uspio postići brzinu bolju od one dobivene u ansamblu s rangiranjem.

Generiranje značajki

U natjecanju Collaborative Systems veliki broj značajki se procjenjuje kao važne za model. Na primjer, auditweights_spark_svd - najvažniji znak, ali nema informacija o tome što znači. Mislio sam da bi bilo vrijedno prebrojati različite agregate na temelju važnih značajki. Na primjer, prosječni auditweights_spark_svd po korisniku, po grupi, po objektu. Isto se može izračunati pomoću podataka na kojima se ne izvodi trening i target = 1, odnosno prosjek auditweights_spark_svd po korisniku po objektima koji su mu se svidjeli. Osim toga važni znakovi auditweights_spark_svd, bilo ih je nekoliko. Ovo su neki od njih:

  • auditweightsCtrGender
  • auditweightsCtrHigh
  • userOwnerCounterCreateLikes

Na primjer, prosjek auditweightsCtrGender prema userId-u pokazalo se da je to važna karakteristika, baš kao i prosječna vrijednost userOwnerCounterCreateLikes prema userId+ownerId. Ovo bi vas već trebalo navesti na pomisao da morate razumjeti značenje polja.

Također su bile važne karakteristike auditweightsLikesCount и auditweightsShowsCount. Dijeleći jedno drugim, dobiva se još važnije obilježje.

Curenje podataka

Natjecanje i modeliranje proizvodnje vrlo su različite zadaće. Prilikom pripreme podataka vrlo je teško uzeti u obzir sve detalje i ne prenijeti neke netrivijalne informacije o ciljnoj varijabli u testu. Ako stvaramo produkcijsko rješenje, pokušat ćemo izbjeći korištenje curenja podataka prilikom uvježbavanja modela. Ali ako želimo pobijediti u konkurenciji, onda je curenje podataka najbolja značajka.

Nakon što ste proučili podatke, to možete vidjeti prema vrijednostima objectId auditweightsLikesCount и auditweightsShowsCount promijeniti, što znači da će omjer maksimalnih vrijednosti ovih značajki odražavati post konverziju mnogo bolje od omjera u trenutku prikaza.

Prvo curenje koje smo pronašli je auditweightsLikesCountMax/auditweightsShowsCountMax.
Ali što ako pažljivije pogledamo podatke? Poredajmo po datumu prikazivanja i dobijemo:

objectId ID korisnika auditweightsShowsCount auditweightsLikesCount meta (sviđa se)
1 1 12 3 vjerojatno ne
1 2 15 3 Možda da
1 3 16 4

Bilo je iznenađenje kada sam pronašao prvi takav primjer i pokazalo se da se moje predviđanje nije obistinilo. Ali, uzimajući u obzir činjenicu da su maksimalne vrijednosti ovih karakteristika unutar objekta dale povećanje, nismo bili lijeni i odlučili smo pronaći auditweightsShowsCountNext и auditweightsLikesCountNext, odnosno vrijednosti u sljedećem trenutku vremena. Dodavanjem značajke
(auditweightsShowsCountNext-auditweightsShowsCount)/(auditweightsLikesCount-auditweightsLikesCountNext) brzo smo napravili oštar skok.
Slična curenja mogu se koristiti pronalaženjem sljedećih vrijednosti za userOwnerCounterCreateLikes unutar userId+ownerId i, na primjer, auditweightsCtrGender unutar objectId+userGender. Pronašli smo 6 sličnih polja s curenjem i iz njih izvukli što je moguće više informacija.

Do tada smo iz kolaborativnih značajki izvukli što je više moguće informacija, ali se nismo vratili natjecanjima slika i teksta. Imao sam sjajnu ideju da provjerim: koliko karakteristike izravno temeljene na slikama ili tekstovima daju u relevantnim natječajima?

Nije bilo curenja u natječajima za slike i tekst, ali do tada sam vratio zadane parametre catboosta, očistio kod i dodao nekoliko značajki. Rezultat je bio:

odluka brzo
Maksimalno sa slikama 0.6411
Maksimalno bez slika 0.6297
Rezultat drugog mjesta 0.6295

odluka brzo
Maksimalno s tekstovima 0.666
Maksimalno bez teksta 0.660
Rezultat drugog mjesta 0.656

odluka brzo
Maksimalno u suradnji 0.745
Rezultat drugog mjesta 0.723

Postalo je očito da nećemo moći puno iscijediti iz tekstova i slika, a nakon što smo isprobali nekoliko najzanimljivijih ideja, prestali smo s njima raditi.

Daljnje generiranje značajki u kolaborativnim sustavima nije dovelo do povećanja i krenuli smo s rangiranjem. U online fazi, klasifikacija i poredak su mi donijeli malo povećanje, kako se pokazalo jer sam premalo trenirao klasifikaciju. Niti jedna od funkcija pogreške, uključujući YetiRanlPairwise, nije proizvela ni blizu rezultata kao LogLoss (0,745 naspram 0,725). Još je bilo nade za QueryCrossEntropy, koji se nije mogao pokrenuti.

Offline faza

U izvanmrežnoj fazi struktura podataka ostala je ista, ali je došlo do manjih promjena:

  • identifikatori userId, objectId, ownerId su ponovno nasumični;
  • nekoliko je znakova uklonjeno, a nekoliko ih je preimenovano;
  • podaci su se povećali otprilike 1,5 puta.

Uz navedene poteškoće, postojao je i jedan veliki plus: timu je dodijeljen veliki server s RTX 2080TI. Dugo sam uživao u htopu.
SNA Hackathon 2019

Postojala je samo jedna ideja - jednostavno reproducirati ono što već postoji. Nakon što smo proveli nekoliko sati postavljajući okruženje na poslužitelju, postupno smo počeli provjeravati jesu li rezultati ponovljivi. Glavni problem s kojim se suočavamo je povećanje količine podataka. Odlučili smo malo smanjiti opterećenje i postaviti catboost parametar ctr_complexity=1. Ovo malo smanjuje brzinu, ali moj model je počeo raditi, rezultat je bio dobar - 0,733. Sergey, za razliku od mene, nije podijelio podatke u 2 dijela i trenirao je na svim podacima, iako je to dalo najbolje rezultate u online fazi, u offline fazi bilo je mnogo poteškoća. Kad bismo uzeli sve značajke koje smo generirali i pokušali ih ubaciti u catboost, tada ništa ne bi funkcioniralo u online fazi. Sergey je napravio optimizaciju tipa, na primjer, pretvarajući tipove float64 u float32. U ovom članku, Informacije o optimizaciji memorije možete pronaći u pandama. Kao rezultat toga, Sergej je trenirao na CPU koristeći sve podatke i dobio oko 0,735.

Ovi rezultati bili su dovoljni za pobjedu, ali smo skrivali svoju pravu brzinu i nismo mogli biti sigurni da druge ekipe ne rade isto.

Boriti se do posljednjeg

Catboost podešavanje

Naše rješenje je u potpunosti reproducirano, dodali smo značajke tekstualnih podataka i slika, tako da je preostalo samo podesiti parametre catboosta. Sergej je trenirao na CPU-u s malim brojem iteracija, a ja sam trenirao na onom s ctr_complexity=1. Ostao je jedan dan, a ako ste samo dodali iteracije ili povećali ctr_complexity, tada biste do jutra mogli postići još veću brzinu i hodati cijeli dan.

U offline fazi, brzine se mogu vrlo lako sakriti jednostavnim odabirom ne najboljeg rješenja na web mjestu. Očekivali smo drastične promjene na ljestvici u posljednjim minutama prije zatvaranja prijava i odlučili smo ne stati.

Iz Anninog videa saznao sam da je za poboljšanje kvalitete modela najbolje odabrati sljedeće parametre:

  • stopa_učenja — Zadana vrijednost izračunava se na temelju veličine skupa podataka. Povećanje learning_rate zahtijeva povećanje broja ponavljanja.
  • l2_reg_lista — Koeficijent regularizacije, zadana vrijednost 3, poželjno je odabrati između 2 i 30. Smanjenje vrijednosti dovodi do povećanja overfita.
  • temperatura_vreće — dodaje randomizaciju težine objekata u uzorku. Zadana vrijednost je 1, gdje se težine izvlače iz eksponencijalne distribucije. Smanjenje vrijednosti dovodi do povećanja overfita.
  • slučajna_snaga — Utječe na izbor podjela u određenoj iteraciji. Što je veća random_strength, to je veća šansa da se odabere podjela niske važnosti. Svakim sljedećim ponavljanjem slučajnost se smanjuje. Smanjenje vrijednosti dovodi do povećanja overfita.

Ostali parametri imaju puno manji utjecaj na konačni rezultat, pa ih nisam pokušavao odabrati. Jedna iteracija obuke na mom GPU skupu podataka s ctr_complexity=1 trajala je 20 minuta, a odabrani parametri na smanjenom skupu podataka malo su se razlikovali od optimalnih na punom skupu podataka. Na kraju sam napravio 30-ak iteracija na 10% podataka, a zatim još 10-ak iteracija na svim podacima. Ispalo je ovako nešto:

  • stopa_učenja Povećao sam za 40% od zadane;
  • l2_reg_lista ostavio isto;
  • temperatura_vreće и slučajna_snaga smanjen na 0,8.

Možemo zaključiti da je model nedovoljno obučen sa zadanim parametrima.

Bio sam jako iznenađen kada sam vidio rezultat na ljestvici:

Model model 1 model 2 model 3 ansambl
Bez ugađanja 0.7403 0.7404 0.7404 0.7407
S podešavanjem 0.7406 0.7405 0.7406 0.7408

Sam sam zaključio da ako nije potrebna brza primjena modela, onda je bolje zamijeniti odabir parametara skupom od nekoliko modela koji koriste neoptimizirane parametre.

Sergey je optimizirao veličinu skupa podataka kako bi ga pokrenuo na GPU-u. Najjednostavnija opcija je odrezati dio podataka, ali to se može učiniti na nekoliko načina:

  • postupno uklanjati najstarije podatke (početak veljače) dok skup podataka ne počne stati u memoriju;
  • ukloniti značajke s najmanjom važnošću;
  • uklonite korisničke ID-ove za koje postoji samo jedan unos;
  • ostavite samo korisničke ID-ove koji su u testu.

I na kraju, od svih opcija napravite ansambl.

Posljednji ansambl

Do kasne večeri posljednjeg dana postavili smo skup naših modela koji su dali 0,742. Preko noći sam pokrenuo svoj model s ctr_complexity=2 i umjesto 30 minuta trenirao je 5 sati. Tek u 4 ujutro je prebrojano, a ja sam napravio zadnji ansambl, koji je na javnoj ljestvici dao 0,7433.

Zbog različitih pristupa rješavanju problema, naša predviđanja nisu bila snažno povezana, što je dalo dobar porast u ansamblu. Da biste dobili dobar skup, bolje je upotrijebiti predviđanja sirovog modela predict(prediction_type='RawFormulaVal') i postaviti scale_pos_weight=neg_count/pos_count.

SNA Hackathon 2019

Na web stranici možete vidjeti konačni rezultati na privatnoj ljestvici.

Ostala rješenja

Mnogi timovi slijedili su kanone algoritama sustava preporuka. Ja, budući da nisam stručnjak za ovo područje, ne mogu ih procijeniti, ali sjećam se 2 zanimljiva rješenja.

  • Rješenje Nikolaya Anokhina. Nikolay, kao zaposlenik Mail.rua, nije se prijavio za nagrade, tako da njegov cilj nije bio postići maksimalnu brzinu, već dobiti lako skalabilno rješenje.
  • Odluka žirija koja je osvojila nagradu na temelju ovaj članak s facebooka, omogućio je vrlo dobro grupiranje slika bez ručnog rada.

Zaključak

Ono što mi je najviše ostalo u sjećanju:

  • Ako u podacima postoje kategoričke značajke, a znate kako ispravno napraviti ciljno kodiranje, ipak je bolje isprobati catboost.
  • Ako sudjelujete u natjecanju, ne biste trebali gubiti vrijeme odabirom parametara osim rate_Learning i iteracija. Brže rješenje je napraviti ansambl od nekoliko modela.
  • Pojačanja mogu učiti na GPU-u. Catboost može vrlo brzo učiti na GPU-u, ali troši puno memorije.
  • Tijekom razvoja i testiranja ideja, bolje je postaviti mali rsm~=0.2 (samo CPU) i ctr_complexity=1.
  • Za razliku od ostalih ekipa, ansambl naših manekena dao je veliki porast. Samo smo razmjenjivali ideje i pisali na različitim jezicima. Imali smo drugačiji pristup dijeljenju podataka i, mislim, svaki je imao svoje greške.
  • Nije jasno zašto je optimizacija rangiranja bila lošija od optimizacije klasifikacije.
  • Stekao sam neko iskustvo u radu s tekstovima i razumijevanje kako se izrađuju sustavi preporuka.

SNA Hackathon 2019

Hvala organizatorima na emocijama, znanju i dobivenim nagradama.

Izvor: www.habr.com

Dodajte komentar