<jupyter_start><jupyter_text>Auto-Encoders for Movie Recommendation SystemDuring this project, we will use the [dataset](https://grouplens.org/datasets/movielens/latest/) of 600 users and 9000 movies with 100000 ratings from Grouplens for a movie recommendation system. The designed stacked Auto-Encoder will determine the ratings of user.   Preparing the data<jupyter_code># Importing the libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
if torch.cuda.is_available():
    torch.set_default_tensor_type('torch.cuda.FloatTensor') # To use GPU on pytorch
    device = 'cuda'
else:
    device = 'cpu'
import torch.nn as nn
import torch.optim as optim
from torch.autograd import Variable
# Download the files
!wget -q "https://files.grouplens.org/datasets/movielens/ml-latest-small.zip"
!unzip ml-latest-small.zip
!del ml-latest-small.zip
ratings = pd.read_csv('ml-latest-small/ratings.csv').drop(columns = ['timestamp'])
ratings.head(10)<jupyter_output><empty_output><jupyter_text>Reduce memory usage of the dataBelow given approach will reduce ratings' data memory usage from 2.3 MB to 886.4 KB<jupyter_code>ratings.info(memory_usage = 'deep')
ratings.describe().iloc[[3, 7], :]
ratings[['userId', 'movieId']] = ratings[['userId', 'movieId']].astype('int32')
ratings[['rating']] = ratings[['rating']].astype('int8')
ratings.info(memory_usage = 'deep')
# Split the ratings data into training and test datasets
df = ratings.copy(deep = True)
training_set = df.iloc[:int(ratings.shape[0]*0.8), :]
test_set = df.iloc[int(ratings.shape[0]*0.8):, :]
print('Shape of Training set: {} and of Test set: {}'.format(training_set.shape, test_set.shape))
# Getting the number of users and movies
nb_users = max(df['userId'])
nb_movies = max(df['movieId'])
print('Max value for user id: {} and for movie id: {}'.format(nb_users, nb_movies))
# Converting the data into a tensor with userId in rows and movieId in columns
def transform(data: pd.DataFrame) -> torch.tensor:
    new_data = torch.zeros([nb_users, nb_movies], device = device, dtype = torch.float32)
    for user_id in range(1, nb_users + 1):
        id_movies = data.iloc[:, 1][data.iloc[:, 0] == user_id]
        id_ratings = data.iloc[:, 2][data.iloc[:, 0] == user_id]
        user_ratings = np.zeros(nb_movies)
        user_ratings[id_movies - 1] = id_ratings
        new_data[user_id - 1] = new_data[user_id - 1].add(torch.from_numpy(user_ratings).to(device))
    return new_data

training_set = transform(training_set)
test_set = transform(test_set)<jupyter_output><empty_output><jupyter_text>Building the Auto-Encoder<jupyter_code># Stacked Auto-Encoder architecture
class SAE(nn.Module):
    def __init__(self):
        super(SAE, self).__init__()
        self.fc1 = nn.Linear(nb_movies, 20) # 1st Full connection layer [Encoding]
        self.fc2 = nn.Linear(20, 10) # 2nd Full connection layer [Encoding]
        self.fc3 = nn.Linear(10, 20) # 3rd Full connection layer [Decoding]
        self.fc4 = nn.Linear(20, nb_movies) # 4rd Full connection layer [Output]
        self.activation = nn.Sigmoid()
    
    def forward(self, x):
        x = self.activation(self.fc1(x))
        x = self.activation(self.fc2(x))
        x = self.activation(self.fc3(x))
        x = self.fc4(x) 
        return x

model = SAE()
criterion = nn.MSELoss()
optimizer = optim.RMSprop(model.parameters(), lr = 0.01, weight_decay = 0.5)
# Training the SAE
nb_epoch, history = 300, {}
for epoch in range(1, nb_epoch + 1):
    train_loss, counter = 0, 0.0 # Keep the count of users rated the movies
    for id_user in range(nb_users):
        input = Variable(training_set[id_user]).unsqueeze(0) # a Batch instead of an 1D array
        target = input.clone() # Input and Target are same
        if torch.sum(target.data > 0) > 0: # Consider users at least once rated
            output = model(input)
            target.require_grad = False
            output[target == 0] = 0 # Exclude non-rated movies from the output
            loss = criterion(output, target)
            mean_corrector = nb_movies/float(torch.sum(target.data > 0) + 1e-10)
            loss.backward()
            train_loss = train_loss + np.sqrt(loss.item() * mean_corrector)
            counter = counter + 1.0
            optimizer.step()
    
    history[epoch] = train_loss/counter

plt.figure()
plt.plot(history.keys(), history.values())
plt.xlabel('Epochs')
plt.ylabel('Train loss')
plt.show()
# Testing the SAE
test_loss, counter = 0, 0.0
for id_user in range(nb_users):
    input = Variable(training_set[id_user]).unsqueeze(0)
    target = Variable(test_set[id_user]).unsqueeze(0)
    if torch.sum(target.data > 0) > 0:
        output = model(input)
        target.require_grad = False
        output[target == 0] = 0
        loss = criterion(output, target)
        mean_corrector = nb_movies/float(torch.sum(target.data > 0) + 1e-10)
        test_loss = test_loss + np.sqrt(loss.item() * mean_corrector)
        counter = counter + 1.0
print("Test loss: {:.3f}".format(test_loss/counter))<jupyter_output>Test loss: 1.376
{'fraction_non_alphanumeric': 0.127755905511811, 'fraction_numerical': 0.02204724409448819, 'mean_word_length': 3.7221189591078065, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 19, 'https://': 2, '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': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '101716', 'license': 'MIT', 'n_tokens_mistral': 1808, 'n_tokens_neox': 1715, 'n_words': 436, 'path': 'Deep Learning/Auto-Encoders/Auto-Encoder for Movie Recommendation System.ipynb', 'repo_name': 'PrettyCharity/Machine_Learning_Practice', 'size': '5080'}