ぷそさんのプログラミング研究所

【Spotify API】AIを使って曲の特徴からアーティスト分類してみた|Python

目次

曲の特徴からアーティスト分類の概要

Spotify APIを使うと,曲の特徴を簡単に取得することができます。

例えば,曲調テンポ(BPM)などが得られます。

そこで,これらの曲の特徴からアーティストを分類するモデルを作る方法をご紹介します。

分類には,機械学習モデルのランダムフォレストを使用します。

アーティスト分類する方法

準備するもの

今回は,Pythonを使って分類をしてみました。

使用するモジュールは以下の通りです。

  • pandas(データ分析用)
  • spotipy(Spotify連携)
  • scikit-learn(ランダムフォレスト)

また,Spotify APIの連携が済んでいない方は連携をしてください。

モジュールのインストールとAPIの設定

まずは,データ取得のためのモジュールやAPIの設定をします。

import pandas as pd
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import time

# spotify developerから取得したclient_idとclient_secretを入力
client_id = '自分のclient_idを入力'
client_secret = '自分のclient_secretを入力'
client_credentials_manager = spotipy.oauth2.SpotifyClientCredentials(client_id, client_secret)
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)

データを取得するための関数を定義(コピペ可)

データを簡単に取得するための関数を定義します。

プレイリスト内の曲IDを取得する関数

# プレイリスト内の曲IDを取得
def getTrackIDs(playlist_ids):
  track_ids = []
  
  for playlist_id in playlist_ids:
    playlist = sp.playlist(playlist_id)
    while playlist['tracks']['next']:
      for item in playlist['tracks']['items']:
        track = item['track']
        if not track['id'] in track_ids:
          track_ids.append(track['id'])
      playlist['tracks'] = sp.next(playlist['tracks'])
    else:
      for item in playlist['tracks']['items']:
        track = item['track']
        if not track['id'] in track_ids:
          track_ids.append(track['id'])

  return track_ids

曲IDを入れると特徴量を取得する関数

# 曲IDを入れると,特徴を取得
def getTrackFeatures(id):
  meta = sp.track(id)
  features = sp.audio_features(id)

  name = meta['name']
  album = meta['album']['name']
  artist = meta['album']['artists'][0]['name']
  release_date = meta['album']['release_date']
  length = meta['duration_ms']
  popularity = meta['popularity']
  key = features[0]['key']
  mode = features[0]['mode']
  danceability = features[0]['danceability']
  acousticness = features[0]['acousticness']
  energy = features[0]['energy']
  instrumentalness = features[0]['instrumentalness']
  liveness = features[0]['liveness']
  loudness = features[0]['loudness']
  speechiness = features[0]['speechiness']
  tempo = features[0]['tempo']
  time_signature = features[0]['time_signature']
  valence = features[0]['valence']

  track = [name, album, artist, release_date, length, popularity, key, mode, danceability, acousticness, energy, instrumentalness, liveness, loudness, speechiness, tempo, time_signature, valence]
  return track

dataframeにまとめる関数

# プレリストIDを入れるとdataframeを作成
def make_artist_df(ID):
    playlist = ID
    track_ids = getTrackIDs(playlist)
    
    tracks = []
    for track_id in track_ids:
        time.sleep(0.3)
        track = getTrackFeatures(track_id)
        tracks.append(track)
        
    df = pd.DataFrame(tracks, columns = ['name', 'album', 'artist', 'release_date', 'length', 'popularity', 'key', 'mode', 'danceability', 'acousticness', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness', 'tempo', 'time_signature', 'valence'])
    return df

アーティストの曲データの取得

今回は,「あいみょん」と「椎名林檎」の曲を分類するモデルを作成します。

学習データには「本人名義の楽曲」,テストデータには「他のアーティストに提供している楽曲」としています。

まずは,「あいみょん」の楽曲が入っているプレイリストを検索してURLを取得します。

URLはhttps://open.spotify.com/playlist/ABCDEFGのようになっていると思いますが,ABCDEFGが必要となります。

例えば,あいみょんのプレリストのURLがhttps://open.spotify.com/playlist/ABCDEFGのときに,

df_aimyon = make_artist_df(['ABCDEFG'])

とすると,このプレイリストの曲を取得することができます。

「あいみょん」と「椎名林檎」の楽曲一覧を取得し,整理するコードは以下の通りです。

# プレイリストの取得
df_aimyon = make_artist_df(['2OeC2Zk6PArga135rOoRf2'])
df_ringo = make_artist_df(['0TVkEpBwbo5YYVvNAvjq7c'])

# 提供曲だけ除く
df_ringo_cut = df_ringo[df_ringo["album"] != "逆輸入 〜航空局〜"]

# 必要な特徴量だけ抽出
df_r = df_ringo_cut[['artist', 'length', 'popularity',
       'key', 'mode', 'danceability', 'acousticness', 'energy',
       'instrumentalness', 'liveness', 'loudness', 'speechiness', 'tempo',
       'time_signature', 'valence']]

df_a = df_aimyon[['artist', 'length', 'popularity',
       'key', 'mode', 'danceability', 'acousticness', 'energy',
       'instrumentalness', 'liveness', 'loudness', 'speechiness', 'tempo',
       'time_signature', 'valence']]

# 本人名義だけ抽出
df_a = df_a[df_a["artist"] == "Aimyon"]
df_r = df_r[df_r["artist"] == "Sheena Ringo"]

次に,テストデータとなる「他のアーティストに提供している楽曲」を同じ方法で取得します。

# 提供曲リスト
df_a_supply = make_artist_df(['1DHx4I1yaelFYhDgsTiRAr'])
df_r_supply = make_artist_df(['6MZGHZDQSSZBytE5wQr1WX'])

df_a_test = df_a_supply[['artist', 'length', 'popularity',
       'key', 'mode', 'danceability', 'acousticness', 'energy',
       'instrumentalness', 'liveness', 'loudness', 'speechiness', 'tempo',
       'time_signature', 'valence']]

df_r_test = df_r_supply[['artist', 'length', 'popularity',
       'key', 'mode', 'danceability', 'acousticness', 'energy',
       'instrumentalness', 'liveness', 'loudness', 'speechiness', 'tempo',
       'time_signature', 'valence']]

df_a_test["artist"] = "Aimyon"
df_r_test["artist"] = "Sheena Ringo"

学習データとテストデータを整理する

ここまでで,使うデータを全て取得できましたのでデータを整理します。

df_trainに学習データ,df_testにテストデータが格納されるように整理します。

ここで,「あいみょん」の学習データ数が79,「椎名林檎」の学習データ数が195となっており,データ不均衡であるため,椎名林檎のデータ数を減らして同じデータ数にしています。

df_train = pd.concat([df_a,df_r.sample(79)],ignore_index=True)
df_test = pd.concat([df_a_test, df_r_test],ignore_index=True)

X_train = df_train.drop("artist", axis=1)
y_train = df_train["artist"]
X_test = df_test.drop("artist", axis=1)
y_test = df_test["artist"]

予測モデルの作成

今回は,ランダムフォレストで予測を行います。

精度は追求しないのでパラメータは初期設定にしています。

# 学習
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier()
model.fit(X_train, y_train) # モデルの学習

結果の確認

それでは,結果を確認してみましょう。

# 予測値と実値との比較
df_result = pd.DataFrame()
df_result["true"] = y_test
df_result["Pred"] = model.predict(X_test)

以下のような出力になると思います。

print(df_result)
-------------------------------------------
            true          Pred
0         Aimyon  Sheena Ringo
1         Aimyon  Sheena Ringo
2         Aimyon  Sheena Ringo
3         Aimyon  Sheena Ringo
4         Aimyon        Aimyon
5         Aimyon        Aimyon
6         Aimyon        Aimyon
7   Sheena Ringo  Sheena Ringo
8   Sheena Ringo        Aimyon
9   Sheena Ringo  Sheena Ringo

これでは,分かりにくいので混同行列で結果を見てみましょう。

今回は椎名林檎の提供楽曲が多かったため,極端になっていますが,Accurateは77%(20/26)でした。

コード全文

今回使用したコードはこちらです。

import pandas as pd
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import time
# spotify developerから取得したclient_idとclient_secretを入力
client_id = '自分のclient_idを入力'
client_secret = '自分のclient_secretを入力'
client_credentials_manager = spotipy.oauth2.SpotifyClientCredentials(client_id, client_secret)
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
# プレイリスト内の曲IDを取得
def getTrackIDs(playlist_ids):
  track_ids = []
  
  for playlist_id in playlist_ids:
    playlist = sp.playlist(playlist_id)
    while playlist['tracks']['next']:
      for item in playlist['tracks']['items']:
        track = item['track']
        if not track['id'] in track_ids:
          track_ids.append(track['id'])
      playlist['tracks'] = sp.next(playlist['tracks'])
    else:
      for item in playlist['tracks']['items']:
        track = item['track']
        if not track['id'] in track_ids:
          track_ids.append(track['id'])
  return track_ids
# 曲IDを入れると,特徴を取得
def getTrackFeatures(id):
  meta = sp.track(id)
  features = sp.audio_features(id)
  name = meta['name']
  album = meta['album']['name']
  artist = meta['album']['artists'][0]['name']
  release_date = meta['album']['release_date']
  length = meta['duration_ms']
  popularity = meta['popularity']
  key = features[0]['key']
  mode = features[0]['mode']
  danceability = features[0]['danceability']
  acousticness = features[0]['acousticness']
  energy = features[0]['energy']
  instrumentalness = features[0]['instrumentalness']
  liveness = features[0]['liveness']
  loudness = features[0]['loudness']
  speechiness = features[0]['speechiness']
  tempo = features[0]['tempo']
  time_signature = features[0]['time_signature']
  valence = features[0]['valence']
  track = [name, album, artist, release_date, length, popularity, key, mode, danceability, acousticness, energy, instrumentalness, liveness, loudness, speechiness, tempo, time_signature, valence]
  return track
# プレリストIDを入れるとdataframeを作成
def make_artist_df(ID):
    playlist = ID
    track_ids = getTrackIDs(playlist)
    
    tracks = []
    for track_id in track_ids:
        time.sleep(0.3)
        track = getTrackFeatures(track_id)
        tracks.append(track)
        
    df = pd.DataFrame(tracks, columns = ['name', 'album', 'artist', 'release_date', 'length', 'popularity', 'key', 'mode', 'danceability', 'acousticness', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness', 'tempo', 'time_signature', 'valence'])
    return df
# プレイリストの取得
df_aimyon = make_artist_df(['2OeC2Zk6PArga135rOoRf2'])
df_ringo = make_artist_df(['0TVkEpBwbo5YYVvNAvjq7c'])
# 提供曲だけ除く
df_ringo_cut = df_ringo[df_ringo["album"] != "逆輸入 〜航空局〜"]
# 必要な特徴量だけ抽出
df_r = df_ringo_cut[['artist', 'length', 'popularity',
       'key', 'mode', 'danceability', 'acousticness', 'energy',
       'instrumentalness', 'liveness', 'loudness', 'speechiness', 'tempo',
       'time_signature', 'valence']]
df_a = df_aimyon[['artist', 'length', 'popularity',
       'key', 'mode', 'danceability', 'acousticness', 'energy',
       'instrumentalness', 'liveness', 'loudness', 'speechiness', 'tempo',
       'time_signature', 'valence']]
# 本人名義だけ抽出
df_a = df_a[df_a["artist"] == "Aimyon"]
df_r = df_r[df_r["artist"] == "Sheena Ringo"]
# 提供曲リスト
df_a_supply = make_artist_df(['1DHx4I1yaelFYhDgsTiRAr'])
df_r_supply = make_artist_df(['6MZGHZDQSSZBytE5wQr1WX'])
df_a_test = df_a_supply[['artist', 'length', 'popularity',
       'key', 'mode', 'danceability', 'acousticness', 'energy',
       'instrumentalness', 'liveness', 'loudness', 'speechiness', 'tempo',
       'time_signature', 'valence']]
df_r_test = df_r_supply[['artist', 'length', 'popularity',
       'key', 'mode', 'danceability', 'acousticness', 'energy',
       'instrumentalness', 'liveness', 'loudness', 'speechiness', 'tempo',
       'time_signature', 'valence']]
df_a_test["artist"] = "Aimyon"
df_r_test["artist"] = "Sheena Ringo"
df_train = pd.concat([df_a,df_r.sample(79)],ignore_index=True)
df_test = pd.concat([df_a_test, df_r_test],ignore_index=True)
X_train = df_train.drop("artist", axis=1)
y_train = df_train["artist"]
X_test = df_test.drop("artist", axis=1)
y_test = df_test["artist"]
# 学習
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier()
model.fit(X_train, y_train) # モデルの学習
# 予測値と実値との比較
df_result = pd.DataFrame()
df_result["true"] = y_test
df_result["Pred"] = model.predict(X_test)

まとめ

楽曲の特徴からアーティストを分類する方法についてご紹介しました。

Spotify APIは楽曲分析には最強のツールだと思います。

データ数を増やしたり特徴量をチューニングするとさらに精度も上がると思います。

ぜひ,いろんな使い方を試してみてください!

↓参考にさせていただいたサイト

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

このブログでは,PythonやLaTeXの使い方などを紹介しています!
仕事でも趣味でもプログラミングをしています。
ブログは2022年8月にスタートしました。
【経歴】東京大学大学院修了→大手IT企業勤務

コメント

コメントする

目次