Chainerを用いて音声からの感情識別を行ってみました
Chainerを用いて音声からの感情識別を実践
音声からの感情抽出
対話機能
我々は現在Pepperを用いた対話アプリケーションの開発を行っております。
対話において重要な要素は感情を理解することが挙げられます。感情を理解することでユーザーが怒っていれば余計なことを話さず、悲しんでいれば慰める。そんなアプリケーションの実現が可能となります。
しかし従来の対話アプリケーションにおいてユーザーの感情を理解する機能は実装されておらず、質問に答えるだけの単純な機能が主流でした。
感情理解
Pepperには標準機能で感情を理解する機能がありますが、中身がブラックボックス化されています。そのため我々は独自に感情を識別する機能の実装を行いました。
対話で得られる情報は表情、音声、言語情報などと多岐に渡ります。今回は音声に着目しました。理由として、海外の人より日本人は感情が分かりづらいと言われています。日本人に対して感情を理解し対応するには表情の画像データよりも音声データを用いる方が有用に働くと考え、音声からの感情識別を機械学習の分野で話題のChainerを用いて実装しました。
実装
Chainerとは深層学習を行うためのオープンソースです。制御構造はすべて Python のものがそのまま使えるため従来のCaffeなどに比べて使いやすさが大幅に向上しています。
音声からの感情識別を用いる重要な場面は他にも会議、ロボット対話、コールセンターなどで使用が予想されます。
今回は下記の論文を参考に音声からの感情識別を行いました。
Kun Han,Dong Yu,Ivan Tashev "Speech Emotion Recognition Using Deep Neural Network and Extreme
Learning Machine", of Inter Speech 2014
”興奮”、”フラストレーション”、”幸せ”、”通常”、”驚き”、”怒り”、”悲しみ”、”恐怖”、”その他”、”うんざりした”
上記の10感情での識別精度がどの程度かを検証してみました。
先に精度を伝えておきます。精度は・・・10の感情において約49%です。
感情をランダムに分類した場合は約10%程度になるのですが、それを5倍程度の精度で識別していますが、問題がいろいろありますので記事を読んで頂ければその問題が把握できます。
構成
音声からの感情識別に使用する特徴量抽出方法、ラベル付与、感情の識別方法、評価について述べます。
- 1:音声からの感情識別方法
- 1.1:音声から感情識別に必要な特徴量
- 1.2:音声から感情識別に必要な特徴量抽出ツール
- 1.3:特徴量データにラベルを付与
- 1.4:特徴量データをWeka用からChainer用に変換
- 2:Chainer用の特徴量データで学習
- 2.1:Deep Neural Network
- 2.2:Chainerによる実装
- 3:識別器の評価
- 4:総括
1:音声からの感情識別方法
1 |
写真引用元:PAKUTASO/ぱくたそ https://www.pakutaso.com |
- 音声データを用意します。
- 音声データからの特徴量抽出します。音声からの特徴量抽出のツールは今回はOpenSMILEを使用しました。
- あらかじめ音声からの特徴データで学習した識別器を用いて学習に使用していない評価データで感情を識別します。OpenSMILEで出力されたデータはWekaという機械学習ツール用に出力されますが、今回はChainerで識別を行いたかったのでデータの形式をChainer用にプログラムで変更しました。
1.1:音声から感情識別に必要な特徴量
音声には感情を表す特徴量があります。
感情を表す特徴量は下記を参考に抽出します。
INTER SPEECH 2009 emotion challenge feature set
実際の入力に使用した音声の特徴量は384です。
- 特徴量が5種類
- 代表的な値12種類(最大値、平均値など)
*注意:60種類に見えますが、窓幅3で値を計算しているので、実質的に計算される値は最大値でも複数あります。よって384種類となります。この点が分からない方は飛ばして頂いても問題ないです。詳しく知りたい方はこちらをご参照ください。
http://web.stanford.edu/class/cs224s/hw/openSMILE_manual.pdf 29ページ参照
音声の特徴量で今回使用した代表的なものを示します。
- 信号の大きさ:音の大きさを測る際に使用されます。
- ゼロクロス率:音声波形の検出などに使用されます。
- F0:人の声の特徴を表します。
- MFCC:人が発した言葉の特徴を取得できます。フィルタをかけることで言葉の特徴をより顕著に取得出来るようにしています。
MFCCを詳しく知りたい方は下記をご参照下さい
http://shower.human.waseda.ac.jp/~m-kouki/pukiwiki_public/66.html
音響的特徴量についての詳細は下記に記されているので興味のある方はご参照下さい。
INTER SPEECH 2009 emotion challenge feature set
1.2:音声から感情識別に必要な特徴量抽出ツール
音声からの感情識別には感情のラベルがついた音声データを用意する必要があります。
さらに音声データから感情の識別に必要なツールも用意する必要があります。
今回は用意した音声データと音声からの特徴量抽出ツールは下記となります。
特徴量抽出ツール
音声からの特徴量抽出
設定ファイルを変えることで音声から様々な特徴量を抽出することが可能で、さらに出力データのデータ型もWekaと呼ばれる機械学習で有名なツールやLIBSVMと呼ばれるSVMのツール用にデータを出力してくれます。
OpenSMILEのインストールは下記の15P以降を参照ください。
http://www.audeering.com/research-and-open-source/files/openSMILE-book-latest.pdf
使用データ:IEMOCAP Database
IEMOCAP Databaseでは英語音声データラベル付で10の感情がラベル付されています。
具体的には
- anger(怒り)
- happiness(幸せ)
- excitement(興奮)
- sadness(悲しみ)
- frustration(フラストレーション)
- fear(恐怖)
- surprise(驚き)
- other(その他)
- neutral state(通常)
- disgust(うんざりした)
こちらのデータを取得するのには事前に申請が必要です。
下記のページの"Please read the license before submitting your request."の行にあるlicenseに関してよく読んでから申請してください。
http://sail.usc.edu/iemocap/iemocap_release.htm
1.3:特徴量データにラベルを付与
今回使用するデータはラベルが別のファイルに記述されているので、その対応付けが必要となります。
find . -name PATH/*.wav > wav_list
上記のコマンドで音声ファイルのwavリストを作成
1 |
wavファイルが置かれているパス/Ses01F_impro07_F004.wav |
Ses01F_impro07:セッション名
F:女性か男性(F or M)
004:番号
上記のようにどのセッションで男性か女性か何番目の音声かをファイル名で把握することができるファイルのリストを作成します。
pythonでプログラムを作成し、下記の手順で実装しました。
- pythonでwav_listを読み込む
- 順次OpenSMILEを実行し音声の特徴量ファイルをarff形式で作成
下記のコマンドで順次実行が可能
1SMILExtract -C IS09_emotion.conf -I 入力音声ファイル名 -O 出力ファイル名
arff形式のファイルはWekaで使用されることが前提なのでファイルの形式を理解する必要があります。
arffのファイルフォーマットは下記を参照しました。
@attributeではデータのフォーマットを設定します。
@dataで@attributeで設定した通りにデータを記述されます。
一例をあげると
最初の行に"sex"とあるのが"man、 woman"の選択肢が設定されるという意味です。
次の行では"age"の値が"real"とあるので数値型を設定します。
1 2 3 4 5 6 |
@attribute sex {man, women} @attribute age real @data man, 24 man, 31 |
実際に出力される音声からの特徴量を抽出したデータ
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@relation openSMILE_features @attribute name string @attribute pcm_RMSenergy_sma_max numeric @attribute pcm_RMSenergy_sma_min numeric @attribute pcm_RMSenergy_sma_range numeric @attribute pcm_RMSenergy_sma_maxPos numeric ・ ・ ・ @data @attribute name stringに対応したデータ,@attribute pcm_RMSenergy_sma_max numericに対応したデータ,@attribute pcm_RMSenergy_sma_min numericに対応したデータ,・・・, ラベルデータ |
今回のデータは音声データに直接、感情ラベルが付与されている訳ではないので出力された特徴量にはラベルがついていません。よってラベルをつける必要があります
今回、ラベル付に使用した方法
- ラベル付に必要な情報をデータから取得
- arffファイルとラベルを紐付けたファイルリストを作成
取得したIEMOCAPのデータには感情のラベルデータが存在しています。
IEMOCAP_full_release_withoutVideos/IEMOCAP_full_release/Session1/dialog/EmoEvaluation/Categorical/Ses01F_impro03_e4.anvil
Session1〜3にデータは存在し、上記のフォルダの中の.anvilファイルに感情のアノテーションデータが存在しています。
ファイルはxml形式で記述されており、下記のようになっています。
Ses01F_impro03.aviでセッションの音声かを把握でき、
track name="Female.Emotion"で男性か女性の発話か
el index="0"で何番目の発話か
attribute name="Happiness"で感情ラベルを識別可能となります。
上記の情報からSes01F_impro03_F000.wavファイルは"Happiness"のラベルがついていることがわかるので。対応するarffファイルに感情ラベルを付与します。
1 2 |
#ファイル名,感情ラベル名 Ses01F_impro03_F000.arff,Happiness |
1.4:特徴量データをWeka用からChainer用に変換
音声データはWekaで使用するarff形式もしくはLIBSVMで使用する形式で出力されるため、今回使用するChainerで使用可能な形式に変更する必要があります。
下記にWeka用のデータをChainer用のデータに変換するまでの流れを載せておきます。
Weka
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@relation openSMILE_features @attribute name string @attribute pcm_RMSenergy_sma_max numeric @attribute pcm_RMSenergy_sma_min numeric @attribute pcm_RMSenergy_sma_range numeric @attribute pcm_RMSenergy_sma_maxPos numeric ・ ・ ・ @data @attribute name stringに対応したデータ, @attribute pcm_RMSenergy_sma_max numericに対応したデータ, @attribute pcm_RMSenergy_sma_min numericに対応したデータ, ・ ・ ・ ラベルデータ |
Chainer
1 2 3 4 5 6 7 8 |
#特徴量データは行列で与えられる(x) #1つ目の音声データ, 2つ目の音声データ,・・・ @attribute name stringに対応したデータ, @attribute name stringに対応したデータ,・・・ @attribute pcm_RMSenergy_sma_max numericに対応したデータ,@attribute pcm_RMSenergy_sma_max numericに対応したデータ,・・・ @attribute pcm_RMSenergy_sma_min numericに対応したデータ,@attribute pcm_RMSenergy_sma_min numericに対応したデータ,・・・ ・ ・ ・ |
1 2 3 |
#ラベルデータ(y) #1つ目の音声データ,2つ目の音声データ,・・・ ラベル,ラベル・・・ |
Chainerではデータをarray形式で扱っています。
arffとラベルの対応付したファイルを用いて、ラベル付のarrayデータを作成します。
1 2 3 4 5 6 7 |
if first_flag == 1: self.x_data = numpy.hstack((self.x_data, x_vector)) self.y_data = numpy.array(self.y_vector) first_flag = 0 else: self.x_data = numpy.vstack((self.x_data, x_vector)) self.y_data = numpy.array(self.y_vector) |
arrayデータを作成する時に注意が必要で行列データ作成の際は最初の処理だけhstackを使用し、次の処理からvstackを使用しないと上手く作成できません。
Chainerでは識別学習に感情ラベルを与えることはできないのでマッピングした数値をプログラムで与えます。前述で作成したarffファイルのリストに感情ラベルが付いているので、それを数値ラベルに変換して処理を行います。
1 2 3 |
self.emotion = {"Anger": 0, "Happiness": 1, "Excited": 2, "Sadness": 3, "Frustration": 4, "Fear": 5, "Surprise": 6, "Other": 7, "Neutral state": 8, "Disgust": 9} |
特に注意するのがラベル付です。10の感情なので”0〜9”のラベルのアノテーションを行っていますが、これは出力層にも影響します。今回は単純に10の出力層で定義しています。
*補足:”0〜10”のラベルの場合は出力層が単純に11必要ではなく”10”に対して2つの出力層が必要になるので12個の出力層が必要になります。
2:Chainer用の特徴量データで学習
2.1:Deep Neural Network
抽出された特徴量を用いて学習する方法はいくつかあります。
代表的なものはSVM、HMMなどがありますが今回はDeep Neural Network を使用します。
Deep Neural Networkとは深層学習のことで最近では精度が高いことで注目を浴びている機械学習の手法のことです。
今回使用したDeep Neural Networkは下記です。
入力層:384(音声の特徴量)
ユニット:
隠れ層:3層
1層目:入力384ユニット、出力256ユニット
2層目:入力256ユニット、出力256ユニット
3層目:入力256ユニット、出力10ユニット
活性化関数:シグモイド関数
2.2:Chainerによる実装
Chainerによる実装で記述していない点だけ記述しておきます。
基本的な実装はChainerのチュートリアルと同様の実装になります。
データ数は学習データ5000音声、評価データ766音声なので学習データを5000音声にsplitによりカットしている点が異なります。
1 2 |
x_train, x_test = np.split(self.x_data, [5000]) y_train, y_test = np.split(self.y_data.astype(np.int32), [5000]) |
隠れ層の入力部は音声から抽出される感情識別に使用される特徴量は384次元なので384次元を入力とし、隠れ層のユニット数は論文に準拠し256としました。出力層は10の感情識別なので10としています。
1 2 3 4 5 |
model = FunctionSet( l1 = F.Linear(384, 256), l2 = F.Linear(256, 256), l3 = F.Linear(256, 10), ) |
順伝搬の関数部分は正規化線形関数(Rectified Linear Unit function)ではなくシグモイド関数でこちらも論文準拠となります。
1 2 3 4 5 6 7 |
def forward(self, x_data, y_data, model): x = Variable(x_data) t = Variable(y_data) h1 = F.sigmoid(model.l1(x)) h2 = F.sigmoid(model.l2(h1)) y = model.l3(h2) return F.softmax_cross_entropy(y, t), F.accuracy(y, t) |
学習の手法はミニバッチを使用しました。
すべてのデータを使って学習するのではなく、いくつかデータをピックアップし、そのデータでパラメータ更新をする手法です。
一回の更新で,限られたデータしか参照しないためメモリ効率が良く,大規模データの処理で有利な処理となります。
3:識別器の評価
学習したモデルを評価データ766音声に使用し精度の評価を行いました。
- 損失と精度を初期化します。
- 順伝播させて誤差と精度を算出します。
- テストデータでの誤差と、正解精度を表示します。
1 2 3 4 5 6 7 8 9 10 11 |
sum_loss,sum_accuracy = 0, 0 for i in range(0, 766, batchsize): x_batch = x_test[i : i + batchsize] y_batch = y_test[i : i + batchsize] loss, accuracy = self.forward(x_batch, y_batch, model) sum_loss += loss.data * batchsize sum_accuracy += accuracy.data * batchsize mean_loss = sum_loss / 766 mean_accuracy = sum_accuracy / 766 print("mean_loss ,", mean_loss) print("mean_accuracy ,", mean_accuracy) |
音声からの感情識別の精度は47〜49%程度でした。
学習データは5000音声使用し、評価データは766音声使用しました。
実は今回使用したデータには問題点があります。
感情 | 'Frustration' | 'Excited' | 'Sadness' | 'Anger' | 'Neutral state' | 'Happiness' | 'Fear' | 'Other' | 'Surprise' | 'Disgust' |
---|---|---|---|---|---|---|---|---|---|---|
データ数 | 2066 | 1382 | 743 | 716 | 404 | 325 | 74 | 35 | 17 | 3 |
データの分布が極端に異なります。では各感情のF measureを見てみます。
データ数が多いものに影響されたモデルになっており、他の感情の識別率が0という結果になってしまっています。
4:総括
ニューラルネットを用いた識別学習は始めての試みでしたが機械学習の基本であるデータの中身を見るという点を注意しないと単純な精度のみを測ってしまうという問題に陥ることを学べました。
このような不均衡データを扱う時は次のような解決策があります。
- データのバランスを取る
- データの比率に応じたパラメータを重みにかける
データのバランスを取る場合、今回のケースですと'Frustration'の音声を減らすか他の音声にホワイトノイズをのせて増やすという解決策があります。データの比率に応じたパラメータをかける場合、今回のケースですとデータの比率の逆数を入力データにかけると少ないデータを極端に重要視することができます。
今回は”データの比率に応じたパラメータを重みにかける”手法を採用していますが、それでも上記のような結果になっています。
どちらも問題があるのでベストな解決策はデータを集めることになります。
機械学習はデータに依存するため、適切なデータを用意することにまずは注力することが最も重要になってきます。
"ニューラルネットの実装が簡単なChainerを用いた音声からの感情識別 (ja)"という題目でポスター発表をした際の資料の詳細になります。
機械学習、音声認識に関心、興味がありましたら tech-sketch@pj.tis.co.jp までご連絡ください。
また私達と一緒にスマートなアプリケーションを開発してくれる仲間も募集中です!
参考サイト一覧
Chainer – A flexible framework of neural networks
http://docs.chainer.org/en/latest/index.html
2015.07.14
Kun Han, Dong Yu, Ivan Tashev "Speech Emotion Recognition Using Deep Neural Network and Extreme
Learning Machine", of Inter Speech 2014
http://research.microsoft.com/pubs/230136/IS140441.PDF
IEMOCAP Database
http://sail.usc.edu/iemocap/
2015.07.14
OpenSMILE
http://web.stanford.edu/class/cs224s/hw/openSMILE_manual.pdf
PAKUTASO/ぱくたそ
https://www.pakutaso.com
2015.07.15
Miyazawa’s Pukiwiki 公開版
http://shower.human.waseda.ac.jp/~m-kouki/pukiwiki_public/66.html
2015.07.15
Deep learning
http://www.slideshare.net/kazoo04/deep-learning-15097274
大人になってからの再学習
http://d.hatena.ne.jp/Zellij/20111011/p1
【更新】確率的勾配降下法とは何か、をPythonで動かして解説する
http://qiita.com/kenmatsu4/items/d282054ddedbd68fecb0
The INTERSPEECH 2009 Emotion Challenge
http://www5.informatik.uni-erlangen.de/Forschung/Publikationen/2009/Steidl09-TI2-talk.pdf