Javaslati rendszerek : User-based Collaborative Filtering using N Nearest Neighbors

Ashay Pathak
Ashay Pathak

Follow

Feb 25, 2019 – 9 min read

Ashay Pathak, Chatana Mandava, Ritesh Patel

A kollaboratív szűrés egy olyan technika, amelyet széles körben használnak az ajánlórendszerekben, és gyorsan fejlődő kutatási terület. A két leggyakrabban használt módszer a memória alapú és a modell alapú.

Ebben a bejegyzésben csak az (User-Based Collaborative Filtering) UB-CF-re koncentrálunk, amely egy memória alapú módszer. Az UB-CF fő gondolata az, hogy a hasonló jellemzőkkel rendelkező emberek hasonló ízléssel rendelkeznek. Például, ha egy filmet szeretnénk ajánlani Bob barátunknak, tegyük fel, hogy Bob és én sok filmet láttunk együtt, és szinte egyformán értékeltük őket. Van értelme azt gondolni, hogy a jövőben is hasonló filmeket fogunk szeretni, és ezt a hasonlósági metrikát használjuk filmek ajánlására.

Próbáljuk meg implementálni az UB-CF-et, és generáljunk egy listát azokról a filmekről, amelyeket Bob barátunk, azaz az aktív felhasználó szívesen megnézne. Ennek a bejegyzésnek az írása mögött az a motiváció áll, hogy mélyen belemerüljünk az algoritmusba, és megértsük, hogyan is működik valójában az UB-CF. A bejegyzés tartalmának nagy részét a Coursera egyik kurzusa ihlette.

User-User Collaborative Filtering

A módszer azonosítja a lekérdezett felhasználóhoz hasonló felhasználókat, és a kívánt értékelést e hasonló felhasználók értékeléseinek súlyozott átlagára becsüli.

Collaborative Filtering : forrás itt

Az ajánlást a MovieLens adathalmazon végeznénk. A használt programozási nyelv a python, az adatelemzési munkát pedig többnyire a pandas könyvtár segítségével végezzük. A használt IDE a jupyter notebook.

Az indulás előtt szeretném megadni a felhasznált könyvtárak listáját :

  1. Pandas
  2. Numpy
  3. sklearn

Szóval haladjunk tovább és értsük meg az ajánlások mögött álló fogalmakat. A jobb megértés érdekében csatoltam néhány kódrészletet és kimenetet a blogban. A teljes ipynb fájlt a blog végén csatoltam.

Score függvény

Egyszerű egy olyan függvényt kitalálni a nem személyre szabott kollaboratív szűréshez (azaz nem vesszük figyelembe az aktív felhasználó tetszéseit, nemtetszéseit és a múltbeli értékelését), amely egy pontszámot ad vissza az u felhasználó és az i elem mint bemeneti paraméterek alapján. A függvény egy olyan pontszámot ad ki, amely számszerűsíti, hogy az u felhasználó mennyire kedveli/preferálja az i elemet.

Ezt tehát általában a felhasználóhoz hasonló emberek értékelései alapján végzik. Mindezt később részletesen tárgyalnánk. Egyelőre az általam használt képlet a következő,

score függvény

ahol ‘s’ az előre jelzett pontszám, ‘u’ a felhasználó, ‘i’ az elem, ‘r’ a felhasználó által adott értékelés és ‘w’ a súly.

Ez esetben a pontszámunk egyenlő az egyes felhasználók által az adott elemre adott értékelések összegével, levonva az adott felhasználó átlagos értékelését, megszorozva valamilyen súllyal, amely azt mutatja, hogy ez a felhasználó mennyire hasonlít vagy mennyire járul hozzá a többi felhasználó előrejelzéséhez. Ez az u és v felhasználó közötti súly. A pontszám 0 és 1 között mozog, ahol a 0 alacsony, az 1 pedig magas. Minden tökéletesnek tűnik, akkor miért vontuk le az egyes felhasználók értékelésének átlagát, és miért használtunk súlyozott átlagot az egyszerű átlag helyett?

A probléma az általunk kezelt felhasználói típusokkal van. Azzal kezdődik, hogy az emberek gyakran nagyon különböző skálákon értékelnek. Lehet, hogy én pozitív és optimista felhasználó vagyok, ahol a nekem tetsző filmet 5-ből 4-re értékelem, de egy másik felhasználó, aki kevésbé optimista, vagy akinek magasak az elvárásai, a kedvenc filmjét 5-ből 2-re értékeli. Itt az ő 2 az én 4-esem. Az algoritmus hatékonyságát növelhetjük, ha normalizáljuk a felhasználók értékelését. Ennek egyik módja, hogy kiszámítjuk az s(u,i) értékelést, azaz a pontszámot, mint a felhasználó által az egyes elemekre adott átlagos értékelést, plusz némi eltérést, és az eltérés azt mutatja, hogy ez az elem mennyivel jobb vagy rosszabb az átlagnál.

A fenti képletben megadott súly kiszámításához a koszinusz hasonlóságot használtam. A szomszédság fogalmát is használtam, amelyet ebben a blogban tárgyalnánk, amint továbblépünk.

Az adatok fenti módon történő normalizálásához a pandasban némi adatelemzésre van szükség. A teljes kódot a végén kaphatja meg. A blogban a fontos fogalmakra fogok koncentrálni.

import pandas as pdmovies = pd.read_csv("movies.csv",encoding="Latin1")
Ratings = pd.read_csv("ratings.csv")
Tags = pd.read_csv("tags.csv",encoding="Latin1")Mean = Ratings.groupby(by="userId",as_index=False).mean()
Rating_avg = pd.merge(Ratings,Mean,on='userId')
Rating_avg=Rating_avg-Rating_avg
Rating_avg.head()

Normalizált értékelések

Szóval most már készen vagyunk a felhasználó normalizált értékelésének kiszámításával. A fenti adatokat később a felhasználó végső pontszámának kiszámításához használnánk.

Ezektől kezdve most az ajánlórendszerekkel kapcsolatos néhány fontos fogalomra fogunk összpontosítani.

Kozinus hasonlóság

A fenti képlethez meg kell találnunk azokat a felhasználókat, akik hasonló gondolatokkal rendelkeznek. Ez olyan érdekesen hangzik, hogy olyan felhasználót találjunk, akinek hasonló a tetszése és nemtetszése. De a kérdés az, hogy hogyan találjuk meg a hasonlóságot?

A válaszhoz a koszinusz hasonlóságot fogjuk használni, és megnézzük, mennyire hasonlóak a felhasználók. Ezt általában a két felhasználó által a múltban adott értékelések alapján számítják ki.

Példánkban a sklearn cosine_similarity függvényét használtam a hasonlóság kiszámításához. De előtte el kell végeznünk némi előfeldolgozást és meg kell tisztítanunk az adatokat.

from sklearn.metrics.pairwise import cosine_similarityfinal=pd.pivot_table(Rating_avg,values='adg_rating',index='userId',columns='movieId')

pivot table

Ez tartalmaz néhány sok NaN értéket, mivel nem minden felhasználó látta az összes filmet, és ez az oka annak, hogy az ilyen típusú mátrixot ritka mátrixnak nevezik. A ritkaság kezelésére olyan módszereket használnak, mint a mátrix-faktorizálás, de ebben a blogban nem erre koncentrálunk. A következő lépés és az egyik fontos lépés ennek a NaN értéknek a helyettesítése.

Ezekre általában két módszert használnak :

  1. A felhasználó átlagának használata a sorban.
  2. A film átlagának használata az oszlopban.

Mindkét módszert használtam, és az alábbi kódban megkaphatjuk. De a magyarázathoz a filmátlag módszert használnám.

# Replacing NaN by Movie Average
final_movie = final.fillna(final.mean(axis=0))

NaN értékek filmátlaggal való helyettesítése

A következő lépés a felhasználók közötti hasonlóság kiszámítása.

# user similarity on replacing NAN by item(movie) avg
cosine = cosine_similarity(final_movie)
np.fill_diagonal(cosine, 0 )
similarity_with_movie =pd.DataFrame(cosine,index=final_movie.index)
similarity_with_movie.columns=final_user.index
similarity_with_movie.head()

Használó Felhasználó koszinusz hasonlóság

Vizsgáljuk meg magunkat, hogy valóban van-e értelme annak, amit kiszámoltunk !!

a = get_user_similar_movies(370,86309)
a = a.loc]
a.head()

movies similar

A fenti képből láthatjuk, hogy az általunk generált hasonlóság igaz, hiszen mindkét adott felhasználó (370,86309) szinte azonos értékeléssel és like-okkal rendelkezik.

Szóval végeztünk a felhasználók közötti hasonlóságok kiszámításával, de még mindig nem vagyok elégedett. Ennek okát a következő lépésben tárgyalnám meg.

Neighborhood for User (K)

A fentiekből láthatjuk, hogy az összes felhasználóra kiszámítottuk a hasonlóságokat. De mivel Big Data hallgató vagyok, a probléma összetettsége mindig hajt engem. Ez alatt azt értem, hogy az ajánlórendszer hatalmas adatokkal dolgozik, és ezért nagyon fontossá válik, hogy csak a fontos és szükséges kiemeléseket tartsuk fenn és rögzítsük az adatokból.

Hogy ezt a filmajánló rendszerünk példájával magyarázzuk, a fent kapott mátrix (862*862), mivel 862 egyedi felhasználó van az adatokban. Ez a szám még mindig kicsi, ha összehasonlítjuk az adatokkal, amelyeken az eredeti rendszer dolgozna. Gondoljunk az Amazonra. Több mint több millió felhasználó van az adatbázisában, így bármely elem pontszámának kiszámítása során nem lenne jó megoldás vagy módszer az összes többi felhasználót folyamatosan figyelembe venni. Ezért ennek leküzdésére létrehozzuk a szomszédság fogalmát. Ez egy adott felhasználóhoz csak a (K) hasonló felhasználók halmazát tartalmazza.

Most tegyük meg a további lépéseket az ötlet megvalósításához. Példánkban a k értékét 30-nak vettem. Így 30 legközelebbi szomszédunk lenne az összes felhasználóhoz.

Egyéni függvényemet, a find_n_neighbours-t használtam, amely a hasonlósági mátrixot és az n értékét veszi inputként, és visszaadja a legközelebbi n szomszédot az összes felhasználóhoz. A kódot megtalálod a blog végén megadott jegyzetfüzetben.

# top 30 neighbours for each user
sim_user_30_m = find_n_neighbours(similarity_with_movie,30)
sim_user_30_m.head()

top 30 neighbors for users

Szóval most ha gondolod, akkor komolyan csökkentettük a felesleges számítások számát. Most már készen állunk arra, hogy kiszámítsuk egy elem pontszámát.

A végső pontszám előállítása S(u,i)

Wow! befejeztük a folyamatot. Tudom, hogy igazi technikai dolgokon mentünk keresztül, de a ráfordított idő megérte, hiszen elértünk az utolsó lépéshez.

Itt megpróbáljuk megjósolni annak a filmnek a pontszámát, amit az adott felhasználó még nem látott.

score = User_item_score(320,7371)
print(score)

megjósolt pontszám

A rendszerünk tehát 4,25 pontot jósolt, ami nagyon jó. Úgy gondolom, hogy a felhasználónak (370) tetszhet a film a (7371) azonosítóval.

Most adjuk meg a végső simítást a rendszerünknek. Szerintem a legtöbben a Netflixet vagy a Hotstar-t használták volna. Tehát amikor megnyitjuk az alkalmazást, megmutatja azt a tételt, amit kedvelünk.

Az ajánlás mindig lenyűgöz, mivel kiderül, hogy azok a filmek, amiket szeretek. Mindez magán az ajánlórendszeren keresztül történik. Mi most ugyanezt fogjuk tenni, és megpróbáljuk megjósolni a top 5 filmet, amit az adott felhasználó szerethet.

A logika nem olyan nehéz. Már generáltuk a pontszámot egy elemhez. Hasonlóképpen generálhatjuk a pontszámot a többi elemre is ugyanazzal a felhasználóval.

De most ismét a Big Data-t figyelembe véve van-e értelme pontszámot generálni az összes többi elemre??

Azt hiszem, NEM!!!

Tekintsük a felhasználót (U) és a felhasználót (K). Most tegyük fel, hogy K nincs U szomszédságában ,lehetséges-e, hogy U felhasználónak tetszik egy olyan film, amit K látott és értékelt. Többnyire nem.

Azt hiszem, megvan a trükk. Igen, minket csak az érdekel, hogy kiszámoljuk azoknak a tételeknek a pontszámát, amelyeket a szomszédos felhasználóik láttak.

Tényleg sikerrel jártunk. A számítást 2500-ról N-re csökkentettük ( ahol N a szomszédságom által kedvelt filmek halmaza ) és ami nagyon kevesebb, mint 2500.

Szóval végül ajánljuk a filmeket.

A user_item_score1 az én egyéni függvényem, amely a fenti megbeszélésünket használja fel az előrejelzések kiszámításához

user = int(input("Enter the user id to whom you want to recommend : "))
predicted_movies = User_item_score1(user)
print(" ")
print("The Recommendations for User Id : ",user)
print(" ")
for i in predicted_movies:
print(i)

Az utolsó!!!!. Megcsináltuk. Megvan a saját ajánlórendszerünk.

A lenti kód ábrázolása nem biztos, hogy könnyen olvasható, ezért kérem, hogy a GitHub tárolómban keresse fel a kódot.

A szükséges adatállományok innen szerezhetők be.

Remélem, tetszett a blog. Bármilyen kérdés esetén küldhet nekem e-mailt. Maradjon hangolva a további blogokra, mivel ez az egyik érdeklődési területem, és mindenképpen új dolgokat fogok közzétenni.

Köszönöm..!!!

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.