Ashay Pathak, Chatana Mandava, Ritesh Patel
Collaborative Filtering è una tecnica ampiamente utilizzata nei sistemi di raccomandazione ed è un’area di ricerca in rapido progresso. I due metodi più comunemente usati sono basati sulla memoria e sul modello.
In questo post, ci concentreremo solo su (User-Based Collaborative Filtering) UB-CF che è un metodo basato sulla memoria. L’idea principale dietro UB-CF è che le persone con caratteristiche simili condividono gusti simili. Per esempio, se siete interessati a raccomandare un film al nostro amico Bob, supponiamo che Bob ed io abbiamo visto molti film insieme e li abbiamo valutati quasi allo stesso modo. Ha senso pensare che anche in futuro continueranno a piacerci film simili e usare questa metrica di somiglianza per raccomandare i film.
Proviamo a implementare UB-CF e a generare una lista di film che il nostro amico Bob, alias utente attivo, potrebbe essere interessato a vedere. La motivazione dietro la scrittura di questo post è di immergersi profondamente nell’algoritmo e capire come funziona effettivamente UB-CF. La maggior parte del contenuto di questo post è ispirato da un corso su Coursera.
User-User Collaborative Filtering
Il metodo identifica gli utenti che sono simili all’utente interrogato e stima il rating desiderato come media ponderata dei rating di questi utenti simili.
Faremmo le raccomandazioni su MovieLens Dataset. Il linguaggio di programmazione utilizzato è python e il lavoro di analisi dei dati è fatto principalmente utilizzando la libreria pandas. L’IDE utilizzato è jupyter notebook.
Prima di iniziare vorrei dare la lista delle librerie utilizzate:
- Pandas
- Numpy
- sklearn
Perciò andiamo avanti e capiamo i concetti dietro le raccomandazioni. Ho allegato alcuni frammenti di codice e output nel blog per una migliore comprensione. L’intero file ipynb è allegato alla fine del blog.
Funzione di punteggio
È facile inventare una funzione per il filtraggio collaborativo non personalizzato (cioè non consideriamo i gusti, le antipatie e le valutazioni dell’utente attivo nel passato) che restituisce un punteggio prendendo l’utente u e l’oggetto i come parametri di input. La funzione restituisce un punteggio che quantifica quanto fortemente all’utente u piace/preferisce l’oggetto i.
Quindi questo viene fatto di solito usando le valutazioni di altre persone simili all’utente. Tutto questo sarà discusso in dettaglio più tardi. Per ora la formula che ho usato è,
dove ‘s’ è il punteggio previsto, ‘u’ è l’utente, ‘i’ è l’oggetto, ‘r’ è la valutazione data dall’utente e ‘w’ è il peso.
In questo caso il nostro punteggio è uguale alla somma delle valutazioni che ogni utente ha dato a quella voce sottraendo la valutazione media di quell’utente moltiplicata per un certo peso che è di quanto questo utente è simile o si suppone che contribuisca alle previsioni degli altri utenti. Questo è il peso tra l’utente u e v. Il punteggio varia da 0 a 1 dove 0 è basso e 1 è alto. Tutto sembra perfetto, allora perché abbiamo sottratto le valutazioni medie da ogni valutazione degli utenti e perché abbiamo usato la media ponderata invece della media semplice?
Il problema è con i tipi di utenti che stiamo gestendo. Inizia con il fatto che le persone valutano spesso su scale molto diverse. Io posso essere un utente positivo e ottimista, dove valuterò il film che mi è piaciuto come 4 su 5, ma qualche altro utente che è meno ottimista o ha degli standard elevati può valutare il suo film preferito come 2 su 5. Qui il suo 2 è il mio 4. Qui il suo 2 è il mio 4. Le modifiche per renderlo migliore sono, possiamo aumentare l’efficienza di questo algoritmo se normalizziamo la valutazione dell’utente. Un modo per farlo è dire che calcoleremo s(u,i) cioè il punteggio come la valutazione media che l’utente dà ad ogni elemento più una certa deviazione e la deviazione sarà quanto questo elemento è migliore o peggiore della media.
Ho usato la similarità del coseno per calcolare il peso dato nella formula sopra. Ho anche usato la nozione di vicinato che sarà discussa in questo blog man mano che andiamo avanti.
Per normalizzare i dati nel modo di cui sopra, è necessaria una certa analisi dei dati in pandas. Potete ottenere l’intero codice alla fine. Per il blog, mi concentrerò sui concetti importanti.
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()
Ora abbiamo finito di calcolare la valutazione normalizzata per un utente. I dati di cui sopra saranno utilizzati per calcolare il punteggio finale per l’utente in seguito.
Da qui ci concentreremo ora su alcuni importanti concetti relativi ai sistemi di raccomandazione.
Similitudine coseno
Per la formula di cui sopra abbiamo bisogno di trovare gli utenti che hanno pensieri simili. Questo sembra molto interessante per trovare un utente che ha simpatie e antipatie simili. Ma la domanda è come facciamo a trovare la somiglianza?
Per rispondere a questo, useremo la Similarità Coseno e vedremo quanto sono simili gli utenti. Di solito si calcola sulle valutazioni che entrambi gli utenti hanno dato in passato.
Nel nostro esempio, ho usato la funzione cosine_similarity di sklearn per calcolare la similarità. Ma prima di questo dobbiamo eseguire un po’ di pre-elaborazione e pulire i dati.
from sklearn.metrics.pairwise import cosine_similarityfinal=pd.pivot_table(Rating_avg,values='adg_rating',index='userId',columns='movieId')
Questa contiene alcuni valori NaN poiché ogni utente non ha visto tutti i film ed è la ragione per cui questo tipo di matrice è chiamata matrice rada. Metodi come la fattorizzazione della matrice sono usati per trattare questa sparsità ma non ci concentreremo su di essa in questo blog. Il prossimo passo e uno dei passi importanti è quello di sostituire questi valori NaN.
Ci sono due metodi comunemente usati per questo:
- Utilizzare la media dell’utente sulla riga.
- Utilizzare la media del film sulla colonna.
Ho usato entrambi i metodi e potete ottenerli nel codice qui sotto. Ma per spiegare userei il metodo della media del film.
# Replacing NaN by Movie Average
final_movie = final.fillna(final.mean(axis=0))