rubyhtk

HTKに関する先日の考察を踏まえて ruby による HTK ラッパーを実装してみました。

いまは連続数字音声認識のタスク(CENSREC-4の音声をrawからwavに変換したデータが対象)に依存したスクリプトになっているのですが、

$ rake clean

$ rake

$ rake train

$ rake hvite

$ rake hresults

のように使うことができます。

実装における基本的な考え方は

  • インタフェースが複雑であったり安定しない場合は、目的に応じて自前のラッパーをかぶせる
  • 手続きではなくモデルに着目してクラスを設計する

というところでしょうか。もはや HInit と HCompV と HRest の違いで初心者が躓くのは時代遅れのような気もするし、やりたいことをコーディングするときに入出力をファイル名やディレクトリ名で指定することも直感的ではないと思うのです。

下記では、音響モデルをコーパスで学習しながら精緻化するプロセスを、Model のインスタンス配列によって扱っています。

models = []
models << Model.first_train(data0,label)
while models.last.num_mixes < TARGET_NUM_MIXES
models << models.last.mixup_train(data0,label)
end
models.last.vite(data1,recout)

first_train メソッドはプロトタイプのモデルを作る処理、HCompV で平均と分散の初期化をする処理、HERest で最初の連結学習を行う処理をまとめてラップしています。クラスメソッドになっており、新しい Model クラスのインスタンスを返します。

mixup_train メソッドは Model のインスタンスメソッドです。HHEd でガウス分布の混合数をひとつ増やし、HERest を再度実行し、学習された新しいモデルのインスタンスを返します。メソッドの中で新しいインスタンスが生成されています。

Model クラスのインスタンスの実体は、一時ディレクトリに作られる MMF ファイルですが、ファイルやディレクトリに通し番号を付けて管理する作業を Model クラス実装の中に隠しています。

Model のインスタンスについて、dir メソッドで MMF が保存されたディレクトリ名を返します。また num_mixes メソッドは混合数を返します。

認識結果を得る HVite コマンドは Model の vite メソッドにラップされています。vite メソッドの引数で認識対象データを指定します。MFCC (*.mfc) ファイルのリストが格納されたスクリプトファイル名を指定しています。

まだまだ中途半端な実装ですが、なかなか興味深い方向性かなと思っています。

R のクラス設計をお手本に考えると、HVite や HResults などの出力をクラス化するのもよさそうです。

  • 2009-06-27 : メソッドの説明を加筆しました。
  • 2009-07-02 : wav 変換されたデータが対象であることを加筆しました。

ヒューマンインターフェースとソフトウェア開発

音声認識の研究者に広く使われているHTKというソフトウェアがあります。

数ヶ月前にリリースされた Ver 3.4.1 をいじりながら、多くの人が「HTKは難しい」と言うのはなぜなのか、改めて考え直しています。

「そんな難しくないですよ」と言いながら、いろいろ試したり説明をしようとしたら、私自身が落とし穴にはまり、落ち込んでしまうこともあります。

HTK は HMM(隠れマルコフモデル)による音声認識のためのツールキットです。最初はケンブリッジ大学で開発され、有償ソフトウェアになったのは 1993 年だそうです。その後 Microsoft に買収され、現在はケンブリッジの手に戻って、無償で配付されています。最新版の英語マニュアル(HTKBook)は384ページにもおよぶ膨大なPDFファイルです。

十数年にわたる音声認識研究の歴史が詰まっている HTK は、最近に至るまで拡張が続いています。今回、久しぶりにチュートリアルを精読してみたら、知ってるつもりで知らなかった機能にいくつか気づくことができました。と同時に、拡張を繰り返してきたツール全体の仕様の見通しの悪さ、コマンドラインオプションの覚えにくさ、などなど、課題もいろいろあるように感じました。

HTKBookのチュートリアルをできるだけそのまま実行してみようとして、チュートリアルの説明と挙動が異なる箇所があること、配付サイトで公開されているファイルだけではチュートリアルを完全に再現できないこと、などにも気づきました(私の誤解かも知れませんが)。

フリーソフトでつくる音声認識システム - パターン認識・機械学習の初歩から対話システムまで

フリーソフトでつくる音声認識システム – パターン認識・機械学習の初歩から対話システムまで

荒木先生が書かれた参考書は HTK の手っ取り早い入門として学生からも評判がよいのですが、突っ込んで使いこなしたい場合には、自力でマニュアルを読んで理解をしなくてはなりません。

最近、信号処理やパターン認識の分野で Matlab や R などスクリプト言語系のツールが普及しつつあります。性能や言語仕様の利便性などの議論はともかく、学習の容易さという点からは高く評価できるでしょう。

また、個人的には Ruby on Rails の勉強において script/console がとても有用であることに気づきました。いわゆる「コード補完」が有効であることにも驚いたし、irb で “hello”.methods を実行すればStringクラスのメソッド一覧が簡単に見られる、というのも目からウロコでした。

プログラミング言語がプログラマーと言語処理系のインタフェースであるとすれば、「ユーザインタフェースの原則」がプログラミングという行為においても重要ではないかと思います。

  • やりたいことが完結に記述できること
  • やりたいことがコマンド名、関数名、メソッド名として「連想が容易」であること
  • 連想された記述法が正しいかどうか、対話的にフィードバックを得られること

などがインタプリタ系言語の嬉しいところです。そう考えていると、

において議論してきた「インタフェースの原則」と重なってきます。

HTKでは数千個という膨大な学習データを扱ったり、膨大なパラメータ数の統計モデルを学習することができます。しかし、やりたいことを完結に記述できるか、コマンドやオプションを容易に連想できるか、操作のフィードバックが容易に得られるか、などと考えると、あちこちに落とし穴(ノーマンの7段階モデル流に言えば「淵」)がありそうです。

HTKについて考察していると「音声認識による機械と人間のインタフェース」にとどまらず「HTK と研究者のインタフェース」についても考えたくなってしまいます。

第61回音音研 6月18日

下記のとおり開催します。直前のお知らせで恐縮ですが、ご興味のある方はお気軽にご参加ください。

  • 第61回 2009年6月18日(木) 18:30-
  • 話題提供1:東京女子大学 松村瞳さん+東京大学 西本
    • テーマ:音声CAPTCHAの検討
    • 要旨:昨年度から東京女子大学と東京大学の共同で行っている「人間が聞き取ることができ、機械に認識されにくい音声」の研究について、昨年度の成果の報告を行い、今年度の研究計画についてご助言をいただきたいと思います。
  • 話題提供2:帝京平成大学・川島先生
    • テーマ:音源分離知覚について
    • 要旨:日常的な場面では人間は重畳信号から個々の音源について知覚する。その理解は例えば何を分離知覚しているのか,あるいはその知覚に関わる要因とメカニズムはどのようなものかという点について行われてきた。今回は音源分離知覚に関するこうした問いについて文献調査と発表者の研究の結果などから得られる回答を整理する。

Rails controllerの複数形と単数形

Ruby on Rails の勉強を始めたころ、なぜここは単数形で名前をつけるのか、なぜここは複数形にするのか、といったことでいちいち悩みました。最近、RESTful routing の勉強をしていて、controller の名前を単数にしてしまった場合の例が見つからず、「名前つけ直そうか」と焦ったのですが。。

以下、Rails 2.2.2 で確認しました。

config/routes.rb で

map.resources :channels

と書くと

$ rake routes
channels GET    /channels                        {:controller=>"channels", :action=>"index"}
formatted_channels GET    /channels.:format                {:controller=>"channels", :action=>"index"}
POST   /channels                        {:controller=>"channels", :action=>"create"}
POST   /channels.:format                {:controller=>"channels", :action=>"create"}
new_channel GET    /channels/new                    {:controller=>"channels", :action=>"new"}
formatted_new_channel GET    /channels/new.:format            {:controller=>"channels", :action=>"new"}
edit_channel GET    /channels/:id/edit               {:controller=>"channels", :action=>"edit"}
formatted_edit_channel GET    /channels/:id/edit.:format       {:controller=>"channels", :action=>"edit"}
channel GET    /channels/:id                    {:controller=>"channels", :action=>"show"}
formatted_channel GET    /channels/:id.:format            {:controller=>"channels", :action=>"show"}
PUT    /channels/:id                    {:controller=>"channels", :action=>"update"}
PUT    /channels/:id.:format            {:controller=>"channels", :action=>"update"}
DELETE /channels/:id                    {:controller=>"channels", :action=>"destroy"}
DELETE /channels/:id.:format            {:controller=>"channels", :action=>"destroy"}

となり、view で <%= link_to “index”, channels_path %> などと書けます。

たまたま controller を channel のように単数形にしてしまうとどうなるのか、試してみました。

map.resources :channel
$ rake routes
channel_index GET    /channel                         {:controller=>"channel", :action=>"index"}
formatted_channel_index GET    /channel.:format                 {:controller=>"channel", :action=>"index"}
POST   /channel                         {:controller=>"channel", :action=>"create"}
POST   /channel.:format                 {:controller=>"channel", :action=>"create"}
new_channel GET    /channel/new                     {:controller=>"channel", :action=>"new"}
formatted_new_channel GET    /channel/new.:format             {:controller=>"channel", :action=>"new"}
edit_channel GET    /channel/:id/edit                {:controller=>"channel", :action=>"edit"}
formatted_edit_channel GET    /channel/:id/edit.:format        {:controller=>"channel", :action=>"edit"}
channel GET    /channel/:id                     {:controller=>"channel", :action=>"show"}
formatted_channel GET    /channel/:id.:format             {:controller=>"channel", :action=>"show"}
PUT    /channel/:id                     {:controller=>"channel", :action=>"update"}
PUT    /channel/:id.:format             {:controller=>"channel", :action=>"update"}
DELETE /channel/:id                     {:controller=>"channel", :action=>"destroy"}
DELETE /channel/:id.:format             {:controller=>"channel", :action=>"destroy"}

のようになりました。

link_to ‘xxx’, channels_path が channel_index_path のようになり、多少名前が冗長にはなりますが、無事に RESTful routing に移行できそうです。

  • 2009-07-22 追記:rails 2.x の scaffold が生成するのは「複数形の名前が付けられたコントローラ」なので、scaffold 流に揃えたほうがよさそうですね。