""" 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): # Get embeddings for minibatch embedded_users = self.user_embeddings(X[:,0]) embedded_items = self.item_embeddings(X[:,1]) # Concatenate user and item embeddings embeddings = torch.cat([embedded_users,embedded_items],dim=1) # Pass embeddings through network preds = self.fc1(embeddings) preds = F.relu(preds) preds = self.fc2(preds) # Scale predicted ratings to target-range [low,high] 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}**")