In [ ]:

    Out[ ]:

    1. 'Lawnmower Man 2: Beyond Cyberspace (1996)',
    2. 'Beautician and the Beast, The (1997)',
    3. 'Crow: City of Angels, The (1996)',
    4. 'Home Alone 3 (1997)']

    Think about what this means. What it’s saying is that for each of these movies, even when a user is very well matched to its latent factors (which, as we will see in a moment, tend to represent things like level of action, age of movie, and so forth), they still generally don’t like it. We could have simply sorted the movies directly by their average rating, but looking at the learned bias tells us something much more interesting. It tells us not just whether a movie is of a kind that people tend not to enjoy watching, but that people tend not to like watching it even if it is of a kind that they would otherwise enjoy! By the same token, here are the movies with the highest bias:

    In [ ]:

    1. idxs = movie_bias.argsort(descending=True)[:5]
    2. [dls.classes['title'][i] for i in idxs]

    Out[ ]:

    1. 'Titanic (1997)',
    2. 'Shawshank Redemption, The (1994)',
    3. 'Star Wars (1977)']

    So, for instance, even if you don’t normally enjoy detective movies, you might enjoy LA Confidential!

    It is not quite so easy to directly interpret the embedding matrices. There are just too many factors for a human to look at. But there is a technique that can pull out the most important underlying directions in such a matrix, called principal component analysis (PCA). We will not be going into this in detail in this book, because it is not particularly important for you to understand to be a deep learning practitioner, but if you are interested then we suggest you check out the fast.ai course . <> shows what our movies look like based on two of the strongest PCA components.

    In [ ]:

    We can see here that the model seems to have discovered a concept of classic versus pop culture movies, or perhaps it is critically acclaimed that is represented here.

    We defined our model from scratch to teach you what is inside, but you can directly use the fastai library to build it. We’ll look at how to do that next.

    We can create and train a collaborative filtering model using the exact structure shown earlier by using fastai’s collab_learner:

    In [ ]:

    1. learn = collab_learner(dls, n_factors=50, y_range=(0, 5.5))

    In [ ]:

    1. learn.fit_one_cycle(5, 5e-3, wd=0.1)

    The names of the layers can be seen by printing the model:

    In [ ]:

    1. learn.model

    We can use these to replicate any of the analyses we did in the previous section—for instance:

    In [ ]:

    1. movie_bias = learn.model.i_bias.weight.squeeze()
    2. idxs = movie_bias.argsort(descending=True)[:5]

    Out[ ]:

    1. ['Titanic (1997)',
    2. "Schindler's List (1993)",
    3. 'Shawshank Redemption, The (1994)',
    4. 'L.A. Confidential (1997)',
    5. 'Silence of the Lambs, The (1991)']

    Another interesting thing we can do with these learned embeddings is to look at distance.

    Embedding Distance

    On a two-dimensional map we can calculate the distance between two coordinates using the formula of Pythagoras: $\sqrt{x^{2}+y^{2}}$ (assuming that x and y are the distances between the coordinates on each axis). For a 50-dimensional embedding we can do exactly the same thing, except that we add up the squares of all 50 of the coordinate distances.

    If there were two movies that were nearly identical, then their embedding vectors would also have to be nearly identical, because the users that would like them would be nearly exactly the same. There is a more general idea here: movie similarity can be defined by the similarity of users that like those movies. And that directly means that the distance between two movies’ embedding vectors can define that similarity. We can use this to find the most similar movie to Silence of the Lambs:

    In [ ]:

    1. movie_factors = learn.model.i_weight.weight
    2. idx = dls.classes['title'].o2i['Silence of the Lambs, The (1991)']
    3. distances = nn.CosineSimilarity(dim=1)(movie_factors, movie_factors[idx][None])

    Out[ ]: