Collaborative Filtering

The collaborative filtering problem we will be doing is one we have done before using the MSE Loss. But, what if try doing it using cross entropy loss, how will that affect the model? Lets find out!

from fastai.collab import *
from fastai.tabular.all import *

Data

Lets grab the data and view it

path = untar_data(URLs.ML_100k)

ratings = pd.read_csv(path/'u.data', delimiter='\t', header=None,
                      names=['user','movie','rating','timestamp'])

ratings.head()
user movie rating timestamp
0 196 242 3 881250949
1 186 302 3 891717742
2 22 377 1 878887116
3 244 51 2 880606923
4 166 346 1 886397596

DataSets

We need to convert our data into a DataBlock. But before that, lets try making a DataSets and forming our x,y.

dblock = DataBlock()
dsets = dblock.datasets(ratings)

x,y = dsets.train[0]
x,y
(user               272
 movie             1101
 rating               5
 timestamp    879454977
 Name: 1509, dtype: int64,
 user               272
 movie             1101
 rating               5
 timestamp    879454977
 Name: 1509, dtype: int64)
x['user'], x['movie']
(272, 1101)

This the data we want to feed

y['rating']
5

This is our label, the rating

DataBlock

Lets put it all togather and create our datablock

def get_x(rating): return tensor([rating['user']-1, rating['movie']-1]) #Must sub 1 to avoid CUDA error: device-side assert triggered
def get_y(rating): return rating['rating']-1 #Must sub 1 to avoid CUDA error: device-side assert triggered


dblock = DataBlock(get_x=get_x, 
                   get_y=get_y, 
                   splitter=RandomSplitter())

dls = dblock.dataloaders(ratings) #path

Class and methods

Lets put all the functions under a class and have it extend the Module class.

class CollabClassification(Module):
    def __init__(self, users_sz, movies_sz, n_factors = 100):
        self.user_factors = Embedding(*users_sz)
        self.movie_factors = Embedding(*movies_sz)
        
        self.layers = nn.Sequential(
            nn.Linear(users_sz[1] + movies_sz[1], n_factors),
            nn.ReLU(),
            nn.Linear(n_factors, 5) #5 output neurons
        )

    def forward(self, x):
        users = self.user_factors(x[:,0])
        movies = self.movie_factors(x[:,1])
        return self.layers(torch.cat((users, movies), dim=1))
n_users = len(ratings.user.unique())
n_movies = len(ratings.movie.unique())
users_factors = 74 #random
movies_factors = 102 #random
model = CollabClassification((n_users, users_factors), (n_movies, movies_factors)) #our model

Training

learn = Learner(dls, model, loss_func=CrossEntropyLossFlat())
learn.fit_one_cycle(5, 5e-3, wd=0.01)
epoch train_loss valid_loss time
0 1.292975 1.282268 00:15
1 1.234408 1.255242 00:14
2 1.189355 1.235191 00:14
3 1.158378 1.231530 00:14
4 1.095451 1.245237 00:14

It didn't perform that well in comparison to the MSE Loss model, but that is to be expected as this problem is not advised to be handled using cross entropy loss.