Ashay Pathak, Chatana Mandava, Ritesh Patel
Collaborative Filtering är en teknik som används i stor utsträckning i rekommendationssystem och är ett snabbt framskridande forskningsområde. De två vanligaste metoderna är minnesbaserade och modellbaserade.
I det här inlägget kommer vi endast att fokusera på (User-Based Collaborative Filtering) UB-CF som är en minnesbaserad metod. Huvudidén bakom UB-CF är att personer med liknande egenskaper delar liknande smak. Om du till exempel är intresserad av att rekommendera en film till vår vän Bob, anta att Bob och jag har sett många filmer tillsammans och att vi betygsatt dem nästan identiskt. Det är logiskt att tro att vi även i framtiden skulle fortsätta att gilla liknande filmer och att vi skulle använda detta likhetsmått för att rekommendera filmer.
Låt oss försöka implementera UB-CF och generera en lista över filmer som vår vän Bob, alias en aktiv användare, skulle kunna vara intresserad av att titta på. Motivationen bakom att skriva det här inlägget är att djupdyka i algoritmen och förstå hur UB-CF faktiskt fungerar. Det mesta av innehållet i det här inlägget är inspirerat av en kurs på Coursera.
User-User Collaborative Filtering
Metoden identifierar användare som liknar den tillfrågade användaren och uppskattar att det önskade betyget är det viktade genomsnittet av betygen från dessa liknande användare.
Vi skulle göra rekommendationerna på MovieLens Dataset. Programmeringsspråket som används är python och dataanalysen görs mestadels med hjälp av pandas-biblioteket. IDE som används är jupyter notebook.
Så innan vi börjar vill jag ge en lista över de bibliotek som används :
- Pandas
- Numpy
- sklearn
Så låt oss gå vidare och förstå begreppen bakom rekommendationer. Jag har bifogat några kodutdrag och utdata i bloggen för bättre förståelse. Hela ipynb-filen är bifogad i slutet av bloggen.
Score-funktion
Det är lätt att ta fram en funktion för icke-personaliserad kollaborativ filtrering (dvs. vi tar inte hänsyn till aktiva användares gilla-, ogilla- och betygsättning från det förflutna) som returnerar en poäng med användaren u och objektet i som inparametrar. Funktionen ger ut en poäng som anger hur starkt användaren u gillar/prefererar objekt i.
Detta görs vanligtvis med hjälp av betyg från andra personer som liknar användaren. Allt detta diskuteras i detalj senare. För tillfället är den formel jag har använt följande:
Varvid “s” är det förutspådda poängtalet, “u” är användaren, “i” är objektet, “r” är det betyg som användaren har gett och “w” är vikten.
I detta fall är vår poäng lika med summan av de betyg som varje användare gav det aktuella objektet, med avdrag för det genomsnittliga betyget för den användaren multiplicerat med en viss vikt som anger hur mycket den här användaren liknar eller förväntas bidra till andra användares förutsägelser. Detta är vikten mellan användare u och v. Poängen varierar mellan 0 och 1, där 0 är lågt och 1 är högt. Allt ser perfekt ut, men varför subtraherade vi då de genomsnittliga betygen från varje användares betyg och varför använde vi vägt genomsnitt i stället för enkelt medelvärde?
Problemet ligger i de typer av användare som vi hanterar. Det börjar med att människor ofta betygsätter på mycket olika skalor. Jag kanske är en positiv och optimistisk användare som betygsätter den film jag gillade som 4 av 5, men en annan användare som är mindre optimistisk eller har höga krav kan betygsätta sin favoritfilm som 2 av 5. Här är hans 2 min 4. För att göra det bättre kan vi öka algoritmens effektivitet om vi normaliserar användarnas betygsättning. Ett sätt att göra det är att säga att vi ska beräkna s(u,i), dvs. poäng som det genomsnittliga betyg som användaren ger varje objekt plus en viss avvikelse, och avvikelsen är hur mycket objektet är bättre eller sämre än genomsnittet.
Jag har använt cosinuslikheten för att beräkna den vikt som anges i formeln ovan. Jag har också använt begreppet grannskap som kommer att diskuteras i den här bloggen när vi går vidare.
För att normalisera data på ovanstående sätt krävs viss dataanalys i pandas. Du kan få hela koden i slutet. För bloggen kommer jag att fokusera på de viktiga begreppen.
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 är vi klara med att beräkna det normaliserade betyget för en användare. Ovanstående data skulle användas för att beräkna det slutliga betyget för användaren senare.
Från här kommer vi nu att fokusera på några viktiga begrepp relaterade till rekommendationssystem.
Cosine Similarity
För ovanstående formel måste vi hitta de användare som har liknande tankar. Detta låter så intressant att hitta en användare som har liknande gillande och ogillande. Men frågan är hur vi hittar likheten?
För att besvara detta kommer vi att använda Cosine Similarity och se hur lika användarna är. Den beräknas vanligtvis över de betyg som båda användarna har betygsatt tidigare.
I vårt exempel har jag använt cosine_similarity-funktionen i sklearn för att beräkna likheten. Men innan dess måste vi utföra en viss förbehandling och rensa data.
from sklearn.metrics.pairwise import cosine_similarityfinal=pd.pivot_table(Rating_avg,values='adg_rating',index='userId',columns='movieId')
Denna innehåller en del massor av NaN-värden eftersom varje användare inte har sett alla filmer och det är orsaken till att den här typen av matris kallas för sparse matrix. Metoder som matrisfaktorisering används för att hantera denna sparsamhet, men vi kommer inte att fokusera på det i den här bloggen. Nästa steg och ett av de viktiga stegen är att ersätta dessa NaN-värden.
Det finns två metoder som vanligtvis används för detta :
- Använd användargenomsnittet över raden.
- Använd filmgenomsnittet över kolumnen.
Jag har använt båda metoderna och du kan få det i koden nedan. Men för att förklara skulle jag använda filmgenomsnittsmetoden.
# Replacing NaN by Movie Average
final_movie = final.fillna(final.mean(axis=0))
Nu är nästa steg att beräkna likheten mellan användarna.
# 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()
Låtsas kontrollera om det vi beräknat verkligen är vettigt !!
a = get_user_similar_movies(370,86309)
a = a.loc]
a.head()
Från ovanstående bild kan vi se att likheten som vi genererade stämmer eftersom båda de givna användarna (370,86309) har nästan samma betyg och gillande.
Så vi är klara med att beräkna likheterna mellan användarna, men jag är fortfarande inte nöjd. Jag kommer att diskutera orsaken i nästa steg.
Närområde för användare (K)
Så från ovan kan vi se att vi har beräknat likheterna för alla användare. Men eftersom jag studerar Big Data, driver problemets komplexitet mig alltid. Med detta menar jag att rekommendationssystemet arbetar med enorma data och att det därför blir mycket viktigt att behålla och fånga endast de viktiga och nödvändiga höjdpunkterna från data.
För att förklara detta med hjälp av vårt exempel på ett filmrekommendationssystem är den matris som vi har fått ovan (862*862), eftersom det finns 862 unika användare i datamaterialet. Detta antal är fortfarande litet jämfört med de data som det ursprungliga systemet skulle arbeta med. Låt oss tänka på Amazon. Amazon har mer än miljontals användare i sin databas och när man beräknar poängen för en artikel är det ingen bra lösning eller metod att titta på alla andra användare hela tiden. För att komma till rätta med detta skapar vi ett begrepp grannskap. Detta omfattar endast uppsättningen av (K) liknande användare för en viss användare.
Nu tar vi ytterligare steg för att genomföra idén. I vårt exempel har jag tagit värdet av k som 30. Så vi skulle ha 30 närmaste grannar för alla användare.
Jag har använt min anpassade funktion find_n_neighbours som tar likhetsmatrisen och värdet på n som indata och returnerar de närmaste n grannarna för alla användare. Du hittar koden i den anteckningsbok som ges i slutet av 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 om du tycker att då har vi på allvar minskat antalet onödiga beräkningar. Nu är vi redo att beräkna poängen för ett objekt nu.
Generering av den slutliga poängen S(u,i)
Wow! vi är klara med processen. Jag vet att vi gick igenom en del riktigt tekniska saker, men den tid vi spenderade är värd det eftersom vi har nått det sista steget.
Här ska vi försöka förutsäga poängen för den film som den givna användaren inte har sett.
score = User_item_score(320,7371)
print(score)
Så vårt system förutspådde att poängen skulle bli 4,25 vilket är riktigt bra. Jag tror att användare (370) kan gilla filmen med id (7371).
Nu kan vi ge vårt system en sista touch. Jag tror att de flesta av oss skulle ha använt Netflix eller Hotstar. Så när vi öppnar appen visar den objektet du gör gillar.
Jag blir alltid fascinerad av rekommendationen eftersom de visar sig vara de filmer jag gillar. Allt detta sker genom själva rekommendationssystemet. Vi kommer nu att göra samma sak och försöka förutsäga de fem bästa filmerna som den givna användaren kan tänkas gilla.
Logiken är inte så svår. Vi har redan genererat poängen för ett objekt. På samma sätt kan vi generera poäng för andra objekt med samma användare.
Men om vi nu återigen tar hänsyn till Big Data, är det då meningsfullt att generera poäng för alla andra objekt?
Jag tror att det inte är meningsfullt!!!
Vi kan tänka oss en användare (U) och en användare (K). Om vi antar att K inte befinner sig i närheten av U, är det då möjligt för användaren U att gilla en film som K har sett och betygsatt. Oftast nej.
Jag tror att du har förstått tricket. Ja, vi är bara intresserade av att beräkna poäng för de objekt som deras grannanvändare har sett.
Vi har verkligen lyckats. Vi har minskat beräkningen från 2500 till N ( där N är mängden filmer som mina grannar gillade ) och som är mycket mindre än 2500.
Så låt oss äntligen rekommendera filmerna.
User_item_score1 är min anpassade funktion som använder vår ovanstående diskussion för att beräkna förutsägelser
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)
Slutligt!!!! Vi gjorde det. Vi har ett eget rekommendationssystem.
Den nedanstående koden är kanske inte så lätt att läsa, så gå till mitt GitHub-förråd för att få tillgång till koden.
De nödvändiga datamängderna kan erhållas härifrån.
Jag hoppas att du gillade bloggen. Om du har några frågor kan du maila mig. Håll ögonen öppna för fler bloggar eftersom detta är ett av mina intresseområden och jag kommer definitivt att lägga upp nya saker.
Tack…!!