Systemy rekomendacji : 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

Collaborative Filtering jest techniką, która jest szeroko stosowana w systemach rekomendacji i jest szybko rozwijającym się obszarem badawczym. Dwie najczęściej używane metody to oparta na pamięci i oparta na modelu.

W tym poście, skupimy się tylko na (User-Based Collaborative Filtering) UB-CF, która jest metodą opartą na pamięci. Główną ideą UB-CF jest to, że ludzie o podobnych cechach dzielą się podobnym gustem. Na przykład, jeśli chcemy polecić film naszemu przyjacielowi Bobowi, załóżmy, że Bob i ja widzieliśmy wiele filmów razem i oceniliśmy je prawie tak samo. Jest sens myśleć, że w przyszłości również będziemy lubić podobne filmy i używać tej metryki podobieństwa do polecania filmów.

Postarajmy się zaimplementować UB-CF i wygenerować listę filmów, które nasz przyjaciel Bob a.k.a aktywny użytkownik może być zainteresowany obejrzeniem. Motywacją do napisania tego postu jest głębokie zagłębienie się w algorytm i zrozumienie jak UB-CF faktycznie działa. Większość treści tego postu jest inspirowana kursem na Coursera.

User-User Collaborative Filtering

Metoda identyfikuje użytkowników, którzy są podobni do zapytanego użytkownika i szacuje pożądaną ocenę jako średnią ważoną ocen tych podobnych użytkowników.

Collaborative Filtering : source here

Będziemy robić rekomendacje na MovieLens Dataset. Używanym językiem programowania jest python, a praca nad analizą danych odbywa się głównie przy użyciu biblioteki pandas. IDE używane to jupyter notebook.

Więc przed rozpoczęciem chciałbym podać listę używanych bibliotek :

  1. Pandas
  2. Numpy
  3. sklearn

Więc przejdźmy do przodu i zrozummy koncepcje stojącą za rekomendacjami. Załączyłem kilka fragmentów kodu i danych wyjściowych na blogu dla lepszego zrozumienia. Cały plik ipynb jest załączony na końcu bloga.

Funkcja score

Łatwo jest wymyślić funkcję dla niespersonalizowanego filtrowania kolaboratywnego (tzn. nie bierzemy pod uwagę upodobań, niechęci i ocen aktywnego użytkownika z przeszłości), która zwraca wynik biorąc użytkownika u oraz element i jako parametry wejściowe. Funkcja zwraca wynik, który określa jak mocno użytkownik u lubi/preferuje element i.

Zwykle jest to robione przy użyciu ocen innych osób podobnych do użytkownika. To wszystko zostanie szczegółowo omówione później. Na razie wzór, którego użyłem to,

funkcja score

Gdzie ‘s’ to przewidywany wynik, ‘u’ to użytkownik, ‘i’ to element, ‘r’ to ocena wystawiona przez użytkownika, a ‘w’ to waga.

W tym przypadku nasz wynik jest równy sumie ocen, które każdy użytkownik dał temu elementowi odejmując średnią ocenę tego użytkownika pomnożoną z pewną wagą, która jest jak bardzo ten użytkownik jest podobny lub ma przyczynić się do przewidywań innych użytkowników. Jest to waga pomiędzy użytkownikiem u i v. Wynik mieści się w przedziale od 0 do 1, gdzie 0 oznacza ocenę niską, a 1 wysoką. Wszystko wygląda idealnie, więc dlaczego odjęliśmy średnią ocen od każdej oceny użytkownika i dlaczego użyliśmy średniej ważonej zamiast prostej średniej? Problem tkwi w typach użytkowników, z którymi mamy do czynienia. Zaczyna się od tego, że ludzie oceniają często na bardzo różnych skalach. Mogę być pozytywnym i optymistycznym użytkownikiem, który oceni film, który mi się podobał na 4 z 5, ale inny użytkownik, który jest mniej optymistyczny lub ma wysokie standardy może ocenić swój ulubiony film na 2 z 5. Tutaj jego 2 jest moim 4. Aby uczynić go lepszym, możemy zwiększyć wydajność tego algorytmu, jeśli znormalizujemy ocenę użytkownika. Jednym ze sposobów na to jest powiedzenie, że będziemy obliczać s(u,i) i.e. wynik jako średnia ocena, którą użytkownik daje do każdego elementu plus jakieś odchylenie i odchylenie będzie jak bardzo ten element jest lepszy lub gorszy od średniej.

Użyłem podobieństwa cosinusowego do obliczenia wagi podanej w powyższym wzorze. Użyłem również pojęcia sąsiedztwa, które zostanie omówione na tym blogu, gdy przejdziemy dalej.

Aby znormalizować dane w powyższy sposób, wymagana jest pewna analiza danych w pandas. Możesz uzyskać cały kod na końcu. Na potrzeby bloga skupię się na ważnych koncepcjach.

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()

Normalizacja ocen

Więc teraz skończyliśmy obliczać znormalizowaną ocenę dla użytkownika. Powyższe dane zostaną użyte do obliczenia ostatecznego wyniku dla użytkownika później.

Z tego miejsca skupimy się teraz na kilku ważnych koncepcjach związanych z systemami rekomendacji.

Podobieństwo cosinusowe

Dla powyższej formuły musimy znaleźć użytkowników, którzy mają podobne myśli. To brzmi tak interesująco, aby znaleźć użytkownika, który ma podobne upodobania i niechęć. Ale pytanie brzmi, jak znajdziemy podobieństwo?

Aby odpowiedzieć na to pytanie, użyjemy podobieństwa cosinusowego i zobaczymy, jak podobni są użytkownicy. Zazwyczaj jest on obliczany na podstawie ocen, które obaj użytkownicy oceniali w przeszłości.

W naszym przykładzie, użyłem funkcji cosine_similarity z sklearn do obliczenia podobieństwa. Ale przed tym musimy wykonać pewne przetwarzanie wstępne i oczyścić dane.

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

tabela przestawna

Zawiera ona wiele wartości NaN, ponieważ każdy użytkownik nie widział wszystkich filmów i to jest powód, dla którego ten typ macierzy jest nazywany macierzą nieliczbową. Metody takie jak faktoryzacja macierzy są używane do radzenia sobie z tym rozproszeniem, ale nie będziemy się na tym skupiać w tym blogu. Następnym krokiem i jednym z ważnych kroków jest zastąpienie tych wartości NaN.

Istnieją dwie metody powszechnie używane do tego :

  1. Użyj średniej użytkownika w wierszu.
  2. Użyj średniej filmu w kolumnie.

Użyłem obu metod i możesz to uzyskać w poniższym kodzie. Ale dla wyjaśnienia użyłbym metody średniej filmowej.

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

Replacing NaN values by movie average

Teraz, następnym krokiem jest obliczenie podobieństwa między użytkownikami.

# 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()

Podobieństwo cosinusowe użytkownika

Sprawdźmy sami czy to co obliczyliśmy naprawdę ma sens !!

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

filmy podobne

Z powyższego obrazu widać, że wygenerowane przez nas podobieństwo jest prawdziwe, ponieważ obaj użytkownicy (370,86309) mają prawie takie same oceny i polubienia.

Więc skończyliśmy z obliczaniem podobieństw między użytkownikami, ale nie jestem jeszcze zadowolony. Omówię powód w następnym kroku.

Neighborhood dla użytkownika (K)

Więc z powyższego możemy zobaczyć, że obliczyliśmy podobieństwa dla wszystkich użytkowników. Ale ponieważ jestem studentem Big Data, złożoność problemu zawsze mnie napędza. Przez to rozumiem, że system rekomendacji pracuje z ogromnymi danymi i stąd staje się bardzo ważne, aby utrzymać i uchwycić tylko ważne i niezbędne podkreślenia z danych.

Aby wyjaśnić to na naszym przykładzie systemu rekomendacji filmów, macierz, którą uzyskaliśmy powyżej jest (862*862), ponieważ istnieje 862 unikalnych użytkowników w danych. Ta liczba jest wciąż mała w porównaniu do danych, na których pracowałby oryginalny system. Pomyślmy o Amazon. Miałby on ponad miliony użytkowników w swojej bazie danych i tak podczas obliczania wyniku dla każdego przedmiotu nie byłoby dobrym rozwiązaniem lub metodą, aby spojrzeć na wszystkich innych użytkowników przez cały czas. Stąd, aby przezwyciężyć ten problem generujemy pojęcie sąsiedztwa. Obejmuje ono tylko zbiór (K) podobnych użytkowników dla danego użytkownika.

Teraz podejmijmy dalsze kroki w celu realizacji tego pomysłu. W naszym przykładzie przyjąłem wartość k jako 30. Więc mielibyśmy 30 najbliższych sąsiadów dla wszystkich użytkowników.

Użyłem mojej niestandardowej funkcji find_n_neighbours, która pobiera macierz podobieństwa i wartość n jako dane wejściowe i zwraca najbliższych n sąsiadów dla wszystkich użytkowników. Kod można znaleźć w notatniku podanym na końcu bloga.

# 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

Więc teraz, jeśli myślisz, że wtedy poważnie zmniejszyliśmy liczbę niepotrzebnych obliczeń. Teraz, jesteśmy gotowi do obliczenia wyniku dla elementu teraz.

Generowanie ostatecznego wyniku S(u,i)

Wow! skończyliśmy z procesem. Wiem, że przeszliśmy przez kilka naprawdę technicznych rzeczy, ale czas, który poświęciliśmy jest tego wart, ponieważ doszliśmy do ostatniego kroku.

Postaramy się tutaj przewidzieć wynik dla filmu, którego dany użytkownik nie widział.

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

predicted score

Więc nasz system przewidział ocenę 4.25 co jest naprawdę dobre. Myślę, że użytkownik (370) mógłby polubić film z id (7371).

Teraz nadajmy ostateczny szlif naszemu systemowi. Myślę, że większość z nas użyłaby Netflix lub Hotstar. Więc kiedy otwieramy aplikację pokazuje ona pozycję, którą robisz like.

Zawsze jestem zafascynowany rekomendacją, ponieważ okazują się one być filmami, które lubię. To wszystko dzieje się przez sam system rekomendacji. Teraz zrobimy to samo i spróbujemy przewidzieć 5 najlepszych filmów, które dany użytkownik może polubić.

Logika nie jest taka trudna. Wygenerowaliśmy już wynik dla jednego elementu. Podobnie możemy wygenerować wynik dla innych pozycji z tym samym użytkownikiem.

Ale, teraz ponownie biorąc pod uwagę Big Data, czy ma sens generowanie wyniku dla wszystkich innych pozycji??

Myślę, że NIEOOOOO!!!

Rozważmy użytkownika (U) i użytkownika (K). Teraz załóżmy, że K nie jest w sąsiedztwie U, czy jest możliwe, aby użytkownik U polubił film, który K widział i ocenił. Mostly No.

Myślę, że dostałeś sztuczkę. Tak, jesteśmy po prostu zainteresowani obliczaniem wyników dla przedmiotów, które ich sąsiedzi użytkownicy widzieli.

Jesteśmy naprawdę udani. Zmniejszyliśmy liczbę obliczeń z 2500 do N (gdzie N jest zbiorem filmów, które lubili moi sąsiedzi), a to jest bardzo mało niż 2500.

Więc na koniec pozwólmy polecić filmy.

User_item_score1 jest moją niestandardową funkcją, która używa naszej powyższej dyskusji do obliczenia przewidywań

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)

Na koniec!!!! Udało nam się. Mamy nasz własny system rekomendacji.

Oprezentacja kodu poniżej może nie być zbyt łatwa do odczytania, więc proszę przejść do mojego repozytorium GitHub, aby uzyskać dostęp do kodu.

Wymagane zestawy danych można uzyskać stąd.

Mam nadzieję, że podobał Ci się blog. W przypadku jakichkolwiek pytań możesz wysłać do mnie maila. Bądź na bieżąco z kolejnymi blogami, ponieważ jest to jeden z obszarów moich zainteresowań i na pewno opublikuję kilka nowych rzeczy.

Dziękuję!!!

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.