やることがたくさんあるときに限ってどうでもいいことが捗ってこまっているいっちーです。 先日、挑戦状を受け取ったので、グロンギ語翻訳の品質改善に挑戦しました。
《緊急告知》2月26日(金)、何かが起こる!!「仮面ライダークウガ」にまつわる新商品のようですが・・・。ページに書かれているのは、グロンギ語?お客様の中で、リントの言葉に翻訳できる方はいらっしゃいますか~? https://t.co/hMDQCST6Tz
— プレミアムバンダイ (@p_bandai) 2016年2月17日
お手軽に試せるページも作ったので、こちらでお試し下さい。
変換の仕組み
変換の仕組みの詳細は以前書いた記事をどうぞ。 概略だけ書いておくと、 日本語からグロンギ語への変換はMeCabを使った読み・品詞推定の結果もとに、 変換ルールを適用して翻訳しています。 グロンギ語から日本語への翻訳は、この翻訳問題が実は仮名漢字変換と同じ問題だということを利用して、 IMEの辞書をグロンギ語対応したものを使っています。
変換ロジックの修正
旧版の問題点
- ボンジジュグギゾガギバギグス: 紺地重視を再開する
- ゲゲルンギバブゾロヅボパザセザ: ゲームのしなくっ持つのはだれだ
- ゲゲルゾザジレスゾ: ゲームを始めるぞ
「この日」は「ボンジ」が正しいのですが、「ボボジ」と変換していたため正しく認識できていませんでした。 「の」は通常「ガ」になるのですが、助詞として現れたときは「ン」になります。 さらに連体詞の一部として出てきたときも「ン」になるのですが、こちらのルールが抜けていました。
さらなる改良
旧版はmecab-skkdicを元にした辞書を使っていましたが、 mozcベースに変更しました。 mozcの辞書はクラスタリングや語彙化のような粒度調整が行われており、変換精度の向上が期待できます。 どのようが調整が行われたかはMozcソースコード徹底解説 や 言語処理学会でのMozcの資料を見るとよいと思います。
mozcの変換エンジンをそのまま使えると良かったのですが、すごく面倒なことがわかったのでギブアップしました。 (依存モジュールの関係で32bit版しかビルドできず64bitのプログラムからは直接呼び出せないとか、C++とかC++とかC++とか) mozcとMeCabの辞書構造は非常に似ているので、MeCabの辞書形式に変換して利用しています。 mozcには共起辞書を使った補正機能(例えば同じ「かいたい」という読みでも、「猫を飼いたい」「マグロを解体」を出し分ける機能)など、 MeCabにはない機能も入っているので、そのうち挑戦してみたいですね。 ただし、mozcには機能だけ組み込まれていて辞書が入っていないので、mozcを使っただけだと大差ないかもしれません。
改良の結果
- ボンジジュグギゾガギバギグス: この日重視を再開する
- ゲゲルンギバブゾロヅボパザセザ: ゲームの資格を持つ子は誰だ
- ゲゲルゾザジレスゾ: ゲームを始めるぞ
だいぶ近くなりました。 「重視」と「遊戯」はグロンギ語で同じ音なので、難しいですね。
変換サーバの実装
ライブラリはPythonで書いてあるので、 PythonのWebフレームワークであるPyramidを使ってAPI化してみました。
デプロイ時のファイル置き換えをアトミックにする
sakuraのVPS上でdrootを使って起動しています。 kazuhoさんの「server-starter が SIGHUP 受け取ると pull 型のデプロイツールが起動して、そいつが新しいディレクトリにイメージを展開して、そこに chroot してアプリケーションが動き出すスタイル」を実践してみたくなったので、以下のようなスクリプトを書いてみました。
CONTAINER_DIR=/var/containers/hogehoge-$$
tar zfx hogehoge.tar.gz -C $CONTAINER_DIR
droot run --root $CONTAINER_DIR exec gunicorn server:application &
CHILD=$!
_term() {
kill -TERM "$CHILD" 2>/dev/null
}
trap _term SIGTERM
_cleanup_child() {
droot rm --root $CONTAINER_DIR
}
trap _cleanup_child EXIT
wait "$CHILD"
起動時にイメージの展開を行い、終了時には展開したディレクトリのお掃除をします。 「デプロイ時のファイルの置き換えを atomic にする」ことができている・・・はず。 MeCabの辞書って上書きして大丈夫なの?とか余計なことを考えなくて済むので楽ちんですね。 (mmapしているから直接書き換えるとまずそう。mvすればinodeが変わるから行けるかもしれないけど、単語辞書と品詞辞書とで不整合起きたらまずくないか?とか) モジュールが遅延ロードされて一部のモジュールだけ最新版になってしまい新旧の互換性が無くて死んだり、 テンプレートだけ都度読み込みになっていたため新旧の互換性が無くて死んだり、といったつらい経験があるのでこれは嬉しいです。
ただ、Dockerイメージをtarで固めたものはそこそこサイズがあって展開に時間がかかるのが不安です。 もたもたしていると Server::Starter が旧プロセスを殺してしまうので、ここは迅速に行う必要があります。 timeoutを付けてパラメータ調整すれば少しは安心かな・・・。
graceful restart
前述のスクリプトを Server::Starter 経由で起動すれば、プロセスの入れ替わりはできるのですが、 graceful restartするにはサーバのプログラムが Server::Starter に対応している必要があります。 PerlであればStarlet,Starman,Gazelleといった対応モジュールが出回っていますが、 残念ながら今回の変換器はPythonです。
PythonのWSGIサーバであるgunicornはRuby版のunicornと大体同じ作りになっているっぽいので、 Server::Starterに対応するとはどういうことか で紹介されているのと同じ方法が使えます。
どこでフックして変換コードを差し込めばいいかよくわかなかったので、さっきの起動スクリプトに差し込んでみました。
if [[ -n "${SERVER_STARTER_PORT-}" ]]; then
export GUNICORN_FD=$(echo $SERVER_STARTER_PORT | tr ';' ' ' | xargs -n1 | cut -d= -f2 | xargs | tr ' ' ',')
fi
僕はShell Script芸人じゃないので、もっとシンプルな書き方があれば教えていただきたい。
あとがき
最初の予告ツイートの商品の販売が始まってましたね。
商品ページのグロンギ語をいくつか解読してみましたが、あまりうまくいかない・・・。 本家グロンギ語は語順が変わっている場合もあって流石に難しいですね。