前回の投稿では、多くの皆様から「いいね」を頂きました。
この場を借りて御礼申し上げます。
前回は画像ファイルでしたが、音ファイルでもやってみたいと思います。
やりたいことは、音ファイルでどの技術が一番効果があるのか数値化します。
以下の流れでやっていきます。
(基本のCNN) → (3つの技術を追加していく)
基本のCNNに、3つの技術を追加しながら、分類精度の上昇幅を比較します。
コードはkerasで書いています。
画像ファイルと同じ結果になるのか?
乞うご期待!
#データのダウンロード
使うデータはESC-50です。
これは、人の音声や自然界の音を集めたデータで、50個のラベルが付与されています。
#データの前処理
音の生データは波になっています。
import librosa
import pandas as pd
import os
# define directories
base_dir = "./"
meta_file = os.path.join(base_dir, "meta/esc50.csv")
# load metadata
meta_data = pd.read_csv(meta_file)
x, fs = load_wave_data(audio_dir, meta_data.loc[5,"filename"])
#show wave
plt.plot(x)
plt.show()
ついでに、以下のようにコマンドすると音の再生ができます。
import IPython.display as ipd
ipd.Audio(x, rate=fs)
##スペクトログラム
波のデータのままでは、うまく特徴が捉えられないので、周波数帯域に
変換してやります。
import librosa.display
# change wave data to stft
def calculate_sp(x, n_fft=512, hop_length=256):
stft = librosa.stft(x, n_fft=n_fft, hop_length=hop_length)
sp = librosa.amplitude_to_db(np.abs(stft))
return sp
# display wave in spectrogram
def show_sp(sp, fs, hop_length):
librosa.display.specshow(sp, sr=fs, x_axis="time", y_axis="log", hop_length=hop_length)
plt.colorbar(format='%+2.0f dB')
plt.title('Spectrogram')
plt.show()
sp = calculate_sp(x)
show_sp(sp, fs, hop_length=256)
横軸は時間(sec)、縦軸は周波数(Hz)です。
科捜研のドラマとかで出てくるスペクトログラムの完成です。
##メルスペクトログラム
前項で得られたスペクトログラムを、ゴニョニョと処理してメルスペクトログラムを求めます。
スペクトログラム → ゴニョニョ → メルスペクトログラム → (MFCC)
ゴニョニョが気になる方は以下のリンクを参考にしてください。
・音楽と機械学習 前処理編 MFCC ~ メル周波数ケプストラム係数
・ケプストラム分析
メルスペクトログラムへ変換することにより、以下の効果があるそうです。
・次元が下がる(本稿の場合、513次元 → 128次元)
・低音域では敏感に、高音域では鈍感になる(人間の耳の仕様に近づく)
具体的に可視化してみます。
# change wave data to mel-stft
def calculate_melsp(x, n_fft=1024, hop_length=128):
stft = np.abs(librosa.stft(x, n_fft=n_fft, hop_length=hop_length))**2
log_stft = librosa.power_to_db(stft)
melsp = librosa.feature.melspectrogram(S=log_stft,n_mels=128)
return melsp
# display wave in heatmap
def show_melsp(melsp, fs):
librosa.display.specshow(melsp, sr=fs, x_axis="time", y_axis="mel", hop_length=128)
plt.colorbar(format='%+2.0f dB')
plt.title('Mel spectrogram')
plt.show()
# example data
melsp = calculate_melsp(x)
show_melsp(melsp, fs)
横軸は時間(sec)、縦軸はメル周波数(Hz)です。
#基本のCNN
基本のCNNは@cvuskさんのコードを参考にしています。
コードは以下の通りです。
import keras
from keras.models import Model
from keras.layers import Input, Dense, Activation
from keras.layers import Conv2D, GlobalAveragePooling2D, Add
# redefine target data into one hot vector
classes = 50
def cba(inputs, filters, kernel_size, strides):
x = Conv2D(filters, kernel_size=kernel_size, strides=strides, padding='same')(inputs)
x = Activation("relu")(x)
return x
# define CNN
inputs = Input(shape=(x_train.shape[1:]))
x_1 = cba(inputs, filters=32, kernel_size=(1,8), strides=(1,2))
x_1 = cba(x_1, filters=32, kernel_size=(8,1), strides=(2,1))
x_1 = cba(x_1, filters=64, kernel_size=(1,8), strides=(1,2))
x_1 = cba(x_1, filters=64, kernel_size=(8,1), strides=(2,1))
x_2 = cba(inputs, filters=32, kernel_size=(1,16), strides=(1,2))
x_2 = cba(x_2, filters=32, kernel_size=(16,1), strides=(2,1))
x_2 = cba(x_2, filters=64, kernel_size=(1,16), strides=(1,2))
x_2 = cba(x_2, filters=64, kernel_size=(16,1), strides=(2,1))
x_3 = cba(inputs, filters=32, kernel_size=(1,32), strides=(1,2))
x_3 = cba(x_3, filters=32, kernel_size=(32,1), strides=(2,1))
x_3 = cba(x_3, filters=64, kernel_size=(1,32), strides=(1,2))
x_3 = cba(x_3, filters=64, kernel_size=(32,1), strides=(2,1))
x_4 = cba(inputs, filters=32, kernel_size=(1,64), strides=(1,2))
x_4 = cba(x_4, filters=32, kernel_size=(64,1), strides=(2,1))
x_4 = cba(x_4, filters=64, kernel_size=(1,64), strides=(1,2))
x_4 = cba(x_4, filters=64, kernel_size=(64,1), strides=(2,1))
x = Add()([x_1, x_2, x_3, x_4])
x = cba(x, filters=128, kernel_size=(1,16), strides=(1,2))
x = cba(x, filters=128, kernel_size=(16,1), strides=(2,1))
x = GlobalAveragePooling2D()(x)
x = Dense(classes)(x)
x = Activation("softmax")(x)
model = Model(inputs, x)
色々なサイズのフィルターを通すことで、短期から長期の特徴を捉えることができるそうです。
早速、以下のように学習させます。入力データはスペクトログラムを使用しています。
# initiate Adam optimizer
opt = keras.optimizers.adam(lr=0.00001, decay=1e-6, amsgrad=True)
# Let's train the model using Adam with amsgrad
model.compile(loss='categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])
hist = model.fit(x_train,y_train,
validation_data=(x_test,y_test),
epochs=50,
verbose=1,
batch_size=20)
学習結果↓
Validationの分類精度は**36.2%**となりました。
まだ伸びる余地はありますが、行っても40%くらいという印象です。
#各技術の追加
これより、各技術を追加します。
基本のCNNと同じ条件にするために、畳込み層は同じ構造にして、epochも20で固定します。
その他、全結合層や正則化層などは無制限です。
前回の結果を受け、追加する技術はData AugmentationとBatch Normalizationに絞ります。
そして、技術というよりは、前処理に近いメルスペクトログラムの有無によって精度が
どれくらい変わるのか観察します。
##メルスペクトログラムの効果
基本のCNNの入力は、スペクトログラムを使っていました。
ここでは、入力をメルスペクトログラムに変えてみます。
学習結果↓
Validationの分類精度は28.2%となり、基本のCNNより6.4%ダウンしました。
メルスペクトログラムは、音声認識では適しているかもしれませんが、自然界の音は
スペクトログラムの方が良いのかもしれません。
##Batch Normalizationの効果
前項のモデルにBatch Normalizaion(BN)を追加します。
def cba(inputs, filters, kernel_size, strides):
x = Conv2D(filters, kernel_size=kernel_size, strides=strides, padding='same')(inputs)
x = BatchNormalization()(x)
x = Activation("relu")(x)
return x
学習結果↓
Validationの分類精度は46.2%で、前項より**17.8%**アップしました。
BNの効果は絶大です。動作は軽い上に、良い仕事をします。
##Data Augmentationの効果
Data Augmentationを追加します。こちらも@cvuskさんのコードを参考にしています。
音程を少し変えたり、ノイズを加えたり、時間をずらしたりしています。
# data augmentation: add white noise
def add_white_noise(x, rate=0.002):
return x + rate*np.random.randn(len(x))
# data augmentation: shift sound in timeframe
def shift_sound(x, rate=2):
return np.roll(x, int(len(x)//rate))
# data augmentation: stretch sound
def stretch_sound(x, rate=1.1):
input_length = len(x)
x = librosa.effects.time_stretch(x, rate)
if len(x)>input_length:
return x[:input_length]
else:
return np.pad(x, (0, max(0, input_length - len(x))), "constant")
x_w = add_white_noise(x)
x_s = shift_sound(x)
x_st = stretch_sound(x)
Validationの分類精度は56%となり、前項より**10%**アップしました。
まだまだ伸びる余地はあり、エポックを増やせば70%くらいまで行けるかもしれません。
前回同様、ここだけで判断するのはちょっと乱暴ですが、音ファイルでは
「Batch Normalization」が一番効果的であることが確認されました。
BNとData Augmentationは、画像ファイルでも良い作用をしましたし、これは外せない技術といえます。
一方、メルスペクトログラムやresblockは慎重に取り扱わないと、悪影響を及ぼす可能性があるので注意が必要です。
#感想
今回使用したデータは、1つのファイルサイズが128×1723×1でとんでもなく重く、保存や学習でかなり時間がかかりました。
前回のCIFAR-10(サイズ:32×32×3)は、100エポックの学習で2時間かかり、気軽にできるレベルでした。しかし、今回は50エポックの学習で4時間かかりました(Colaboratory)。
また、batchsizeは20が限界で、これ以上だとColaboratoryからメモリーエラーで怒られました。
ColaboratoryでESC-50を使う際は、気長に待ちましょう。