File size: 5,758 Bytes
3284fa6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ce6b56
 
3284fa6
6ce6b56
 
 
 
 
 
 
 
 
 
 
 
 
 
3284fa6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
"""

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}**")