Recommendation Systems Meetup 30-5-2017

Gisteren was ik bij een meetup van Recommendation Systems Amsterdam (http://www.recommenders.nl/). Dit was de eerste die ze via meetup.com hadden georganiseerd (via die site werd ik er op geattendeerd) en dat heeft er voor gezorgd dat deze editie drukker was bezocht dan voorgaande en dat er ook meer developers aanwezig waren. Bij eerdere edities waren het met name mensen vanuit de academische wereld dus men was blij dat het nu diverser publiek was.

De eerste spreker (Nava Tintarev) hield een pleidooi voor het correct ‘uitleggen’ van recommendations. Door uit te leggen waarom je een bepaalde aanbeveling doet kun je de verwachtingen beter managen en voorkom je teleurstellingen. Het blijkt dat hoe ‘persoonlijker’ je de aanbeveling uitlegt (bijvoorbeeld: ‘… omdat je een man van 36 bent die graag Grolsch drinkt tijdens een actiefilm’) hoe groter de kans op teleurstelling. Het is dus nuttig om na te denken of en welke uitleg nodig is. Bij het gebruik van ‘anderen bekeken ook’ aanbevelingen bestaat de kans op filter-bubbels. Dit valt te doorbreken door te variëren en meer ‘novel’ aanbevelingen te doen. Met name bij novel recommendations is uitleg belangrijk. Maak gebruik van feedback-loops om beslismodellen te verbeteren. Evalueren werkt het best door mensen persoonlijk te vragen naar hun mening over de voorgestelde items. Hou bij het ontwerpen van je interfaces rekening met ruimte voor de uitleg. Deze vullen namelijk de ‘information gap’.

De tweede spreker (Daan Odijk) werkt als lead data-scientist bij Blendle. Men maakt daar uiteraard ook gebruik van recommendations om gebruikers dagelijks in de mailing te attenderen op mogelijk interessante artikelen. Het probleem daarbij is dat die artikelen op het moment van versturen (8:00) nog door bijna niemand zijn bekeken, dus het is vrijwel onmogelijk om aanbevelingen te doen op basis van views door anderen. Om toch selecties te kunnen maken gebruikt men diverse tools om de inhoud van artikelen te classificeren op basis van auteur, lengte, taalgebruik, trefwoorden etc etc. Deze data wordt realtime verwerkt op het moment dat de artikelen beschikbaar komen. Daarnaast wordt dagelijks de gebruikersprofielen verrijkt met allerhande data die men over gebruikers verzamelt; welk soort (lengte, auteur, medium, etc etc) artikelen lees je, voor welk soort recommendations ben je gevoelig, hoe lang zit je op de site etc etc. Vervolgens kan men bij het versturen razendsnel voor elke gebruiker de persoonlijke aanbevelingen voor de nieuwsbrief bepalen. Er vindt dagelijks een (automatische) update van het selectiemechanisme plaats zodat het bij elke variatie kan leren (welke variant converteert beter) en zo kan optimaliseren.

De laatste sprekers waren Barend Linders and Robbert van Waardhuizen van NPO. Ze gaven een indruk van het recommendation-systeem dat in de app en op de site wordt gebruikt om aanbevelingen te doen zodra je een aflevering hebt bekeken of pauzeert. Er worden op dit moment alleen aanbevelingen gedaan op ‘serie’ niveau en niet op ‘aflevering’ omdat je anders natuurlijk veel afleveringen zult voorschotelen (uit dezelfde serie) die men natuurlijk al had gezien. Daarnaast probeert men te voldoen aan de missie van de NPO om mensen een zo breed mogelijk aanbod aan programma’s voor te schotelen. Door aanbevelingen te doen die zo goed mogelijk passen bij wat ze zojuist hebben bekeken creëer je echter een filter bubbel en zal men weinig nieuws ontdekken. Op dit moment worden nog geen aanbevelingen gedaan op user-niveau (‘aanbevolen voor jou’) maar puur item-based recommendations (‘anderen bekeken ook’). Een van de redenen hiervoor is dat het lastig blijkt om gebruikers te ‘volgen’ over de site en over meerdere sessies. In de nabije toekomst komt er een verfijnder systeem voor het toestemming geven van tracking cookies en de verwachting is dat men dan gebruikers beter kan volgen en dus betere aanbevelingen kan doen.

Minder random dan het lijkt

Elke programmeertaal heeft wel een functie om een random getal te genereren. Maar de uitkomst van zo’n functie is minder random dan je misschien zou verwachten. Sterker nog; de uitkomst is zelfs goed voorspelbaar. Dat betekent dat als je random-functies gebruikt voor bijvoorbeeld sessie-cookies of andere security-gerelateerde zaken je daar rekening mee zou moeten houden.

Het probleem zit hem er in dat een computer (of eigenlijk een CPU) geen functie kent voor een compleet willekeurig getal. Dus wordt er gebruik gemaakt van ‘pseudo-random’ functies (die term zul je ook tegen komen in de handleiding van random-functies in elke programmeertaal). Met pseudo-random wordt bedoeld dat het op het oog willekeurig lijkt, maar het in werkelijkheid niet is. Zulke functies maken gebruik van een ‘teller’ (ook wel ‘seed’ genoemd) op basis waarvan het volgende getal wordt berekend. Dat betekent dat als je 2x achter elkaar een random getal opvraagt bij dezelfde ‘seed’ je ook 2x het zelfde ‘willekeurige’ getal krijgt.

Het volgende script laat dat zien:

<?php
srand(12345);

for ($i = 0; $i < 10; $i++) {
    echo rand(0, 9);
}

echo " ";

srand(12345);

for ($i = 0; $i < 10; $i++) {
    echo rand(0, 9);
}

De output hiervan is (hoe vaak je het ook draait):

1312002032 1312002032

Waarom lijkt een random-functie bij normaal gebruik dan toch random? Dat komt omdat de meeste random-implementaties automatisch een ‘seed’ instellen zodat de kans dat je 2x een aanroep met dezelfde seed doet niet zo heel groot is. Vaak wordt de huidige tijd (in milliseconden) gebruikt als seed.

Als je sessie-ids (of andere ‘random’ strings) genereert met een standaard random-functie werkt dat dus beperkend in het aantal unieke strings dat je zou kunnen genereren, ongeacht de lengte van je unieke string.

Bijvoorbeeld: als je strings van 10 tekens samenstelt uit de reeks A t/m Z heb je theoretisch 26 ^ 10 unieke strings (141.167.095.653.376 mogelijkheden). Maar als een seed een 32-bits integer is, zijn er dus maar 2 ^ 32 unieke strings (4.294.967.296) als uitkomst mogelijk.

Is dat erg? Dat ligt er aan. Als van de buitenkant niet zichtbaar (of te achterhalen) is op welke manier de sessie-ids worden gegenereerd, zal een kwaadwillende alsnog alle combinaties moeten proberen om er eentje te kunnen overnemen. Maar als bekend is op welke manier de strings tot stand komen kun je (offline, dus met veel rekenkracht) een tabel aanleggen met alle mogelijke combinaties met elke mogelijke input-seed. En als je vervolgens de site bezoekt en je krijgt een bepaalde sessie-id toegewezen kun je in die tabel opzoeken welke sessie-sleutels er ‘in de buurt’ liggen op basis van de seed (die afhangt van het tijdstip). En dan wordt de kans op een gestolen sessie-sleutel ineens een stuk groter.

Een oplossing zit hem er in door voor security-gerelateerde zaken andere random-functies te gebruiken. Deze kun je vinden onder de noemer ‘cryptographically secure pseudo-random number generators’ voor bijvoorbeeld PHP (CSPRNG) of Java (SecureRandom).