音声認識にてデータベースのクエリ操作を制御するシステムの構築例

 音声認識によるコンピュータ制御用のツールはオープンソースが商用製品に対して大幅に立ち後れている分野の1つだが、今回私は、現状のコンピュータにて音声認識によるデータベースのクエリ操作と音声読み上げがどこまでできるかを実際に試してみることにした。そのためにはオープンソース系ツールを多数組み合わせなくてはならなかったものの、今回の結果はかなり満足できるレベルに到達できたのである。

 ここで行うデータベースのクエリ操作については、下記の4つの基本コンポーネントが必要となる。

  • 構文と語彙目録――ここでの処理で使用される単語およびその組み合わせが何を意味するかの指定
  • 音響モデル――音声認識エンジンに識別させる音声についての統計情報
  • 音声認識エンジン――人の話す音声を単語として解析するコンポーネント
  • ダイアログマネージャ――認識された指示をシステム制御用のコマンドに変換した上で応答を返すためのコンポーネント

 今回のシステム構築では、メジャーなものとマイナーなものを含めて下記のオープンソース系ツール群を使用している。

Juliusを選択した理由
 今回使用する音声認識エンジンについては、有名なOSS系の音声制御システムを事前にいくつか試してみた。この分野の文献で多く言及されているのはPerlboxであるが、このプロジェクトは既に活動を停止しているようである。また私はGNOMEを使用していないので、GNOMEの音声制御システムも有効な選択肢ではなかった。私が利用するディストリビューションにはKDEの音声制御システムが同梱されてはいるものの、これは思うように動作してくれず、また今回目指したタスクで必要とされるほどの柔軟性に欠けていた。Simonは新規に立ち上げられたダイアログマネージャプロジェクトの1つだが、これはドイツ語だけでしか利用できない。Sphinx 2、3、4はこの種の用途で広範に使用されている有名なツールだが、私のシステムには簡単にインストールできなかった。結局、今回初めてインストールしてみたJuliusおよびHTKツールキットの組み合わせが私にとって有効に機能するツールとなってくれたのだが、だからと言ってこの種の処理にその他のプロジェクトがすべて適していないという訳ではなく、今回はたまたま私のシステムにてJuliusが適していたということだけのはずである。
  • Integrated Taxonomic Information System(ITIS)およびMySQL――ITISデータベース(今回のクエリ処理の対象とするLatin Taxonomyとして知られる科学データベース)は自由にダウンロードできるが、付属するSQLスキームはInformix用のものであってMySQL用のものではない。ただしこのスキームをMySQLの構文に変換する方法は、いくつかのサイトにて解説されている
  • PHPおよびPerl――スクリプトの記述に使用
  • HMM Toolkit (HTK)――ケンブリッジ大学から提供されている音響モデル構築用の数学エンジン(このソフトウェアが真の意味でオープンソースであるかについては異論も出されているが、その真偽についてはライセンス条項に目を通した上で各自で判断して頂きたい)
  • Julius――音声認識エンジン
  • Audacity――音声サンプルの録音ツールとして使用
  • British English Example Pronunciationsその他――音素構造を参照するための語彙目録として使用
  • Voxforge――PerlとHTKによる音響モデル構築時に用いたユーティリティ
  • Festival――エジンバラ大学の提供する音声合成システム。ダイアログマネージャの制御下で返される結果の読み上げに使用

 本稿を書き下ろす際に主として参考にさせて頂いたのはVoxforgeサイトであるが、ここに記載されている解説はこの種の音響モデルを構築する際のガイドとして役立つはずである。私がこのチュートリアルに触れるのは今回が最初の経験であり、記載されている内容に一通り目を通すだけでも数時間がかりの作業となったが、以前に同様のプロセスを行った経験を有してこの種の作業にて発生するであろうエラーを事前に回避できる人間であれば、サンプル音声の録音を含めた全過程に要する時間は大幅に短縮されることになるだろう。

コマンド指定用の構文

 今回の試みは不特定多数の話し言葉をカバーする汎用的な音声認識アプリケーションを構築しようという訳ではなく、対象となるのは特定の作業についての制御コマンドとして用いられるごく限られた数の単語だけであり、それらはすべて事前に把握できるものばかりである。

 具体例としては下記のようなコマンドを識別できればいいはずだ。

  • COMPUTER SLEEP――ユーザが他の作業をする際など、コンピュータによる音声コマンド認識を一時停止させる
  • COMPUTER WAKE――コンピュータによるコマンド認識を再開させる
  • COMPUTER RESET―― 設定を初期状態に復帰させる
  • COMPUTER QUIT――プログラムを終了させる

 そしてこれらを総合する一種のメタフォーマットとしては次のようなものを想定しておく。

COMPUTER ( SLEEP | WAKE | RESET | QUIT )

 これは“コンピュータ”という単語の後にはパーレン内部の単語のいずれか1つのみが続いて指定されるということを示している。同様にこの構文は、下記のような指定についても拡張されることになる。

CONNECT ( LOCALHOST | SERVER1 | SERVER2 )
USE ( ITIS | OTHERDB )

 これらの構文指定が何を意味するかは、特に説明しなくても分かって頂けるだろう。

音響モデル

 音声認識に必要となるコマンド指定用の構文が定義し終わったら、PerlやHTKその他のコマンドを用いてVoxforgeツールで用いる音響モデルの生成用ファイル群を準備する。そうした作業の中には、実際のユーザとなる私が先の構文を読み上げたものをサンプル用の音素として録音するという処理も含まれる。そしてこの録音データをVoxforgeツールで処理することにより、音声認識エンジン(SRE:Speech Recognition Engine)の利用する統計イメージや音響モデルと呼ばれるものが得られるのである。

 Voxforgeサイトには音響モデルの準備手順を説明した資料として「チュートリアル」および「ハウツー」という2つのドキュメントが用意されている。このうち「チュートリアル」には実行すべきプロセスの詳細が解説されており、この種の作業を初めて行うユーザにとっての最適な入門書と評していいだろう。それに対して「ハウツー」はいわば「チュートリアル」の簡易バージョンであり、全プロセスの概要を短時間で把握できるようになっている。私も最初は「チュートリアル」を読み進めることから始めたが、その後は先に定めた構文およびそれに対応する音声データの改善を手早く済ませるため「ハウツー」を参考にして作業を進めるようにした。

 今回私はAudacityを用いて自分の読み上げるコマンドサンプルの録音を行ったが、この作業については他の同様のユーティリティを使っても問題はないはずだ。ただしAudacityの場合は、すべてのコマンドを一括して長大な.wavファイルとして録音しておき、ラベル機能を用いて個々のオーディオファイルを識別させ、メニューにある“Export multiple”コマンドによって分割すればいいので、作業全体を効率的に進められるのである。

 次に準備するのは、認識させる単語のスペルとその音素構成を対応させた語彙目録である。これは具体的には下記のような情報として示される。

COMPUTER [COMPUTER] k ax m p y uw t ax r

 このプロセスが終了して得られる音響モデルは、他の任意のユーザでも使えるという訳ではなく、サンプルのコマンド読み上げを行ったユーザに特化した情報となる。これはセキュリティ上のメリットと見なせると同時に、構築したシステムの汎用性を損なうデメリットともなる要件である。

 コマンドを読み上げるユーザが誰であるかに依存しない汎用的な音声認識モデルの構築法は本稿での説明とはまったく別の話となるが、例えばVoxforgeサイトではその詳細および、オープンソース系ツールを用いたソリューション構築法が解説されている。

音声認識エンジン

 以上の準備が終了したら、音声認識エンジンを介した音響モデルの動作試験を行う。

julius -input mic -C julian.jconf

 ここで音声認識エンジンのJuliusが行うのは、マイクロフォンから入力される音声に対して、julian.jconfという設定ファイルにて指定された音響モデルによる解析を施すという作業である。そしてこの解析結果は、各種の診断情報と共にターミナルウィンドウに出力される。以下のサンプルは私が“COMPUTER WAKE”というコマンドを読み上げた際の出力例である。

### read waveform input
pass1_best: <s> COMPUTER WAKE
pass1_best_wordseq: 0 9 5
pass1_best_phonemeseq: sil | k ax m p y uw t ax r | w ey k
pass1_best_score: -12243.838867
### Recognition: 2nd pass (RL heuristic best-first)
STAT: 00 _default: 26 generated, 26 pushed, 5 nodes popped in 444
sentence1: <s> COMPUTER WAKE </s>
wseq1: 0 9 5 1
phseq1: sil | k ax m p y uw t ax r | w ey k | sil
cmscore1: 1.000 1.000 1.000 1.000
score1: -12466.839844

 この実行例の場合、この音声認識エンジンは最初の解析パスにおいて正しいコマンド認識を行っており、2回目のパスでも同様の結果が得られている(このエンジンは常に2度の解析を行うようになっている)。また診断情報の中にはスコア値も含まれていて、これは認識されにくい単語を特定する際の目安として使えるはずだが、このスコア値そのものは音響モデルや使用環境によって左右される性質の数値である点も考慮する必要があるだろう。

 先のコマンドラインをLinuxマシンで直接入力する場合は、下記のような指定になる。

julius -input mic -C julian.jconf | mydialogmanager.php

 ここでは音声からの認識結果を該当するコマンドに対応させる際に、すべての出力をダイアログマネージャにパイプしているが、実際に使用されるのはそのうち一部だけである。

ダイアログマネージャ

 ここで必要となる本質的な情報は先の実行例のうち“sentence1:”で始まる行だけであり、デバッグモードでの作業やスコア値を確認するような場合を除き、その他の情報は必要ない。このように必要な情報行を特定するのがダイアログマネージャの役割であり、その前後にある不要な情報を削除して、今回のケースであればコマンドとして処理可能な“COMPUTER WAKE”という文字列のみを抽出するのである。下記のサンプルは、PHPで作られた簡易的なダイアログマネージャの記述例である。

#!/usr/local/bin/php -q
<?php
$awake = false;
$myloop = true;
$in  = defined('STDIN')  ? STDIN  : fopen('php://stdin' , 'r');
while ($myloop) { // infinite loop
    $line = fgets($in,1024);
    if (substr($line,0,8) == 'sentence') {
        $sent = substr($line,15,-5);
        $ok = more_stuff($sent);
    }
}
?>

 このコードでは最初にステータス変数をいくつか定義しておき、次にJuliusからの入力チャンネルが開かれているかを確認してから、最後に無限ループに入って処理すべき文字列が入力されているかを連続的にチェックさせている。そしてこの待機ループの繰り返し中に指定された文字列がヒットした時点で、該当するコマンド文字列を抽出するのである。

 問題はmore_stuff()で行う具体的な処理として何を実行させるかであるが、考えられる1つの方法はswitchステートメントによる分岐処理である。

switch ($sent) {
    case 'COMPUTER WAKE':
        $awake = true;
        break;
    case 'COMPUTER SLEEP':
        $awake = false;
        break;
    case 'COMPUTER RESET':
        // statements depending on other parts of the DM
        break;
    case 'COMPUTER QUIT':
        $myloop = false;
        break;
    default:
        if ($awake) {
   // go on and do interesting things with other statements
        } else {
   // do nothing
        }
        break;
}

 ここではコマンド文字列の処理プロセスの一環としてFestivalによる読み上げも行わせたいのだが、そのために必要な処理を実行するのが下記の関数であり、その際には音声としての読み上げと同時にデバック用の画面出力も行わせている。

function saytext($phrase) {
    echo $phrase."\n";
    exec('festival -b \'(SayText "'.$phrase.'")\'');
}

まとめ

 私の口から出されたコマンドはJuliusによって100%に近い正解率で認識されているのだが、セッション開始時の発声部だけはその例外である。これはおそらく最初に出される音声についてはコンピュータ側にとって解析上のヒントとなる要素が不足しているので、その分だけ誤認識が生じやすくなっているのであろう。この問題の対策としては、最初の発声部はプログラム的に廃棄させるという回避法が考えられるし、その実装はそれほど難しくないはずだ。

 音声認識に詳しい人間の目からすると、ここで用いた認識モデルの不備として、無音の“サイレンス”部に関する処理を設けて無関係なコマンドの排除や認識率の向上をさせるプロセスが抜けている点が気になるところだろう。そうした処理に関する詳しい情報はVoxforgeサイトにまとめられているので、興味のある読者はそちらを参照して頂きたい。

 またここで定義した構文フォーマットについては、下記のようなコマンド指定を行えるようにすることもできる。

DIAL < ONE | TWO | THREE | FOUR >

 こうした角カッコで囲んだ部分の指定によって、例えば“DIAL TWO THREE”、“DIAL ONE”、“DIAL FOUR ONE TWO TWO”といったすべての指定をHTKツールキットが有効と見なすようにできるのだが、この種のフォーマットを実際に使用した場合は認識率が大幅に低下してしまった。

 いずれにせよここで用いた構文フォーマットが自然言語の認識プロセスとはほど遠いものであるのは確かだが、Festivalと簡単なダイアログマネージャを介するだけで、驚くほど高い精度で人間の口から発するコマンドを理解して実用上の問題もほとんど発生しないデータベース読み上げシステムが構築できてしまうのである。

Colin Beckinghamは東部オンタリオに在住し、フリーランスのプログラマ兼ライターとして活動しており、現在は、バイオマス燃焼装置の運用ログをオープンソースリソースを活用して記録および視覚化するプロジェクトに取り組んでいる。

Linux.com 原文