Recently Posted Articles:
前の記事で、Windows の php(cli)では ctrl-c がキャッチできないと書いた。
しかしできないなら、そういうエクステンションを書けばいいのではないか。
つまり
- モジュールの初期化時に sapi が cli だったら SetConsoleCtrlHandler() を呼んで、適当な関数でキャッチするようにしておく
- シグナル受信時のハンドラとなる php 側コールバック関数のリストを保持しておく
- ハンドラを登録する php の関数を公開する
- ハンドラをディスパッチする php の関数を公開する。ハンドラをディスパッチする、とはシグナル受信済みだったら、登録された php 側の関数を順繰りに実行すること
- tick 関数でもハンドラをディスパッチする(tick まわりは deprecated なのかな?)
というような感じ。
これにより、たとえば
wincon_register_signal(function () {
release_resources();
exit(0);
});
while (true) {
wincon_dispatch_signal();
do_something_fantastic();
}
というようなコードで ctrl-c をキャッチして終了時の後始末を行えるようになるはず。
pcntl のソースを見てみたが大体同じようなことをやっていた。
誰か作ってくれないかなー(チラッ
ぼちぼち配布のことについて考える。
しかし、配布するかどうかは定かではない。なにしろ、満足に動かすまでがとても面倒なのだ。
最低限必要なもの:
あるといいもの:
- 英単語をそれなりに読ませる場合: php_sqlite3.dll
- サービスとして動かす場合: php_win32service.dll
- 漢字の読みやアクセントをマシにする場合:
- php_chasen.dll
- php_xsl.dll
- ChaSen 2.3.3+
- UniDic (UTF-8) 1.3.12+
- ChaOne 1.3.3+
- NumTrans 0.51+
そもそも Windows に PHP を入れてあるような人はだいたい開発畑の人だろうから、単にリストしてあるのを入れてね☆ で済むのかもしれないが……。
そうでない人には無理だこれ!
形態素解析に ChaSen を使うようにした。
前の記事のとおりアクセントの確定に ChaOne を使うわけだが、さらに数値文字列+接尾辞を自然な読み上げに変換するために NumTrans も使うようにした。
これらのツールに対して、いくつか不満がないわけではない。
- やはり再配布禁止 + ダウンロードするにはユーザ登録が必要、という規定が厳しすぎる
- ChaOne、NumTrans は xslt で書かれているわけだが、とても重い
- NumTrans のアーカイブにマニュアルが同梱されていないので、どういう入力をすればいいのか、他のプログラムのソースを覗かないとわからない
- 茶筅、茶碗と来てなんで NumTrans は茶柱とかじゃないの! ばかなの! しぬの!
とまあどうでもいいものばかりだ。一方で性能はとてもよいと思う。ChaOne については名詞が連続した際のアクセント位置の移動も行ってくれるようだ。たぶん。なので、呼び出し側で行う事前処理はモーラが連続する際の読点の自動挿入だけになった。これは助詞と助詞以外(ただし非自立な動詞は除く)の境界で、直前の読点から 12 モーラより離れている場合に読点を挿入するというものだ。こうしないと読点少な目の長めの文章を一気に読まれて聞き取りづらくなる。12 という値は経験則だ(5、7、12、17 あたりの数値から選択した)。
NumTrans について。ChaOne と同様に xsl ファイルで構成される。したがって、入力は xml のはずなのだけど、なぜかマニュアルが同梱されていない。
とりあえずいじってみた感じ、変換対象になるプレーンテキストを xml の形式にでっち上げて入力すればいいようだ。当然、でっち上げたとしても整形式である必要はある。
変換前のテキストが
ニュージーランドのゴールデン湾で、100頭近くのゴンドウクジラが浜辺に乗り上げているのが見つかった。
であれば
<?xml version="1.0" encoding="UTF-8"?>
<cha:D xmlns="http://www.unidic.org/chasen/ns/structure/1.0">
ニュージーランドのゴールデン湾で、100頭近くのゴンドウクジラが浜辺に乗り上げているのが見つかった。
</cha:D>
な感じにして NumTrans に与える(つまり、この xml を入力として、numtrans.xsl を読み込んだ XSL プロセッサに与えて、変換を行う)。名前空間とタグ名は重要ではない。たぶん名前空間はなくてもいいし、オレオレ名前空間でもいいし、タグ名もなんでもいい。
ただし、変換対象になる文字列の末尾には改行を入れておかないと変換されない。
変換するとこんな具合になる。
<?xml version="1.0" encoding="UTF-8"?>
<cha:D xmlns:cha="http://www.unidic.org/chasen/ns/structure/1.0">ニュージーランドのゴールデン湾で、<num:A xmlns:num="http://www.unidic.org/numtrans/ns/structure/1.0" type="decimal" origText="
100">百</num:A>頭近くのゴンドウクジラが浜辺に乗り上げているのが見つかった。
</cha:D>
num:A 要素の属性は今のところ特に必要ではないので、cha:D 要素の textContent 属性を取り出して、一番最初のプレーンテキストに上書きすればいい。
さて ChaSen にも php エクステンションがある(
なぜか pecl のサイトからは辿れない)。
php_mecab のビルドには失敗したが、こちらは成功した: VC9、non thread safe(参考:
PHP の Chasen モジュールを作成するための Patch)。
それにしても pecl エクステンションを Windows でビルドするのは面倒だー。
Windows も、最適化甘ちょろバージョンでいいから、Windows のコンポーネントに cl.exe 等一式の build_essentials を含めてくれれば、こういうソース配布するから後は自分でビルドしてね! という流儀に十分対応できるのに…
softalk の辞書の代替の件。12 万の単語のほかに 3502 件の異なる種別の結合ヒント(join.stk)、280 件の数値に接続する接尾辞(num.stk)も追加してしばらく放っておいた。
確かに読める漢字は増えているのだが、増えた単語の中には前後の文脈から判断しないと区別できないものも少なくない(「行った」と「行った」とか)。そうすると、ちんたましいみたいに「明らかにそうは読まないだろ!」というのは減ったものの、その代わり「確かにそうとも読むけど、その文脈ではそうじゃないだろ!」というパターンが増えた。
うーん。
softalk 内蔵の読み仮名摘出処理というのは、悪くはないけど、結局のところ大掛かりな文字列置換でしかないと思うのだなー。つまり文脈も見てよというのは無理な注文なのだ。
とすると、そのあたりもフィードリーダー側でやるべきなのか。せっかく MeCab を使っているのだ。
さて、その形態素解析だが、実は MeCab ではなく ChaSen に切り替えようかと思っている。ChaSen + UniDic + ChaOne の組み合わせで単語単位のアクセントだけではなく、活用により変化する場合のアクセントの移動や、語頭・語尾が変化するタイプの変換なんかをやってくれるのだ。さすがに名詞同士の結合時のアクセント変化までは面倒見てくれないと思うけど。
したがって、処理の流れでいうと
- フィード読み込み
- 読み上げるテキストの原型を生成
- 英単語のカタカナ変換
- Chase + UniDic + ChaOne で形態素解析、アクセント情報の取得
- 名詞結合時のアクセント移動処理
- モーラが連続しすぎている際の読点挿入
- softalkw.exe へ渡す
ということになる。
フィードリーダーを cli な PHP スクリプトとして書いている。
つまりコンソールアプリケーションということになる。したがって、ctrl-c を押すと中断する。
ところで Windows + cli な PHP って、ctrl-c をキャッチできないのが *ものすごく* 欠陥だと思うのだけどどうか。そういうのを PHP で処理するときは、pcntl_signal() とかを使ったりするのだけど、そもそも pcntl 関数群が Windows 版 PHP に存在しない。基本無限ループで動作し、かつ終了時にクリーンアップの必要な処理というのをどう書けばいいのか?
フィードリーダーに関しては特にクリーンアップ処理はないのでまあいいけど。強いて言えば、起動中の softalkw.exe がそのまま残り続けることくらいか。これは発声の終了と同時にプロセスも終了する(たまに発声が途中で止まることもあるのでまったく問題ないというと嘘になる)。
何とか無理やり ctrl-c をキャッチできないのか。
たとえば、a.php と b.php を用意する。
ユーザーが起動するフロントエンドは a.php とする。実際の処理は b.php に書き、a.php は b.php を子プロセスとして起動する。
a.php は
子プロセスを起動();
while (true) {
touch(適当なファイルA);
if (子プロセスが終了してる())
exit(0);
sleep(10);
}
b.php は
while (true) {
素敵ななにか();
if (ファイルAのmtimeから十分な時間が経過()) {
exit(0);
}
こうすれば、ctrl-c だろうがなんだろうが a.php が終了したことを b.php 側で間接的に知ることができないか。
試してないけど。
日本アクセント学会の人(が実在するかは知りませんが)にとっては常識なのだろうけど、日本語は単語、というか特定の形態素が連結すると、それた自身が持つアクセント位置を上書きする形でアクセント位置が変化する。
特に漢字熟語が連結すると、たいてい前半の部分は平板型になって、後半の先頭にアクセント位置が移動する。
ということで、そんな感じで試しにいじってみたら、(漢字熟語の連続する部分に関しては)かなり自然な発音になって驚いた。
たとえば UniDic によると、それぞれ「操作」のアクセントは「
そうさ」、「可能」は「かのう」だ。が、これが連結すると、「そうさ
かのう」になる。そして、確かにそのアクセントで自然に聞こえる。
おもしろい!
しかしながら、何ごとにも例外はある。「経済-産業-省」は「けーざいさんぎょーしょー」であくまで平板型を維持する。何が違うのか…?
素人の考えでいくと、尾高型の単語を除いて、本質的に日本人は単語を右肩下がりのトーンで発音するのではないだろうか。で、下がりすぎたなぁ、と感じたときにゆり戻しが発生し、アクセントのリセットが行われるのではないか。このときトーンが下がるコストは音韻で異なる(母音や長音のような、発声しやすい音が多いとコストが低い?)のではないか?
いやぜんぜんわかりませんけど。日本アクセント学会員の添削求む。
edict、cmudict、のスキーマに levenshtein 値を追加し、変換対象の metaphone 改の値により近いものを結果とするようにした。
MeCab とのやり取り。いまは、mecab.exe を呼び出している。php_mecab.dll みたいなのがあったらなー!! と思ったら、
あった。すばらしい。
さっそく落としてビルドしてみる。ビルドできない! なんだか mecab_node_t のメンバがない。
GitHub に置いてあるソースは Mecab 0.991 にまだ対応してないようだ(参考:
php_mecabのインストールに凄くハマりました(時間を費やしたという意味で))。
あきらめた。あぁ…
dic.stkの代替に動詞も入れた。133014 行。あと最後に接尾語が残っているのでもうちょっと増える。
賢くなったのかというと、多少は漢字が読める子になっているのだけどまだまだだなあ。17 日は阪神大震災のあった日なのでニュースの記事によく「鎮魂」の字が現れたのだが、とりあえず「ちんたましい」とは読まなくなった。
日本語の読みではなく、edic や cmudict、metaphone、つまり英単語のカタカナ化辞書の話に戻るのだけど、edic が微妙だ。もともと和英辞書なのをひっくり返してるためなのだが、ときどき「ん?」となるものがある。
ひっくり返すロジックは、
・日本語の単語がカタカナ(おそらく外来語)だけで構成
・英語の説明が 1 単語だけで完結
の行を抜き出して英単語 -> カタカナの形に構成しなおすというものだ。
でも日本語でカタカナって外来語だけとは限らないんだー。
なので、edic 由来のカタカナ化辞書で fool を引くと「アホ」と返ってきたり、mother を引くと「オモニ」と帰ってきたりする。
うーん。
変換時に、変換候補として
・edict 由来の辞書
・cmudict 由来の辞書
・metaphone 由来の動的変換
の順で存在するカタカナを返しているわけだけど、各辞書由来のカタカナと動的変換したカタカナの
levenshtein distanceを取り、動的変換結果に近いほうを返したほうがいいのか?
でも php 組み込みの levenshtein() はマルチバイト文字にたぶん対応してないから、また車輪の再発明しないといけない。
前に書いたような気がするが、ときどき softalkw.exe が途中で発声を中断したままゾンビ化する(通常は発声が終わったらプロセスが終了する)ことがある。
とりあえず、ini ファイルでフックプロセスに対するタイムアウトを指定できるようにして、その時間以上プロセスが生き残っていたら強制的に終了するようにした。デフォルトは 15 分。
さてそれはそれでいいとして、この記事ではアクセントについて考えてみる。前に SofTalk は漢字かな混じり文から読みを得るために MS IME の機能を利用しているのではないか? と考えたが、インストールディレクトリをよく見たらそうではなかった。自前の辞書を持っていて、それにより読みを得ているらしかった。
ほう。
メインとなる dic.stk は 3 万行くらい。客観的に見て、SofTalk は漢字や熟語の読み間違えはかなり多い方だと思うが、そうすると 3 万行の辞書というのは相場的には足りないんだろう。
ところでフィードリーダーでは MeCab を利用して品詞の修正なんかを行っている。それだけではなく、読みも MeCab で得て SofTalk には結果のひらがな列を渡せばいいんじゃない? と思いつくが、しかしそうするとアクセント情報が抜けているので、聞いていて見事なほどに何を言ってるか聞き取れない発声になる。
dic.stk には満遍なく単語のアクセント位置が記述されていて、それが dic.stk の存在意義を高めているのである。
MeCab 側で形態素と一緒にアクセント情報は取れないのかな? 調べてみると、標準でインストールされる IPA 辞書ではなく、
UniDic 辞書にはノードごとの核アクセント位置も一緒に記述されているようだ。アクセント情報は単に核アクセント位置だけでなく高アクセントが連続するモーラ数とか品詞の前後関係とか活用で変化するタイプがあるとかもっといろいろ複雑怪奇らしいけど、とりあえず核アクセント位置がわかるだけでも大きい。
でも、UniDic は公式サイトに行ってユーザー登録して住所氏名を入力して…ってやらないと入手できないんだなこれが。フィードリーダーの標準装備にするにはちょっと難しい。
うーん。
SofTalk の dic.stk は単なるテキストファイルで、構造も公開されている。とりあえず漢字列の名詞にしぼって dic.stk になくて UniDic にあるものを抜き出し、アクセント情報を含めた上で SofTalk の辞書構造に構築しなおし、使いたい人はユーザー辞書として使ってね的な感じでアーカイブに納めるようにすればいいのかな? UniDic のライセンス的には問題ないのかな?
* * *
そういうふうにやってみた。もともとの dic.stk が 31756 行なのに対して、126216 行に増えた。増えたのはほとんど名詞だ。動詞はまだ手をつけていない。
どうでもいいけど辞書ファイルのおしりに EOF(^Z)が付いていると softalkw が起動してくれないようだ。
いじっている途中で不思議なことに気が付いた。大相撲の記事で「序の口」を「じょのこう」と読むのが気になる。こうというのは「口」の音読みだ。ところが、dic.stk にはちゃんと「じょのくち」も登録されているのだ。うーん? 何か不思議なルールが絡み合って対象から零れ落ちているのか?
そういう現象があるとすると、単に辞書の登録数を増やしても即賢くはならないのかも…。
metaphone を使ってやってみた。
metaphone は、英単語の基本的な発声の知識をもとに、英発音をコード化するためのアルゴリズム。たとえば -ck は -K と発音する、とか j のうしろに ge、gy、gi が続く場合は J に変化する、とかいうルールに基づいて、単語ごとのキーを生成する。
english、pronounce、昨日の synchrogazer はそれぞれ、ENKLX、PRNNS、SNXRKSR というキーが対応する(X に違和感があるかもしれないが、X は -sh- の発音に対応する。それとアルゴリズムに r の前の ch が K に変化するルールが含まれていないので、ch も sh 扱いになる)。
metaphone のアルゴリズムは、生成の最後に不要な母音をすべて取り除くのだが、取り除かない状態のキーならカタカナを導き出すのに利用できるはず。そこで、metaphone 自体は PHP の組み込み関数として用意されているのだが、車輪の再生産した。といっても単にmb_ereg_replace() しまくるだけなのだが。もちろん、最後の母音削除はしない。
それから、上記の ch - r ルールなどいくつかルールを追加して、それをもとにカタカナを生成させて…おお、できた。ちゃんと synchrogazer もシンクロゲイザーになる。もちろん、基本的な発音のルールから外れた変な単語にはお手上げなのだけど、まあそれなりにそれなりな結果が出る。
ただ単語ごとに mb_ereg_replace() を 60 回近く施すという力技実装なので重い。
ちなみに
2011年5月現在、世界のブラウザシェアで第5位にあるOperaですが、デスクトップ以外のモバイルやゲーム機、テレビなどの家電製品にも組み込まれており、むしろ、そちらでは世界トップに近いシェアを占めています。特に昨年末あたりから開発ペースをあげていて、スマートフォン向けのOpera Miniは、Android版に続いて5月にiPhone版を公開。デスクトップ向けも最新版となるOpera 11.50のベータ版を公開し、新しい機能が試せるようになっています。
Operaの中の人に聞く、ブラウザの未来とはをそのまま SofTalk に読み上げさせると当然英語部分はおーぴーいーあーるえーじゅういちてんごーぜろ…などとなってしまうのだが、今回作った変換処理を事前に行うことで
2011年5月現在、世界のブラウザシェアで第5位にあるアプラですが、デスクトップ以外のモバイルやゲーム機、テレビなどの家電製品にも組み込まれており、むしろ、そちらでは世界トップに近いシェアを占めています。特に昨年末あたりから開発ペースをあげていて、スマートフォン向けのアプラ/ミニは、アンドロイド版に続いて5月にアイ/フォン版を公開。デスクトップ向けも最新版となるアプラ11.50のベータ版を公開し、新しい機能が試せるようになっています。
という感じに。Opera がアプラなのはまあ、ご愛嬌。とりあえずこれで、英単語混じりまくりのフィードでも変な読み方はしなくなった。
甦られない、じゃなくて読みが得られない場合、最終的に Bing の翻訳サービスに頼ると書いてきたが、まだフィードリーダーに組み込んでいない。
なぜか。2 つの理由がある。
Microsoft といえど知ってることしか知らないわけで、読めない単語はそのまま送り返されることがあるというのは以前の記事のとおり。たとえばいまマイナビニュースを見たら、どこかの柴犬が「Synchrogazer」というアルバムを出したとある。で、これを Bing Translator に対して what is synchrogazer? と聞いても残念な思いをするだけなのだ。というかシンクロゲイザーってマジでなんだ。
また、読み上げの事前処理でフィードの読み込み以外のネットワーク通信が発生すると、それが終了するまで待つかどうかの選択を迫られる。フィードの読み込みは原始的な協調型マルチタスクによって並列に行われるので、たとえば Bing のサーバが死んでると困るのだ。さりとて、Bing とのやりとりもマルチタスクの一部として扱うと、翻訳の結果が反映されるのは、翻訳を必要としたフィードではなくなってしまう。
どうするか。辞書にない単語であっても外部に頼らず、無理やり読みたい。
metaphone を使うとどんな単語も機械的に単純化できる。これを使ってみるか…?
前回の記事はちょっととりとめがなかった。
目的は「英単語に対応するカタカナでの読みを得たい。」である。
そのために英語→日本語の翻訳や辞書サービスの api なんかを調べているのだけど、あくまでやりたいのは読みを得ることであって、翻訳や辞書で英単語の意味を得ることではないのだ…。
それに、とりあえず Bing のサービスを用いて英単語を変換するスクリプトは書いた(まだフィードリーダーには組み込んでいない)が、なんか単語によっては英単語をそのまま返してくる。固有名詞扱いなのか?
うーん。
よし! わかった。ちょっと見方を変えてみる。英単語の発音記号列が得られれば、それをもとに無理やりカタカナをひねり出すことは可能なはず。ということで、
フリーの発音辞書を参照してみる。おお、CMU だ。メロメロメロンだ。夕張バリアだ。
この辞書をもとに英単語→カタカナのテーブルをローカルの sqlite データベースあたりに作って、読み上げ前にそれを参照しつつ英単語の変換をする。このテーブルには 13 万語くらいあるはずだが、もしこのテーブルにないナウい英単語であれば、Bing を参照する…という 2 段構えの構成にしようかと思う。
* * *
というわけで作って組み込んでみた。…のだが、まあ大体動いているのだけど。たとえばですね、apple をその発音記号にしたがって無理やりカタカナをひねり出すと、「アパル」というのが出てくる。なんだアパルて。いや変換アルゴリズムが悪いといえばそれまでなのだが。
それと、確かにネイティブはそういう感じに発声するだろうけど、なに気取ってんだ!欧米か! というのもある。sushi が「スーシー」とか。
まあ全体的に変というわけではなくて、変なのもあるという感じなのだけど。
うーん。
よし! わかった。前の記事で教えていただいた edict も併用しよう。こちらは読みの辞書ではなく和英辞書なのだが、カタカナに対して 1 単語だけで説明されているもののみ抜き出し、それもデータベースにおいておくことにする。edict を優先して、edict にないものは cmudict を参照し、それもなければ Bing に頼るという形だ。
SofTalk、というか AquesTalk ライブラリは、日本語の発声に絞られたライブラリであるので、英単語は対象外である。SofTalk のレベルでは、SAPI 由来のコンポーネントから発声させることもできるので、日本語の文字だけの並びとアルファベットの文字だけの並びで読み上げライブラリを切り替えつつ発声とかやってくれてもよさそうだけど、そうはなっていない。
したがって、現実的な解としては、英単語を事前にカタカナに置き換えてやることが考えられる。それが可能か?
オンラインの辞書サービス api というのはいくつかある。またローカルにフリーの辞書を持つ形でもいい。しかし、そういった api やフリー辞書の形式は、結局のところインタラクティブな操作から呼ばれる前提のようで、つまり帰ってくる結果は人間に目視させるためのものという立場のテキストであって、マシンリーダブルな形式ではない、気がする。
結果が xml で帰ってきて、ある英単語 foobar についてカタカナの読みは <reading>フーバー</reading> とかで得られれば非常に楽なのだけど、そういうのはないし、おそらく国内の辞書サービス提供側もそういう形式は考えてない(マシンリーダブルにして大量にアクセスされても儲けにならないので)。
というわけで海外のサービスに目を向けると、Dictionary.com と Bing の api が使えるかもしれない(Google にも似たようなのがあるけど有料)。ちょっと調べてみよう。
技術系のRSSフィードは英単語が入り混じりまくりになりがちなので、英単語変換はどうしても必要だ。
一通りできてきたので、適当な新聞社のフィードなんかを登録してほっといてみたが割と一日中しゃべりっぱなしで面白い。
ただ、日本語の「は」が気になる。これが助詞として用いられた場合は「wa」、その他は「ha」と発音してほしいのだけど、softalkw.exe 内蔵のそのあたりの仕組みはけっこう適当のようだ。今日は小沢元代表の裁判の記事がやたら多くて、その記事の中によくある「(小沢被告)はい。」というのを毎回「おざわひこくわい…」って気の抜けた声で言うもんだから面白くて困る。
内蔵のというか、おそらく softalk は MS IME の再変換機構あたりを利用してよみがなを得ていると思うのけど、そのあたりがあまりよくないのか?
そういうわけで、mecab が利用できる場合は発声の前に mecab を通して文を品詞に分解し、助詞としての「は」は「わ」に置き換えるようにしてみた。そのほかにも助詞の直後に助詞以外の品詞が来たときは強制的に「/」(これは、softalk が使用している AquesTalk ライブラリに対して、文節の区切りを指定する意味がある)をはさむようにしてみたり、読み仮名に長音記号が含まれているものは読み仮名そのものを読ませるようにしたり(「以上」は「いじょー」になる)。
この辺はもっといろいろヒント情報を入れられそうだけど、奥が深すぎて難しい。また、いろいろ小細工したからといって発声が自然になるわけでもない。たとえば上記の「以上」を「いじょー」と読ませると、「異常」以外の何者にも聞こえなくなってしまう。
Advertisement