<jupyter_start><jupyter_text>**1. Mount Google Drive and get authentication from Google SDK**This step includes mounting the Google Drive and receiving authentication via Google SDK to gain access to our database and custom libraries.<jupyter_code>from google.colab import drive, auth
from oauth2client.client import GoogleCredentials
import gspread

# mount Google Drive
drive.mount('/content/drive')
# authenticate to gain access to read CSV files from Google Drive
auth.authenticate_user()
gc = gspread.authorize(GoogleCredentials.get_application_default())<jupyter_output><empty_output><jupyter_text>------------------------------------------**2. Install and import all dependencies**<jupyter_code>#@title
# Install libraries not local to Colab
!pip install tensorflow
!pip install tensorflow-gpu
!pip install import-ipynb
!pip install chinese-converter
!pip install anvil-uplink
#@title
# Must move current directory into Tinger's main code directory
%cd '/content/drive/MyDrive/Tinger/Code/'
#@title
# import dependencies
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import os
import glob
import chinese_converter
from PIL import Image
from timeit import default_timer

# import keras
from tensorflow import keras

# import custom SNN functions from UseSNN.ipynb in the same directory
import import_ipynb
import UseSNN

# import sklearn
from sklearn.neighbors import KNeighborsClassifier

# import difflib
from difflib import SequenceMatcher<jupyter_output>importing Jupyter notebook from UseSNN.ipynb
Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).
Requirement already satisfied: tensorflow in /usr/local/lib/python3.7/dist-packages (2.4.1)
Requirement already satisfied: typing-extensions~=3.7.4 in /usr/local/lib/python3.7/dist-packages (from tensorflow) (3.7.4.3)
Requirement already satisfied: wrapt~=1.12.1 in /usr/local/lib/python3.7/dist-packages (from tensorflow) (1.12.1)
Requirement already satisfied: tensorboard~=2.4 in /usr/local/lib/python3.7/dist-packages (from tensorflow) (2.4.1)
Requirement already satisfied: wheel~=0.35 in /usr/local/lib/python3.7/dist-packages (from tensorflow) (0.36.2)
Requirement already satisfied: h5py~=2.10.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow) (2.10.0)
Requirement already satisfied: termcolor~=1.1.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow) (1.1.0)
Requirement already satisfied: absl-p[...]<jupyter_text>------------------------------------------**3. Pre-process and store data in advance to save runtime**By making calculations and transforming data in advance to making recommendations in real-time, Tinger is able to cut down on runtime.<jupyter_code>path = '/content/drive/MyDrive/Tinger/Documentation/'

mandarin = pd.read_csv(path + 'Final Playlist Data (Mandarin).csv')
mandarin = mandarin.drop(columns=['Length of Song', 'Length in Seconds'])
mandarin['bpm'].replace('', np.nan, inplace=True)
mandarin = mandarin.dropna(subset = ["bpm"])
mandarin
mandarin.loc[len(mandarin.index)] = ['WAP', '<NAME>', 1, 2020, 0, '1,1,2,1,1,2,1,1', 0.0, 'sa', 'dasd', 132.8644257, 1.191560388]
mandarin.loc[len(mandarin.index)+1] = ['Rockstar', 'Post Malone', 0, 2017, 0, '6,5,6,5,6,5,6,5', 0.0, 'dasd', 'dasdas', 161.6918945, 1.118424296]
mandarin.loc[len(mandarin.index)+2] = ['Holy', '<NAME>', 0, 2020, 0, '1,5,6,4,1,5,6,4', 0.0, 'sda', 'sad', 86.79120636, 1.164459944]
english = pd.read_csv(path + 'Final Playlist Data (English).csv')
english = english.drop(columns=['Length of Song', 'Length in Seconds'])
english['bpm'].replace('', np.nan, inplace=True)
english = english.dropna(subset = ["bpm"])
english
# Calculate chord differences for English dataset
chord_progressions = english['Main Chord Progression']
chord_list = chord_progressions.values.tolist()
empty_list = []
for value in chord_list:
    temp = list(map(int, value.split(',')))
    empty_list.append([j-i for i, j in zip(temp[:-1], temp[1:])])
english['Chord Difference'] = empty_list
    
# Calculate chord differences for Mandarin dataset
chord_progressions1 = mandarin['Main Chord Progression']
chord_list_mandarin = chord_progressions1.values.tolist()
empty_list1 = []
for value in chord_list_mandarin:
    temp1 = list(map(int, value.split(',')))
    empty_list1.append([j-i for i, j in zip(temp1[:-1], temp1[1:])])
mandarin['Chord Difference'] = empty_list1<jupyter_output><empty_output><jupyter_text>------------------------------------------**4. Define Tinger's main recommendation functions**<jupyter_code>def similar(a, b):
    return SequenceMatcher(None, a, b).ratio()
def tinger_no_snn(song_title, eng):
    """
    
    Inputs: 
        song_title : string, the title of song the user likes
        
        eng : boolean, True if input song is in English; False if input song is in Mandarin
        
    Outputs:
        song_recs : 5 song recommendations in the other language
    
    """

    # Identify all the paths needed -- paths may be different for different users
    model_path = '/content/drive/MyDrive/Tinger/Code/'

    mandarin_mel_path = '/content/drive/MyDrive/Tinger/Code/WAVtoShortMelSpectrogram-Output/Mel_Mandarin'
    english_mel_path = '/content/drive/MyDrive/Tinger/Code/WAVtoShortMelSpectrogram-Output/Mel_English'

    mandarin_csv_path = '/content/drive/MyDrive/Tinger/Documentation/Final Playlist Data (Mandarin).csv'
    english_csv_path = '/content/drive/MyDrive/Tinger/Documentation/Final Playlist Data (English).csv'
        
    if eng:
        #get romance label of input song
        r = english[english['Song Title']==song_title]['Romance']
        
        #add metadata distances
        X = mandarin.iloc[:, 2:5].values
        y = mandarin.iloc[:, 0].values
        x = english[english['Song Title']==song_title].iloc[:, 2:5].values
        classifier = KNeighborsClassifier(n_neighbors=len(mandarin))
        classifier.fit(X, y)
        distances, indices = classifier.kneighbors(x, n_neighbors=len(mandarin))
        dist = pd.DataFrame(distances[0], indices[0]).rename(columns={0: "meta_dist"})
        final = mandarin.merge(dist, how='inner', left_index=True, right_index=True)
        
        #add bpm distances
        man_X = mandarin.iloc[:, 9:11].values
        man_y = mandarin.iloc[:, 0].values
        x1 = english[english['Song Title']==song_title].iloc[:, 8:10].values
        bpm_classifier = KNeighborsClassifier(n_neighbors=len(mandarin))
        bpm_classifier.fit(man_X, man_y)
        distances1, indices1 = bpm_classifier.kneighbors(x1, n_neighbors=len(mandarin))
        dist1 = pd.DataFrame(distances1[0], indices1[0]).rename(columns={0: "bpm_dist"})
        final = final.merge(dist1, how='inner', left_index=True, right_index=True)
        
        #add chord progression similarity scores
        diff = english.loc[english['Song Title'] == song_title]['Chord Difference'].values[0]
        final['chord_sim'] = mandarin['Chord Difference'].map(lambda y: similar(y, diff))
        
        # #add snn similarity scores
        # output = UseSNN.similarities_matrix(model_path, mandarin_mel_path, english_mel_path, english_csv_path, song_title, 1)
        # similarities_matrix = output[0]
        # comparison_titles = output[3]
        # final_dict = UseSNN.similarities_dict(similarities_matrix, comparison_titles)

    else:
        #get romance label of input song
        r = mandarin[mandarin['Song Title']==song_title]['Romance']

        #add metadata distances
        X = english.iloc[:, 2:5].values
        y = english.iloc[:, 0].values
        x = mandarin[mandarin['Song Title']==song_title].iloc[:, 2:5].values
        meta_classifier = KNeighborsClassifier(n_neighbors=len(english))
        meta_classifier.fit(X, y)
        distances, indices = meta_classifier.kneighbors(x, n_neighbors=len(english))
        dist = pd.DataFrame(distances[0], indices[0]).rename(columns={0: "meta_dist"})
        final = english.merge(dist, how='inner', left_index=True, right_index=True)
        
        #add bpm distances
        eng_X = english.iloc[:, 8:10].values
        eng_y = english.iloc[:, 0].values
        x1 = mandarin[mandarin['Song Title']==song_title].iloc[:, 9:11].values
        bpm_classifier = KNeighborsClassifier(n_neighbors=len(english))
        bpm_classifier.fit(eng_X, eng_y)
        distances1, indices1 = bpm_classifier.kneighbors(x1, n_neighbors=len(english))
        dist1 = pd.DataFrame(distances1[0], indices1[0]).rename(columns={0: "bpm_dist"})
        final = final.merge(dist1, how='inner', left_index=True, right_index=True)
        
        #add chord progression similarity scores
        diff = mandarin.loc[mandarin['Song Title'] == song_title]['Chord Difference'].values[0]
        final['chord_sim'] = english['Chord Difference'].map(lambda y: similar(y, diff))

        #add snn similarity scores
        # path = '/content/drive/My Drive/Tinger/Code/WAVtoShortMelSpectrogram-Output/Mel_Mandarin/'
    
    # Compute final similarity score
    final['score'] = -.005*final['meta_dist']-.03*final['bpm_dist']+.8*final['chord_sim']
#    final['score'] = -.005*final['meta_dist']-.01*final['bpm_dist']+.5*final['chord_sim']
#     final['score'] = -.1*final['meta_dist']-.1*final['bpm_dist']+.5*final['snn_sim']+.5*final['chord_sim']
    
    # Filter out songs with different romance label
    final = final[final['Romance']==r.iloc[0]].sort_values('score', ascending=False)
    
    # Select top 5 most similar songs
    song_recs = final['Song Title'].iloc[0:5]

    return song_recs
def tinger(song_title, eng):
    """
    
    Inputs: 
        song_title : string, the title of song the user likes
        
        english : boolean, True if input song is in English; False if input song is in Mandarin
        
    Outputs:
        song_recs : 5 song recommendations in the other language
    
    """

    # Identify all the paths needed -- paths may be different for different users
    model_path = '/content/drive/MyDrive/Tinger/Code/'

    mandarin_mel_path = '/content/drive/MyDrive/Tinger/Code/WAVtoShortMelSpectrogram-Output/Mel_Mandarin'
    english_mel_path = '/content/drive/MyDrive/Tinger/Code/WAVtoShortMelSpectrogram-Output/Mel_English'

    mandarin_csv_path = '/content/drive/MyDrive/Tinger/Documentation/Final Playlist Data (Mandarin).csv'
    english_csv_path = '/content/drive/MyDrive/Tinger/Documentation/Final Playlist Data (English).csv'
        
    if eng:
        #get romance label of input song
        r = english[english['Song Title']==song_title]['Romance']
        
        #add metadata distances
        X = mandarin.iloc[:, 2:5].values
        y = mandarin.iloc[:, 0].values
        x = english[english['Song Title']==song_title].iloc[:, 2:5].values
        classifier = KNeighborsClassifier(n_neighbors=len(mandarin))
        classifier.fit(X, y)
        distances, indices = classifier.kneighbors(x, n_neighbors=len(mandarin))
        dist = pd.DataFrame(distances[0], indices[0]).rename(columns={0: "meta_dist"})
        final = mandarin.merge(dist, how='inner', left_index=True, right_index=True)
        
        #add bpm distances
        man_X = mandarin.iloc[:, 9:11].values
        man_y = mandarin.iloc[:, 0].values
        x1 = english[english['Song Title']==song_title].iloc[:, 8:10].values
        bpm_classifier = KNeighborsClassifier(n_neighbors=len(mandarin))
        bpm_classifier.fit(man_X, man_y)
        distances1, indices1 = bpm_classifier.kneighbors(x1, n_neighbors=len(mandarin))
        dist1 = pd.DataFrame(distances1[0], indices1[0]).rename(columns={0: "bpm_dist"})
        final = final.merge(dist1, how='inner', left_index=True, right_index=True)
        
        #add chord progression similarity scores
        diff = english.loc[english['Song Title'] == song_title]['Chord Difference'].values[0]
        final['chord_sim'] = mandarin['Chord Difference'].map(lambda y: similar(y, diff))
        
        #add snn similarity scores
        output = UseSNN.similarities_matrix(model_path, english_mel_path, mandarin_mel_path, mandarin_csv_path, song_title, 0)
        similarities_matrix = output[0]
        comparison_titles = output[3]
        final_dict = UseSNN.similarities_dict(similarities_matrix, comparison_titles)
        d = {'Song Title': final_dict.keys(), 'snn_sim': final_dict.values()}
        snn_df = pd.DataFrame(data=d)
        final = final.merge(snn_df, how='inner', on='Song Title')
        

    else:
        #get romance label of input song
        r = mandarin[mandarin['Song Title']==song_title]['Romance']

        #add metadata distances
        X = english.iloc[:, 2:5].values
        y = english.iloc[:, 0].values
        x = mandarin[mandarin['Song Title']==song_title].iloc[:, 2:5].values
        meta_classifier = KNeighborsClassifier(n_neighbors=len(english))
        meta_classifier.fit(X, y)
        distances, indices = meta_classifier.kneighbors(x, n_neighbors=len(english))
        dist = pd.DataFrame(distances[0], indices[0]).rename(columns={0: "meta_dist"})
        final = english.merge(dist, how='inner', left_index=True, right_index=True)
        
        #add bpm distances
        eng_X = english.iloc[:, 8:10].values
        eng_y = english.iloc[:, 0].values
        x1 = mandarin[mandarin['Song Title']==song_title].iloc[:, 9:11].values
        bpm_classifier = KNeighborsClassifier(n_neighbors=len(english))
        bpm_classifier.fit(eng_X, eng_y)
        distances1, indices1 = bpm_classifier.kneighbors(x1, n_neighbors=len(english))
        dist1 = pd.DataFrame(distances1[0], indices1[0]).rename(columns={0: "bpm_dist"})
        final = final.merge(dist1, how='inner', left_index=True, right_index=True)
        
        #add chord progression similarity scores
        diff = mandarin.loc[mandarin['Song Title'] == song_title]['Chord Difference'].values[0]
        final['chord_sim'] = english['Chord Difference'].map(lambda y: similar(y, diff))

        #add snn similarity scores
        output = UseSNN.similarities_matrix(model_path, mandarin_mel_path, english_mel_path, english_csv_path, song_title, 0)
        similarities_matrix = output[0]
        comparison_titles = output[3]
        final_dict = UseSNN.similarities_dict(similarities_matrix, comparison_titles)
        d = {'Song Title': final_dict.keys(), 'snn_sim': final_dict.values()}
        snn_df = pd.DataFrame(data=d)
        final = final.merge(snn_df, how='inner', on='Song Title')
    
    # Compute final similarity score
    final['score'] = -.005*final['meta_dist']-.01*final['bpm_dist']+.8*final['chord_sim']+.2*final['snn_sim']
    
    # Filter out songs with different romance label
    final = final[final['Romance']==r.iloc[0]].sort_values('score', ascending=False)
    
    # Select top 5 most similar songs
    song_recs = final['Song Title'].iloc[0:5]

    return song_recs<jupyter_output><empty_output><jupyter_text>------------------------------------------**5. Initialize Tinger's full stack**Tinger's models requires roughly 10x more time to run in its initial trial immediately after initialization. The following code block runs an initial test trial to decrease runtime for future trials: 13s on average per recommendation.<jupyter_code>print(tinger_no_snn('A Thousand Years', eng=True))
print(tinger('A Thousand Years', eng=True))<jupyter_output>199      不将就
31      好久不见
23     恋爱ing
110       男孩
171      不将就
Name: Song Title, dtype: object
53      男孩
17    你的背包
95    目不转睛
51      兜圈
20    小步舞曲
Name: Song Title, dtype: object<jupyter_text>-------------------------------**[Optional] Steps to implement the Tinger siamese neural network**<jupyter_code># Step 1: Identify all the paths needed -- paths may be different for different users
model_path = '/content/drive/MyDrive/Tinger/Code/'

mandarin_mel_path = '/content/drive/MyDrive/Tinger/Code/WAVtoShortMelSpectrogram-Output/Mel_Mandarin'
english_mel_path = '/content/drive/MyDrive/Tinger/Code/WAVtoShortMelSpectrogram-Output/Mel_English'

mandarin_csv_path = '/content/drive/MyDrive/Tinger/Documentation/Final Playlist Data (Mandarin).csv'
english_csv_path = '/content/drive/MyDrive/Tinger/Documentation/Final Playlist Data (English).csv'<jupyter_output><empty_output><jupyter_text>**Note before advancing:**1. Make sure that all necessary dependencies are installed and imported. Please run the first 5 code blocks, more than once if necessary, to ensure proper installation.2. Click on the RAM/Disk info above on the top right of this notebook. Ensure that the notebook is running on a **Python 3 Google Compute Engine backend (GPU)**. If not, click **Change Runtime Type** on the bottom of the Resources tab to change the backend to a GPU, then re-run the notebook.3. Prior test trials have shown that the SNN takes roughly 10x the runtime to compute similarity matrices on its first run. Before showcasing, try to run the model at least once in advance.<jupyter_code># Step 2: Select a target song to run through Tinger's model stack
# Also identify the direction in which Tinger is recommending songs:
# Mandarin >> English:  direction = 0
# English  >> Mandarin: direction = 1

target = '好喜歡你'
direction = 0

# The function similarities_matrix() outputs a list of length 5 as follows:
# [results_matrix, target_title, target_data, comparison_title, comparison_data]
# In order to create the desired dictionary, only results_matrix and comparison_title are needed.
# All objects are provided regardless for the sake of completeness

# Arguments:
# similarities_matrix(snn_path, mandarin_mel_path, english_mel_path, csv_path, target_title, direction)

start_time = default_timer()
output = UseSNN.similarities_matrix(model_path, mandarin_mel_path, english_mel_path, english_csv_path, target, direction)
end_time = default_timer()
print('Calculating the similarities matrix takes {0} seconds to run.'.format(end_time-start_time))
similarities_matrix = output[0]
comparison_titles = output[3]
# Step 3: Create a heatmap using the generate_heatmap() function for a graphical visual
# Use the similarities matrix from Step 2 as the input.
# Change the n_row and n_col arguments to create extracts of the matrix.
# Default values: n_row = 5 (highly recommended), n_col = 80
# Identify a filepath to save the heatmap

# Arguments:
# generate_heatmap(mat, n_row=5, n_col=40, save_path=None)

UseSNN.generate_heatmap(similarities_matrix)
# Step 4: Retrieve the similarities matrix and comparison titles from the output in Step 2.
# Use them as inputs into the similarities_dict() function that outputs our final dictionary.
# This dictionary will be used as an input into the complete Tinger stack

start_time = default_timer()
final_dict = UseSNN.similarities_dict(similarities_matrix, comparison_titles)
end_time = default_timer()
print('Compiling the similarities dictionary takes {0} seconds to run.'.format(end_time-start_time))
print('Can\'t Lie has a similarty constant of {0} with the target song, {1}'.format(final_dict["Can't Lie"], target))<jupyter_output>Compiling the similarities dictionary takes 0.0030612240000209567 seconds to run.
Can't Lie has a similarty constant of 0.004525488981744275 with the target song, 好喜歡你<jupyter_text>-------------------------------**[Optional] Sample Outputs for Tinger's stack without and without the Siamese Neural Network**<jupyter_code># Test run of Tinger without siamese neural network
print(tinger_no_snn('A Thousand Years', eng=True))
print('\n')
print(tinger_no_snn('不将就', eng=False))
print(tinger('Perfect', eng=True))
print('\n')
print(tinger('雪花落下', eng=False))
print('\n')
print(tinger('暖暖', eng=False))
print('\n')
print(tinger('意外', eng=False))
print('\n')
print(tinger('你，好不好', eng=False))
print('\n')
print(tinger('男孩', eng=False))
# 'Fake Mandarin' Sanity Test
print(tinger('WAP', eng=False))
print('\n')
print(tinger('Holy', eng=False))
print('\n')
print(tinger('Rockstar', eng=False))
# 'Fake Mandarin' Sanity Test 2
print(tinger_no_snn('WAP', eng=False))
print('\n')
print(tinger_no_snn('Holy', eng=False))
print('\n')
print(tinger_no_snn('Rockstar', eng=False))<jupyter_output>146              Perfume
35           Uptown Funk
134        Big Girls Cry
114         Shake it Off
143    Unpack Your Heart
Name: Song Title, dtype: object


7            Rockstar
137    If I Die Young
73               Maps
80          24K Magic
30      See You Again
Name: Song Title, dtype: object


6                WAP
3         Rain on me
106    Thank U, Next
120         Firework
119             Roar
Name: Song Title, dtype: object<jupyter_text>-------------------------------**6. Export to Web App using Anvil****This cell must be running in order for our web app to stay alive.**Must run all the cells above (except the 'Steps to Implement Tinger's SNN stack') to ensure dashboard works properly.Most importantly, make sure all the dependencies are installed and imported correctly.<jupyter_code>import anvil.server

anvil.server.connect('AEDGXEE47FPVOY7EIKVZQWP4-A4FM7JK7KIDQP2JF')

@anvil.server.callable
def make_recommendation(song_title, eng):
  recs = tinger(song_title, eng)
  recs = recs.values

  return recs  

anvil.server.wait_forever()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12863814330864665, 'fraction_numerical': 0.022595405443653002, 'mean_word_length': 3.9637087333177243, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 1, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '257339', 'license': 'Unlicense', 'n_tokens_mistral': 7181, 'n_tokens_neox': 6749, 'n_words': 1712, 'path': 'TingerIntegration.ipynb', 'repo_name': 'rbqpark/tinger', 'size': '21207'}