<jupyter_start><jupyter_text>**NEURAL COLLABORATIVE RECOMMENDER SYSTEM FOR MOVIES** ![Neural Nets](https://i.imgur.com/sJBTWPN.png?1!)<jupyter_code>%tensorflow_version 2.x
from __future__ import absolute_import, division, print_function, unicode_literals
print("Run on TensorFlow 2.x")<jupyter_output>Run on TensorFlow 2.x<jupyter_text>I. Initial Stage IMPORT LIBRARIES<jupyter_code># Import Dependencies
import pandas as pd
import numpy as np
from google.colab import drive
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras import initializers
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, auc

# The following lines adjust the granularity of reporting. 
pd.options.display.max_rows = 10
pd.options.display.float_format = "{:.1f}".format

# The following line improves formatting when ouputting NumPy arrays.
np.set_printoptions(linewidth = 200)

print("--" * 17)
print("ALL MODULES IMPORTED AND ACTIVATED")<jupyter_output>----------------------------------
ALL MODULES IMPORTED AND ACTIVATED<jupyter_text>INITIALIZE EAGER EXECUTION<jupyter_code># After eager execution is enabled, operations are executed as they are defined, 
# and Tensor objects hold concrete values.

tf.compat.v1.enable_eager_execution(
    config=None, device_policy=None, execution_mode=None
)
print("EAGER EXECUTION ENABLED")<jupyter_output>EAGER EXECUTION ENABLED<jupyter_text>LOADING DATA<jupyter_code># Mounting Google Drive
drive.mount('/content/gdrive')

# Changing Directory
%cd /content/gdrive/My\ Drive/
# Loading Clean Data
ratings_df = pd.read_csv('/content/gdrive/My Drive/CKME 136 Data Analytics - Capstone - Art Shablinski - 501009387/DATA/CLEAN DATA/ratings_df.csv')<jupyter_output><empty_output><jupyter_text>II. Data Pre-Processing ENCODING VECTORS<jupyter_code># Encoding user vectors
user_ids = ratings_df["userId"].unique().tolist()
user2user_encoded = {x: i for i, x in enumerate(user_ids)}
userencoded2user = {i: x for i, x in enumerate(user_ids)}

# Encoding movie vectors
movie_ids = ratings_df["movieId"].unique().tolist()
movie2movie_encoded = {x: i for i, x in enumerate(movie_ids)}
movie_encoded2movie = {i: x for i, x in enumerate(movie_ids)}

ratings_df["user"] = ratings_df["userId"].map(user2user_encoded)
ratings_df["movie"] = ratings_df["movieId"].map(movie2movie_encoded)

num_users = len(user2user_encoded)
num_movies = len(movie_encoded2movie)
ratings_df["rating"] = ratings_df["rating"].values.astype(np.float32)

# min and max values will be used to normalize the ratings later
min_rating = min(ratings_df["rating"])
max_rating = max(ratings_df["rating"])

print(
    "Total Number of Users: {}, Total Number of Rated Movies: {}, Min Rating: {}, Max Rating: {}".format(
        num_users, num_movies, min_rating, max_rating
    )
)<jupyter_output>Total Number of Users: 138493, Total Number of Rated Movies: 26744, Min Rating: 0.5, Max Rating: 5.0<jupyter_text>DATA SPLIT AND SCALING<jupyter_code># Randomize classes to improve Model generalizability
df = ratings_df.sample(frac=1, random_state=23)
x = ratings_df[["user", "movie"]].values

# Normalize the labels between 0 and 1 to prevent gradients from exploding, and
# making learning of parameters more stable
y = ratings_df["rating"].apply(lambda x: (x - min_rating) / (max_rating - min_rating)).values

# Splitting the data to train on 70% of the data and validating on 30%
train_indices = int(0.7 * df.shape[0])
x_train, x_val, y_train, y_val = (
    x[:train_indices],
    x[train_indices:],
    y[:train_indices],
    y[train_indices:],
)<jupyter_output><empty_output><jupyter_text>III. Model Architecture DEFINE AND COMPILE THE MODEL<jupyter_code>EMBEDDING_SIZE = 50

class CNRC(keras.Model):
    def __init__(self, num_users, num_movies, embedding_size, **kwargs):
        super(CNRC, self).__init__(**kwargs)
        self.num_users = num_users
        self.num_movies = num_movies
        self.embedding_size = embedding_size
        self.user_embedding = layers.Embedding(
            num_users,
            embedding_size,
            embeddings_initializer="he_normal",
            embeddings_regularizer=keras.regularizers.l2(1e-6),
        )
        self.user_bias = layers.Embedding(num_users, 1)
        self.movie_embedding = layers.Embedding(
            num_movies,
            embedding_size,
            embeddings_initializer="he_normal",
            embeddings_regularizer=keras.regularizers.l2(1e-6),
        )
        self.movie_bias = layers.Embedding(num_movies, 1)

    def call(self, inputs):
        user_vector = self.user_embedding(inputs[:, 0])
        user_bias = self.user_bias(inputs[:, 0])
        movie_vector = self.movie_embedding(inputs[:, 1])
        movie_bias = self.movie_bias(inputs[:, 1])
        dot_user_movie = tf.tensordot(user_vector, movie_vector, 2)
        # Add all the components (including bias)
        x = dot_user_movie + user_bias + movie_bias
        # The sigmoid activation forces the rating to between 0 and 1
        return tf.nn.sigmoid(x)

model = CNRC(num_users, num_movies, EMBEDDING_SIZE)
model.compile(
    loss=tf.keras.losses.MeanSquaredError(), optimizer = keras.optimizers.Adam(lr=0.003), metrics=["Precision", "Recall"]
)<jupyter_output><empty_output><jupyter_text>TRAINING THE MODEL<jupyter_code># Destroys the current TF graph and session, and creates a new one.
tf.keras.backend.clear_session()

# Training our Neural Network
history = model.fit(
    x=x_train,
    y=y_train,
    batch_size=1000,
    epochs=5,
    verbose=1,
    validation_data=(x_val, y_val),
)<jupyter_output>Epoch 1/5
14001/14001 [==============================] - 961s 69ms/step - loss: 0.2640 - precision: 0.9880 - recall: 0.9439 - val_loss: 0.2102 - val_precision: 0.9930 - val_recall: 0.7870
Epoch 2/5
14001/14001 [==============================] - 970s 69ms/step - loss: 0.2755 - precision: 0.9879 - recall: 0.9848 - val_loss: 0.1799 - val_precision: 0.9931 - val_recall: 0.7798
Epoch 3/5
14001/14001 [==============================] - 968s 69ms/step - loss: 0.2447 - precision: 0.9879 - recall: 0.9889 - val_loss: 0.1719 - val_precision: 0.9931 - val_recall: 0.7816
Epoch 4/5
14001/14001 [==============================] - 923s 66ms/step - loss: 0.2506 - precision: 0.9879 - recall: 0.9850 - val_loss: 0.1908 - val_precision: 0.9930 - val_recall: 0.7903
Epoch 5/5
14001/14001 [==============================] - 981s 70ms/step - loss: 0.2432 - precision: 0.9879 - recall: 0.9857 - val_loss: 0.1935 - val_precision: 0.9930 - val_recall: 0.7908<jupyter_text>SAVING AND SUMMARIZING THE MODEL PARAMETERS<jupyter_code>model.save('CNRS')
new_model = keras.models.load_model('CNRS')
new_model.summary()<jupyter_output>INFO:tensorflow:Assets written to: CNRS/assets
Model: "cnrc_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_4 (Embedding)      multiple                  6924650   
_________________________________________________________________
embedding_5 (Embedding)      multiple                  138493    
_________________________________________________________________
embedding_6 (Embedding)      multiple                  1337200   
_________________________________________________________________
embedding_7 (Embedding)      multiple                  26744     
=================================================================
Total params: 8,427,087
Trainable params: 8,427,087
Non-trainable params: 0
_________________________________________________________________<jupyter_text>IV. Evaluation Metrics DEFINE A PLOTTING FUNCTION<jupyter_code>def plot_curve(epochs, hist, list_of_metrics):
  plt.figure()
  plt.xlabel("Epoch")
  plt.ylabel("Value")

  for m in list_of_metrics:
    x = hist[m]
    plt.plot(epochs[1:], x[1:], label=m)

  plt.legend()

print("Loaded the plot_curve function.")<jupyter_output>Loaded the plot_curve function.<jupyter_text>TRAIN VS TEST PERFORMANCE<jupyter_code>plt.plot(history.history["loss"])
plt.plot(history.history["val_loss"])
plt.title("model loss")
plt.ylabel("loss")
plt.xlabel("epoch")
plt.legend(["train", "test"], loc="upper right")
plt.show()<jupyter_output><empty_output><jupyter_text>TOTAL LOSS, PRECISION AND RECALL VISUALIZED<jupyter_code>pd.DataFrame(history.history).plot(figsize=(8,5))
plt.grid(True)
plt.gca().set_ylim(0,1)
plt.show()<jupyter_output><empty_output><jupyter_text>TOP 10 RECOMMENDATION GENERATOR<jupyter_code>movies_df = pd.read_csv('/content/gdrive/My Drive/CKME 136 Data Analytics - Capstone - <NAME> - 501009387/DATA/CLEAN DATA/movies_df.csv')

user_id = df.userId.sample(1).iloc[0]
movies_watched_by_user = df[df.userId == user_id]
movies_not_watched = movies_df[
    ~movies_df["movieId"].isin(movies_watched_by_user.movieId.values)
]["movieId"]
movies_not_watched = list(
    set(movies_not_watched).intersection(set(movie2movie_encoded.keys()))
)
movies_not_watched = [[movie2movie_encoded.get(x)] for x in movies_not_watched]
user_encoder = user2user_encoded.get(user_id)
user_movie_array = np.hstack(
    ([[user_encoder]] * len(movies_not_watched), movies_not_watched)
)
ratings = model.predict(user_movie_array).flatten()
top_ratings_indices = ratings.argsort()[-10:][::-1]
recommended_movie_ids = [
    movie_encoded2movie.get(movies_not_watched[x][0]) for x in top_ratings_indices
]

print("Showing recommendations for user: {}".format(user_id))
print("====" * 9)
print("Movies with high ratings from user")
print("----" * 8)
top_movies_user = (
    movies_watched_by_user.sort_values(by="rating", ascending=False)
    .head(5)
    .movieId.values
)
movies_df_rows = movies_df[movies_df["movieId"].isin(top_movies_user)]
for row in movies_df_rows.itertuples():
    print(row.title, ":", int(row.year), ":", row.genres)

print("----" * 8)
print("Top 10 movie recommendations")
print("----" * 8)
recommended_movies = movies_df[movies_df["movieId"].isin(recommended_movie_ids)]
for row in recommended_movies.itertuples():
    print(row.title, ":", int(row.year), ":", row.genres)<jupyter_output>Showing recommendations for user: 121328
====================================
Movies with high ratings from user
--------------------------------
Pulp Fiction : 1994 : Comedy|Crime|Drama|Thriller
Devil's Advocate, The : 1997 : Drama|Mystery|Thriller
Road Trip : 2000 : Comedy
Me, Myself & Irene : 2000 : Adventure|Comedy
Kill Bill: Vol. 1 : 2003 : Action|Crime|Thriller
--------------------------------
Top 10 movie recommendations
--------------------------------
Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb : 1964 : Comedy|War
Rear Window : 1954 : Mystery|Thriller
North by Northwest : 1959 : Action|Adventure|Mystery|Romance|Thriller
Casablanca : 1942 : Drama|Romance
Princess Bride, The : 1987 : Action|Adventure|Comedy|Fantasy|Romance
Apocalypse Now : 1979 : Action|Drama|War
Sting, The : 1973 : Comedy|Crime
Amelie (Fabuleux destin d'<NAME>, Le) : 2001 : Comedy|Romance
Eternal Sunshine of the Spotless Mind : 2004 : Drama|Romance|Sci-Fi
Dark Knight, The : 2008[...]
{'fraction_non_alphanumeric': 0.15006723442402511, 'fraction_numerical': 0.04258180188256387, 'mean_word_length': 4.7005620848237095, 'pattern_counts': {'":': 4, '<': 47, '<?xml version=': 0, '>': 47, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '72314', 'license': 'MIT', 'n_tokens_mistral': 4153, 'n_tokens_neox': 3684, 'n_words': 821, 'path': 'Collaborative_Neural_Rec_Sys.ipynb', 'repo_name': 'ashablinski/Capstone', 'size': '11171'}