Ashay Pathak, Chatana Mandava, Ritesh Patel
Collaborative Filtering on tekniikka, jota käytetään laajalti suosittelujärjestelmissä ja joka on nopeasti etenevä tutkimusalue. Kaksi yleisimmin käytettyä menetelmää ovat muistipohjainen ja mallipohjainen.
Tässä postauksessa keskitymme vain (User-Based Collaborative Filtering) UB-CF:ään, joka on muistipohjainen menetelmä. UB-CF:n pääidea on, että ihmisillä, joilla on samanlaiset ominaisuudet, on samanlainen maku. Esimerkiksi, jos olet kiinnostunut suosittelemaan elokuvaa ystävällemme Bobille, oletetaan, että Bob ja minä olemme nähneet monta elokuvaa yhdessä ja arvioimme ne lähes identtisesti. On järkevää ajatella, että tulevaisuudessakin pitäisimme samanlaisista elokuvista ja käyttäisimme tätä samankaltaisuusmittaria elokuvien suositteluun.
Yritetään toteuttaa UB-CF ja luoda lista elokuvista, joiden katsomisesta ystävämme Bob eli aktiivinen käyttäjä voisi olla kiinnostunut. Motivaatio tämän postauksen kirjoittamisen taustalla on sukeltaa syvälle algoritmiin ja ymmärtää, miten UB-CF itse asiassa toimii. Suurin osa tämän postauksen sisällöstä on saanut inspiraationsa Courseran kurssista.
User-User Collaborative Filtering
Menetelmä tunnistaa käyttäjät, jotka ovat samankaltaisia kuin kysytty käyttäjä, ja arvioi halutun arvosanan olevan näiden samankaltaisten käyttäjien arvosanojen painotettu keskiarvo.
Olisimme tekemässä suositusta MovieLens Datasetille. Ohjelmointikielenä käytetään pythonia ja data-analyysityö tehdään pääosin pandas-kirjastolla. IDE:nä käytetään jupyter notebookia.
Etukäteen haluaisin siis antaa listan käytetyistä kirjastoista :
- Pandas
- Numpy
- sklearn
Siirrymme siis eteenpäin ja ymmärrämme käsitteet suositusten takana. Olen liittänyt joitakin koodinpätkiä ja tuotoksia blogiin paremman ymmärtämisen vuoksi. Koko ipynb-tiedosto on liitetty blogin loppuun.
Score-funktio
On helppo keksiä funktio ei-personalisoitua yhteissuodatusta varten (eli emme ota huomioon aktiivisen käyttäjän tykkäämisiä, vastenmielisyyksiä ja arvosteluja menneisyydestä), joka palauttaa pistemäärän ottamalla tuloparametreiksi käyttäjä u ja kohde i. Funktio antaa tulokseksi pistemäärän, joka ilmaisee, kuinka vahvasti käyttäjä u pitää/suosii kohdetta i.
Tämä tehdään yleensä käyttämällä muiden käyttäjän kaltaisten ihmisten luokituksia. Tätä kaikkea käsitellään yksityiskohtaisesti myöhemmin. Toistaiseksi käyttämäni kaava on,
Jossa ‘s’ on ennustettu pistemäärä, ‘u’ on käyttäjä, ‘i’ on kohde, ‘r’ on käyttäjän antama arvosana ja ‘w’ on painoarvo.
Tässä tapauksessa pisteytyksemme on yhtä suuri kuin kunkin käyttäjän kyseiselle kohteelle antamien arvostelujen summa, josta on vähennetty kyseisen käyttäjän keskimääräinen arvostelu kerrottuna jollakin painolla, joka kuvaa sitä, kuinka paljon kyseinen käyttäjä on samankaltainen tai kuinka paljon hänen oletetaan vaikuttavan muiden käyttäjien ennusteisiin. Tämä on käyttäjän u ja v välinen painoarvo. Pistemäärä vaihtelee välillä 0-1, jossa 0 on alhainen ja 1 korkea. Kaikki näyttää täydelliseltä, mutta miksi sitten vähensimme keskimääräiset arvosanat kunkin käyttäjän arvosanasta ja miksi käytimme painotettua keskiarvoa yksinkertaisen keskiarvon sijasta?
Ongelma on käsittelemiemme käyttäjätyyppien kanssa. Se alkaa siitä, että ihmiset arvioivat usein hyvin erilaisilla asteikoilla. Saatan olla positiivinen ja optimistinen käyttäjä, jolloin arvioin elokuvan, josta pidin, 4:ksi viidestä, mutta joku toinen käyttäjä, joka on vähemmän optimistinen tai jolla on korkeat standardit, saattaa arvioida suosikkielokuvansa 2:ksi viidestä. Tässä tapauksessa hänen 2:nsa on minun 4:ni. Algoritmin tehokkuutta voidaan parantaa, jos normalisoimme käyttäjien arvosanat. Yksi tapa tehdä se on sanoa, että laskemme s(u,i) eli pistemäärän keskiarvona, jonka käyttäjä antaa kullekin kohteelle, lisättynä jollakin poikkeamalla, ja poikkeama on se, kuinka paljon tämä kohde on parempi tai huonompi kuin keskiarvo.
Olen käyttänyt kosinin samankaltaisuutta laskeakseni yllä olevassa kaavassa annetun painon. Olen myös käyttänyt naapuruston käsitettä, jota käsiteltäisiin tässä blogissa, kun siirrymme eteenpäin.
Normalisoidakseni datan edellä mainitulla tavalla, tarvitaan jonkin verran data-analyysiä pandasissa. Saat koko koodin lopussa. Blogissa keskityn tärkeisiin käsitteisiin.
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()
Nyt olemme siis valmiita laskemaan normalisoidun arvosanan käyttäjälle. Edellä mainittuja tietoja käytettäisiin myöhemmin käyttäjän lopullisen pistemäärän laskemiseen.
Tästä eteenpäin keskitymme nyt joihinkin tärkeisiin suosittelujärjestelmiin liittyviin käsitteisiin.
Kosinusmainen samankaltaisuus
Yllä olevaa kaavaa varten meidän on löydettävä käyttäjät, joilla on samankaltaisia ajatuksia. Tämä kuulostaa niin mielenkiintoiselta löytää käyttäjä, jolla on samanlaisia mieltymyksiä ja vastenmielisyyksiä. Mutta kysymys on, miten löydämme samankaltaisuuden?
Vastaaksemme tähän käytämme Cosine Similarity -menetelmää ja katsomme, kuinka samankaltaisia käyttäjät ovat. Se lasketaan yleensä niiden arvostelujen perusteella, joita molemmat käyttäjät ovat arvostelleet aiemmin.
Esimerkissämme olen käyttänyt sklearnin cosine_similarity-funktiota samankaltaisuuden laskemiseen. Mutta ennen sitä meidän on suoritettava esikäsittely ja puhdistettava tiedot.
from sklearn.metrics.pairwise import cosine_similarityfinal=pd.pivot_table(Rating_avg,values='adg_rating',index='userId',columns='movieId')
Tässä on paljon NaN-arvoja, koska jokainen käyttäjä ei ole nähnyt kaikkia elokuvia, ja siksi tämäntyyppisiä matriiseja sanotaan harvalukuisiksi. Tämän harvan matriisin käsittelyyn käytetään menetelmiä, kuten matriisifaktorointia, mutta emme keskity siihen tässä blogissa. Seuraava askel ja yksi tärkeimmistä vaiheista on korvata nämä NaN-arvot.
Tähän käytetään yleisesti kahta menetelmää :
- Käytä käyttäjän keskiarvoa rivillä.
- Käytä elokuvan keskiarvoa sarakkeessa.
Olen käyttänyt molempia menetelmiä ja saat sen alla olevasta koodista. Mutta selittämiseen käyttäisin elokuvan keskiarvomenetelmää.
# Replacing NaN by Movie Average
final_movie = final.fillna(final.mean(axis=0))
Nyt seuraavaksi lasketaan samankaltaisuudet käyttäjien välillä.
# 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()
Tarkistetaan itseltämme onko laskemassamme oikeasti järkeä !!
a = get_user_similar_movies(370,86309)
a = a.loc]
a.head()
Yllä olevasta kuvasta näemme, että generoimamme samankaltaisuus pitää paikkansa, koska molemmilla annetuilla käyttäjillä (370,86309) on melkein samanlaiset arvosanat ja tykkäykset.
Käyttäjien välisten samankaltaisuuksien laskeminen on siis valmis, mutta en ole vielä tyytyväinen. Keskustelen syystä seuraavassa vaiheessa.
Neighborhood for User (K)
Yllä olevasta nähdään siis, että olemme laskeneet samankaltaisuudet kaikille käyttäjille. Mutta koska olen Big Data -opiskelija, ongelman monimutkaisuus ajaa minua aina eteenpäin. Tällä tarkoitan sitä, että suosittelujärjestelmä toimii valtavan suuren datan kanssa, ja siksi on erittäin tärkeää ylläpitää ja kaapata vain tärkeät ja tarpeelliset kohokohdat datasta.
Selittääkseni tämän käyttämällä esimerkkiä elokuvien suosittelujärjestelmästä, edellä saamamme matriisi on (862*862), koska datassa on 862 ainutlaatuista käyttäjää. Tämä määrä on silti pieni verrattuna siihen dataan, jota alkuperäinen järjestelmä käsittelisi. Ajatellaanpa Amazonia. Sen tietokannassa on yli miljoonia käyttäjiä, joten minkään tuotteen pistemäärää laskettaessa ei olisi hyvä ratkaisu tai menetelmä tarkastella kaikkia muita käyttäjiä koko ajan. Näin ollen tämän ongelman ratkaisemiseksi luomme naapuruston käsitteen. Tämä sisältää vain (K) samankaltaisten käyttäjien joukon tietylle käyttäjälle.
Viedään nyt jatkotoimenpiteet idean toteuttamiseksi. Esimerkissämme olen ottanut k:n arvoksi 30. Joten meillä olisi 30 lähintä naapuria kaikille käyttäjille.
Olen käyttänyt omaa funktiotani find_n_neighbours, joka ottaa syötteenä samankaltaisuusmatriisin ja n:n arvon ja palauttaa lähimmät n naapuria kaikille käyttäjille. Koodin löydät blogin lopussa annetusta muistikirjasta.
# top 30 neighbours for each user
sim_user_30_m = find_n_neighbours(similarity_with_movie,30)
sim_user_30_m.head()
Nyt jos nyt luulet, niin sitten olemme vähentäneet tosissaan tarpeettomien laskutoimitusten määrää. Nyt olemme valmiita laskemaan pisteytyksen kohteelle nyt.
Loppupisteytyksen muodostaminen S(u,i)
Vau! olemme valmiita prosessin kanssa. Tiedän, että kävimme läpi todella teknisiä asioita, mutta käyttämämme aika on sen arvoista, sillä olemme päässeet viimeiseen vaiheeseen.
Tässä yritämme ennustaa pistemäärän elokuvalle, jota kyseinen käyttäjä ei ole nähnyt.
score = User_item_score(320,7371)
print(score)
Systeemimme ennusti siis elokuvan pistemääräksi 4.25, mikä on todella hyvä. Uskon, että käyttäjä (370) voisi pitää elokuvasta id:llä (7371).
Viimeistellään nyt järjestelmämme. Luulen, että useimmat meistä olisivat käyttäneet Netflixiä tai Hotstaria. Joten kun avaamme sovelluksen, se näyttää kohteen, jonka teet tykkää.
Suositus kiehtoo minua aina, koska ne osoittautuvat elokuviksi, joista pidän. Tämä kaikki tapahtuu itse suosittelujärjestelmän kautta. Teemme nyt saman ja yritämme ennustaa 5 parasta elokuvaa, joista kyseinen käyttäjä saattaa pitää.
Logiikka ei ole niin vaikea. Olemme jo tuottaneet pisteet yhdelle kohteelle. Vastaavasti voimme tuottaa pisteet muille kohteille saman käyttäjän kanssa.
Mutta, nyt taas Big Data huomioon ottaen, onko järkevää tuottaa pisteet kaikille muille kohteille?
Olen sitä mieltä, että EI!!!
Harkitaan käyttäjää (U) ja käyttäjää (K). Nyt oletetaan, että K ei ole U:n naapurustossa ,onko mahdollista, että käyttäjä U pitää elokuvasta, jonka K on nähnyt ja arvioinut. Useimmiten ei.
Luulempa että ymmärsit tempun. Kyllä, meitä kiinnostaa vain laskea pisteet kohteille, jotka niiden naapurikäyttäjät ovat nähneet.
Olemme todella onnistuneet. Vähensimme laskennan 2500:sta N:ään ( jossa N on joukko elokuvia, joista naapurini piti ) ja joka on hyvin paljon vähemmän kuin 2500.
Viimein siis suositellaan elokuvia.
User_item_score1 on oma funktioni, joka käyttää yllä olevaa keskusteluamme ennusteiden laskemiseen
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)
Viimeiseksi!!!!. Me teimme sen. Meillä on oma suosittelujärjestelmämme.
Alhaalla olevan koodin esitys ei välttämättä ole kovin helppolukuinen, joten siirry GitHub-arkistooni päästäksesi käsiksi koodiin.
Tarvittavat datasetit saat täältä.
Toivottavasti pidit blogista. Jos sinulla on kysyttävää, voit lähettää minulle sähköpostia. Pysy kuulolla lisää blogeja varten, koska tämä on yksi kiinnostukseni kohteista ja haluaisin ehdottomasti lähettää uusia juttuja.
Kiitos..!!!