Ashay Pathak, Chatana Mandava, Ritesh Patel
Collaborative Filtering er en teknik, som er meget udbredt i anbefalingssystemer og er et hurtigt fremadskridende forskningsområde. De to mest almindeligt anvendte metoder er hukommelsesbaserede og modelbaserede.
I dette indlæg vil vi kun fokusere på (User-Based Collaborative Filtering) UB-CF, som er en hukommelsesbaseret metode. Hovedidéen bag UB-CF er, at folk med lignende egenskaber deler samme smag. Hvis du f.eks. er interesseret i at anbefale en film til vores ven Bob, så antag at Bob og jeg har set mange film sammen, og at vi har vurderet dem næsten identisk. Det giver mening at tro, at vi også i fremtiden vil fortsætte med at kunne lide lignende film og bruge denne lighedsmetrik til at anbefale film.
Lad os prøve at implementere UB-CF og generere en liste over film, som vores ven Bob a.k.a. aktiv bruger kunne være interesseret i at se. Motivationen bag at skrive dette indlæg er at dykke dybt ned i algoritmen og forstå, hvordan UB-CF faktisk fungerer. Det meste af indholdet af dette indlæg er inspireret af et kursus på Coursera.
User-User Collaborative Filtering
Metoden identificerer brugere, der ligner den forespurgte bruger, og estimerer den ønskede bedømmelse til at være det vægtede gennemsnit af bedømmelserne af disse lignende brugere.
Vi ville lave anbefalingen på MovieLens Dataset. Det anvendte programmeringssprog er python, og dataanalysearbejdet udføres for det meste ved hjælp af pandas-biblioteket. IDE brugt er jupyter notebook.
Så før vi begynder, vil jeg gerne give listen over anvendte biblioteker :
- Pandas
- Numpy
- sklearn
Så lad os komme videre og forstå koncepterne bag anbefalinger. Jeg har vedhæftet nogle kodestumper og output i bloggen for bedre forståelse. Hele ipynb-filen er vedhæftet i slutningen af bloggen.
Score-funktion
Det er nemt at finde på en funktion til ikke-personaliseret kollaborativ filtrering (dvs. vi overvejer ikke aktive brugeres likes, dislikes og rating fra fortiden), der returnerer en score, der tager bruger u og item i som inputparametre. Funktionen udsender en score, der kvantificerer, hvor stærkt brugeren u kan lide/præfererer element i.
Så dette gøres normalt ved hjælp af bedømmelser fra andre personer, der ligner brugeren. Alt dette vil blive diskuteret i detaljer senere. For nu er den formel, jeg har brugt, følgende,
Hvor “s” er den forudsagte score, “u” er brugeren, “i” er varen, “r” er den vurdering, som brugeren har givet, og “w” er vægten.
I dette tilfælde er vores score lig med summen af de bedømmelser, som hver bruger har givet det pågældende emne, idet den gennemsnitlige bedømmelse af den pågældende bruger fratrækkes, ganget med en vægt, som er udtryk for, hvor meget denne bruger ligner eller formodes at bidrage til de andre brugeres forudsigelser. Dette er vægten mellem bruger u og v. Scoren ligger mellem 0 og 1, hvor 0 er lav og 1 er høj. Alt ser perfekt ud, men hvorfor har vi så trukket de gennemsnitlige bedømmelser fra hver enkelt brugers bedømmelse, og hvorfor har vi brugt et vægtet gennemsnit i stedet for et simpelt gennemsnit?
Problemet ligger i de typer brugere, vi håndterer. Det starter med, at folk ofte vurderer på meget forskellige skalaer. Jeg kan være en positiv og optimistisk bruger, hvor jeg vil bedømme den film, jeg kunne lide, som 4 ud af 5, men en anden bruger, som er mindre optimistisk eller har nogle høje standarder, vil måske bedømme sin yndlingsfilm som 2 ud af 5. Her er hans 2 min 4. For at gøre det bedre kan vi øge effektiviteten af denne algoritme, hvis vi normaliserer brugernes vurdering. En måde at gøre det på er at sige, at vi vil beregne s(u,i), dvs. score som den gennemsnitlige bedømmelse, som brugeren giver hvert emne plus en vis afvigelse, og afvigelsen vil være, hvor meget dette emne er bedre eller dårligere end gennemsnittet.
Jeg har brugt cosinussammenfaldet til at beregne den vægt, der er angivet i ovenstående formel. Jeg har også brugt begrebet naboskab, som ville blive diskuteret i denne blog, når vi går videre.
For at normalisere dataene på ovenstående måde er der behov for en vis dataanalyse i pandas. Du kan få hele koden i slutningen. For bloggen vil jeg fokusere på de vigtige begreber.
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()
Så nu er vi færdige med at beregne den normaliserede bedømmelse for en bruger. Ovenstående data vil blive brugt til at beregne den endelige score for brugeren senere.
Fra her vil vi nu fokusere på nogle vigtige begreber relateret til anbefalingssystemer.
Cosine Similarity
For ovenstående formel skal vi finde de brugere, der har lignende tanker. Dette lyder så interessant at finde en bruger, der har lignende liking’s og disliking. Men spørgsmålet er, hvordan vi finder ligheden?
For at besvare dette, vil vi bruge Cosinus Similarity og se, hvor ens brugerne er. Den beregnes normalt over de vurderinger, som begge brugere har vurderet tidligere.
I vores eksempel har jeg brugt cosine_similarity-funktionen i sklearn til at beregne ligheden. Men før det skal vi udføre nogle forbehandlinger og rense dataene.
from sklearn.metrics.pairwise import cosine_similarityfinal=pd.pivot_table(Rating_avg,values='adg_rating',index='userId',columns='movieId')
Dette indeholder nogle masser af NaN-værdi, da alle brugere ikke har set alle film, og det er grunden til, at denne type matrix kaldes sparsom matrix. Metoder som matrixfaktorisering bruges til at håndtere denne sparsomhed, men vi vil ikke fokusere på det i denne blog. Næste skridt og et af de vigtige skridt er at erstatte disse NaN-værdier.
Der er to metoder, der almindeligvis bruges til dette :
- Brug brugergennemsnittet over rækken.
- Brug filmgennemsnittet over kolonnen.
Jeg har brugt begge metoder, og du kan få det i koden nedenfor. Men for at forklare vil jeg bruge filmgennemsnitsmetoden.
# Replacing NaN by Movie Average
final_movie = final.fillna(final.mean(axis=0))
Nu er det næste skridt at beregne ligheden mellem brugerne.
# 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()
Lader os selv kontrollere, om det vi har beregnet virkelig giver mening !!
a = get_user_similar_movies(370,86309)
a = a.loc]
a.head()
Fra ovenstående billede kan vi se, at den lighed, vi genererede, er sand, da begge de givne brugere (370,86309) har næsten samme bedømmelser og liking’s.
Så vi er færdige med at beregne lighederne mellem brugerne, men jeg er stadig ikke tilfreds. Jeg vil diskutere årsagen i næste trin.
Nærområde for bruger (K)
Så fra ovenstående kan vi se, at vi har beregnet lighederne for alle brugerne. Men da jeg er en Big Data-studerende, driver kompleksiteten af problemet altid mig. Hermed mener jeg, at anbefalingssystemet arbejder med de enorme data, og derfor bliver det meget vigtigt at vedligeholde og indfange kun de vigtige og nødvendige højdepunkter fra dataene.
For at forklare dette ved hjælp af vores eksempel på filmanbefalingssystem er den matrix, vi har opnået ovenfor, (862*862), da der er 862 unikke brugere i dataene. Dette antal er stadig lille i forhold til de data, som det oprindelige system ville arbejde med. Lad os tænke på Amazon. Amazon har mere end millioner af brugere i sin database, og det ville derfor ikke være en god løsning eller metode at se på alle de andre brugere hele tiden, mens man beregner scoren for en vare. For at løse dette problem skaber vi derfor et begreb om naboskab. Dette omfatter kun sættet af (K) lignende brugere for en bestemt bruger.
Nu skal vi tage yderligere skridt til at gennemføre idéen. I vores eksempel har jeg taget værdien af k som 30. Så vi ville have 30 nærmeste nabo for alle brugere.
Jeg har brugt min brugerdefinerede funktion find_n_neighbours, som tager lighedsmatrixen og værdien af n som input og returnerer de nærmeste n naboer for alle brugere. Du kan finde koden i den notesbog, der er givet i slutningen af bloggen.
# top 30 neighbours for each user
sim_user_30_m = find_n_neighbours(similarity_with_movie,30)
sim_user_30_m.head()
Så nu hvis du synes, så har vi for alvor reduceret antallet af unødvendige beregninger. Nu er vi klar til at beregne scoren for et emne nu.
Generering af den endelige score S(u,i)
Wow! vi er færdige med processen. Jeg ved godt, at vi har været igennem nogle rigtig tekniske ting, men den tid, vi har brugt, er det hele værd, da vi er nået til det sidste trin.
Her vil vi forsøge at forudsige scoren for den film, som den givne bruger ikke har set.
score = User_item_score(320,7371)
print(score)
Så vores system forudsagde scoren til at være 4,25, hvilket er rigtig godt. Jeg tror, at bruger (370) kunne lide filmen med id (7371).
Nu kan vi give vores system det sidste touch. Jeg tror, at de fleste af os ville have brugt Netflix eller Hotstar. Så når vi åbner appen, viser den det emne, du gør som.
Jeg bliver altid fascineret af anbefalingen, da de viser sig at være de film, jeg kan lide. Det sker alt sammen gennem selve anbefalingssystemet. Vi vil nu gøre det samme og forsøge at forudsige de 5 bedste film, som den givne bruger måske kan lide.
Logikken er ikke så svær. Vi har allerede genereret scoren for et emne. På samme måde kan vi generere scoren for de andre emner med den samme bruger.
Men nu igen med Big Data i betragtning giver det mening at generere score for alle de andre emner?
Jeg tror NEJ!!!!
Lader os overveje bruger (U) og bruger (K). Hvis vi nu antager, at K ikke er i nærheden af U, er det så muligt for brugeren U at kunne lide en film, som K har set og vurderet. For det meste nej.
Jeg tror, at du har forstået tricket. Ja, vi er bare interesseret i at beregne scoren for de elementer, som deres nabobrugere har set.
Vi er virkelig lykkedes. Vi har reduceret beregningen fra 2500 til N ( hvor N er mængden af film, som mit nabolag kunne lide ), og som er meget mindre end 2500.
Så lad os endelig anbefale filmene.
User_item_score1 er min brugerdefinerede funktion, som bruger vores ovenstående diskussion til at beregne forudsigelser
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)
Et sidst!!!! Vi gjorde det. Vi har vores eget anbefalingssystem.
Den nedenstående repræsentation af koden er måske ikke særlig let at læse, så gå venligst til mit GitHub-repository for at få adgang til koden.
De nødvendige datasæt kan fås herfra.
Jeg håber, at du kunne lide bloggen. For eventuelle forespørgsler kan du sende mig en mail. Stay tuned for flere blogs, da dette er et af mine interesseområder, og jeg vil helt sikkert skrive nogle nye ting.
Tak…!!!