Architecture Note
The networks don’t need to be identical - if user content was substantially larger than movie content, you might increase the complexity of the user network. In this case, content is similar so networks are the same.
Objective: Implement content-based filtering using a neural network to build a recommender system for movies using rich user and movie features.
Content-based filtering generates user and movie feature vectors but recognizes there may be other information available about the user and/or movie that may improve the prediction. The additional information is provided to a neural network which then generates the user and movie vector.
The training set consists of all ratings made by users, with some ratings repeated to boost underrepresented genres. Split into:
You need to create two sequential neural networks with identical architectures:
# User Networkuser_NN = tf.keras.models.Sequential([ ### START CODE HERE ### tf.keras.layers.Dense(256, activation='relu'), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(num_outputs) # num_outputs = 32 ### END CODE HERE ###])
# Item Networkitem_NN = tf.keras.models.Sequential([ ### START CODE HERE ### tf.keras.layers.Dense(256, activation='relu'), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(num_outputs) # num_outputs = 32 ### END CODE HERE ###])
num_outputs
units and linear/no activationArchitecture Note
The networks don’t need to be identical - if user content was substantially larger than movie content, you might increase the complexity of the user network. In this case, content is similar so networks are the same.
The lab uses scikit-learn scalers:
# Scale input featuresscalerItem = StandardScaler()item_train = scalerItem.transform(item_train)
scalerUser = StandardScaler()user_train = scalerUser.transform(user_train)
# Scale target ratings to [-1, 1]scalerTarget = MinMaxScaler((-1, 1))y_train = scalerTarget.transform(y_train.reshape(-1, 1))
# Split data (80/20)item_train, item_test = train_test_split(item_train, train_size=0.80, shuffle=True, random_state=1)user_train, user_test = train_test_split(user_train, train_size=0.80, shuffle=True, random_state=1)y_train, y_test = train_test_split(y_train, train_size=0.80, shuffle=True, random_state=1)
The complete model uses Keras functional API:
# User input processinginput_user = tf.keras.layers.Input(shape=(num_user_features))vu = user_NN(input_user)vu = tf.linalg.l2_normalize(vu, axis=1) # Normalize to unit length
# Item input processinginput_item = tf.keras.layers.Input(shape=(num_item_features))vm = item_NN(input_item)vm = tf.linalg.l2_normalize(vm, axis=1) # Normalize to unit length
# Compute dot productoutput = tf.keras.layers.Dot(axes=1)([vu, vm])
# Create complete modelmodel = tf.keras.Model([input_user, input_item], output)
# Configure and train modelcost_fn = tf.keras.losses.MeanSquaredError()opt = keras.optimizers.Adam(learning_rate=0.01)model.compile(optimizer=opt, loss=cost_fn)
# Train for 30 epochsmodel.fit([user_train[:, u_s:], item_train[:, i_s:]], y_train, epochs=30)
# Create new user profilenew_user_id = 5000new_rating_ave = 0.0new_action = 0.0new_adventure = 5.0 # Likes adventure moviesnew_animation = 0.0# ... other genre preferencesnew_fantasy = 5.0 # Likes fantasy movies
user_vec = np.array([[new_user_id, new_rating_count, new_rating_ave, new_action, new_adventure, new_animation, # ... all genre preferences]])
# Generate predictions for all moviesuser_vecs = gen_user_vecs(user_vec, len(item_vecs))suser_vecs = scalerUser.transform(user_vecs)sitem_vecs = scalerItem.transform(item_vecs)
y_p = model.predict([suser_vecs[:, u_s:], sitem_vecs[:, i_s:]])y_pu = scalerTarget.inverse_transform(y_p) # Unscale predictions
def sq_dist(a, b): """ Returns the squared distance between two vectors Args: a (ndarray (n,)): vector with n features b (ndarray (n,)): vector with n features Returns: d (float): distance """ ### START CODE HERE ### d = np.sum(np.square(a - b)) ### END CODE HERE ### return d
# Create model to extract movie vectorsinput_item_m = tf.keras.layers.Input(shape=(num_item_features))vm_m = item_NN(input_item_m)vm_m = tf.linalg.l2_normalize(vm_m, axis=1)model_m = tf.keras.Model(input_item_m, vm_m)
# Generate feature vectors for all moviesscaled_item_vecs = scalerItem.transform(item_vecs)vms = model_m.predict(scaled_item_vecs[:,i_s:])
# Compute all pairwise distancescount = 50dim = len(vms)dist = np.zeros((dim,dim))
for i in range(dim): for j in range(dim): dist[i,j] = sq_dist(vms[i, :], vms[j, :])
# Mask diagonal to avoid self-similaritym_dist = ma.masked_array(dist, mask=np.identity(dist.shape[0]))
What You Must Implement
After completing this assignment: