Aidemy Tech Blog

機械学習・ディープラーニング関連技術の活用事例や実装方法をまとめる、株式会社アイデミーの技術ブログです。

【音声認識 超入門】 固定長音声データの分類

概要

以前から音声認識には興味があったので, その第一歩として, 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の順に発声したので全て正しく分類されていました. この程度のデータ数で自分の声だけならこれだけちゃんとできるのであれば, 家電とか声で操作するの一から作ってもそこまで難しくないかも!?