Pousser la chansonnette : Classification et stylométrie de paroles de pop¶

Introduction ¶

Le but de ce projet est de réaliser un projet simple et naïf sur le text mining. Je voulais revoir les bases de cette discipline et j'ai trouvé l'occasion de le faire en croisant le très intéressant git de Hugo Nattagh qui présentait son travail ce qui m'a donné envie de faire la même chose.

Dans ce projet, je vais prédire à quel artiste se rapproche le plus le texte d'une chanson. L'idée étant récréative, j'ai décidé de ne faire cette comparaison que sur 20 des artistes les plus populaires en pop.

Pour réaliser ce projet j'ai donc téléchargé un corpus grâce à l'api genius à partir de laquelle j'ai téléchargé les paroles des 20 artistes parmis les plus influents dans la pop. Pour utiliser des algorithmes de machine learning classiques, je suis passé par une vectorisation TF-IDF afin de transformer les mots en une matrice de valeurs numériques. Une fois le corpus vectorisé, j'ai séparé la base de données en 2 : une partie pour l'apprentissage et l'autre pour le test. Le but étant ainsi de créer un modèle ayant des paroles de chanson en entrée et l'artiste vers lequel ces paroles se rapprochent le plus en sortie. Pour cela j'ai entrainé un modèle de classification multinomial naif bayesien avec les données d'apprentissage. Enfin, j'ai fini par évaluer la performance du modèle entrainé avec les données de test.

Table des matières¶

  • Introduction
  • Création du corpus
    • Utilisation du Package lyricsgenius
    • Importation du corpus
    • Suppression des doublons
    • Enregistrement du Corpus
  • Preprocessing
    • Restructuration de la base
    • Pipeline de traitement automatique de la base
    • Vectorisation
  • Modélisation
    • Choix du modèle
    • Optimisation des Hyperparamètres
    • Evaluation du modèle
    • Prédiction
      • "Hello" de Adele
      • "What a Wonderful World" de Louis Armstrong
  • Conclusion
  • Documentation

Création du corpus ¶

Utilisation du Package lyricsgenius ¶

In [1]:
import requests
import lyricsgenius as genius

L'idée ici est d'importer les paroles de chanson de 20 artistes des plus influents afin de créer notre Corpus. Pour cela, on va utiliser une api permettant de nous connecter à Genius et ainsi récupérer les paroles des chansons des artistes qui nous intéresse.

J'ai enregistré mes clés dans un fichier annexe. Vous pouvez créer les votre en cliquant sur ce lien : https://docs.genius.com/.
J'importe donc mes clés afin de me connecter à l'API de Genius. J'ai choisi de le faire avec trois Tokens différents pour parralléliser et accélerer le processus (qui peut être long le cas échéant).

In [2]:
nom_fichier = "genius_id.txt"
dico_token = {}

with open(nom_fichier, "r", encoding="utf-8") as fichier:
    for ligne in fichier:
        cle, valeur = ligne.split(":", 1)
        dico_token[cle.strip()] = valeur.strip()

Dans un premier temps, essayons donc de comprendre le fonctionnement de l'api en récupérant les 2 chansons les plus populaires de Rihanna...

In [3]:
#Connection à l'api et création de ma requête.
api=genius.Genius(dico_token["client_token_1"])
artist=api.search_artist('Rihanna', max_songs=2)

#Je ne veux que les métadonnées titres et paroles des chansons.
titles=[song.title for song in artist.songs]
lyrics=[song.lyrics for song in artist.songs]
thingstosave=[]
for i in range(0,len(artist)):
    thingstosave.append((titles[i],lyrics[i]))
In [4]:
thingstosave[0]
Out[4]:
('Work',
 '[Chorus: Rihanna]\nWork, work, work, work, work, work\nHe said me haffi work, work, work, work, work, work\nHe see me do mi dirt, dirt, dirt, dirt, dirt, dirt\nAh so me put in work, work, work, work, work, work\nWhen you ah guh learn, learn, learn, learn, learn, learn?\nMe nuh cyar if him hurt, hurt, hurt, hurt, hurting\n\n[Verse 1: Rihanna]\nDry, me ah desert him\nNuh time to have you lurking\nHim ah go act like he nuh like it\nYou know I dealt with you the nicest\nNuh body touch me, you nuh righteous\nNuh badda, text me in a crisis\nI believed all of your dreams, adoration\nYou took my heart and my keys and my patience\nYou took my heart on my sleeve for decoration\nYou mistaken my love I brought for you for foundation\nAll that I wanted from you was to give me\nSomething that I never had\nSomething that you\'ve never seen\nSomething that you\'ve never been, mm\nBut I wake up and act like nothing\'s wrong\n\n[Chorus: Rihanna]\nJust get ready fi work, work, work, work, work, work\nHe said me haffi work, work, work, work, work, work\nHe see me do mi dirt, dirt, dirt, dirt, dirt, dirt\nAh so me put in work, work, work, work, work, work\nNer, ner, ner, ner, ner, ner\nWhen you ah guh learn, learn, learn, learn, learn, learn?\nBefore the tables turn, turn, turn, turn, turn, turn\n\n[Verse 2: Rihanna]\nBeg you something, please\nBaby, don\'t you leave\nDon\'t leave me stuck here in the streets, uh-huh\nIf I get another chance to\nI will never, no, never neglect you\nI mean, who am I to hold your past against you?\nI just hope that it gets to you\nI hope that you see this through\nI hope that you see this true\nWhat can I say?\nPlease recognize I\'m tryin\', babe\n\n[Chorus: Rihanna & Drake]\nI haffi work, work, work, work, work, work\nHe said me haffi work, work, work, work, work, work\nHe see me do mi dirt, dirt, dirt, dirt, dirt, dirt\nSo me put in work, work, work, work, work, work\nWhen you ah guh learn, learn, learn, learn, learn, learn?\nMe nuh cyar if him hurt, hurt, hurt, hurt, hurting (\nYeah, okay\n)\n\n[Verse 3: Drake, Rihanna, Both]\nYou need to get done, done, done, done at work, come over\nWe just need to slow the motion\nDon\'t give that away to no one\nLong distance, I need you (Ah)\nWhen I see potential, I just gotta see it through\nIf you had a twin, I would still choose you\nI don\'t wanna rush into it if it\'s too soon\nBut I \nknow you need to get done, done, done, done\nIf you come over\nSorry if I\'m way less friendly\nI got niggas tryna end me, oh (Yeah)\nI spilled all my emotions tonight, I\'m sorry\nRollin\', rollin\', rollin\', rollin\', rollin\'\nHow many more shots until you\'re rollin\'?\nWe just need a face-to-face\nYou could pick the time and the place\nYou spent some time away\nNow you need to forward\n\n[Chorus: Rihanna, Rihanna & Drake]\nAnd give me all the work, work, work, work, work\n, work\nHe said me haffi \nwork, work, work, work, work, work\nHe see me do mi dirt, dirt, dirt, dirt, dirt, dirt\nSo me put in work, work, work, work, work, work\nWhen you ah guh learn, learn, learn, learn, learn, learn?\nMe nuh cyar if him hurt, hurt, hurt, hurt, hurting\n\n[Outro: Rihanna, Rihanna & Drake]\nMm\nMm\nWork, work, work, work, work, work\nMm\n\n[Click here to learn more about the making of "Work"]\n\n[Produced by Boi-1da]')

Importation du corpus ¶

Passons maintenant à plus grande échelle ! Nous allons paralléliser par clef d'api.
Je prépare donc en premier lieu la trame pour la méthode pool.map

In [5]:
liste_token = ["client_token_"+ str(i) for i in range(1,4)]

liste_artiste = ["Bruno Mars","Rihanna","Beyonce","Alicia Keys","Frank Ocean",
                 "Amy Winehouse","Justin Timberlake", "Usher", "Mariah Carey", "Justin Bieber","Michael Jackson",
                 "Christina Aguilera", "Chris Brown", "Ne-Yo", "Destiny's Child", "Aaliyah", "Mary J. Blige",
                 "Marvin Gaye", "Janet Jackson", "Stevie Wonder"]

dico_artiste_token = {"client_token_1" : liste_artiste[:7],
                      "client_token_2" : liste_artiste[7:14],
                      "client_token_3" : liste_artiste[14:]}
dico_artiste_token
Out[5]:
{'client_token_1': ['Bruno Mars',
  'Rihanna',
  'Beyonce',
  'Alicia Keys',
  'Frank Ocean',
  'Amy Winehouse',
  'Justin Timberlake'],
 'client_token_2': ['Usher',
  'Mariah Carey',
  'Justin Bieber',
  'Michael Jackson',
  'Christina Aguilera',
  'Chris Brown',
  'Ne-Yo'],
 'client_token_3': ["Destiny's Child",
  'Aaliyah',
  'Mary J. Blige',
  'Marvin Gaye',
  'Janet Jackson',
  'Stevie Wonder']}

Je créé ensuite les fonctions que je vais paralleliser.

In [6]:
# Fonction permettant de récupérer n_chansons d'un artiste et de les lister dans un dictionnaire
def telecharger_paroles(api,artiste,n_chansons = 200,download_each_file = False):
    dico_artiste = {}
    artiste_info=api.search_artist(artiste, max_songs=n_chansons)
    dico_artiste = {song.title : song.lyrics for song in artiste_info.songs}
    if download_each_file :
        nom_fichier = artiste.replace(" ","_").replace(",","").replace("&","").replace("-"," ") + ".txt"
        with open(nom_fichier, 'w') as outfile:
            json.dump(dico_artiste, outfile)
    return dico_artiste

# Fonction qui va être paralléliser et permettant de se connecter à l'api Genius afin de créer un dictionnaire par artiste.
data_json = {}
def connect_api(Token,artistes, n_chansons = 200):
    str_token = dico_token[Token]
    api=genius.Genius(str_token,timeout=20, retries=5)
    for artiste in artistes[Token]:
        dico_artiste = telecharger_paroles(api,artiste, n_chansons = n_chansons)
        data_json[artiste] = dico_artiste

Et je lance le programme...
/!\ cette cellule prend du temps /!\

In [7]:
from concurrent.futures import ThreadPoolExecutor
import json

with ThreadPoolExecutor(3) as pool:
  values = pool.map(lambda x : connect_api(x,dico_artiste_token),liste_token)

with open("data_json.json", 'w') as outfile:
    json.dump(data_json, outfile)

Suppression des doublons ¶

Il y a parfois des chansons qui sont en doublons étant donné qu'il y a souvent des remix, des edits de radio, ... On va donc faire en sorte qu'il n'y ait qu'une occurence de chaque chanson.
J'aurais pu intégrer cette étape lors de la connection à l'api, mais comme je m'en suis rendu compte après le téléchargement du corpus, autant ne pas relancer la boucle et travailler avec ce que j'avais.

In [8]:
import collections
import re
from IPython.display import HTML, display

def supp_doublons_chansons(dico, artiste):
    titres_bruts = list(dico[artiste].keys())
    
    # On créé la Regex pour identifier la partie "variante" à nettoyer
    pattern_nettoyage = re.compile(
        r"\s*(?:[-([][^([)]*(?:remix|edit|live|acoustic|version|radio|mix|ft\.|feat\.)[^([)]*[-)\]]?)",
        flags=re.IGNORECASE,
    )
    
    # On regroupe tous les titres originaux par leur nom "propre"
    groupes_titres = collections.defaultdict(list)
    for titre_original in titres_bruts:
        titre_propre = pattern_nettoyage.sub("", titre_original).strip().lower()
        groupes_titres[titre_propre].append(titre_original)
    
    # Pour chaque groupe, on choisit le MEILLEUR exemplaire original
    # Comme on veut la version épurée, on part du principe que ca sera toujours la version la plus courte
    titres_finaux_conserves = []
    
    for titre_propre, liste_originaux in groupes_titres.items():
        titre_court = min(liste_originaux, key=len, default="")
        titres_finaux_conserves.append(titre_court)
        
    # On affiche les titres écartés
    titres_ecartes = sorted(set(titres_bruts).difference(set(titres_finaux_conserves)))

    if titres_ecartes:
        contenu_html = f"<p>Les chansons de l'artiste <b>{artiste}</b> qui ont été supprimées sont les suivantes :</p>"
        contenu_html += """<details>
        <summary>Afficher la liste des titres supprimés</summary>
        <ul>"""
        for element in titres_ecartes:
            contenu_html += f"<li>{element}</li>"
        contenu_html += "</ul></details>"
    else:
        contenu_html = f"<p>Aucune chanson écartée pour l'artiste {artiste}.</p>"
    display(HTML(contenu_html))

    dico_artiste_sans_doublons = {chanson:paroles for chanson, paroles in dico[artiste].items() if chanson in titres_finaux_conserves}
    return dico_artiste_sans_doublons
In [9]:
corpus = {}
for artiste in data_json:
    dico_artiste_sans_doublons = supp_doublons_chansons(data_json,artiste)
    corpus[artiste] = dico_artiste_sans_doublons

Les chansons de l'artiste Destiny's Child qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • Bills, Bills, Bills (Digital Black-N-Groove Club Mix)
  • Bills, Bills, Bills (Maurice’s Xclusive Dub Mix)
  • Bills, Bills, Bills (Maurice’s Xclusive Livegig Mix)
  • Bootylicious (Big Boyz Remix)
  • Bootylicious (Love: Destiny Version)
  • Bootylicious (Rockwilder Remix)
  • Bug A Boo (Maurice’s Xclusive Bug-A-Boo Club Mix)
  • Bug A Boo (Refugee Camp Remix)
  • Cater 2 U (Scorch Remix Instrumental)
  • Cater 2 U (Storch Remix Edit)
  • Cater 2 U (Storch Remix)
  • Dot (The E-Poppi Mix)
  • Emotion (The Neptunes Remix)
  • Get On The Bus (Radio Edit - Without Rap)
  • Get on the Bus (Radio Version)
  • Girl (Single Version)
  • Girl (The Freshman Remix)
  • Illusion (Maurice’s Destiny Club Mix)
  • Illusion (Maurice’s Radio Mix)
  • Independent Women Part I (Maurice’s Radio Mix)
  • Independent Women Part I (Pasadena Remix)
  • Independent Women Part I (Victor Calderone Club Mix)
  • Jumpin’ Jumpin’ (Maurice’s Jumpin’ Retro Mix)
  • Jumpin’ Jumpin’ (Maurice’s Radio Mix)
  • Jumpin’ Jumpin’ (So So Def Remix)
  • Jumpin’, Jumpin’ (Disney Party Version)
  • Jumpin’, Jumpin’ (Remix Extended Version)
  • Lose My Breath (MGM Mix)
  • Lose My Breath (Maurice’s Nu Soul Mix)
  • Lose My Breath (Paul Johnson’s Club Mix)
  • Nasty Girl (Azzu’s Nu Soul Mix)
  • No, No, No (Camdino Soul Extended Remix)
  • No, No, No (MCD Funki Dred Remix)
  • Say My Name (#1's Edit)
  • Say My Name (Cyril Hahn Remix)
  • Say My Name (Daddy D Remix)
  • Say My Name (Kobe Remix)
  • Say My Name (Live)
  • Say My Name (Maurice’s Old Skool Dub Mix)
  • Say My Name (Radio Edit)
  • Say My Name (Timbaland Remix)
  • So Good (Digital Black-N-Groove Club Mix)
  • So Good (Maurice’s Soul Remix)
  • Soldier (Bizarre Remix)
  • Soldier (Maurice’s Nu Soul Remix)
  • Soldier (Radio Edit)
  • Soldier (Remix)
  • Stand Up for Love (2005 World Children’s Day Anthem) [Radio Edit]
  • Survivor (#1's Edit)
  • Survivor (Azza’z Soul Remix)
  • Survivor (Extended Remix)
  • Survivor (Victor Calderone Club Mix)

Les chansons de l'artiste Usher qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • Burn (Full Phatt Radio Mix)
  • Burn (Radio Mix)
  • Confessions Part II (Remix)
  • DJ Got Us Fallin’ In Love (Alternate Version)
  • DJ Got Us Fallin’ In Love (No Pitbull Version)
  • Hey Daddy (Daddy’s Home) (Remix)
  • More (RedOne & Jimmy Joker Remix)
  • Nice & Slow (B-Rock’s Basement Mix)
  • Nice & Slow (Live)
  • Nice & Slow (Radio Version)
  • No Limit (G-Mix)
  • No Limit (Remix)
  • Throwback (Remix)
  • U Don’t Have to Call (Remix)
  • U Remind Me (Remix)
  • Yeah! (Remix)
  • You Make Me Wanna... (Extended Version)

Les chansons de l'artiste Aaliyah qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • 4 Page Letter (Quiet Storm Mix)
  • 4 Page Letter (Timbaland’s Main Mix)
  • Age Ain’t Nothing but a Number (LP Mix - No Intro)
  • At Your Best (You Are Love) (Gangstar Child Remix)
  • At Your Best (You Are Love) (LP Mix - No Intro)
  • At Your Best (You Are Love) (Stepper’s Ball Remix)
  • Back & Forth (Mr. Lee & R. Kelly’s Remix)
  • Back & Forth (Mr. Lee’s Club Mix)
  • Back & Forth (Ms. Mello Remix)
  • Back & Forth (Video Mix)
  • Don’t Know What To Tell Ya (Handcuff Remix)
  • Down with the Clique (Dancehall Mix)
  • Down with the Clique (Madhouse Mix Radio Edit I)
  • Down with the Clique (Madhouse Mix Radio Edit II)
  • Down with the Clique (Reggae Mix)
  • Enough Said (Solo Version)
  • Got to Give It Up (Radio Edit)
  • Got to Give It Up (Remix)
  • Hot Like Fire (Album Mix Instrumental)
  • Hot Like Fire (Feel My Horns Mix)
  • Hot Like Fire (Timbaland’s Groove Mix)
  • I Can Be (Bud’da Remix)
  • I Don’t Wanna (Remix)
  • If Your Girl Only Knew (Extended Mix)
  • If Your Girl Only Knew (Remix)
  • If Your Girl Only Knew (The New Remix)
  • Miss You (Remix)
  • More Than a Woman (Bump & Flex Club Mix)
  • More Than a Woman (Krunchie Remix ’04)
  • More Than a Woman (MAW Main Mix)
  • One In A Million (Rodney Jerkins Remix)
  • One in a Million (Remix)
  • Rock The Boat (Club Mix by Doug Lazy)
  • Rock The Boat (Club Mix by Mixzo)
  • Rock The Boat (Haruna Remix) [Mixed]
  • Rock The Boat (KAYTRANADA Remix) [Mixed]
  • Steady Ground (Chris Flame Mix)
  • The One I Gave My Heart To (Radio Mix)
  • The One I Gave My Heart To (Soul Solution Club Mix)
  • The Thing I Like (PG Tips Satellite Mix)
  • The Thing I Like (Paul Gotel’s Classic Anthem Mix)
  • The Thing I Like (Paul Gotel’s Deep & Dubby Mix)
  • The Thing I Like (Paul Gotel’s Radio Mix)
  • Try Again (D’Jam Hassan Remix)
  • Try Again (Krunchie Remix ’04)
  • Try Again (Timbaland Remix)
  • We Need A Resolution [Skyrock Radio Edit]
  • What If (J-Dub Remix)

Les chansons de l'artiste Bruno Mars qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • 24K Magic (R3hab Remix)
  • All I Ask (Live at the BBC)
  • Finesse (James Hype Remix)
  • Finesse (Pink Panda Remix)
  • Finesse (Remix)
  • Gorilla (G-Mix)
  • Grenade (Acoustic)
  • Grenade (Dexpistols Remix)
  • Grenade (John de Sohn Mix)
  • Grenade (Michael Meds Mix)
  • Grenade (Passion Pit Remix)
  • Grenade (Remix)
  • Grenade (Walmart Soundcheck Version)
  • I Just Might (Austin Millz Remix)
  • Just The Way You Are (Carl Louis & Martin Danielle Classic Mix)
  • Just The Way You Are (Lee Wright Remix)
  • Just The Way You Are (Manufactured Superstars & Jquintel Remix)
  • Just The Way You Are (Simon Steur Club Mix)
  • Just The Way You Are (Skrillex BatBoi Remix)
  • Just The Way You Are (Steve Smart & WestFunk Club Mix)
  • Just The Way You Are (Walmart Soundcheck Version)
  • Just the Way You Are (2010 Remix)
  • Just the Way You Are (Carl Louis & Martin Danielle Club Mix)
  • Just the Way You Are (J. Reyez Remix)
  • Locked Out of Heaven (Cazzette’s Answering Machine Mix)
  • Locked Out of Heaven (Major Lazer Remix)
  • Locked Out of Heaven (Paul Oakenfold Remix)
  • Locked Out of Heaven (Sultan & Ned Shepard Remix)
  • Locked Out of Heaven (The M Machine Remix)
  • Money Make Her Smile (Remix)
  • Moonshine (The Futuristics Remix)
  • Our First Time (Mixtape Version)
  • Talking to the Moon (Acoustic Piano Version)
  • That’s What I Like (Alan Walker Remix)
  • That’s What I Like (BLVK JVCK Remix)
  • That’s What I Like (Gucci Mane Remix)
  • That’s What I Like (Ludacris Remix)
  • That’s What I Like (PARTYNEXTDOOR Remix)
  • The Other Side (Walmart Soundcheck Version)
  • Treasure (Audien Radio Edit)
  • Treasure (Audien Remix)
  • Treasure (Bailey Smalls Radio Edit)
  • Treasure (Cash Cash Radio Mix)
  • Treasure (Robert DeLong Radio Edit)
  • Treasure (Sharam Radio Remix)

Les chansons de l'artiste Mariah Carey qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • #Beautiful (A$AP Rocky Remix)
  • A No No (Remix)
  • All I Want for Christmas Is You (So So Def Remix)
  • Always Be My Baby (Mr. Dupri Mix)
  • Angels Cry (Remix)
  • Breakdown (The Mo’ Thugs Remix)
  • Bye Bye (Konvict Remix)
  • Fantasy (Bad Boy Remix)
  • Heartbreaker (Remix)
  • Honey (Bad Boy Remix)
  • It’s a Wrap (Remix)
  • I’ll Be Lovin’ U Long Time (Remix)
  • Loverboy (Remix)
  • Obsessed (Remix)
  • Oh Santa! (Remix)
  • Shake It Off (Remix)
  • Thank God I Found You (Make It Last Remix)
  • The Roof (Back in Time) [Mobb Deep Extended Version]
  • Up Out My Face (Remix)
  • We Belong Together (Remix)
  • You’re Mine (Eternal) [Remix]

Les chansons de l'artiste Mary J. Blige qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • Be With You (Remix)
  • Be Without You (Kendu Mix)
  • Be Without You (Rap Version)
  • Beautiful (Black Star Remix)
  • Come See About Me (Remix)
  • Each Tear (Tiziano Ferro Version)
  • Everything [Quiet Mix]
  • Family Affair (Pete Rock Remix)
  • Family Affair (Remix)
  • Feel Inside (Original Version)
  • I Love You (Remix)
  • I’m Goin’ Down (Remix)
  • Just Fine (Treat ’Em Right Remix)
  • Love @ 1st Sight [Kurtis Mantronik’s Rock Lobster Vocal Mix]
  • Love No Limit (Bad Boy Remix)
  • Love Yourself (Remix)
  • MJB da MVP (Remix)
  • Mary Jane (All Night Long) (Remix)
  • Missing You [Curtis + Moore Remix]
  • My Love (Remix)
  • No More Drama (Bad Boy Remix)
  • Ooh! (Bad Boy Remix)
  • Real Love (Remix)
  • Reminisce (Remix)
  • What’s The 411? (Remix)
  • You Don’t Have To Worry (Remix)
  • You Remind Me (Daddy Hip Hop Remix)

Les chansons de l'artiste Justin Bieber qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • Baby (Acoustic Version)
  • Boyfriend (Remix)
  • One Time (Acoustic)
  • Peaches (Remix)
  • Runaway Love (Kanye West Remix)
  • Somebody to Love (Remix)
  • Sorry (Latino Remix)
  • That Should Be Me (Remix)
  • What Do You Mean? (Remix)
  • Yummy (Summer Walker Remix)

Les chansons de l'artiste Rihanna qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • Birthday Cake (Remix)
  • Bitch Better Have My Money (GTA Remix)
  • Bitch Better Have My Money (Remix)
  • Breakin’ Dishes (Soul Seekerz Remix)
  • California King Bed (The Bimbo Jones Radio)
  • Cockiness (Love It) [Remix]
  • Consideration (MK Remix)
  • Diamonds (Jacob Plant Dubstep Remix)
  • Diamonds (Pitbull Remix)
  • Diamonds (Remix)
  • Hate That I Love You (Spanglish Version)
  • Kiss It Better (KAYTRANADA Edition)
  • Kiss It Better (R3hab Remix)
  • Kiss It Better (Remix)
  • Love on the Brain (Don Diablo Remix)
  • Love on the Brain (Gigamesh Remix)
  • Loveeeeeee Song (Mixed)
  • Needed Me (R3hab Remix)
  • Only Girl (In The World) [The Bimbo Jones Radio]
  • Pon de Replay (Full Phatt Remix)
  • Pon de Replay (Remix)
  • Pour It Up (Remix)
  • Rude Boy (Klean Remix)
  • Rude Boy (Remix)
  • S&M (J. Cole Remix)
  • S&M (Remix)
  • Sex with Me (MK Remix)
  • Stay (Solo Version)
  • Talk That Talk (Remix)
  • Umbrella (Acoustic)
  • Umbrella (Music Video Version)
  • Umbrella (Solo Version)
  • We Found Love (Calvin Harris Extended Mix)
  • We Ride (Lenny B Club Mix)
  • Where Have You Been (Remix)
  • Work (Lost Kings Remix)
  • Work (R3HAB Remix)

Les chansons de l'artiste Marvin Gaye qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • Distant Lover (Live 1974)
  • How Sweet It Is (To Be Loved By You) (Live)
  • I Want You (Single Version)
  • I Won’t Cry Anymore (1973 Version)
  • Let’s Get it On (Single Version)
  • The Star-Spangled Banner (Live at the 1983 NBA All-Star Game)
  • What’s going on - single version / stereo
  • You’re All I Need to Get By

Les chansons de l'artiste Michael Jackson qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • A Place with No Name (Original Version)
  • Behind the Mask (Mike’s Mix)
  • Billie Jean (2008 Kanye West Mix)
  • Billie Jean (Extended Version)
  • Billie Jean (Pepsi Version)
  • Billie Jean (Single Version)
  • Blue Gangsta (Original Version)
  • Chicago (Original Version)
  • Dangerous (Immortal Version)
  • Do You Know Where Your Children Are (Original Version)
  • Heal the World (7" Edit)
  • Love Never Felt So Good (Original Version)
  • Loving You (Original Version)
  • P.Y.T. (Pretty Young Thing) (Demo Version)
  • Slave to the Rhythm (Original Version)
  • The Lady In My Life (Full Version)
  • They Don’t Care About Us (Immortal Version)
  • They Don’t Care About Us (LP Edit)
  • They Don’t Care About Us (Love To Infinity’s Classic Paradise Radio Mix)
  • They Don’t Care About Us (Single Edit)
  • They Don’t Care About Us (live Version)
  • Thriller (Single Version)
  • Wanna Be Startin’ Somethin’ (Long Version)
  • Xscape (Original Version)
  • You Rock My World (Album Edit)
  • You Rock My World (Track Masters Remix)

Les chansons de l'artiste Beyonce qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • AMERICA HAS A PROBLEM (Remix)
  • BREAK MY SOUL (THE QUEENS REMIX)
  • Blow (Remix)
  • CUFF IT (WETTER REMIX)
  • Check on It (LP Version)
  • Crazy In Love (Remix)
  • Diva (Homecoming Live)
  • Drunk in Love (Remix)
  • Ego (Remix)
  • Formation (Video Version)
  • Get Me Bodied (Extended Mix)
  • I Been On (Remix)
  • If I Were a Boy (Remix)
  • Irreplaceable (Rap Version)
  • Jealous (Remix)
  • Partition (Remix) [Live at On the Run Tour]
  • Party (Remix)
  • Sorry (Homecoming Live)
  • Standing on the Sun (Remix)
  • Video Phone (Extended Remix)

Les chansons de l'artiste Janet Jackson qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • All for You (Video Mix)
  • Alright (Hip-Hop Mix)
  • Any Time, Any Place (R. Kelly Mix)
  • Doesn’t Really Matter (“All for You” Version)
  • Go Deep (Timbaland Remix)
  • I Get Lonely (TNT Remix)
  • Made for Now (Latin Version)
  • No Sleeep (Single Version)
  • Rhythm Nation (Single Edit with Pledge)
  • Someone To Call My Lover (So So Def Remix)
  • Someone to Call My Lover (Single Edit)
  • Son of a Gun (The Original Flyte Tyme Remix)
  • You Want This (Remix)

Les chansons de l'artiste Christina Aguilera qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • Candyman (RedOne Mix)
  • Come on Over (All I Want Is You) [Video Version]
  • Cuando Me Dé la Gana
  • Genie in a Bottle (The Eddie Arroyo Rhythm Mix)
  • Reflection (Pop Version)
  • The Christmas Song (Holiday Remix)
  • What a Girl Wants (Live - Spotify Anniversaries Version)
  • What a Girl Wants (Video Mix)

Les chansons de l'artiste Alicia Keys qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • A Woman’s Worth (Remix)
  • Blended Family (What You Do For Love) [Remix]
  • Fallin’ (Ali Soundtrack Version)
  • Fallin’ (Remix)
  • Girl on Fire (Bluelight Version)
  • Girl on Fire (Inferno Version)
  • If I Ain’t Got You (Kanye West Remix)
  • If I Ain’t Got You (Piano & Vocal Version)
  • If I Ain’t Got You (Remix)
  • If I Ain’t Got You (Spanish Version)
  • In Common (Black Coffee Remix)
  • In Common (Remix)
  • Like You’ll Never See Me Again (Remix)
  • No One (Acoustic)
  • No One (Live)
  • No One (Remix)
  • Show Me Love (Remix)
  • Try Sleeping With A Broken Heart (Remix)
  • Un-thinkable (I’m Ready) (Live from VH1 Storytellers)
  • Un-thinkable (I’m Ready) [Remix]
  • Underdog (Nicky Jam & Rauw Alejandro Remix)
  • Underdog (Remix)
  • Wasted Energy (Remix)
  • You Don’t Know My Name (Radio Edit)
  • You Don’t Know My Name (Remix)

Les chansons de l'artiste Chris Brown qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • Back to Sleep (Remix)
  • Deuces (Remix)
  • Gimme That (Remix)
  • Go Crazy (Remix)
  • Loyal (East Coast Version)
  • Loyal (West Coast Version)

Les chansons de l'artiste Frank Ocean qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • Cayendo (Sango Remix)
  • Chanel (Remix)
  • Dear April (Justice Remix)
  • Godspeed (dvsn remix)
  • In My Room (BennY RevivaL Remix)
  • Little Demon (Arca Remix)
  • Mitsubishi Sony (Magazine Print Version)
  • Nights (Original Version)
  • Nikes (Magazine CD Version)
  • Nikes (Music Video Version)
  • Pink Matter (Remix)
  • Slide On Me (Remix)
  • White Ferrari (Magazine Print Version)

Les chansons de l'artiste Amy Winehouse qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • (There Is) No Greater Love (Live, Janice Long Session, Miami/2008)
  • Back to Black (Live At Shepherd’s Bush Empire, London / 2007)
  • Back to Black (Live at iTunes Festival London)
  • Back to Black (Mushtaq Vocal Remix)
  • Back to Black (The Rumble Strips Remix)
  • Back to Black (Vodafone Live at TVA)
  • Back to Black (Zilla Rocca Remix)
  • Cherry (Live At Shepherd’s Bush Empire, London / 2007)
  • Cherry (Live at Glastonbury, 2007)
  • Fuck Me Pumps (Live At Shepherd’s Bush Empire, London / 2007)
  • Fuck Me Pumps (MJ Cole Remix)
  • Fuck Me Pumps (Mylo Remix)
  • Hey Little Rich Girl (Live At Shepherd’s Bush Empire, London / 2007)
  • I Heard Love Is Blind (Live At Concorde - Brighton/2008)
  • In My Bed (Bugz In The Attic Vocal Mix)
  • In My Bed (CJ Mix)
  • In My Bed (Live At Concorde - Brighton/2008)
  • Just Friends (Live At Shepherd’s Bush Empire, London / 2007)
  • Know You Now (Live)
  • Love Is A Losing Game (Live @ Mercury Awards)
  • Love Is a Losing Game (Kardinal Beats Remix)
  • Love Is a Losing Game (Live At Shepherd’s Bush Empire, London / 2007)
  • Love Is a Losing Game (Live at iTunes Festival London)
  • Love Is a Losing Game (Moody Boyz Original Ruffian Badboy Remix)
  • Love Is a Losing Game (Truth & Soul Remix)
  • Love is a Losing Game (Live on Other Voices)
  • Me & Mr. Jones (Mixed)
  • Monkey Man (Live At Shepherd’s Bush Empire, London / 2007)
  • Monkey Man (Live On Jools Holland Hootenanny / 2006)
  • Monkey Man - Live at iTunes Festival London
  • Mr Magic (Through The Smoke) (Live, Janice Long Session, Miami/2008)
  • Rehab (Demo Version)
  • Rehab (Desert Eagle Discs Remix)
  • Rehab (Hot Chip Remix)
  • Rehab (I Can’t Help Myself Remix)
  • Rehab (Live At Shepherd’s Bush Empire, London / 2007)
  • Rehab (Live On Jools Holland)
  • Rehab (Live On Later With Jools Holland / 2006)
  • Rehab (Pharoahe Monch Remix)
  • Rehab (Remix)
  • Some Unholy War (Live At Shepherd’s Bush Empire, London / 2007)
  • Stronger Than Me (Harmonic 33 Remix)
  • Take The Box (Live At Concorde - Brighton/2008)
  • Take The Box (Seijis Buggin’ Mix)
  • Take the Box (Live From The Mercury Prize Awards / 2004)
  • Take the Box (The Headquarters Mix)
  • Tears Dry On Their Own (Al Usher Remix)
  • Tears Dry On Their Own (Alix Alvarez Sole Channel Mix)
  • Tears Dry On Their Own (Jo Whiley Live Lounge 2007)
  • Tears Dry On Their Own (Kardinal Beats Remix)
  • Tears Dry On Their Own (Live On Later With Jools Holland / 2006)
  • Tears Dry On Their Own (Live at iTunes Festival London)
  • Tears Dry On Their Own (NYPC’s Fucked Mix)
  • Tears Dry On Their Own (Vodafone Live At TBA)
  • Tears Dry on Their Own (Eric Clapton Remix)
  • Tears Dry on Their Own (Live At Shepherd’s Bush Empire, London / 2007)
  • Tears Dry on Their Own (Live at Glastonbury, 2007)
  • To Know Him Is To Love Him (Live From Dermot O’Leary’s Saturday Sessions / 2006)
  • Valerie (BBC Radio 1 Live Lounge)
  • Valerie (Live At Shepherd’s Bush Empire, London / 2007)
  • Wake Up Alone (Live At Shepherd’s Bush Empire, London / 2007)
  • What Is It About Men (Live At North Sea Jazz Festival)
  • You Know I’m No Good (Demo Version)
  • You Know I’m No Good (Fettes Brot Remix)
  • You Know I’m No Good (Ghostface UK Version)
  • You Know I’m No Good (Live At Shepherd’s Bush Empire, London / 2007)
  • You Know I’m No Good (Live Jo Whiley, BBC Live Lounge Session / 2007)
  • You Know I’m No Good (Skeewiff Mix)
  • You Know I’m No Good (Vodafone Live At TBA)

Les chansons de l'artiste Ne-Yo qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • Because of You (Radio Edit)
  • Because of You (Remix)
  • Do You (Remix)
  • Get Down Like That (Remix)
  • Link Up (Remix)
  • Miss Independent (Remix)
  • Sexy Love (Acoustic)
  • She Knows (Remix 2)
  • She Knows (Remix)
  • So Sick (Acoustic)
  • So Sick (Remix)
  • Stay (Remix)
  • U 2 Luv (Remix)

Les chansons de l'artiste Justin Timberlake qui ont été supprimées sont les suivantes :

Afficher la liste des titres supprimés
  • CAN’T STOP THE FEELING! (Film Version)
  • Cry Me A River (Remix)
  • Cry Me a River (Bill Hamel Justinough Vocal Mix)
  • Cry Me a River (Dirty Vegas Vocal Mix)
  • Like I Love You (Basement Jaxx Mixshow)
  • Like I Love You (Basement Jaxx Vocal Mix)
  • LoveStoned / I Think She Knows (Interlude) (Don Zee Remix Radio Edit)
  • LoveStoned / I Think She Knows (Interlude) [Radio Edit]
  • Mirrors (Live from the BRITs 2013)
  • Mirrors (Mixed)
  • Mirrors (US Radio Edit)
  • My Love (Friscia & Lamboy Remix)
  • My Love (Paul Oakenfold Mix)
  • My Love (Paul Oakenfold Radio Edit)
  • My Love (Single Version)
  • My Love (Steve Angello & Sebastian Ingrosso Mix)
  • My Love (Sticky Count the Money remix)
  • My Love (Terry’s Chosen Few club mix)
  • My Love (The DFA Remix)
  • Not A Bad Thing (Radio Edit)
  • Rock Your Body (Paul Oakenfold Mix)
  • Rock Your Body (Sander Kleinenberg’s Just In The Club Mix)
  • Say Something (Live)
  • Say Something (Video Version)
  • Sexy Ladies (Remix)
  • SexyBack (Armand’s Edit)
  • SexyBack (Armand’s Mix)
  • SexyBack (DJ Sneak’s Sexy Main Mix)
  • SexyBack (DJ Wayne Williams Ol’ Skool Remix)
  • SexyBack (Dean Coleman Radio Edit)
  • SexyBack (Linus Loves Remix)
  • SexyBack (Pokerface Remix)
  • Señorita (Num Club Mix)
  • Señorita (Radio Edit)
  • Suit & Tie (Oliver Nelson Remix)
  • Suit & Tie (Radio Edit)
  • Suit & Tie (Remix)
  • Summer Love (Radio Edit)
  • TKO (Black Friday Remix)
  • TKO (Radio Edit 2)
  • TKO (Radio Edit)
  • Tunnel Vision (Radio Version)
  • What Goes Around... Comes Around (Junkie XL Small Room Mix)
  • What Goes Around... Comes Around (Terry Hunter remix)

J'ai dorénavant un fichier json avec la structure que je souhaite sans doublon que je peux utiliser sans repasser par l'api à chaque fois. Je l'exporte donc au format json.
La base de données brute est maintenant prête à être utilisée, je vais la traiter avant de créer mon modèle.

Enregistrement du Corpus ¶

In [10]:
with open("corpus.json", 'w') as outfile:
    json.dump(corpus, outfile)

Preprocessing ¶

In [11]:
import json
from unidecode import unidecode

import nltk
from nltk.stem import PorterStemmer
from nltk.stem import WordNetLemmatizer

wnl = WordNetLemmatizer()
stm = PorterStemmer()
with open("corpus.json",encoding="utf-8") as f:
    corpus = json.load(f)
    
import re
import string

corpus ={cle:val for cle, val in corpus.items()}

Restructuration de la base ¶

On commence par décomposer notre base pour avoir un vecteur réponse (Y) et une matrice (X) qu'on transformera a terme en matrice TF-IDF :

In [12]:
# Extraction et aplatissement des Titres (title_song_tool), des Données (X) et des Artistes (Y)
X = []
titres_bruts = []
Y = []

for artiste, chansons in corpus.items():
    for titre, paroles in chansons.items():
        titres_bruts.append(titre)
        X.append(paroles)
        Y.append(artiste)

title_song_tool = {i: titre for i, titre in enumerate(titres_bruts)}

print(len(title_song_tool),"chansons importées")
3193 chansons importées
In [13]:
import re
pattern_variante = re.compile(
    r"\s*(?:[-([][^([)]*(?:remix|edit|live|acoustic|version|radio|mix|ft\.|feat\.)[^([)]*[-)\]]?)",
    flags=re.IGNORECASE)

titres_variants_trouves = [titre for titre in title_song_tool.values() if re.search(pattern_variante, titre)]
compteur_variantes = len(titres_variants_trouves)

# On affiche les résultats
print(f"Nombre de chansons variantes : {compteur_variantes}")
contenu_html = """<details>
<summary>Afficher la liste des chansons variantes détectées</summary>
<ul>"""
for titre in titres_variants_trouves:
    contenu_html += f"<li>{titre}</li>"
contenu_html +="</ul></details>"
display(HTML(contenu_html))
Nombre de chansons variantes : 87
Afficher la liste des chansons variantes détectées
  • Jumpin’ Jumpin’ (Azza’s Remix)
  • Upside Down (Live)
  • No No No (Part 2 - Extended Remix)
  • A New Way To Walk (Album Version)
  • No, No, No (Funki Dred Remix)
  • With Me (Full Crew Revocaled Radio Version)
  • Somebody to Love (Remix)
  • Confessions (Remix)
  • U.O.E.N.O (Remix)
  • Are You That Somebody (Remix)
  • Aaliyah - You Never Do A Thing For Me (Shitz Gon’ Change) (Remix) (Unreleased)
  • Round N’ Round (ft. Ne-Yo)
  • Your Girl 2000 (The 45 King HOT Remix)
  • One In A Million / Get Dripped [Remix] [Mixed]
  • Leave The Door Open (Live)
  • Nothin’ On You (Remix)
  • Catch a Grenade (The Hooligans Remix)
  • 24K Magic / Wow. / My Oh My / Havana [Mixed]
  • That’s What I Like / Talk / Circles (Mixed)
  • Ophelia / Locked Out of Heaven (Sultan & Ned Shepard Remix) [Mixed]
  • I Still Believe / Pure Imagination (Damizza Remix)
  • Daydream Interlude (Fantasy Sweet Dub Mix)
  • My All / Stay Awhile (So So Def Remix)
  • Trust Issues (Remix)
  • Hotline Bling (Remix)
  • One Dance (Remix)
  • Love the Way You Lie (Piano Version)
  • Cheers (Gregor Salto Remix)
  • Nothing is Promised (SOPHIE Remix) [Young Thug Version]
  • What’s My Name (Solo Version)
  • Work (Bad Royale Remix) [Clean]
  • You’re the Man (Alternate Version 2)
  • You’re the Man, Pt. I & II (Single Version)
  • My Last Chance (SalaAM ReMi Remix)
  • I’ve Got My Music (Original Vocal Version of “Turn on Some Music”)
  • Jan (Live 1974)
  • I’d Give My Life for You [Alternate Mix]
  • Baby, Baby, Baby (Original Version of ”Til’ Tomorrow”)
  • Don’t Stop ’Till You Get Enough (The DJ Meme Definitive 2016 Remix)
  • Flawless (Remix)
  • WELCOME TO THE RENAISSANCE (Renaissance World Tour Live)
  • Mi Gente (Homecoming Live)
  • Top Off (Homecoming Live)
  • Irreplaceable (Irreemplazable) [Spanish Version]
  • Sexy Lil’ Thug (Remix)
  • Son of a Gun (P. Diddy Remix)
  • Come On Over Baby (All I Want Is You) [Radio Version]
  • My Favorite Things (Live from the Eiffel Tower)
  • Someone You Loved (Grammy Remix)
  • Place To Call My Own (AK Version)
  • Juiciest (Mixtape Version)
  • Fallin’ (live)
  • Killing Me Softly (Live)
  • The River (Alicia’s Version)
  • I Don’t Like (Remix)
  • 24 Hours (Remix)
  • Marvin’s Room (Remix)
  • Tuesday (Remix)
  • Studio (Remix)
  • Sucka for Love (Alternate Version)
  • Feelings Gone (Side-B Acoustic)
  • Easy (Magazine Print Version)
  • Close to You / Never Can Say Goodbye (Live at FYF 2017)
  • Only You (Live at FYF 2017)
  • Night.s (Magazine Print Version)
  • Valerie (’68 Version)
  • Tears Dry (Original Version)
  • We’re Still Friends (Live At The Union Chapel)
  • To Know Him Is to Love Him (Live)
  • Teach Me Tonight (Live, Hootenanny, London, 2004)
  • I Heard It Through the Grapevine (Live On Jools Holland Hootenanny / 2006)
  • Best Friend (Acoustic)
  • I Should Care (Live From The Stables / 2004)
  • He Can Only Hold Her / Doo Wop (That Thing) [Live At Shepherd’s Bush Empire, London / 2007]
  • I Saw Mommy Kissing Santa Claus (Live At Union Chapel, Islington For “The Gospel According To Christmas” / BBC Radio 2)
  • Me & Mr Jones (Live At Shepherd’s Bush Empire, London / 2007)
  • Lullaby of Birdland (Live From The Stables / 2004)
  • Tenderly (Live On Later With Jools Holland / 2006)
  • Intro / Addicted (Live At Shepherd’s Bush Empire, London / 2007)
  • Rehab (Vodafone) [Live at TBA]
  • HUMBLE. (Remix)
  • Because of You (Remix) [Snippet]
  • What Goes Around... Comes Around (Radio Edit)
  • Can’t Stop the Feeling (Disco Suckz Remix)
  • LoveStoned/I Think She Knows (Justice Remix)
  • SexyBack / About Damn Time / Get Low / Hollaback Girl (Mixed)
  • LoveStoned / I Think She Knows (Tiësto Remix)

On se rend compte qu'il reste des chansons avec des titres alternatifs au titre original dans la base de données. En vérifiant de mon côté j'ai remarqué que la plupart des ces variantes étaient restées car le titre original n'était pas parmis les chansons importées. C'est la raison pour laquelle j'ai décidé de toutes les laisser dans le corpus.

Pipeline de traitement automatique de la base ¶

Il s'agit maintenant de nettoyer notre corpus qui constituera notre base de données. Le but est donc de le standardiser pour capter le plus de variations possibles des termes utilisés dans les chansons. Pour cela on supprime la ponctuation, les majuscules, les espaces inutiles.
Je choisi également de supprimer les chansons dont les paroles font moins de 20 mots.

In [14]:
def cleaner(chanson):
    # On passe en minuscules, on vire les accents et on remplace l'apostrophe courbe ’ de Genius par une apostrophe droite '
    chanson = unidecode(chanson).lower()
    chanson = chanson.replace("’", "'")

    # Nettoyage des bruits spécifiques de Genius (Balises et boutons de partage)
    chanson = re.sub(r"\[.*?\]", " ", chanson)
    chanson = re.sub(r"\d*\s*embed\s*share.*", " ", chanson)
    chanson = re.sub(r"embed\s*copy", " ", chanson)

    # Normalisation des contractions anglaises courantes
    chanson = re.sub(r"n't\b", " not", chanson)
    chanson = re.sub(r"\bi'm\b", "i am", chanson)
    chanson = re.sub(r"'s\b", " is", chanson)
    chanson = re.sub(r"'ll\b", " will", chanson)
    chanson = re.sub(r"'ve\b", " have", chanson)
    chanson = re.sub(r"'re\b", " are", chanson)
    chanson = re.sub(r"'d\b", " would", chanson)

    # On supprime la ponctuation
    ponctuations = string.punctuation + "\n\r"
    chanson = re.sub(f"[{re.escape(ponctuations)}]", " ", chanson)

    # Nettoyage final des espaces multiples
    chanson = re.sub(r"\s+", " ", chanson)

    return chanson.strip()

values = map(cleaner,X)
values = list(values)

who_do_we_remove = {idx:(Y[idx],title_song_tool[idx]) for idx,valeur in enumerate(values) if len(valeur) <= 20}
print(f"{len(who_do_we_remove)} chansons ont été supprimées car il y a moins de 20 mots dans leur texte.")
30 chansons ont été supprimées car il y a moins de 20 mots dans leur texte.
In [15]:
print("la liste des chansons qui ont été supprimées est la suivante :")
for i in who_do_we_remove.keys():
    print(i,":",title_song_tool[i])
la liste des chansons qui ont été supprimées est la suivante :
130 : So Emotional
138 : S.Y.T (Sweet, Young & Tender)
141 : Intro/Overture
142 : Soldier (Dance Interlude)
145 : Life
407 : Don’t Call Me No More
429 : Back & Forth (Ms. Mello Instrumental)
1418 : Main Theme From Trouble Man (2)
1434 : “T” Plays It Cool
1441 : I Want You (Intro Jam)
1444 : T’' Stands For Trouble
1940 : Interlude: Online
1959 : Are You Still Up
1967 : Sweet Dreams
1978 : Back
1981 : Interlude: Come Back
2158 : Kimono Girl
2321 : As I Am (Intro)
2325 : De Novo Adagio
2348 : KEYS
2619 : In Here Somewhere
2625 : White
2626 : Rushes To
2636 : Florida
2995 : Over You
3016 : New Love
3152 : Fresh Leaves
3177 : People Everyday
3185 : She Likes The Rain
3188 : As Dreamers Do
In [16]:
Y  = [artist for i,artist in  enumerate(Y) if i not in who_do_we_remove.keys()]
X  = [lyrics for i,lyrics in  enumerate(values) if i not in who_do_we_remove.keys()]

title_song_tool = {cle:valeur for cle, valeur in title_song_tool.items() if cle not in list(who_do_we_remove.keys())}
## On vérifie que ca a bien supprimé
print(len(title_song_tool))
3163

On passe à la racinisation des mots pour capter le plus de variations possibles du même mot. On en profite pour supprimer les mots vides car ils n'apportent aucune information au document.

In [17]:
from nltk.stem import PorterStemmer
from nltk.corpus import stopwords
english_stopwords = set(stopwords.words("english"))

def stem_stopwords(chanson,liste_motvides):
    return(
        [stm.stem(mot) for mot in chanson.split() if mot not in liste_motvides]
    )

X = [stem_stopwords(chanson,english_stopwords) for chanson in X]

Vectorisation ¶

On passe ensuite à la vectorisation. On garde les n-gram de taille 1 et 2 qui sont parfois des informations pertinentes. Les refrains répétant certains mots des dizaines de fois dans les chansons, j'ai fais le choix d'appliquer une échelle logarithmique afin de réduire cet effet parasite (sublinear_tf=True). Les bigrammes vont également créer énormément de bruit car la majorité d'entre eux ne sont présents que dans un seul titre. Je n'ai donc conservé que les unigrammes et bigrammes présents dans au moins deux documents du corpus.

In [18]:
from sklearn.feature_extraction.text import TfidfVectorizer

def dummy_fun(doc):
    return doc

vectorizer = TfidfVectorizer(ngram_range=(1, 2),
                             use_idf=True, smooth_idf=True, # idf lissé
                             min_df=2,
                             sublinear_tf=True,
                             norm='l2',
                             analyzer = 'word',
                             tokenizer=dummy_fun,
                             preprocessor = dummy_fun)
In [19]:
dtm = vectorizer.fit_transform(X)
C:\Users\User\anaconda3\envs\projet_musique\Lib\site-packages\sklearn\feature_extraction\text.py:526: UserWarning: The parameter 'token_pattern' will not be used since 'tokenizer' is not None'
  warnings.warn(

On visualise la matrice TF-IdF qu'on obtient :

In [20]:
import pandas as pd
pd.DataFrame(dtm.toarray(),columns=vectorizer.get_feature_names_out())
Out[20]:
0 000 01 01 know 1 1 1 1 2 10 10 9 10 minut ... zone caus zone got zone move zoom zoom zoom zoomin zz zz bee zz come zz zz
0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
3 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
4 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
3158 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
3159 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
3160 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
3161 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
3162 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0

3163 rows × 53903 columns

Modélisation ¶

Choix du modèle ¶

Maintenant que notre matrice TF-IDF est terminée, on passe à la partie prédiction. Le but de ce projet n'étant pas d'être le plus précis possible mais de se réapproprier les différentes techniques de text-mining, j'ai fait le choix de partir sur un modèle de prédiction simple : un modèle de classification multinomial naif bayesien. Il n'a pas beaucoup de paramètres, est rapide d'execution tout en étant capable de gérer le fléau de dimension. C'est d'ailleurs généralement le modèle de référence en classification de texte, notamment en classification multinomial.
J'ai décidé de conserver 67% des observations pour l'apprentissage du modèle et 33% pour le tester et m'assurer de ne pas faire de sur-apprentissage. J'ai réalisé une validation croisée pour le choix des hyperparamètres.

In [21]:
from sklearn.model_selection import train_test_split,GridSearchCV
from sklearn.naive_bayes import MultinomialNB

x_train,x_test,y_train,y_test = train_test_split(dtm,Y,test_size = 0.33)
In [22]:
MultinomialNB().get_params()
Out[22]:
{'alpha': 1.0, 'class_prior': None, 'fit_prior': True, 'force_alpha': True}

Optimisation des Hyperparamètres ¶

Le critère de performance que j'ai décidé d'utiliser pour le choix de mes paramètres est la métrique ROC AUC OVR Weighted. C'est une métrique classique, robuste et spécialement conçu pour les problèmes de classification multiclasse, parfaitement adaptée à ma problématique actuelle. Cette métrique a été utilisée pour le choix des hypermaramètres dans une validation croisée.

In [23]:
import numpy as np

mod= MultinomialNB()

parametres = {
    "alpha": [0.001,0.005,0.01,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0],
    "fit_prior" : [True, False],

}
grid = GridSearchCV(mod,parametres,verbose=1,scoring = "roc_auc_ovr_weighted",n_jobs=4)
grid.fit(x_train,y_train)
Fitting 5 folds for each of 28 candidates, totalling 140 fits
Out[23]:
GridSearchCV(estimator=MultinomialNB(), n_jobs=4,
             param_grid={'alpha': [0.001, 0.005, 0.01, 0.05, 0.1, 0.2, 0.3, 0.4,
                                   0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
                         'fit_prior': [True, False]},
             scoring='roc_auc_ovr_weighted', verbose=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
Parameters
estimator estimator: estimator object

This is assumed to implement the scikit-learn estimator interface.
Either estimator needs to provide a ``score`` function,
or ``scoring`` must be passed.
MultinomialNB()
param_grid param_grid: dict or list of dictionaries

Dictionary with parameters names (`str`) as keys and lists of
parameter settings to try as values, or a list of such
dictionaries, in which case the grids spanned by each dictionary
in the list are explored. This enables searching over any sequence
of parameter settings.
{'alpha': [0.001, 0.005, ...], 'fit_prior': [True, False]}
scoring scoring: str, callable, list, tuple or dict, default=None

Strategy to evaluate the performance of the cross-validated model on
the test set.

If `scoring` represents a single score, one can use:

- a single string (see :ref:`scoring_string_names`);
- a callable (see :ref:`scoring_callable`) that returns a single value;
- `None`, the `estimator`'s
:ref:`default evaluation criterion ` is used.

If `scoring` represents multiple scores, one can use:

- a list or tuple of unique strings;
- a callable returning a dictionary where the keys are the metric
names and the values are the metric scores;
- a dictionary with metric names as keys and callables as values.

See :ref:`multimetric_grid_search` for an example.
'roc_auc_ovr_weighted'
n_jobs n_jobs: int, default=None

Number of jobs to run in parallel.
``None`` means 1 unless in a :obj:`joblib.parallel_backend` context.
``-1`` means using all processors. See :term:`Glossary `
for more details.

.. versionchanged:: v0.20
`n_jobs` default changed from 1 to None
4
refit refit: bool, str, or callable, default=True

Refit an estimator using the best found parameters on the whole
dataset.

For multiple metric evaluation, this needs to be a `str` denoting the
scorer that would be used to find the best parameters for refitting
the estimator at the end.

Where there are considerations other than maximum score in
choosing a best estimator, ``refit`` can be set to a function which
returns the selected ``best_index_`` given ``cv_results_``. In that
case, the ``best_estimator_`` and ``best_params_`` will be set
according to the returned ``best_index_`` while the ``best_score_``
attribute will not be available.

The refitted estimator is made available at the ``best_estimator_``
attribute and permits using ``predict`` directly on this
``GridSearchCV`` instance.

Also for multiple metric evaluation, the attributes ``best_index_``,
``best_score_`` and ``best_params_`` will only be available if
``refit`` is set and all of them will be determined w.r.t this specific
scorer.

See ``scoring`` parameter to know more about multiple metric
evaluation.

See :ref:`sphx_glr_auto_examples_model_selection_plot_grid_search_digits.py`
to see how to design a custom selection strategy using a callable
via `refit`.

See :ref:`this example
`
for an example of how to use ``refit=callable`` to balance model
complexity and cross-validated score.

.. versionchanged:: 0.20
Support for callable added.
True
cv cv: int, cross-validation generator or an iterable, default=None

Determines the cross-validation splitting strategy.
Possible inputs for cv are:

- None, to use the default 5-fold cross validation,
- integer, to specify the number of folds in a `(Stratified)KFold`,
- :term:`CV splitter`,
- An iterable yielding (train, test) splits as arrays of indices.

For integer/None inputs, if the estimator is a classifier and ``y`` is
either binary or multiclass, :class:`StratifiedKFold` is used. In all
other cases, :class:`KFold` is used. These splitters are instantiated
with `shuffle=False` so the splits will be the same across calls.

Refer :ref:`User Guide ` for the various
cross-validation strategies that can be used here.

.. versionchanged:: 0.22
``cv`` default value if None changed from 3-fold to 5-fold.
None
verbose verbose: int

Controls the verbosity: the higher, the more messages.

- >1 : the computation time for each fold and parameter candidate is
displayed;
- >2 : the score is also displayed;
- >3 : the fold and candidate parameter indexes are also displayed
together with the starting time of the computation.
1
pre_dispatch pre_dispatch: int, or str, default='2*n_jobs'

Controls the number of jobs that get dispatched during parallel
execution. Reducing this number can be useful to avoid an
explosion of memory consumption when more jobs get dispatched
than CPUs can process. This parameter can be:

- None, in which case all the jobs are immediately created and spawned. Use
this for lightweight and fast-running jobs, to avoid delays due to on-demand
spawning of the jobs
- An int, giving the exact number of total jobs that are spawned
- A str, giving an expression as a function of n_jobs, as in '2*n_jobs'
'2*n_jobs'
error_score error_score: 'raise' or numeric, default=np.nan

Value to assign to the score if an error occurs in estimator fitting.
If set to 'raise', the error is raised. If a numeric value is given,
FitFailedWarning is raised. This parameter does not affect the refit
step, which will always raise the error.
nan
return_train_score return_train_score: bool, default=False

If ``False``, the ``cv_results_`` attribute will not include training
scores.
Computing training scores is used to get insights on how different
parameter settings impact the overfitting/underfitting trade-off.
However computing the scores on the training set can be computationally
expensive and is not strictly required to select the parameters that
yield the best generalization performance.

.. versionadded:: 0.19

.. versionchanged:: 0.21
Default value was changed from ``True`` to ``False``
False
MultinomialNB(alpha=0.2, fit_prior=False)
Parameters
alpha alpha: float or array-like of shape (n_features,), default=1.0

Additive (Laplace/Lidstone) smoothing parameter
(set alpha=0 and force_alpha=True, for no smoothing).
0.2
force_alpha force_alpha: bool, default=True

If False and alpha is less than 1e-10, it will set alpha to
1e-10. If True, alpha will remain unchanged. This may cause
numerical errors if alpha is too close to 0.

.. versionadded:: 1.2
.. versionchanged:: 1.4
The default value of `force_alpha` changed to `True`.
True
fit_prior fit_prior: bool, default=True

Whether to learn class prior probabilities or not.
If false, a uniform prior will be used.
False
class_prior class_prior: array-like of shape (n_classes,), default=None

Prior probabilities of the classes. If specified, the priors are not
adjusted according to the data.
None
In [24]:
import matplotlib.pyplot as plt

plt.scatter(y= grid.cv_results_["mean_test_score"],
            x = grid.cv_results_["param_alpha"].data,
            color="blue",
            edgecolor="k",
            alpha=0.7)
plt.title(
    "Impact du paramètre de régularisation (Alpha) sur le score de validation",
    fontsize=12,
    fontweight="bold",
    pad=15,
)
plt.xlabel("Valeur à l'échelle logarithmique de l'Hyperparamètre Alpha", fontsize=10)
plt.ylabel("Score ROC AUC moyen (OVR Pondéré)", fontsize=10)

plt.xscale("log")

plt.grid(True, which="both", linestyle="--", alpha=0.5)
plt.show()
No description has been provided for this image

On affiche les meilleurs paramètres :

In [25]:
grid.best_params_
Out[25]:
{'alpha': 0.2, 'fit_prior': False}

Evaluation du modèle ¶

Et on affiche les résultats de prédiction des meilleurs paramètres

In [26]:
prediction = grid.predict(x_test)
In [27]:
from sklearn.metrics import classification_report, roc_auc_score
roc_auc_weighted = roc_auc_score(
    y_test,
    grid.predict_proba(x_test),
    multi_class="ovr",
    average="weighted",
)

print(classification_report(y_test, prediction))
print("Nombre d'erreur de prédictions sur un total de %d observations : %d, ce qui représente %.3f de précision." % (x_test.shape[0], (y_test != prediction).sum(),(1-(y_test != prediction).sum()/x_test.shape[0])))
print(f"Le Score ROC AUC moyen (OVR Pondéré) est de : {roc_auc_weighted:.3f}")
                    precision    recall  f1-score   support

           Aaliyah       1.00      0.38      0.55        37
       Alicia Keys       0.81      0.21      0.34        61
     Amy Winehouse       0.94      0.33      0.48        49
           Beyonce       0.50      0.09      0.16        64
        Bruno Mars       1.00      0.11      0.20        37
       Chris Brown       0.17      0.88      0.28        56
Christina Aguilera       0.58      0.26      0.36        68
   Destiny's Child       0.77      0.17      0.28        58
       Frank Ocean       0.62      0.30      0.41        66
     Janet Jackson       0.52      0.26      0.35        53
     Justin Bieber       0.21      0.27      0.23        60
 Justin Timberlake       0.56      0.10      0.18        48
      Mariah Carey       0.24      0.38      0.30        55
       Marvin Gaye       0.41      0.92      0.57        53
     Mary J. Blige       0.24      0.50      0.33        58
   Michael Jackson       0.63      0.54      0.58        48
             Ne-Yo       0.45      0.33      0.38        64
           Rihanna       0.33      0.09      0.14        46
             Usher       0.26      0.30      0.28        63

          accuracy                           0.34      1044
         macro avg       0.54      0.34      0.34      1044
      weighted avg       0.52      0.34      0.33      1044

Nombre d'erreur de prédictions sur un total de 1044 observations : 690, ce qui représente 0.339 de précision.
Le Score ROC AUC moyen (OVR Pondéré) est de : 0.850

Prédiction ¶

Regardons maintenant vers quel artiste se rapproche des titres iconiques de la musique.

"Hello" de Adele ¶

In [28]:
lyrics = """Hello, it's me I was wondering if after all these years you'd like to meet To go over everything 
They say that time's supposed to heal ya But I ain't done much healing 
Hello, can you hear me? I'm in California dreaming about who we used to be
When we were younger and free 
I've forgotten how it felt before the world fell at our feet
There's such a difference between us
And a million miles
Hello from the other side
I must've called a thousand times
To tell you I'm sorry for everything that I've done
But when I call, you never seem to be home
Hello from the outside
At least I can say that I've tried
To tell you I'm sorry for breaking your heart
But it don't matter, it clearly doesn't tear you apart anymore
Hello, how are you?
It's so typical of me to talk about myself, I'm sorry
I hope that you're well
Did you ever make it out of that town where nothing ever happened?
It's no secret that the both of us are running out of time
So hello from the other side (Other side)
I must've called a thousand times (Thousand times)
To tell you I'm sorry for everything that I've done
But when I call, you never seem to be home
Hello from the outside (Outside)
At least I can say that I've tried (I've tried)
To tell you I'm sorry for breaking your heart
But it don't matter, it clearly doesn't tear you apart anymore
Ooh-ooh, anymore
Ooh-ooh-ooh, anymore
Ooh-ooh, anymore
Anymore
Hello from the other side (Other side)
I must've called a thousand times (Thousand times)
To tell you I'm sorry for everything that I've done
But when I call, you never seem to be home
Hello from the outside (Outside)
At least I can say that I've tried (I've tried)
To tell you I'm sorry for breaking your heart
But it don't matter, it clearly doesn't tear you apart anymore"""

lyrics = cleaner(lyrics)
lyrics = stem_stopwords(lyrics,english_stopwords)

lyrics_dtm = vectorizer.transform(lyrics)
who_does_adele_sound_like = grid.predict_proba(lyrics_dtm)
pd.DataFrame({"artiste":grid.classes_,"probabilité":who_does_adele_sound_like[0]}).sort_values(by = "probabilité",ascending = False).head()
Out[28]:
artiste probabilité
3 Beyonce 0.118202
18 Usher 0.100323
11 Justin Timberlake 0.082943
0 Aaliyah 0.082004
5 Chris Brown 0.075223

"What a Wonderful World" de Louis Armstrong ¶

In [29]:
lyrics = """
I see trees of green, red roses too
I see them bloom for me and you
And I think to myself what a wonderful world.
I see skies of blue and clouds of white
The bright blessed day, the dark sacred night
And I think to myself what a wonderful world.
The colors of the rainbow so pretty in the sky
Are also on the faces of people going by
I see friends shaking hands saying how do you do
They're really saying I love you.
I hear babies crying, I watch them grow
They'll learn much more than I'll never know
And I think to myself what a wonderful world
Yes I think to myself what a wonderful world.
"""

lyrics = cleaner(lyrics)
lyrics = stem_stopwords(lyrics,english_stopwords)

lyrics_dtm = vectorizer.transform(lyrics)
who_does_adele_sound_like = grid.predict_proba(lyrics_dtm)
pd.DataFrame({"artiste":grid.classes_,"probabilité":who_does_adele_sound_like[0]}).sort_values(by = "probabilité",ascending = False).head(5)
Out[29]:
artiste probabilité
10 Justin Bieber 0.090341
15 Michael Jackson 0.080668
3 Beyonce 0.076520
4 Bruno Mars 0.064778
5 Chris Brown 0.062814

Conclusion ¶

Ce projet était un moyen ludique pour moi de me remémorer ce qui se faisait en NLP et les étapes dites "classiques" de text mining. J'ai pu revoir les différents procédés pouvant mener à des modèles prédictifs à partir de données textuelles.

J'ai identifié de nombreux points que j'aurais pu développer pour répondre de façon plus pertinente à la question de départ. La pop étant un style musical plutôt large, j'aurai pu par exemple me restreindre à un style moins vaste comme le neo-soul ou plus riche en terme de texte comme le rap. J'aurai également pu passer par le procédé de lemmatisation plutôt que celui de racinisation. Une autre autre piste aurait été de télécharger la discographie complète de chacun des artistes et d'utiliser des modèles de prédiction plus complexes comme des llm qui sont connus pour être très performants sur des données textuelles (mais beaucoup plus contraignant d'un point de vue énergétique).

Quoiqu'il en soit, ce projet a été un plaisir pour moi de retravailler sur ce type de données et j'espère à l'avenir refaire des projets dans ce domaine, qui plus est dans le domaine de la musique. Encore merci à Hugo Nattagh pour l'idée du projet !

Documentation ¶

  • https://stackoverflow.com/questions/24873773/web-scraping-rap-lyrics-on-rap-genius-w-python
  • https://docs.genius.com/

Une idée préliminaire est de s'inspirer du travail de Hugo Nattagh : https://github.com/Hugo-Nattagh/2017-Hip-Hop