Ashay Pathak, Chatana Mandava, Ritesh Patel
Filtrarea colaborativă este o tehnică care este utilizată pe scară largă în sistemele de recomandare și este un domeniu de cercetare care avansează rapid. Cele două metode cel mai frecvent utilizate sunt cele bazate pe memorie și cele bazate pe modele.
În acest post, ne vom concentra doar pe (User-Based Collaborative Filtering) UB-CF, care este o metodă bazată pe memorie. Ideea principală din spatele UB-CF este că persoanele cu caracteristici similare împărtășesc gusturi similare. De exemplu, dacă sunteți interesați să îi recomandați un film prietenului nostru Bob, să presupunem că Bob și cu mine am văzut multe filme împreună și le-am evaluat aproape identic. Este logic să ne gândim că și în viitor vom continua să ne placă filme similare și să folosim această metrică de similaritate pentru a recomanda filme.
Să încercăm să implementăm UB-CF și să generăm o listă de filme pe care prietenul nostru Bob, alias utilizatorul activ, ar putea fi interesat să le vadă. Motivația din spatele scrierii acestei postări este de a pătrunde adânc în algoritm și de a înțelege cum funcționează de fapt UB-CF. Cea mai mare parte a conținutului acestei postări este inspirată de un curs de pe Coursera.
User-User Collaborative Filtering
Metoda identifică utilizatorii care sunt similari cu utilizatorul interogat și estimează ratingul dorit ca fiind media ponderată a ratingurilor acestor utilizatori similari.
Am face recomandări pe setul de date MovieLens. Limbajul de programare utilizat este python, iar activitatea de analiză a datelor se realizează în principal cu ajutorul bibliotecii pandas. IDE-ul utilizat este jupyter notebook.
Acum, înainte de a începe, aș dori să dau lista bibliotecilor utilizate :
- Pandas
- Numpy
- sklearn
Acum să mergem mai departe și să înțelegem conceptele din spatele recomandărilor. Am atașat câteva fragmente de cod și rezultate pe blog pentru o mai bună înțelegere. Întregul fișier ipynb este atașat la sfârșitul blogului.
Funcția de scor
Este ușor să venim cu o funcție pentru filtrarea colaborativă nepersonalizată (adică nu luăm în considerare plăcerile, displacerile și evaluările din trecut ale utilizatorului activ) care returnează un scor luând ca parametri de intrare utilizatorul u și elementul i. Funcția generează un scor care cuantifică cât de mult îi place/preferă un utilizator u elementul i.
De obicei, acest lucru se face folosind evaluările altor persoane similare cu utilizatorul. Toate acestea vor fi discutate în detaliu mai târziu. Deocamdată, formula pe care am folosit-o este,
Unde “s” este scorul prezis, “u” este utilizatorul, “i” este elementul, “r” este ratingul acordat de utilizator și “w” este ponderea.
În acest caz, scorul nostru este egal cu suma evaluărilor pe care fiecare utilizator le-a dat acelui element, scăzând evaluarea medie a acelui utilizator înmulțită cu o anumită pondere care reprezintă cât de mult acest utilizator este similar sau se presupune că ar trebui să contribuie la predicțiile celorlalți utilizatori. Aceasta este ponderea dintre utilizatorul u și v. Punctajul variază între 0 și 1, unde 0 este mic și 1 este mare. Totul pare perfect, dar atunci de ce am scăzut ratingurile medii din ratingul fiecărui utilizator și de ce am folosit media ponderată în loc de media simplă?
Problema este legată de tipurile de utilizatori pe care le gestionăm. Începe cu faptul că oamenii evaluează adesea pe scale foarte diferite. Eu pot fi un utilizator pozitiv și optimist, în cazul în care voi evalua filmul care mi-a plăcut cu 4 din 5, dar un alt utilizator care este mai puțin optimist sau are niște standarde ridicate poate evalua filmul său preferat cu 2 din 5. În acest caz, 2 al lui este 4 al meu. Pentru a-l îmbunătăți, putem crește eficiența acestui algoritm dacă normalizăm evaluarea utilizatorului. O modalitate de a face acest lucru este să spunem că vom calcula s(u,i), adică scorul ca fiind ratingul mediu pe care utilizatorul îl acordă fiecărui element plus o anumită abatere, iar abaterea va reprezenta cât de mult acest element este mai bun sau mai rău decât media.
Am folosit similitudinea cosinusului pentru a calcula ponderea dată în formula de mai sus. Am folosit, de asemenea, noțiunea de vecinătate care va fi discutată în acest blog pe măsură ce vom merge mai departe.
Pentru a normaliza datele în modul de mai sus, este necesară o anumită analiză a datelor în pandas. Puteți obține întregul cod la sfârșit. Pentru blog, mă voi concentra pe conceptele importante.
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()
Acum am terminat de calculat ratingul normalizat pentru un utilizator. Datele de mai sus vor fi folosite pentru a calcula mai târziu scorul final pentru utilizator.
De aici ne vom concentra acum asupra unor concepte importante legate de sistemele de recomandare.
Similitudine cosinusală
Pentru formula de mai sus trebuie să găsim utilizatorii care au gânduri similare. Acest lucru sună atât de interesant pentru a găsi un utilizator care are simpatii și antipatii similare. Dar întrebarea este cum găsim similitudinea?
Pentru a răspunde la această întrebare, vom folosi Cosine Similarity și vom vedea cât de asemănători sunt utilizatorii. De obicei, aceasta este calculată pe baza evaluărilor pe care ambii utilizatori le-au evaluat în trecut.
În exemplul nostru, am folosit funcția cosine_similarity din sklearn pentru a calcula similitudinea. Dar, înainte de aceasta, trebuie să efectuăm o preprocesare și să curățăm datele.
from sklearn.metrics.pairwise import cosine_similarityfinal=pd.pivot_table(Rating_avg,values='adg_rating',index='userId',columns='movieId')
Aceasta conține o mulțime de valori NaN, deoarece fiecare utilizator nu a văzut toate filmele și acesta este motivul pentru care acest tip de matrice se numește matrice rară. Metode cum ar fi factorizarea matricei sunt utilizate pentru a face față acestui tip de matrice rarefiată, dar nu ne vom concentra asupra ei în acest blog. Următorul pas și unul dintre pașii importanți este înlocuirea acestor valori NaN.
Există două metode utilizate în mod obișnuit pentru acest lucru :
- Utilizați media utilizatorilor pe rând.
- Utilizați media filmelor pe coloană.
Am utilizat ambele metode și le puteți obține în codul de mai jos. Dar pentru a explica aș folosi metoda mediei filmului.
# Replacing NaN by Movie Average
final_movie = final.fillna(final.mean(axis=0))
Acum, următorul pas este să calculăm similaritatea dintre utilizatori.
# 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()
Să ne verificăm dacă ceea ce am calculat are cu adevărat sens !!
a = get_user_similar_movies(370,86309)
a = a.loc]
a.head()
Din imaginea de mai sus putem vedea că similaritatea pe care am generat-o este adevărată, deoarece ambii utilizatori dați (370,86309) au aproape aceleași evaluări și aprecieri.
Așa că am terminat de calculat similitudinile dintre utilizatori, dar nu sunt încă mulțumit. Voi discuta motivul în pasul următor.
Neighborhood for User (K)
Din cele de mai sus putem vedea că am calculat similitudinile pentru toți utilizatorii. Dar, de când sunt student la Big Data, complexitatea problemei mă conduce întotdeauna. Prin aceasta mă refer la faptul că sistemul de recomandare lucrează cu date uriașe și, prin urmare, devine foarte important să menținem și să capturăm doar elementele importante și necesare din date.
Pentru a explica acest lucru folosind exemplul nostru de sistem de recomandare a filmelor, matricea pe care am obținut-o mai sus este (862*862), deoarece există 862 de utilizatori unici în date. Acest număr este încă mic în comparație cu datele pe care ar trebui să lucreze sistemul original. Să ne gândim la Amazon. Acesta ar avea mai mult de milioane de utilizatori în baza sa de date și, prin urmare, în timpul calculării scorului pentru orice articol, nu ar fi o soluție sau o metodă bună să ne uităm tot timpul la toți ceilalți utilizatori. Prin urmare, pentru a depăși această problemă, generăm o noțiune de vecinătate. Aceasta include doar setul de (K) utilizatori similari pentru un anumit utilizator.
Acum să luăm măsuri suplimentare pentru a implementa ideea. În exemplul nostru am luat valoarea lui k ca fiind 30. Așadar, vom avea 30 de vecini apropiați pentru toți utilizatorii.
Am folosit funcția mea personalizată find_n_neighbours care ia matricea de similaritate și valoarea lui n ca intrare și returnează cei mai apropiați n vecini pentru toți utilizatorii. Puteți găsi codul în caietul de notițe dat la sfârșitul blogului.
# top 30 neighbours for each user
sim_user_30_m = find_n_neighbours(similarity_with_movie,30)
sim_user_30_m.head()
Acum, dacă vă gândiți, atunci am redus serios numărul de calcule inutile. Acum, suntem gata să calculăm acum scorul pentru un element.
Generarea scorului final S(u,i)
Wow! am terminat cu procesul. Știu că am trecut prin niște chestii tehnice reale, dar timpul pe care l-am petrecut merită, deoarece am ajuns la ultimul pas.
Aici vom încerca să prezicem scorul pentru filmul pe care utilizatorul dat nu l-a văzut.
score = User_item_score(320,7371)
print(score)