Spaces:
Sleeping
Sleeping
File size: 9,315 Bytes
b5feb1d 597f3ab 2529603 b5feb1d 2529603 b5feb1d |
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 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
import numpy as np
import gradio as gr
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
import plotly.express as px
import plotly.graph_objects as go
import umap
embedding_df = pd.read_csv('all-MiniLM-L12-v2_embeddings.csv')
embeddings = np.array(embedding_df.drop('id', axis=1))
feature_df = pd.read_csv('feature_df.csv', index_col=0)
feature_df= (feature_df - feature_df.mean() ) / feature_df.std() #standardize
info_df = pd.read_csv('song_info_df.csv')
info_df.sort_values(['artist_name','song_title'], inplace=True)
def feature_similarity(song_id):
std_drop = 4 #drop songs with strange values
song_vec = feature_df[feature_df.index.isin([song_id])].to_numpy()
songs_matrix = feature_df[~feature_df.index.isin([song_id])].copy()
songs_matrix = songs_matrix[(songs_matrix<std_drop).any(axis=1)]
song_ids = list(songs_matrix.index)
songs_matrix=songs_matrix.to_numpy()
num_dims=songs_matrix.shape[1]
distances = np.sqrt(np.square(songs_matrix-song_vec) @ np.ones(num_dims)) #compute euclidean distance
max_distance = np.nanmax(distances)
similarities = (max_distance - distances)/max_distance #low distance -> high similarity
return pd.DataFrame({'song_id': song_ids, 'feature_similarity': similarities})
def embedding_similarity(song_id):
song_index = embedding_df[embedding_df.id==song_id].index.values[0]
song_ids = embedding_df[embedding_df.id != song_id].id.to_list()
emb_matrix = np.delete(np.copy(embeddings), song_index, axis=0)
similarities = cosine_similarity(emb_matrix, np.expand_dims(np.copy(embeddings[song_index,:]), axis=0))
return pd.DataFrame({'song_id': song_ids, 'cosine_similarity': similarities[:,0]})
def decode(song_id):
temp_df = info_df[info_df.song_id == song_id]
artist = temp_df.artist_name.values[0]
song = temp_df.song_title.values[0]
youtube_url = f"""<a href=https://www.youtube.com/results?search_query=
{artist.replace(' ','+')}+{song}.replace(' ','+') target=_blank>{song}</a>"""
url = f'''<a href="https://www.youtube.com/results?search_query=
{artist.strip().replace(' ','+')}+{song.strip().replace(' ','+')}" target="_blank" style="color:blue; text-decoration: underline">
{song} </a> by {artist}'''
return url
def plot(artist, song):
plot_df['color'] = 'blue'
plot_df.loc[(plot_df.artist_name==artist) & (plot_df.song_title==song), 'color'] = 'red'
plot_df['size'] = 1.5
plot_df.loc[(plot_df.artist_name==artist) & (plot_df.song_title==song), 'size'] = 3
try:
fig2.data=[]
except:
pass
fig2 = px.scatter(plot_df[~((plot_df.artist_name==artist) & (plot_df.song_title==song))],
'x',
'y',
template='simple_white',
hover_data=['artist_name', 'song_title']).update_traces(marker_size=1.5, marker_opacity=0.7)
fig2.add_trace(go.Scatter(x=[plot_df.loc[(plot_df.artist_name==artist) & (plot_df.song_title==song), 'x'].values[0]],
y=[plot_df.loc[(plot_df.artist_name==artist) & (plot_df.song_title==song), 'y'].values[0]],
mode = 'markers',
marker_color='red',
hovertemplate="Your selected song<extra></extra>",
marker_size = 4))
fig2.update_xaxes(visible=False)
fig2.update_yaxes(visible=False).update_layout(height = 800,
width = 1000,
showlegend=False,
title = {
'text': "UMAP Projection of Lyric Embeddings",
'y':0.9,
'x':0.5,
'xanchor': 'center',
'yanchor': 'top'
})
fig2.data = [fig2.data[1], fig2.data[0]]
return fig2
def recommend(artist, song_title, embedding_importance, topk=5):
feature_importance = 1 - embedding_importance
song_id = info_df[(info_df.artist_name == artist) & (info_df.song_title == song_title)]['song_id'].values[0]
feature_sim = feature_similarity(song_id)
embedding_sim = embedding_similarity(song_id)
result = embedding_sim.merge(feature_sim, how='left',on='song_id').dropna()
result['cosine_similarity'] = (result['cosine_similarity'] - result['cosine_similarity'].min())/ \
(result['cosine_similarity'].max() - result['cosine_similarity'].min())
result['feature_similarity'] = (result['feature_similarity'] - result['feature_similarity'].min())/ \
(result['feature_similarity'].max() - result['feature_similarity'].min())
result['score'] = embedding_importance*result.cosine_similarity + feature_importance*result.feature_similarity
exclude_phrases = [r'clean', 'interlude', 'acoustic', r'mix', 'intro', r'original', 'version',\
'edited', 'extended']
result = result[~result.song_id.isin(info_df[info_df.song_title.str.lower().str.contains('|'.join(exclude_phrases))].song_id)]
body='<br>'.join([decode(x) for x in result.sort_values('score', ascending=False).head(topk).song_id.to_list()])
fig = plot(artist, song_title)
return f'<h3 style="text-align: center;">Recommendations</h3><p style="text-align: center;"><br>{body}</p>', fig
out = umap.UMAP(n_neighbors=30, min_dist=0.2).fit_transform(embedding_df.iloc[:,:-1])
plot_df = pd.DataFrame({'x':out[:,0],'y':out[:,1],'id':embedding_df.id, 'size':0.1})
plot_df['x'] = ((plot_df['x'] - plot_df['x'].mean())/plot_df['x'].std())
plot_df['y'] = ((plot_df['y'] - plot_df['y'].mean())/plot_df['y'].std())
plot_df = plot_df.merge(info_df, left_on='id', right_on='song_id')
plot_df = plot_df[(plot_df.x.abs()<4) & (plot_df.y.abs()<4)]
fig = px.scatter(plot_df,
'x',
'y',
template='simple_white',
hover_data=['artist_name', 'song_title']
).update_traces(marker_size=1.5,
opacity=0.7,
)
fig.update_xaxes(visible=False)
fig.update_yaxes(visible=False).update_layout(height = 800,
width =1000,
title = {
'text': "UMAP Projection of Lyric Embeddings",
'y':0.9,
'x':0.5,
'xanchor': 'center',
'yanchor': 'top'
})
app = gr.Blocks()
with app:
gr.Markdown("# Hip Hop gRadio - A Lyric Based Recommender")
gr.Markdown("""### About this space
The goal of this space is to provide recommendations for hip-hop/rap songs strictly by utilizing lyrics. The recommendations
are a combination of ranked similarity scores. We calculate euclidean distances between our engineered feature vectors for each song,
as well as a cosine distance between document embeddings of the lyrics themselves. A weighted average of these two results in our
final similarity score that we use for recommendation. (feature importance = (1 - embedding importance))
Additionally, we provide a 2-D projection of all document embeddings below. After entering a song of your choice, you will see it as
a red dot, allowing you to explore both near and far. This projection reduces 384-dimensional embeddings down to 2-d, allowing visualization.
This is done using Uniform Manifold Approximation and Projection [(UMAP)](https://umap-learn.readthedocs.io/en/latest/), a very interesting approach to dimensionalty
reduction, I encourage you to look into it if you are interested! ([paper](https://arxiv.org/abs/1802.03426))
The engineered features used are the following: song duration, number of lines, syllables per line, variance in syllables per line,
total unique tokens, lexical diversity (measure of repitition), sentiment (using nltk VADER), tokens per second, and syllables per second.
**Model used for embedding**: [all-MiniLM-L12-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L12-v2)<br/>
**Lyrics**: from [genius](https://genius.com/)
""")
with gr.Row():
with gr.Column():
artist = gr.Dropdown(choices = list(info_df.artist_name.unique()),
value = 'Kanye West',
label='Artist')
song = gr.Dropdown(choices = list(info_df.loc[info_df.artist_name=='Kanye West','song_title']),
label = 'Song Title')
slider = gr.Slider(0,1,value=0.5, label='Embedding Importance')
but = gr.Button()
with gr.Column():
t = gr.Markdown('<h3 style="text-align: center;">Recomendations</h3>')
with gr.Row():
p = gr.Plot(fig)
def artist_songs(artist):
return gr.components.Dropdown.update(choices=info_df[info_df.artist_name == artist]['song_title'].to_list())
artist.change(artist_songs, artist, outputs=song)
but.click(recommend, inputs=[artist, song,slider], outputs=[t, p])
app.launch()
|