Pythonで音楽ジャンルを分類してみた

シェアする

  • このエントリーをはてなブックマークに追加
スポンサーリンク

はじめに

今回は、今までずっと気になりつつできていなかった音楽ジャンルの分類についての記事を読んで気になったので試してみました。

これが次のサイトになります。

今回はこのサイトを参考にしつつ勉強していきたいと思います。

記事のまとめ

この記事に書いてあることを簡単にまとめて見ます。

まず、Spotifyについて触れ、このような企業では、顧客に適切なオススメのコンテンツを提供する、もしくは購入している曲から近いジャンルの曲を提供することによって消費者の購買意欲を促しています。つまり、音楽ジャンルの分類は、企業の売り上げを向上する為の第一歩です。機械学習の技術は大量のデータから傾向やパターンを抽出するのに成功しています。これは音楽分野においても同様です。

このように、音楽ジャンルの分類の必要性とどのように使われているのかを簡潔に述べています。そのあとは、Pythonで音楽処理をする為のライブラリの紹介と、基本的な処理を交えながら、どのように分析していけばいいのかを紹介しています。

私は、このような処理の知識がほぼない為、この記事に沿ながら、自分なりにまとめていきたいと思います。

Pythonによる音楽処理

音は、周波数、帯域幅、デシベルなどのパラメータを有すオーディオ信号の形で表される。典型的なオーディオ信号は、振幅および時間の関数として表すことができる。

オーディオライブラリ

Pythonには、LibrosaやPyAudioなどのオーディオ処理用の優れたライブラリがいくつかあります。オーディオの取得と再生には主に2つのライブラリを使用します。

  • Librosa
  • IPython.display.Audio

IPython.display.Audioの方は、jupyter notebookで直接オーディオを再生できます。

オーディオファイルの読みこみ

今回使う音源は、手元にあった音源を使用しています。

import librosa 
audio_path = 'piano.wav' 
x , sr = librosa.load(audio_path) 
print(type(x), type(sr)) 
<class 'numpy.ndarray'> <class 'int'> 
print(x.shape, sr)(2014272,) 22050

これは、オーディオの時系列を配列として返します(デフォルトのサンプリングレート(sr)がは22KHz)。次のコマンドでサンプリングレートを変更できます。

librosa.load(audio_path, sr=44100)

44.1KHzでリサンプリングする。

librosa.load(audio_path, sr=None)

リサンプリングを無効にします。

サンプルレートは、1秒間に伝送されるオーディオのサンプル数で、単位はHzまたはkHzです。

音楽を再生する

IPython.display.Audioを使って、音声を再生する。

import IPython.display as ipd 
ipd.Audio(audio_path)

次のようにju​​pyterノートブックのオーディオウィジェットを返します。次の画像は実際にGoogleColaboratory上で動かしてみたときのスクリーンショットです。

オーディオを視覚化する

librosa.display.waveplotを使って、プロットできます。

%matplotlib inline 
import matplotlib.pyplot as plt 
import librosa.display 
plt.figure(figsize=(14, 5)) 
librosa.display.waveplot(x, sr=sr)

スペクトログラム

スペクトログラムとは、複合信号を窓関数に通して、周波数スペクトルを計算した結果を指す。3次元のグラフ(時間、周波数、信号成分の強さ)で表される。

スペクトログラムは声紋の鑑定、動物の鳴き声の分析、音楽、ソナー/レーダー、音声処理などに使われている。

librosa.display.specshow.を使ってスペクトログラムを表示することができます。

X = librosa.stft(x)
Xdb = librosa.amplitude_to_db(abs(X))
plt.figure(figsize=(14,5))
librosa.display.specshow(Xdb,sr=sr,x_axis='time',y_axis='hz')
plt.colorbar()

縦軸は周波数(0〜10kHz)、横軸はクリップの時間を表します。すべての動作がスペクトルの一番下で行われていることがわかるので、周波数軸を対数軸に変換できます。

librosa.display.specshow(Xdb、sr = sr、x_axis = 'time'、y_axis = 'log') 
plt.colorbar()

…といってもこれを見て何が分かるのか全然わからなかった為、調べて見ました。

スペクトログラム について調べていたところ、次の論文を見つけました。

このスペクトログラム画像を用いて、楽曲を分類するという、結構近いものを見つけました。これによると、スペクトログラムの画像特徴量を利用した楽曲分類の 研究というのは以前からあるようで、楽曲 のある一定時間のスペクトログラム画像を縦方向(周波数 方向)で分割し,各々の周波数帯で抽出した特徴量を用いて 分類器を作り楽曲のジャンル分類を行うといった物があるようです。

ざっと見た感じ、スペクトログラム画像を用いることである程度の傾向を捉えることができるらしい。

オーディオの出力

librosa.output.write_wav : numpy配列をwavファイルに保存する

librosa.output.write_wav('piano.wav', x, sr)

オーディオ信号を作成する

220Hzのオーディオ信号を作ってみる。音声信号はでこぼこの配列なので、作成してそれを音声関数に渡します。

import numpy as np
sr = 22050 # sample rate
T = 5.0 # seconds
t = np.linspace(0, T, int(T*sr), endpoint=False) # time variable
x = 0.5*np.sin(2*np.pi*220*t)# pure sine wave at 220 Hz
#Playing the audio
ipd.Audio(x, rate=sr) # load a NumPy array
#Saving the audio
librosa.output.write_wav('tone_220.wav', x, sr)

特徴抽出

全てのオーディオ信号は多くの要素で構成されています。ただし、解決しようとしている問題に関連する特徴を抽出する必要があります。

ゼロクロス率

  • ゼロ交差率:信号に沿ってサインの変化、すなわち、信号の正から負への変化又はバック率の割合。
  • この特徴は、音声認識音楽情報検索の両方に多用されている。

ゼロクロスレートを計算して見る。

# Load the signal 
x, sr = librosa.load('piano.mp3') 
#Plot the signal: 
plt.figure(figsize=(14, 5)) 
librosa.display.waveplot(x, sr=sr)

# Zooming in 
n0 = 9000 
n1 = 9100 
plt.figure(figsize=(14, 5)) 
plt.plot(x[n0:n1]) plt.grid()

zero_crossings = librosa.zero_crossings(x [n0:n1]、pad = False) 
print(sum(zero_crossings))

52

ゼロ地点と交差してる箇所は52箇所あることがこのコードで見ることができます。

スペクトルセントロイド

音の「重心」がどこにあるかを示し、音の中に存在する周波数の加重平均として計算される。

この記事では、ブルースとメタルの曲を例に挙げて説明しています。曲を通してずっと同じであるブルースジャンルの歌と比較して、金属の歌は終わりに向かってより多くの周波数を持っています。そのため、ブルースの曲のスペクトル重心はそのスペクトルの中央付近に存在し、金属の曲のスペクトル重心は端に存在します。

librosa.feature.spectral_centroid : 各フレームのスペクトル重心を計算します。

spectral_centroids = librosa.feature.spectral_centroid(x, sr=sr)[0] 
spectral_centroids.shape 
(3935,)
# Computing the time variable for visualization 
frames = range(len(spectral_centroids)) 
t = librosa.frames_to_time(frames) 
# Normalising the spectral centroid for visualisation 
def normalize(x, axis=0): 
  return sklearn.preprocessing.minmax_scale(x, axis=axis) 
#Plotting the Spectral Centroid along the waveform 
librosa.display.waveplot(x, sr=sr, alpha=0.4) 
plt.plot(t, normalize(spectral_centroids), color='r')

記事のサンプル音声では終わりにスペクトル重心が上昇していましたが、私の用意したものでは、起伏が少なく、中央付近に集中していますが、大きさはあまり差がありません。

スペクトルロールオフ

このロールオフもスペクトルの形状を表す一つの尺度とされている。スペクトル分布の全帯域の 85% を占める周波数のことである。(調べたが、人によっては95%だったりしていた)

librosa.feature.spectral_rolloff :信号内の各フレームのロールオフ周波数を計算します。

spectral_rolloff = librosa.feature.spectral_rolloff(x+0.01, sr=sr)[0] 
librosa.display.waveplot(x, sr=sr, alpha=0.4) 
plt.plot(t, normalize(spectral_rolloff), color='r')

メル周波数ケプストラム係数

信号のメル周波数ケプストラム係数(MFCC)は、スペクトルエンベロープの全体的な形状を簡潔に表す小さな一連の特徴(通常は約10〜20)です。人間の声の特徴をモデル化したものです。

…といってもまだ自身がよく理解できないので調べました。

メル周波数ケプストラム係数とは「メル周波数」の「ケプストラム」の係数です。つまり、分けてまず「メル周波数」について調べました。

メル尺度(メルしゃくど、英語mel scale)は、音高の知覚的尺度である。メル尺度の差が同じであれば、人間が感じる音高の差が同じになることを意図している。

メル尺度-Wikipedia

周波数成分が複数ある音(自然界や楽器の音は全てそうである)から、人間がどのようにして音高を捉えているのかは、はっきりとは分かっていない。可聴域の下限に近い音は高め、上限に近い音は低めに聴こえる。一般に大きい音ほど(僅かだが)高めに聴こえ、低音域では音の振幅が大きくなるほど、音高は低く知覚される。また、倍音の多い(強い)音ほど高めに聴こえる。

音高-Wikipedia

まとめると、

  • 「メル尺度」とは人間の音高知覚が考慮された尺度である
  • 可聴域の下限に近い音は高く、上限に近い音は低く聞こえる
  • 倍音の多い(強い)音ほど高めに聞こえる

周波数パラメータの取り方によりいくつかの変種があるが、周波数fをメル尺度mに変換する一般式は次のようになる。

逆変換は次式で表される。

次に「ケプストラム」について調べてみました。

ケプストラムの定義は以下の通り。

  • 口語的定義: (信号の)ケプストラムとは、(信号の)フーリエ変換の対数(位相アンラッピングを施したもの)をフーリエ変換したものである。スペクトルのスペクトルとも呼ばれる。
  • 数学的定義: 信号のケプストラムは FT(log(|FT(信号)|)+jm) である。ここで m は、複素対数関数の虚数成分または角度の位相アンラッピングを正しく行うのに必要とされる整数である。
  • アルゴリズム的定義: 信号 → FT → abs() → log → 位相アンラッピング → FT → ケプストラム

処理過程を FT → log → IFT(フーリエ逆変換)として説明しているものがよく見受けられる。すなわち、ケプストラムを「スペクトルの対数のフーリエ逆変換」と定義しているのである。これはオリジナルの論文にある定義ではないが、広く用いられている。

ケプストラム-Wikipedia

理解するのに参考にさせてもらいました。ケプストラムを使うとスペクトルの細かな変動(スペクトル微細構造)とスペクトルのなだらかな変動(スペクトル包絡)が分離できるのがいいらしい。このスペクトル包絡は、人間の声道の特性を表しているため音声認識や音声合成では重要な特徴になるらしい。

つまり、「メル周波数ケプストラム係数」は「ケプストラム」と同じく、人間の声道の特性を表す特徴量で、「メル周波数」を用いる事で人間の音高知覚の特徴を考慮しているという事でしょうか。

参考記事ではコード1行でMFCCを計算していますが、調べている中でMFCCの抽出手順をまとめてある記事を見つけたので、参考にさせてもらいました。

MFCCの抽出手順をまとめると

  1. プリエンファシスフィルタで波形の高域成分を強調する
  2. 窓関数をかけた後にFFTして振幅スペクトルを求める
  3. 振幅スペクトルにメルフィルタバンクをかけて圧縮する
  4. 上記の圧縮した数値列を信号とみなして離散コサイン変換する
  5. 得られたケプストラムの低次成分がMFCC

メル周波数ケプストラム係数(MFCC) – 人工知能に関する断創録 

一応少しは理解したところで参考記事に戻ります。

参考記事では単純なループ波を使ってMFCCを表示させていますが、自分の手元にはそのような音源がなかったので今まで使ってきた音源をそのまま使っています。

x, fs = librosa.load('piano.mp3')
librosa.display.waveplot(x, sr=sr)

mfccs = librosa.feature.mfcc(x, sr=fs) 
print(mfccs.shape)
 #Displaying the MFCCs: 
librosa.display.specshow(mfccs, sr=sr, x_axis='time')

また、各係数次元がゼロ平均および単位分散を持つように、フィーチャスケーリングを実行することもできます。

import sklearn 
mfccs = sklearn.preprocessing.scale(mfccs, axis=1)
print(mfccs.mean(axis=1)) print(mfccs.var(axis=1)) 
librosa.display.specshow(mfccs, sr=sr, x_axis='time')

クロマ周波数

クロマの特徴は、音楽のオクターブの12個の異なる半音(またはクロマ)を表す12個のビンにスペクトル全体が投影される、音楽オーディオの面白くて強力な表現です。

クロマ周波数について調べましたが、ほとんどヒットせずよくわからなかったのですが、クロマについて次のものを見つけました。

音の高さの知覚

  • ハイト(height)
    • 正弦波において周波数が大きいほど高く感じられること
    • 蝸牛の場所の符号化による
  • クロマ(chroma)
    • 2倍(オクターブ)の関係にある周波数同士が等価に感じされること
    • 蝸牛の位相固定の符号化による
  • 複合音
    • 基本音と倍音(harmonics)から構成される
    • 複数の帯域に周波数成分が存在するが基本音周波数に対する知覚を得られる

【VR学まとめ】2.3 聴覚

クロマ(chroma)は、色彩においては彩度・あざやかさを意味し、音感に関しては周波数が約2倍である関係に2音が近づくと、二つの音が「似ている」もしくは「元に戻った」という感覚になることをいうそうです。

librosa.feature.chroma_stft

# Loadign the file 
x, sr = librosa.load('piano.mp3') 
hop_length = 512 
chromagram = librosa.feature.chroma_stft(x, sr=sr, hop_length=hop_length) 
plt.figure(figsize=(15, 5))
librosa.display.specshow(chromagram, x_axis='time', y_axis='chroma', hop_length=hop_length, cmap='coolwarm')

曲をさまざまなジャンルに分類する

音響信号、それらの特徴、およびそれらの特徴抽出プロセスの概要を把握した後、私たちの新しく開発されたスキルを利用して機械学習問題に取り組んでみましょう。

目的

このセクションでは、曲をさまざまなジャンルに分類するための分類子をモデル化します。何らかの理由で、音楽が含まれていると想定される、ランダムに名前が付けられたMP3ファイルがハードディスク上にたくさんあるシナリオを考えてみましょう。私たちの仕事は、音楽のジャンルに従ってそれらをジャズ、クラシック、カントリー、ポップ、ロック、そしてメタルのような異なるフォルダーに分類することです。

データセット

今回、有名なGITZANデータセットを使用します。このデータセットは、IEEE Transactions on Audio and Speech Processing 2002のG. TzanetakisおよびP. Cookによる「オーディオ信号の音楽ジャンル分類」の著名な論文で使用されています。

データセットは、30秒ごとに1000のオーディオトラックで構成されています。ブルース、クラシック、カントリー、ディスコ、ヒップホップ、ジャズ、レゲエ、ロック、メタル、ポップの 10ジャンル各ジャンルは100のサウンドクリップから成ります。

データの前処理

分類モデルを学習する前に、オーディオサンプルの生データをより意味のある表現に変換する必要があります。オーディオクリップは、オーディオファイルを読み込むためのpythonのwaveモジュールと互換性を持たせるために、.auフォーマットから.wavフォーマットに変換する必要があります。変換にはオープンソースのSoXモジュールを使用した。

sox input.au output.wav

分類

  • 特徴抽出

それから、オーディオファイルから意味のある特徴を抽出する必要があります。オーディオクリップを分類するために、メル周波数ケプストラム係数、スペクトル重心、ゼロクロッシングレート、クロマ周波数、スペクトルロールオフの5つの特徴量を使います。分類アルゴリズムを使用できるように、すべての特徴量が.csvファイルに追加されます。

  • 分類

特徴が抽出されたら、既存の分類アルゴリズムを使用して曲をさまざまなジャンルに分類できます。スペクトログラム画像を直接分類に使用することも、特徴を抽出して分類モデルを使用することもできます。

どちらの方法でも、モデルに関して多くの実験を行うことができます。あなたは自由にあなたの結果を実験し改善することができます。(スペクトログラム画像上で)CNNモデルを使用すると、精度が上がり、試す事もできます。

全てのコードを載せると長くなってしまうので今回の記事を試したGoogleColaboratoryのファイルを置いておきますのでよかったら試してみてください。使う音源は各々用意すれば試す事ができます。

元の記事のデータセットのリンクが切れていたのでデータセットはこちらからダウンロードしてください。

GTZAN dataset:
http://opihi.cs.uvic.ca/sound/genres.tar.gz

また、私は今回データセットをアップロードする際、そのままだと結構時間がかかったので一度解凍した後、ジャンルごと圧縮しGoogleColaboratoryにアップロードしました。そのため、コードが一部変更してあります。

最後まで読んでいただきありがとうございました。よろしければこの記事をシェアしていただけると励みになります。よろしくお願いします

スポンサーリンク
レクタングル広告(大)
レクタングル広告(大)

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

スポンサーリンク
レクタングル広告(大)