| """
|
| Attribution: https://github.com/AIPI540/AIPI540-Deep-Learning-Applications/
|
|
|
| Jon Reifschneider
|
| Brinnae Bent
|
|
|
| """
|
|
|
| import streamlit as st
|
| from PIL import Image
|
| import numpy as np
|
| import os
|
| import numpy as np
|
| import pandas as pd
|
| import pandas as pd
|
| import os
|
| import json
|
| import pandas as pd
|
| import torch
|
| import numpy as np
|
| import pandas as pd
|
| import torch.nn as nn
|
| import torch.nn.functional as F
|
| import matplotlib.pyplot as plt
|
|
|
| class NNColabFiltering(nn.Module):
|
|
|
| def __init__(self, n_playlists, n_artists, embedding_dim_users, embedding_dim_items, n_activations, rating_range):
|
| super().__init__()
|
| self.user_embeddings = nn.Embedding(num_embeddings=n_playlists,embedding_dim=embedding_dim_users)
|
| self.item_embeddings = nn.Embedding(num_embeddings=n_artists,embedding_dim=embedding_dim_items)
|
| self.fc1 = nn.Linear(embedding_dim_users+embedding_dim_items,n_activations)
|
| self.fc2 = nn.Linear(n_activations,1)
|
| self.rating_range = rating_range
|
|
|
| def forward(self, X):
|
|
|
| embedded_users = self.user_embeddings(X[:,0])
|
| embedded_items = self.item_embeddings(X[:,1])
|
|
|
| embeddings = torch.cat([embedded_users,embedded_items],dim=1)
|
|
|
| preds = self.fc1(embeddings)
|
| preds = F.relu(preds)
|
| preds = self.fc2(preds)
|
|
|
| preds = torch.sigmoid(preds) * (self.rating_range[1]-self.rating_range[0]) + self.rating_range[0]
|
| return preds
|
|
|
| def generate_recommendations(artist_album, playlists, model, playlist_id, device, top_n=10, batch_size=1024):
|
| '''
|
| Loads the prefetched data from the output dir
|
|
|
| Inputs:
|
| artist_album: the dataframe containing the mappings for the artist and albums
|
| playlists: the dataframe containing the playlists contents
|
| model: the trained model
|
| playlist_id: the playlist id to generate recommendation for
|
| device: the gpu or cpu device define by torch
|
| top_n: the number of recommendations to generate
|
| batch_size: the batch size to use
|
|
|
| Returns:
|
| album: the recommended album
|
| playlists: the recommended artist
|
| '''
|
| model.eval()
|
|
|
| all_movie_ids = torch.tensor(artist_album['artist_album_id'].values, dtype=torch.long, device=device)
|
| user_ids = torch.full((len(all_movie_ids),), playlist_id, dtype=torch.long, device=device)
|
|
|
| all_predictions = torch.zeros(len(all_movie_ids), device=device)
|
|
|
| with torch.no_grad():
|
| for i in range(0, len(all_movie_ids), batch_size):
|
| batch_user_ids = user_ids[i:i+batch_size]
|
| batch_movie_ids = all_movie_ids[i:i+batch_size]
|
|
|
| input_tensor = torch.stack([batch_user_ids, batch_movie_ids], dim=1)
|
| batch_predictions = model(input_tensor).squeeze()
|
| all_predictions[i:i+batch_size] = batch_predictions
|
|
|
| predictions = all_predictions.cpu().numpy()
|
| albums_listened = set(playlists.loc[playlists['playlist_id'] == playlist_id, 'artist_album_id'].tolist())
|
| unlistened_mask = np.isin(artist_album['artist_album_id'].values, list(albums_listened), invert=True)
|
|
|
| top_indices = np.argsort(predictions[unlistened_mask])[-top_n:][::-1]
|
| recs = artist_album['artist_album_id'].values[unlistened_mask][top_indices]
|
|
|
| recs_names = artist_album.loc[artist_album['artist_album_id'].isin(recs)]
|
| album, artist = recs_names['album_name'].values, recs_names['artist_name'].values
|
|
|
| return album.tolist(), artist.tolist()
|
|
|
|
|
| def load_data():
|
| '''
|
| Loads the prefetched data from the output dir
|
|
|
| Inputs:
|
|
|
| Returns:
|
| artist_album: pandas DataFrame with the best sentiment score
|
| playlists: pandas DataFrame with the worst sentiment score
|
| '''
|
| artist_album = pd.read_csv(os.path.join(os.getcwd() + '/data/processed','artist_album.csv'))
|
| artist_album = artist_album[['artist_album_id','artist_album','artist_name','album_name']].drop_duplicates()
|
| playlists = pd.read_csv(os.path.join(os.getcwd() + '/data/processed','playlists.csv'))
|
|
|
| return artist_album, playlists
|
|
|
| artist_album, playlists = load_data()
|
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| model = torch.load('models/recommender.pt', map_location=device)
|
|
|
| if __name__ == '__main__':
|
|
|
| st.header('Spotify Playlists')
|
|
|
| img1, img2 = st.columns(2)
|
|
|
| music_notes = Image.open('assets/music_notes.png')
|
| img1.image(music_notes, use_column_width=True)
|
|
|
| trumpet = Image.open('assets/trumpet.png')
|
| img2.image(trumpet, use_column_width=True)
|
|
|
| with st.sidebar:
|
| playlist_name = st.selectbox(
|
| "Playlist Selection",
|
| ( list(set(playlists['name'].dropna())) )
|
| )
|
| playlist_id = playlists['playlist_id'][playlists['name'] == playlist_name].values[0]
|
| albums, artists = generate_recommendations(artist_album, playlists, model, playlist_id, device)
|
|
|
| st.dataframe(data=playlists[['artist_name','album_name','track_name']][playlists['playlist_id'] == playlist_id])
|
|
|
| st.write(f"*Recommendations for playlist:* {playlists['name'][playlists['playlist_id'] == playlist_id].values[0]}")
|
| col1, col2 = st.columns(2)
|
| with col1:
|
| st.write(f'Artist')
|
| with col2:
|
| st.write(f'Album')
|
|
|
| for album, artist in zip(albums, artists):
|
| with col1:
|
| st.write(f"**{artist}**")
|
| with col2:
|
| st.write(f"**{album}**")
|
|
|
|
|