概要
以前から音声認識には興味があったので, その第一歩として, Yes と No の固定長の音声を機械学習を用いて分類しました. (どっちも2秒) 今回作成したソースコードはgithub上げときました.
環境
macOS, python3 (anaconda)
必要なものをインストールする.
portaudioとpyaudioをインストールする.
brew install portaudio
pip install pyaudio
他のOSの場合など詳しくはここを見てね. soundfileをインストールする.
pip install soundfile
データを作る
今回は自分の声を分類する分類器を作りたいから, 録音するプログラムから作りました. 録音するプログラムにimportしているrecorderのソースコードもgithubに上げたから気になったら見てください. 下のプログラムで連続して録音しました. (YesとNo, 20回ずつ)
import sys
import pyaudio
import wave
import recorder
cnt = 0
print('input prefix of output filename:')
fname = input()
print('input number to start with:')
cnt = int(input())
rec = recorder.WaveRecorder()
while True:
print('Press enter to start recoding. Type end to finish recording.')
if input() == 'end':
break
rec.record('{}_{}.wav'.format(fname, cnt))
cnt += 1
モデルの学習
分類器は, Random Forest, Gradient Boosting, Support Vector, KNNの4つを試しました. まず, それぞれのwaveデータから一次元データを作成し, くっつける.
n_datas = 20
X = []
y = []
for i in range(n_datas):
no, _ = sf.read('no_{}.wav'.format(i))
yes, _ = sf.read('yes_{}.wav'.format(i))
no = no[:, 0] #2chで録音したので片方だけ取ってくる
yes = yes[:, 0]
X.append(no)
X.append(yes)
y.append(0) #Noはクラス0
y.append(1) #Yesはクラス1
X = np.array(X)
y = np.array(y)
次に説明変数をFFTの絶対値と位相にする。
X_fft = np.array([np.fft.fft(x) for x in X])
X_fft = np.fft.fft(X)
X = np.array([np.hstack((x.real**2+x.imag**2, np.arctan2(x.real, x.imag))) for x in X_fft])
そしてcross_val_scoreで5分割して交差検証, 正答率を表示。
#ここをGradientBoostingClassifier(), SVC()やKNeighborsClassifier()に変える
clf = RandomForestClassifier()
scores = cross_val_score(clf, X, y, cv=5)
print('score:{:.3f} (+/-{:.3f})'.format(scores.mean(), scores.std()*2))
結果
Random Forest の結果は0.975 (+/-0.100), Gradient Boostingは1.000 (+/-0.000), SVCは0.700 (+/-0.339), KNNは0.675 (+/-0.436)でした. この結果から, Gradient Boostingが最も良いと考えられます. データ数がそれぞれ20個ずつしか無いのに思ったより精度が高かったです.
この理由は,
・ 分類クラスが2クラスだけ
・ 雑音がほぼなかった
・ trainもtestも自分の声しか入っていないので周波数とか似てる
だと思います. 実行するとわかると思いますが, Gradient Boostingは3手法の中では1番学習に時間がかかっています. 学習時, 全ての弱分類器が独立でないBoostingという手法を使っているのが原因だと考えられます. ただ, 学習には時間がかかっても, 実際に使うときには短時間で計算できるため, これを使っても問題はないです.
遊び
実際に学習したモデルで, 新たに録音した音声を分類させてみる.
#全データで学習
clf.fit(X, y)
rec = recorder.WaveRecorder()
yesno = ['No', 'Yes']
while True:
print('Press enter to start recording. Type end to finish recording.')
if input() == 'end':
break
rec.record('output.wav')
wav, _ = sf.read('output.wav')
wav = np.array(wav[:, 0])
wf = np.fft.fft(wav)
wav = np.hstack((wf.real**2+wf.imag**2, np.arctan2(wf.real, wf.imag)))
pred = clf.predict(np.array([wav]))
print(yesno[int(pred)])
実行すると次のようになった.
* recording
* done recording
Yes
Press enter to start recording. Type end to finish recording.
* recording
* done recording
Yes
Press enter to start recording. Type end to finish recording.
* recording
* done recording
No
Press enter to start recording. Type end to finish recording.
* recording
* done recording
No
Press enter to start recording. Type end to finish recording.
* recording
* done recording
Yes
Press enter to start recording. Type end to finish recording.
* recording
* done recording
No
Press enter to start recording. Type end to finish recording.
end
Yes, Yes, No, No, Yes, Noの順に発声したので全て正しく分類されていました. この程度のデータ数で自分の声だけならこれだけちゃんとできるのであれば, 家電とか声で操作するの一から作ってもそこまで難しくないかも!?