はじめに
Juliusと音声認識APIを使用したときのことをまとめます。主にJuliusを触りました。また、Watson Speech to Text APIも扱ったのでJuliusとAPIの比較検証をする内容になっています。いずれも音声ファイルを認識させています。
音声認識とは
音声認識とは人間の会話の音声を認識し、それをテキスト化する技術です。
音声認識のおおまかな流れを説明すると、
音声を入力→入力された音声を音波に変換→音波から音素を特定→音素の並びを辞書と照らし合わせる→単語に変換→変換された文章をテキストで出力
上記工程を経ることで音声認識が行われます。
APIでの音声認識とは
APIとはアプリケーションプログラミングインターフェースの略です。APIはソフトウェアやアプリケーションなどの一部を外部に向けて公開することにより、第3者が開発したソフトウェアと機能を共有できるようにしてくれるものです。今、機械学習の多くの機能はオープンソースとして公開され、APIを通じて世界中の方が利用できます。特に機械学習においてはデータを蓄積して学習させ、精度を上げる必要があるため、APIを使用することでそのようなコストを抑えることができます。
代表的な音声認識API
Google Cloud Speech-to-Text
Watson Speech to Text
Bing Speech API
Amazon Transcribe
上から順にGoogle、IBM、MicroSoft、Amazon社が提供している音声認識APIです。
今回使用したもの
Watson Speech to Text
Julius
Watson Speech to Text とは
Speech to Text はWatsonの音声認識機能であり、IBM社が提供しています。ディープ・ラーニングを活用し、音響的な特徴と言語知識から正確にテキストを書き起こします。クラウド上でAPIとして提供する音声認識システムであり、長い時間のストリーム音声や幅広い入力フォーマットをサポートしています。日本語のほかにもアメリカ英語やイギリス英語、フランス語、中国語など複数の言語に対応し、帯域制限された電話音声専用のモデルも提供します。Watsonは基本的な語彙をあらかじめ学習していますが、さらにカスタマイズ機能により特有の単語や言い回しを追加学習できます。そのため、クリアな音声が取得できればさまざまな使用環境で認識精度を高めることができます。
Juliusとは
音声認識システムの開発・研究のためのオープンソースの高性能な汎用大語彙連続音声認識エンジンです。数万語の語彙を対象とした文章発声の認識を行う能力を持っています。
Juliusの特徴
Juliusの最大の特徴はその可搬性にあります。Juliusは音声認識のモジュールを組み替えることで様々な用途に応用できます。また、OSSであり、ソースコードを公開しているため、商用利用への制限がないです。
Juliusには3つの派生版が存在します。
1 Juliusオリジナル版
2 Julius for Windows SAPI
3 JuliusLib/JuliusGUI
Juliusの動作に必要なもの
・音響モデル(音素ごとの音声波形パターンのモデル)
・単語辞書(各単語の読みの定義)
・言語モデル(単語間の接続制約を決定する)
GMMとDNN
DNN (Deep Neural Network)のシステムでは高精度な音響モデルを使用します。そのため処理が重くなり,手順も複雑になりますが,GMM版よりも認識精度が向上します。
Watson Speeh to Text を使う
"model=ja-JP_BroadbandModel" で日本語モデルに変更でき、日本語の音声ファイルを認識できます。
$ curl -X POST -u "apikey:{API鍵}" --header "Content-Type: audio/flac" --data-binary @{path_to_file}audio-file.flac "{url}/v1/recognize?model=ja-JP_BroadbandModel"
Watson Speech to Text 認識結果
※一部抜粋
{
"results": [
{
"alternatives": [
{
"confidence": 0.83,
"transcript": "青森県 八戸市 で 古く から 愛 される 郷土 料理 せんべい 汁 領野 借りて 取った 獲物 具材 に した 汁物 に ちぎった 南部 せんべい を 入れて 食べた のが せんべい 汁 の 始まり です "
}
],
"final": true
},
"result_index": 0
//"confidence"0.0~1.0の範囲までです。この数値が大きいほど文書の関連度が高くなります。
//"transcript"認識した文書の結果です。
confidenceが0.83なので完全ではないですが、文章の主旨は理解できる精度です。
次にPHPでWatson Speech to Textを使います。
<?php
$file = file_get_contents('/mnt/c/Users/h-saito/Downloads/senbeijiru.flac');
$url = 'https://gateway-tok.watsonplatform.net/speech-to-text/api/v1/recognize';
$model = 'ja-JP_NarrowbandModel';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . '?model=' . $model);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $file);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_USERPWD, 'apikey' . ':' . 'Jajkdaljdjfa;jfjadfjajfdsjfdjaf489apikey');
$headers = array();
$headers[] = 'Content-Type: audio/flac';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
}
curl_close($ch);
print_r($result);
?>
PHP Watson Speech to Text認識結果
※一部抜粋
{
"results": [
{
"alternatives": [
{
"confidence": 0.76,
"transcript": "青森県 八戸市 で 売る から 愛 される 郷土 料理 せんべい 汁 "
}
],
"final": true
},
{
"alternatives": [
{
"confidence": 0.62,
"transcript": "良也 借りて とった ええ もの ござい ました 汁物 に "
}
],
"final": true
],
"result_index": 0
先ほどと同じ音声ファイルを使用しました。
Juliusを使う
※julius音声ファイルはchannel1でサンプルレート16000である必要があります。
ディレクトリをdictationkitに移動し実行します。またはパスを指定します。
julius -C main.jconf -C am-gmm.jconf -input rawfile
Julius 認識結果
Recognition: 2nd pass (RL heuristic best-first) STAT: 00 _default: 33344 generated, 3122 pushed, 388 nodes popped in 328
sentence1: これ は マイク の テスト です 。
wseq1:これ+代名詞 は+助詞 マイク+名詞 の+助詞 テスト+名詞 です+助動詞
phseq1: silB | k o r e | w a | m a i k u | n o | t e s u t o | d e s u | silE
cmscore1: 0.517 0.687 0.481 0.570 0.323 0.187 0.734 1.000 score1: -7960.165039
// "sentence" で始まる行が第2パスの認識結果の出力文字列です。
//"wseq1" は言語モデルのエントリ列(N-gram ではN-gramエントリ名、文法ではカテゴリ番号)です。
//"phseq" は音素並びであり、"|" は単語区切りを表します。
//"cmscore" は認識結果の各単語の単語信頼度を表します。"score" はその仮説の尤度であり,音響モデルの出力確率と重みづけられた言語モデルの出力確率の対数尤度の和です。(Watsonの"confidence")
これでJuliusが動きました。
次にPHPでJulius(GMM)を動かします。
<?php
$JULIUS_HOME = "/home/h-saito/juius/.";
$JULIUS_EXEC = "julius -C ./julius-4.4.2.1/dictation-kit-v4.4/main.jconf -C ./julius-4.4.2.1/dictation-kit-v4.4/am-gmm.jconf -input fil e";
$SERVER_PORT = 8000;
$ASR_FILEPATH = '/home/h-saito/julius/';
$ASR_IN = 'Sample.wav';
$ASR_RESULT = 'Sample.out';
$OUT_CHIKUM = 5;
$descriptorspec = array(
0 =array("pipe","r"),//stdin
1 =array("pipe","w"),//stdout
2 =array("file","/home/h-saito/julius/error-output.txt","a"),//stderr ファイルに書き込み
);
$cwd = $JULIUS_HOME; $env = array(null);
$p = proc_open($JULIUS_EXEC,$descriptorspec,$pipes,$cwd,$env);
if(is_resource($p)){
fwrite($pipes[0],$ASR_IN);
fclose($pipes[0]);
echo stream_get_contents($pipes[1]);
fclose($pipes[1]); $return_value = proc_close($p);
echo "command returned $return_value\n";
}
?>
PHP Julius(GMM) 認識結果
Recognition: 2nd pass (RL heuristic best-first) STAT: 00 _default: 715548 generated, 28051 pushed, 4128 nodes popped in 1670 sentence1: S は 古来 茶 を いただき まし て な 、 まだ 、 はい 、 健康 、 五 年 、 時 に 、 何 の 音 の 出る もの の 電源 が 起き 、 また 、 巨額 の ない 録音 、 さて 、 五 人 旅客 機 に 釜 の 公 教育 、 四 、 S 。 wseq1: S+記号 は+助詞 古来+名詞 茶+名詞 を+助詞 いただき+動詞 まし+助動詞 て+助詞 な+助詞 、+補助記号 まだ+副詞 、+補助記号 はい+ 感動詞 、+補助記号 健康+名詞 、+補助記号 五+名詞 年+名詞 、+補助記号 時+名詞 に+助詞 、+補助記号 何+代名詞 の+助詞 音+名詞 の+助詞 出る+動詞 もの+名詞 の+助詞 電源+名詞 が+助詞 起き+動詞 、+補助記号 また+接続詞 、+補助記号 巨額+形状詞 の+助詞 ない+形容詞 録音+名詞 、+補助記号 さて+接続詞 、+補助記号 五+名詞 人+接尾辞 旅客+名詞 機+名詞 に+助詞 釜+名詞 の+助詞 公+名詞 教育+名詞 、+補助記号 四+名詞 、+補 助記号 S+記号phseq1: silB | e s u | w a | k o r a i | ch a | o | i t a d a k i | m a sh i | t e | n a | sp | m a d a | sp | h a i | sp | k e N k o: | sp | g o | n e N | sp | j i | n i | sp | n a N | n o | o t o | n o | d e r u | m o n o | n o | d e N g e N | g a | o k i | sp | m a t a | sp | ky o g a k u | n o | n a i | r o k u o N | sp | s a t e | sp | g o | n i N | ry o k a k u | k i | n i | k a m a | n o | k o: | ky o: i k u | sp | y o N | sp | e s u | silE cmscore1: 1.000 0.211 0.304 0.093 0.072 0.277 0.728 0.210 0.241 0.049 0.237 0.007 0.207 0.072 0.688 0.108 0.072 0.274 0.024 0.937 0.093 0.017 0.642 0.266 0.321 0.370 0.355 0.035 0.329 0.904 0.168 0.883 0.069 0.419 0.573 0.075 0.065 0.425 0.165 0.599 0.012 0.042 0.732 0.362 0.017 0.384 0.034 0.057 0.066 0.559 0.022 0.203 0.035 0.337 0.594 0.011 1.000 score1: -53826.011719
認識精度は低く、文章の意味が推測できないです。"score"が0.1を切る単語も多く散見されます。
Julius(DNN)とWatson Speech to Text 比較
音響モデルをGMMは認識精度が低かったため、認識精度の高いDNNに変えてWatson Speech to Textと比較します。
GMM
$JULIUS_EXEC = "julius -C ./julius-4.4.2.1/dictation-kit-v4.4/main.jconf -C ./julius-4.4.2.1/dictation-kit-v4.4/am-gmm.jconf -input file";
DNN
$JULIUS_EXEC = "julius -C ./julius-4.4.2.1/dictation-kit-v4.4/main.jconf -C ./julius-4.4.2.1/dictation-kit-v4.4/am-dnn.jconf -dnnconf .
/julius-4.4.2.1/dictation-kit-v4.4/julius.dnnconf -input file";
Julius(DNN) 認識結果
Recognition: 2nd pass (RL heuristic best-first)
WARNING: 00 _default: hypothesis stack exhausted, terminate search now
STAT: 00 _default: 12 sentences have been found
STAT: 00 _default: 189280 generated, 16581 pushed, 2334 nodes popped in 1660
sentence1: 本日 は ご 来場 いただき まして 巻 の 中 が 退学 会議 に 先立ち まし て 、 お 客 様 に お 願い も し て あげ ます 携帯 電話 など 音 の 出る もの の 電源 を 切り ください マナー 許可 の ない 録音 撮影 は ご 遠慮 ください 皆 様 の ご 協力 を よろしく 笑 。
wseq1: 本日+名詞 は+助詞 ご+接頭辞 来場+名詞 いただき+動詞 まして+副詞 巻+名詞 の+助詞 中+名詞 が+助詞 退学+名詞 会議+名詞 に+助詞 先立ち+動詞 まし+助動詞 て+助詞 、+補助記号 お+接頭辞 客+名詞 様+接尾辞 に+助詞 お+接頭辞 願い+名詞 も+助詞 し+動詞 て+助詞 あげ+動詞 ます+助動詞 携帯+名詞 電話+名詞 など+助詞 音+名詞 の+助詞 出る+動詞 もの+名詞 の+助詞 電源+名詞 を+助詞 切り+動詞 ください+動詞 マナー+名詞 許可+名詞 の+助詞 ない+形容詞 録音+名詞 撮影+名詞 は+助詞 ご+接頭辞 遠慮+名詞 ください+動詞 皆+名詞 様+接尾辞 の+助詞 ご+接頭辞 協力+名詞 を+助詞 よろしく+副詞 笑+名詞
phseq1: sp_S | h_B o_I N_I j_I i_I ts_I u_E | w_B a_E | g_B o_E | r_B a_I i_I j_I o:_E | i_B t_I a_I d_I a_I k_I i_E | m_B a_I sh_I i_I t_I e_E | m_B a_I k_I i_E | n_B o_E | n_B a_I k_I a_E | g_B a_E | t_B a_I i_I g_I a_I k_I u_E | k_B a_I i_I g_I i_E | n_B i_E | s_B a_I k_I i_I d_I a_I ch_I i_E | m_B a_I sh_I i_E | t_B e_E | sp_S | o_S | ky_B a_I k_I u_E | s_B a_I m_I a_E | n_B i_E | o_S | n_B e_I g_I a_I i_E | m_B o_E | sh_B i_E | t_B e_E | a_B g_I e_E | m_B a_I s_I u_E | k_B e:_I t_I a_I i_E | d_B e_I N_I w_I a_E | n_B a_I d_I o_E | o_B t_I o_E | n_B o_E | d_B e_I r_I u_E | m_B o_I n_I o_E | n_B o_E | d_B e_I N_I g_I e_I N_E | o_S | k_B i_I r_I i_E | k_B u_I d_I a_I s_I a_I i_E | m_B a_I n_I a:_E | ky_B o_I k_I a_E | n_B o_E | n_B a_I i_E | r_B o_I k_I u_I o_I N_E | s_B a_I ts_I u_I e:_E | w_B a_E | g_B o_E | e_B N_I ry_I o_E | k_B u_I d_I a_I s_I a_I i_E | m_B i_I n_I a_E | s_B a_I m_I a_E | n_B o_E | g_B o_E | ky_B o:_I ry_I o_I k_I u_E | o_S | y_B o_I r_I o_I sh_I i_I k_I u_E | w_B a_I r_I a_I i_E | sp_S
cmscore1: 1.000 0.924 0.529 0.982 0.881 0.188 0.775 0.009 0.854 0.037 0.170 0.017 0.543 0.813 0.991 0.774 0.689 0.741 0.668 0.946 0.590 0.156 0.938 0.743 0.096 0.961 0.018 0.089 0.355 0.990 0.009 0.790 0.880 0.403 0.179 0.485 0.965 0.992 0.522 0.109 0.535 0.030 0.475 0.956 0.310 0.815 0.775 0.934 0.908 0.998 0.330 0.942 0.509 0.889 0.758 0.894 0.123 0.765 0.007 1.000
score1: 955.145691
15~20秒ほどGMMより実行に時間かかりますが、認識精度は高くなることを確認できました。GMMの"score"よりも平均して高い値が返ってきました。
同じ音声ファイルを使い、Watson Speech to Text でもやってみました。
Watson Speech to Text 認識結果
※一部抜粋
{
"results": [
{
"alternatives": [
{
"confidence": 0.95,
"transcript": "本日 は ご来場 いただき まして まことに ありがとう ございます "
}
],
"final": true
},
{
"alternatives": [
{
"confidence": 0.81,
"transcript": "開演 に 先立ち まして お客様 に お願い 申し上げ ます "
}
],
"final": true
},
Julius(DNN)は"score"にバラつきが出ましたがWatson Seech to Textは"confidence"が0.8以上を安定して出すことができました。
JuliusとWatson Speech to Textの認識精度
Watson Speech to Text
Watson Speech to Textの方がJuliusよりも認識精度高いことがわかりました。Watson Speech to Textは今回、使用したクリアな音声であればconfidenceが0.8を超え、オリジナルの状態でも認識結果がとても高かったです。また、単語単体で発話するよりも文章にするとより認識精度が上がりました。(まずは単語単体を認識してから文章で補正するからです。)
Julius
名詞or固有名詞は誤認識が多かったです。一方、動詞や助詞は比較的認識率が高かったです(正しい認識結果ではない)。Juliusは文章として認識することは難しいようです(デフォルトでは)。音響モデルをGMMからDNNにすることで単語ごとの認識精度("score")は高くなりますが、その分、変更後の認識速度は低下します。また、会話の音声に感情があると認識が難しく感じました。今回の記事とは別に社内の勉強会動画を認識させたときはサンプルの音声ファイルに比べ、文章の意味を推測できないほど精度は低下しました。
まとめ
実用には大幅な認識精度の向上が必要であると感じました。Juliusは拡張性が高いため、条件(特定キーワード)を絞ることで精度向上が見込まれると考えられます。また、独自辞書を作成し、辞書のデータを充実させることも精度向上につながると考えられます。
Watsonはカスタマイズすることで精度向上が見込まれます。カスタマイズは無償のライトプランではできないため、精度向上を望むならば、有償プランの検討が必要です。
音声認識を使う用途によって、適切な評価基準が変わるため、評価基準の最適化を行う必要があるでしょう。