Doporučovací systémy : User-based Collaborative Filtering using N Nearest Neighbors

Ashay Pathak
Ashay Pathak

Follow

25. února, 2019 – 9 minut čtení

Ashay Pathak, Chatana Mandava, Ritesh Patel

Kollaborativní filtrování je technika, která se široce používá v doporučovacích systémech a je rychle se rozvíjející oblastí výzkumu. Dvě nejčastěji používané metody jsou metody založené na paměti a na modelu.

V tomto příspěvku se zaměříme pouze na (User-Based Collaborative Filtering) UB-CF, což je metoda založená na paměti. Hlavní myšlenkou UB-CF je, že lidé s podobnými charakteristikami sdílejí podobný vkus. Například pokud máme zájem doporučit film našemu kamarádovi Bobovi, předpokládejme, že jsme s Bobem viděli společně mnoho filmů a hodnotili jsme je téměř stejně. Dává smysl si myslet, že i v budoucnu se nám budou líbit podobné filmy, a použít tuto metriku podobnosti k doporučování filmů.

Pokusíme se implementovat UB-CF a vygenerovat seznam filmů, o které by mohl mít zájem náš přítel Bob alias aktivní uživatel. Motivací k napsání tohoto příspěvku je hlubší ponoření se do algoritmu a pochopení toho, jak UB-CF vlastně funguje. Většina obsahu tohoto příspěvku je inspirována kurzem na Coursera.

User-User Collaborative Filtering

Metoda identifikuje uživatele, kteří jsou podobní dotazovanému uživateli, a odhaduje požadované hodnocení jako vážený průměr hodnocení těchto podobných uživatelů.

Collaborative Filtering : zdroj zde

Doporučení bychom prováděli na datasetu MovieLens. Použitým programovacím jazykem je python a práce na analýze dat probíhá převážně pomocí knihovny pandas. Použité IDE je jupyter notebook.

Ještě než začneme, rád bych uvedl seznam použitých knihoven :

  1. Pandas
  2. Numpy
  3. sklearn

Takže pojďme dál a pochopme pojmy, které stojí za doporučeními. Pro lepší pochopení jsem v blogu přiložil několik úryvků kódu a výstupů. Celý soubor ipynb je přiložen na konci blogu.

Funkce skóre

Je snadné vymyslet funkci pro nepersonalizované kolaborativní filtrování (tj. nezohledňujeme sympatie, antipatie a hodnocení aktivního uživatele z minulosti), která vrací skóre, přičemž jako vstupní parametry bere uživatele u a položku i. Výstupem funkce je skóre, které kvantifikuje, jak silně se uživateli u líbí/preferuje položku i.

Takto se obvykle provádí pomocí hodnocení jiných lidí podobných uživateli. To vše by bylo podrobně rozebráno později. Prozatím jsem použil vzorec,

score funkce

Kde “s” je předpokládané skóre, “u” je uživatel, “i” je položka, “r” je hodnocení udělené uživatelem a “w” je váha.

V tomto případě se naše skóre rovná součtu hodnocení, které každý uživatel udělil dané položce, po odečtení průměrného hodnocení tohoto uživatele vynásobeného určitou váhou, která je z toho, jak moc je tento uživatel podobný nebo má přispět k předpovědím ostatních uživatelů. Jedná se o váhu mezi uživatelem u a v. Skóre se pohybuje v rozmezí 0 až 1, kde 0 znamená nízké a 1 vysoké. Vše vypadá perfektně, proč jsme tedy od hodnocení jednotlivých uživatelů odečetli průměrná hodnocení a proč jsme použili vážený průměr místo prostého průměru?”

Problém je v typech uživatelů, se kterými pracujeme. Začíná to tím, že lidé hodnotí často na velmi rozdílných stupnicích. Já mohu být pozitivní a optimistický uživatel, u kterého ohodnotím film, který se mi líbil, známkou 4 z 5, ale jiný uživatel, který je méně optimistický nebo má nějaké vysoké nároky, může svůj oblíbený film ohodnotit známkou 2 z 5. V takovém případě se může stát, že se mi film líbil. Zde jeho 2 je moje 4. Zlepšení, které by to zlepšilo, spočívá v tom, že můžeme zvýšit účinnost tohoto algoritmu, pokud normalizujeme hodnocení uživatele. Jedním ze způsobů, jak to udělat, je říci, že budeme počítat s(u,i), tj. skóre jako průměrné hodnocení, které uživatel udělí každé položce, plus nějaká odchylka a odchylka bude vyjadřovat, o kolik je tato položka lepší nebo horší než průměr.

Pro výpočet váhy uvedené ve výše uvedeném vzorci jsem použil kosinovou podobnost. Použil jsem také pojem sousedství, o kterém by se dalo hovořit v tomto blogu, až budeme pokračovat.

Pro normalizaci dat výše uvedeným způsobem je třeba provést určitou analýzu dat v programu pandas. Celý kód najdete na konci. Pro tento blog se zaměřím na důležité koncepty.

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

Normalizované hodnocení

Takže nyní jsme dokončili výpočet normalizovaného hodnocení pro uživatele. Výše uvedené údaje by se později použily k výpočtu konečného hodnocení uživatele.

Odtud se nyní zaměříme na některé důležité pojmy související s doporučovacími systémy.

Kosinová podobnost

Pro výše uvedený vzorec potřebujeme najít uživatele, kteří mají podobné myšlenky. To zní tak zajímavě, že najdeme uživatele, který má podobné sympatie a antipatie. Otázkou však je, jak tuto podobnost zjistíme?

Pro odpověď na tuto otázku použijeme Cosine Similarity a zjistíme, jak moc jsou si uživatelé podobní. Obvykle se počítá přes hodnocení, která oba uživatelé hodnotili v minulosti.

V našem příkladu jsem k výpočtu podobnosti použil funkci cosine_similarity programu sklearn. Předtím však musíme provést určité předzpracování a vyčistit data.

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

přehledová tabulka

Tato obsahuje několik spoust NaN hodnot, protože každý uživatel neviděl všechny filmy, a to je důvod, proč se tomuto typu matice říká řídká matice. K vypořádání se s touto řídkostí se používají metody jako faktorizace matice, ale v tomto blogu bychom se na ni nezaměřovali. Dalším krokem a jedním z důležitých kroků je nahradit tyto hodnoty NaN.

Běžně se k tomu používají dvě metody :

  1. Použít průměr uživatele přes řádek.
  2. Použít průměr filmu přes sloupec.

Použil jsem obě metody a můžete je získat v kódu níže. Ale pro vysvětlení bych použil metodu filmového průměru.

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

Záměna hodnot NaN za filmový průměr

Nyní je třeba v dalším kroku vypočítat podobnost mezi uživateli.

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

Kosinová podobnost uživatelů

Zkontrolujeme si, zda to, co jsme vypočítali, má opravdu smysl !!

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

filmy podobné

Z výše uvedeného obrázku vidíme, že námi vygenerovaná podobnost je pravdivá, protože oba daní uživatelé (370,86309) mají téměř stejné hodnocení a lajky.

Takže jsme s výpočtem podobností mezi uživateli hotovi, ale stále nejsem spokojen. Důvod bych probral v dalším kroku.

Sousedství pro uživatele (K)

Z výše uvedeného je tedy vidět, že jsme vypočítali podobnosti pro všechny uživatele. Ale protože jsem studentem oboru Big Data, složitost problému mě vždy pohání. Tím mám na mysli, že doporučovací systém pracuje s obrovským množstvím dat, a proto se stává velmi důležitým zachovat a zachytit z dat pouze důležité a potřebné zajímavosti.

Abych to vysvětlil na našem příkladu systému doporučování filmů, matice, kterou jsme získali výše, je (862*862), protože v datech je 862 jedinečných uživatelů. Toto číslo je stále malé ve srovnání s daty, se kterými by pracoval původní systém. Zamysleme se nad společností Amazon. Ten by měl ve své databázi více než miliony uživatelů, a tak by při výpočtu skóre pro jakoukoli položku nebylo dobrým řešením nebo metodou dívat se neustále na všechny ostatní uživatele. Proto k překonání tohoto problému vytvoříme pojem sousedství. To zahrnuje pouze množinu (K) podobných uživatelů pro konkrétního uživatele.

Nyní podnikneme další kroky k realizaci této myšlenky. V našem příkladu jsem přijal hodnotu k jako 30. Takže bychom měli 30 nejbližších sousedů pro všechny uživatele.

Použil jsem svou vlastní funkci find_n_neighbours, která bere jako vstup matici podobnosti a hodnotu n a vrací nejbližší n sousedů pro všechny uživatele. Kód najdete v zápisníku uvedeném na konci blogu.

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

nejlepších 30 sousedů pro uživatele

Takže teď jestli myslíte, tak jsme vážně snížili počet zbytečných výpočtů. Nyní jsme připraveni vypočítat skóre pro danou položku.

Vytvoření výsledného skóre S(u,i)

Páni, s tímto procesem jsme hotovi. Vím, že jsme si prošli skutečnými technickými záležitostmi, ale strávený čas se vyplatí, protože jsme dospěli k poslednímu kroku.

Tady se pokusíme předpovědět skóre pro film, který daný uživatel neviděl.

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

předpovězené skóre

Náš systém tedy předpověděl skóre 4,25, což je opravdu dobré. Myslím, že uživateli (370) by se film s id (7371) mohl líbit.

Nyní dodejme našemu systému finální podobu. Myslím, že většina z nás by použila Netflix nebo Hotstar. Takže když otevřeme aplikaci, zobrazí se položka, která se vám líbí.

Vždycky mě fascinuje doporučení, protože se ukáže, že jsou to filmy, které se mi líbí. To vše se děje prostřednictvím samotného systému doporučení. Nyní uděláme totéž a pokusíme se předpovědět 5 nejlepších filmů, které se danému uživateli mohou líbit.

Logika není tak těžká. Skóre pro jednu položku jsme již vygenerovali. Stejně tak můžeme vygenerovat skóre pro další položky u stejného uživatele.

Ale má nyní opět s ohledem na Big Data smysl generovat skóre pro všechny ostatní položky?

Myslím, že NE!!!

Uvažujme uživatele (U) a uživatele (K). Nyní předpokládejme, že K není v sousedství U ,je možné, aby se uživateli U líbil film, který K viděl a hodnotil. Většinou ne.

Myslím, že jste pochopili trik. Ano, zajímá nás pouze výpočet skóre pro položky, které viděli jejich sousedé uživatelé.

Jsme opravdu úspěšní. Snížili jsme výpočet z 2500 na N ( kde N je množina filmů, které se líbily mému okolí ) a která je velmi menší než 2500.

Takže konečně pojďme doporučovat filmy.

User_item_score1 je moje vlastní funkce, která využívá naši výše uvedenou diskusi k výpočtu předpovědí

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)

Nakonec!!!!. Podařilo se nám to. Máme vlastní doporučovací systém.

Zobrazení kódu níže nemusí být příliš přehledné, proto prosím přejděte na můj repozitář GitHub, kde se ke kódu dostanete.

Potřebné datové sady můžete získat zde.

Doufám, že se vám blog líbil. V případě jakýchkoli dotazů mi můžete napsat. Zůstaňte naladěni na další blogy, protože je to jedna z oblastí mého zájmu a určitě bych zveřejnil nějaké nové věci.

Děkuji…!!!

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.