<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Exercise (embeddings)
In this exercise you'll answer some questions about embeddings and experiment with a couple variations on the movie rating prediction model we trained in the tutorial.
Run the cell below to do some setup. (It will take a minute or two to run, since we're training a couple models for use later in the exercise. While it's running, you can jump ahead and start on part 1.)
Aside
Step1:  Part 1
Step2:  Part 2
Step3:  At the start of the notebook we also trained a model with 64-dimensional movie and user embeddings. How do you expect the results to compare? Make a prediction, then run the cell below to see.
Step4:  If you're feeling experimental, feel free to try some other configurations. So far we've varied movie and user embedding size in lock step, but there's no reason they have to be the same. Do you have an intuition about whether one or the other should be bigger?
Step5:  When you're ready, uncomment the cell below for some explanation of what's going on.
Step6:  Part 3
Step7:  Do you have an idea about what the bias values will look like? Are there certain movies you expect will have high or low biases?
Step8:  Coding up biases
Below is the code we used to train our embedding model in the tutorial. Modify it (where indicated by the comments) to add per-movie biases.
Step9:  No idea where to start? Don't panic! Uncomment and run the line below for some hints.
Step10:  Training
Run the code below to train the model you built in the previous step. (If you get an error when trying to fit your model, you may have made a mistake in your bias implementation. See part3.c.solution() for a working implementation.)
Step11:  How does it compare to the results we got from the model without biases? Run the code cell below to compare their loss over the course of training.
Step12:  How did adding biases affect our results?
Averaged over many runs, biases seem to help a bit, but there's enough variance between runs (as a result of different random initializations), that you might be seeing better or worse results. If you're seeing a big difference (more than, say, +-.02 in the final loss) in either direction, something may have gone wrong.
So biases weren't the huge win we might have hoped for, but it still seems worth testing our hypothesis about how bias values will be distributed among movies.
Inspecting learned bias values
Let's take a look at the bias values our model has learned. Fill in the missing code in the cell below to load an array of bias values - b should be an array with one number for each movie in our training set.
Hint
Step13:  Once you've successfully set the value of bias_layer, run the cell below which loads a dataframe containing movie metadata and adds the biases found in the previous step as a column.
Step14:  Which movies have the lowest and highest learned biases? Run the cell below to find out.
Step15:  Run the cell below to generate a scatter plot of movies' average ratings against the biases learned for those movies.
Step16:  Considering this plot and the list of our highest and lowest bias movies, do our model's learned biases agree with what you expected?
<ASSISTANT_TASK:>
Python Code:
# Setup code. Make sure you run this first!
import os
import random
import math
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import tensorflow as tf
from tensorflow import keras
from learntools.core import binder; binder.bind(globals())
from learntools.embeddings.ex1_embedding_layers import *
#_RM_
input_dir = '../input/movielens_preprocessed'
#_UNCOMMENT_
#input_dir = '../input'
# Load a 10% subset of the full MovieLens data.
df = pd.read_csv(os.path.join(input_dir, 'mini_rating.csv'))
# Some hyperparameters. (You might want to play with these later)
LR = .005 # Learning rate
EPOCHS = 8 # Default number of training epochs (i.e. cycles through the training data)
hidden_units = (32,4) # Size of our hidden layers
def build_and_train_model(movie_embedding_size=8, user_embedding_size=8, verbose=2, epochs=EPOCHS):
    tf.set_random_seed(1); np.random.seed(1); random.seed(1) # Set seeds for reproducibility
    user_id_input = keras.Input(shape=(1,), name='user_id')
    movie_id_input = keras.Input(shape=(1,), name='movie_id')
    user_embedded = keras.layers.Embedding(df.userId.max()+1, user_embedding_size, 
                                           input_length=1, name='user_embedding')(user_id_input)
    movie_embedded = keras.layers.Embedding(df.movieId.max()+1, movie_embedding_size, 
                                            input_length=1, name='movie_embedding')(movie_id_input)
    concatenated = keras.layers.Concatenate()([user_embedded, movie_embedded])
    out = keras.layers.Flatten()(concatenated)
    # Add one or more hidden layers
    for n_hidden in hidden_units:
        out = keras.layers.Dense(n_hidden, activation='relu')(out)
    # A single output: our predicted rating
    out = keras.layers.Dense(1, activation='linear', name='prediction')(out)
    model = keras.Model(
        inputs = [user_id_input, movie_id_input],
        outputs = out,
    )
    model.compile(
        tf.train.AdamOptimizer(LR),
        loss='MSE',
        metrics=['MAE'],
    )
    history = model.fit(
        [df.userId, df.movieId],
        df.y,
        batch_size=5 * 10**3,
        epochs=epochs,
        verbose=verbose,
        validation_split=.05,
    )
    return history
# Train two models with different embedding sizes and save the training statistics.
# We'll be using this later in the exercise.
history_8 = build_and_train_model(verbose=0)
history_32 = build_and_train_model(32, 32, verbose=0)
print("Setup complete!")
Explanation: Exercise (embeddings)
In this exercise you'll answer some questions about embeddings and experiment with a couple variations on the movie rating prediction model we trained in the tutorial.
Run the cell below to do some setup. (It will take a minute or two to run, since we're training a couple models for use later in the exercise. While it's running, you can jump ahead and start on part 1.)
Aside: Training the model in the tutorial took around a minute per epoch. To make training time more manageable, we'll be working with a sample of 10% of the total dataset (or around 2 million ratings). We've also limited our sample to relatively popular movies (movies with more than 1k ratings in the full dataset). For these reasons, our results in this exercise won't be directly comparable to the error rates reported in the tutorial.
End of explanation
# embedding_variables should contain all the variables you would use an embedding layer for
# For your convenience, we've initialized it with all variables in the dataset, so you can 
# just delete or comment out the variables you want to exclude.
embedding_variables = {
    'stream_id',
    'user_id',
    'song_id',
    'timestamp',
    'artist_id',
    'song_duration',
    'explicit',
    'user_country',
}
part1.check()
#%%RM_IF(PROD)%%
# Incorrect
embedding_variables = {
    'stream_id',
}
part1.check()
#%%RM_IF(PROD)%%
# Correct 1
embedding_variables = {
    'user_id',
    'song_id',
    'artist_id',
}
part1.check()
#%%RM_IF(PROD)%%
# Correct 2 (optional extra val)
embedding_variables = {
    'user_id',
    'song_id',
    'artist_id',
    'user_country',
}
part1.check()
#_COMMENT_IF(PROD)_
part1.solution()
Explanation: Part 1: When to use embeddings?
You're a data scientist for the hot new music streaming service, Tidify All Access™ with the task of building a machine learning model to generate suggested songs to show users. You have lots of historical data about songs that users have listened to in the past. The dataset of 500 million streams includes the following variables:
| Variable name | Description                                       | Number of unique values | Example values
|---------------|---------------------------------------------------|------|----------------------------------
| stream_id     | Unique id for this streaming event (i.e. per row) | 500m | 480744481269, 228441807745, 182969356277, ...
| user_id       | Unique id for this user                           | 5m   | 3592022173, 2596402742, 3506743568, ...
| song_id       | Unique id for this song                           | 10m  | 3150630225,  590655137, 3617674674, ...
| timestamp     | When did the song start playing?                  | 80m  | 10/08/2017 01:20:44 PM, 04/22/2017 01:58:59 PM, ...
| artist_id     | Unique id for the recording artist of this song   | 1m   | 122168143,  52958907, 803608525, ...
| song_duration | How long is the song (in seconds)?                | 450  | 257, 155, 212...
| explicit      | Is this song flagged as having adult language?    | 2    | True, False
| user_country  | Where is this user located? (3-letter ISO country code) | 150  | CAN, KOR, CHE...
If you wanted to train a neural net on streaming data, which of the variables above would you include and use use an embedding layer for? Use the variable embedding_variables below to give your answer.
(You can assume that, if necessary, string variables can be converted to unique numerical identifiers as a preprocessing step.)
End of explanation
history_FS = (15, 5)
def plot_history(histories, keys=('mean_absolute_error',), train=True, figsize=history_FS):
    if isinstance(histories, tf.keras.callbacks.History):
        histories = [ ('', histories) ]
    for key in keys:
        plt.figure(figsize=history_FS)
        for name, history in histories:
            val = plt.plot(history.epoch, history.history['val_'+key],
                           '--', label=str(name).title()+' Val')
            if train:
                plt.plot(history.epoch, history.history[key], color=val[0].get_color(), alpha=.5,
                         label=str(name).title()+' Train')
        plt.xlabel('Epochs')
        plt.ylabel(key.replace('_',' ').title())
        plt.legend()
        plt.title(key)
        plt.xlim([0,max(max(history.epoch) for (_, history) in histories)])
plot_history([ 
    ('base model', history_8),
])
Explanation: Part 2: Choosing embedding sizes
In the tutorial, we (somewhat arbitrarily) set output_dim=8 when creating our movie and user embedding layers. Run the code cell below to see a visualization of our training and validation error over 8 epochs of training, again using 8-dimensional embeddings.
End of explanation
plot_history([ 
    ('8-d embeddings', history_8),
    ('32-d embeddings', history_32),
])
Explanation: At the start of the notebook we also trained a model with 64-dimensional movie and user embeddings. How do you expect the results to compare? Make a prediction, then run the cell below to see.
End of explanation
# Example: shrinking movie embeddings and growing user embeddings
#history_biguser_smallmovie = build_and_train_model(movie_embedding_size=4, user_embedding_size=16)
#%%RM_IF(PROD)%%
history_biguser_smallmovie = build_and_train_model(movie_embedding_size=4, user_embedding_size=16)
#%%RM_IF(PROD)%%
plot_history([ 
    ('8-d embeddings', history_8),
    ('32-d embeddings', history_32),
    ('m4u16', history_biguser_smallmovie),
])
Explanation: If you're feeling experimental, feel free to try some other configurations. So far we've varied movie and user embedding size in lock step, but there's no reason they have to be the same. Do you have an intuition about whether one or the other should be bigger?
End of explanation
#_COMMENT_IF(PROD)_
part2.solution()
Explanation: When you're ready, uncomment the cell below for some explanation of what's going on.
End of explanation
#_COMMENT_IF(PROD)_
part3.a.solution()
Explanation: Part 3: Adding biases
In this final section, you'll implement a modification to our model's architecture: per-movie biases.
In ML-speak, a bias is just a number that gets added to a node's output value. For each movie, we'll learn a single number that we'll add to the output of what was previously our final node. Here's what that looks like:
<img src="https://docs.google.com/a/google.com/drawings/d/e/2PACX-1vSmf5H7Rcr771flhidl7Wf31OXiZTUgNH2qzoVc-2dtH6Cf9XmSF3xQcY7m1RwCRBu2_lE-dH5Vb6ny/pub?w=1050&h=594" />
Why?
Do you think this will improve the accuracy of our predictions? Why or why not?
Think about it, then uncomment the line below to see an explanation.
End of explanation
#_COMMENT_IF(PROD)_
part3.b.solution()
Explanation: Do you have an idea about what the bias values will look like? Are there certain movies you expect will have high or low biases?
End of explanation
user_embedding_size = movie_embedding_size = 8
user_id_input = keras.Input(shape=(1,), name='user_id')
movie_id_input = keras.Input(shape=(1,), name='movie_id')
user_embedded = keras.layers.Embedding(df.userId.max()+1, user_embedding_size, 
                                       input_length=1, name='user_embedding')(user_id_input)
movie_embedded = keras.layers.Embedding(df.movieId.max()+1, movie_embedding_size, 
                                        input_length=1, name='movie_embedding')(movie_id_input)
concatenated = keras.layers.Concatenate()([user_embedded, movie_embedded])
out = keras.layers.Flatten()(concatenated)
# Add one or more hidden layers
for n_hidden in hidden_units:
    out = keras.layers.Dense(n_hidden, activation='relu')(out)
# A single output: our predicted rating (before adding bias)
out = keras.layers.Dense(1, activation='linear', name='prediction')(out)
################################################################################
############################# YOUR CODE GOES HERE! #############################
# TODO: you need to create the variable movie_bias. Its value should be the output of calling a layer.
# I recommend giving the layer that holds your biases a distinctive name (this will help in an upcoming question)
#movie_bias = 
################################################################################
out = keras.layers.Add()([out, movie_bias])
model_bias = keras.Model(
    inputs = [user_id_input, movie_id_input],
    outputs = out,
)
model_bias.compile(
    tf.train.AdamOptimizer(LR),
    loss='MSE',
    metrics=['MAE'],
)
model_bias.summary()
Explanation: Coding up biases
Below is the code we used to train our embedding model in the tutorial. Modify it (where indicated by the comments) to add per-movie biases.
End of explanation
#_COMMENT_IF(PROD)_
part3.c.hint()
#_COMMENT_IF(PROD)_
part3.c.solution()
#%%RM_IF(PROD)%%
# Bad solution (no bias adding)
user_id_input = keras.Input(shape=(1,), name='user_id')
movie_id_input = keras.Input(shape=(1,), name='movie_id')
user_embedded = keras.layers.Embedding(df.userId.max()+1, user_embedding_size, 
                                       input_length=1, name='user_embedding')(user_id_input)
movie_embedded = keras.layers.Embedding(df.movieId.max()+1, movie_embedding_size, 
                                        input_length=1, name='movie_embedding')(movie_id_input)
concatenated = keras.layers.Concatenate()([user_embedded, movie_embedded])
out = keras.layers.Flatten()(concatenated)
# Add one or more hidden layers
for n_hidden in hidden_units:
    out = keras.layers.Dense(n_hidden, activation='relu')(out)
# A single output: our predicted rating
out = keras.layers.Dense(1, activation='linear', name='prediction')(out)
bias_embedded = keras.layers.Embedding(df.movieId.max()+1, 1, input_length=1, name='bias',
                                      )(movie_id_input)
movie_bias = keras.layers.Flatten()(bias_embedded)
#out = keras.layers.Add()([out, movie_bias])
model_bias = keras.Model(
    inputs = [user_id_input, movie_id_input],
    outputs = out,
)
model_bias.compile(
    tf.train.AdamOptimizer(LR),
    loss='MSE',
    metrics=['MAE'],
)
part3.c.check()
#%%RM_IF(PROD)%%
# Canonical correct solution
user_embedding_size = movie_embedding_size = 8
user_id_input = keras.Input(shape=(1,), name='user_id')
movie_id_input = keras.Input(shape=(1,), name='movie_id')
user_embedded = keras.layers.Embedding(df.userId.max()+1, user_embedding_size, 
                                       input_length=1, name='user_embedding')(user_id_input)
movie_embedded = keras.layers.Embedding(df.movieId.max()+1, movie_embedding_size, 
                                        input_length=1, name='movie_embedding')(movie_id_input)
concatenated = keras.layers.Concatenate()([user_embedded, movie_embedded])
out = keras.layers.Flatten()(concatenated)
# Add one or more hidden layers
for n_hidden in hidden_units:
    out = keras.layers.Dense(n_hidden, activation='relu')(out)
# A single output: our predicted rating (before adding bias)
out = keras.layers.Dense(1, activation='linear', name='prediction')(out)
################################################################################
############################# YOUR CODE GOES HERE! #############################
# TODO: you need to create the variable movie_bias. Its value should be the output of calling a layer.
# I recommend giving the layer that holds your biases a distinctive name (this will help in an upcoming question)
bias_embedded = keras.layers.Embedding(df.movieId.max()+1, 1, input_length=1, name='bias',
                                      )(movie_id_input)
movie_bias = keras.layers.Flatten()(bias_embedded)
################################################################################
out = keras.layers.Add()([out, movie_bias])
model_bias = keras.Model(
    inputs = [user_id_input, movie_id_input],
    outputs = out,
)
model_bias.compile(
    tf.train.AdamOptimizer(LR),
    loss='MSE',
    metrics=['MAE'],
)
part3.c.check()
Explanation: No idea where to start? Don't panic! Uncomment and run the line below for some hints.
End of explanation
history_bias = model_bias.fit(
    [df.userId, df.movieId],
    df.y,
    batch_size=5 * 10**3,
    epochs=EPOCHS,
    verbose=2,
    validation_split=.05,
);
Explanation: Training
Run the code below to train the model you built in the previous step. (If you get an error when trying to fit your model, you may have made a mistake in your bias implementation. See part3.c.solution() for a working implementation.)
End of explanation
plot_history([ 
    ('no_bias', history_8),
    ('bias', history_bias),
]);
Explanation: How does it compare to the results we got from the model without biases? Run the code cell below to compare their loss over the course of training.
End of explanation
bias_layer = None
part3.d.check()
(b,) = bias_layer.get_weights()
print("Loaded biases with shape {}".format(b.shape))
#_COMMENT_IF(PROD)_
part3.d.solution()
#%%RM_IF(PROD)%%
# Bad solution (wrong layer)
bias_layer = model_bias.get_layer(index=10)
part3.d.check()
(b,) = bias_layer.get_weights()
print("Loaded biases with shape {}".format(b.shape))
#%%RM_IF(PROD)%%
# Canonical correct solution
bias_layer = model_bias.get_layer('bias')
part3.d.check()
(b,) = bias_layer.get_weights()
print("Loaded biases with shape {}".format(b.shape))
Explanation: How did adding biases affect our results?
Averaged over many runs, biases seem to help a bit, but there's enough variance between runs (as a result of different random initializations), that you might be seeing better or worse results. If you're seeing a big difference (more than, say, +-.02 in the final loss) in either direction, something may have gone wrong.
So biases weren't the huge win we might have hoped for, but it still seems worth testing our hypothesis about how bias values will be distributed among movies.
Inspecting learned bias values
Let's take a look at the bias values our model has learned. Fill in the missing code in the cell below to load an array of bias values - b should be an array with one number for each movie in our training set.
Hint: you may want to check out the docs for keras.Model.get_layer. This will be easier if you gave your Embedding bias layer a distinctive name in part 2. If you didn't, it may help to look at the output of model_bias.summary().
End of explanation
movies = pd.read_csv(os.path.join(input_dir, 'movie.csv'), index_col=0, 
                     usecols=['movieId', 'title', 'genres', 'year'])
ntrain = math.floor(len(df) * .95)
df_train = df.head(ntrain)
# Mapping from original movie ids to canonical ones
mids = movieId_to_canon = df.groupby('movieId')['movieId_orig'].first()
# Add bias column
movies.loc[mids.values, 'bias'] = b
# Add columns for number of ratings and average rating
g = df_train.groupby('movieId_orig')
movies.loc[mids.values, 'n_ratings'] = g.size()
movies.loc[mids.values, 'mean_rating'] = g['rating'].mean()
movies.dropna(inplace=True)
movies.head()
Explanation: Once you've successfully set the value of bias_layer, run the cell below which loads a dataframe containing movie metadata and adds the biases found in the previous step as a column.
End of explanation
from IPython.display import display
n = 10
display(
    "Largest biases...",
    movies.sort_values(by='bias', ascending=False).head(n),
    "Smallest biases...",
    movies.sort_values(by='bias').head(n),
)
Explanation: Which movies have the lowest and highest learned biases? Run the cell below to find out.
End of explanation
n = 1000
mini = movies.sample(n, random_state=1)
fig, ax = plt.subplots(figsize=(13, 7))
ax.scatter(mini['mean_rating'], mini['bias'], alpha=.4)
ax.set_xlabel('Mean rating')
ax.set_ylabel('Bias');
#%%RM_IF(PROD)%%
# Experiment: Draw line of expected fit
n = 1000
mini = movies.sample(n, random_state=1)
fig, ax = plt.subplots(figsize=(13, 7))
ax.scatter(mini['mean_rating'], mini['bias'], alpha=.4)
row = df.iloc[0]
global_mean_rating = row.rating - row.y
lw = 1
ax.axhline(0, ls='--', color='grey', lw=lw)
ax.axvline(global_mean_rating, ls='--', label='Overall mean rating', color='indigo', lw=lw)
# Draw line of expected fit (if biases were to match data means)
pt = (global_mean_rating, 0)
x0, y0 = 0.5, (0.5 - global_mean_rating)
x1, y1 = 5, (5 - global_mean_rating)
ylim0 = ax.get_ylim()
ax.plot([x0, x1], [y0, y1], linestyle='-', lw=3, color='lime', alpha=.5)
ax.set_ylim(ylim0)
ax.set_xlabel('Mean rating')
ax.set_ylabel('Bias');
Explanation: Run the cell below to generate a scatter plot of movies' average ratings against the biases learned for those movies.
End of explanation
#%%RM%%
# (Discussion in solution text doesn't really make sense anymore now that we're thresholding obscure movies
# for this exercise.)
#_COMMENT_IF(PROD)_
part3.e.solution()
Explanation: Considering this plot and the list of our highest and lowest bias movies, do our model's learned biases agree with what you expected?
End of explanation
<END_TASK>