pyroongaの挙動

GroongaをPythonから扱うのにpyroongaを使ってみたのですが、いくつかハマリポイントがありました。 Groonga serverが必要 rroongaは単独で動作するのに対して、pyroongaはGroongaがserverとして起動している必要があります。 カラム名が予約語だとハマる pyroongaはテーブル名をクラス名に、カラム名をアトリビュートにマッピングして動作します。したがって、”from”などの予約語がカラム名だとうまく動きません。あまりPythonには詳しくないのですが、これを回避する方法は思いつきませんでした。 複数語のクエリで期待した結果が返ってこないことがあある いまいち原因がわからないのですが、”foo bar”というようなクエリをコマンドから与えた時に得られる結果と、queryメソッドで同じクエリを与えた時の結果が異なります。ちょっとよくわかりません。 対処: subprocess呼び出し 非常にダサい方法ではありますが、pythonからgroongaコマンドを直接呼び出すことで期待する結果が得られるようになりました。 これで期待通りの結果が得られるようになりました。 追記(2015/0512) ロケールが設定されてない環境(CGIなど)でqueryがunicode文字列の場合、引数をきちんとUTF-8でencodeしてやる必要があります。でないと、UnicodeEncodeErrorが発生します。私はこれで数時間悩みました。

Published
Categorized as 開発

gensimとword2vec

何かと話題のword2vecですが、自分も使ってみようと思って試行錯誤したことを書いてみます。間違いなどあればぜひコメントください。 GoogleのC実装 https://code.google.com/p/word2vec/にある、C言語による実装は独立したアプリケーションで、Apache 2.0ライセンスで提供されています。 ビルドした状態でword2vecコマンドを使って学習、モデルを作成し、distance等のコマンドからモデルをもとにデータを出力します。以下はword2vecを引数なしで実行したときの実行例です。 各オプションの意味は以下になります。 train: 学習対象のテキストデータ(コーパス) size: 次元数(デフォルトは100) window: skip-gramの対象とする単語の最大数を指定(多分)デフォルトは5 sample: 単語を無視する際の頻度の閾値(デフォルトは1e-3) threads: 処理するスレッド数 hs: Hierarchical Softmax(階層的Softmax)を使う(1)/使わない(0) negative: ネガティブサンプリングに用いる単語数 binary: 出力結果をバイナリ形式にする(1)/テキスト形式(0) cbow: Continuous Bag-of-Words 連続単語集合モデルを使う(1)/使わない(0) iter: 学習する回数(イテレーション)の指定。デフォルトは5 CBOWとSkip-gramについてはFiS Projectの記事がわかりやすいです。階層的SoftmaxとネガティブサンプリングについてはnishioさんによるQiitaの記事が詳しいです。 gensim(Python)実装 gensimはPythonとCを組み合わせて記述された、文書解析ソフトウェアパッケージです。ライセンスはLGPL3+で、ソースはhttps://github.com/piskvorky/gensimで公開されています。 もともと複数の計算モデルを扱えるようになっていたようなのですが、2013年9月ごろにword2vecが追加されたようです。webで紹介される実装はこちらの方が多いようです。 Google実装はサンプル実装という面が強く、コマンドからできることに限りがあります。それに比べるとgensimはできることが多い(任意の数の単語のpositive/negativeな演算ができる)のですが、モデル構築にメモリをより使用します。 1GBのテキストをコーパスとしてgensimのword2vecに与えたところ、メインメモリ8GBのマシンでスラッシングを起こしてしまいました。Google実装であれば、1GB程度のテキストはオンメモリで処理できるようです。また、POSIX threadによるマルチスレッド処理にも対応しています。 幸いなことに、gensimのWord2VecではGoogle実装のモデルを読み込ませることができます。大きなデータを扱う際には、これらを組み合わせるとよい感じです。 pythonで処理したモデルもファイルに保存することができます。 gensimでは、trainメソッドで後から追加学習させることもできるのですが、既に構築したモデルに存在しない単語は扱えない(語彙が増やせない)ようです。これに気付かず、分割したテキストデータをちょっとづつ学習させるというようなことをやらせようとしてはまりました。 結局、大きなコーパスを取り扱う際にはC実装でモデルを構築して、それをgensimで読み込むという使い方をするのが良いようです。 その他参考にした資料 ディープラーニングチュートリアル 応用編 追記(2015/5/20) gensimでword2vecの処理がシングルスレッドでしかできないような表現でしたが、Word2Vecの引数にworker=2等とすることでマルチコア動作することに気付きました。それでも処理速度はGoogle実装の方が早いとは思いますが、記録として残しておきます。

LanguageToolのgrammer.xml

先日の東京エリアDebian勉強会ハックタイムで、もう少しLanguageToolをみて行きました。いくつか誤解があったのと、gitのコードを見ていて気づいたことを記録します。 私が参照していたJapanese.javaがgrammar.xmlでカバーできないケースをソースコードレベルで対応するものだと勘違いしていたのですが、ここは単にテキストの分割と品詞情報の付加を行うための処理を記述するもののようでした。 grammar.xmlはもっと柔軟性が高いようで、LanguageTool Wikiに高度な処理を行う方法も記述されています。一方でやはりgrammar.xmlだけで対応しきれないケースはJavaで書けることも記述されています。 今回追加しようと考えていたのは、長音符「-」の直前の文字がひらがな、カタカナ以外のケースです。基本的にそれはありえないので、LanguageToolが指摘すべき問題とすべきでしょう。 Doc-ja Wikiに書いた方法で-tオプションを利用すれば、そのようなケースの時に「-」はどのような品詞情報が付加されるかを調べることができます。 単独の長音符「ー」は「未知語」と解釈されることがわかりました。単純にtokenがーでPOSが未知語である情報が検出されたら警告すればよさそうです。 しかし現状のLanguageToolはmasterのバージョンが3.0となっており、grammar.xml自体の文法も変わっていることに気付きました。したがって、Doc-ja Wikiの説明も今後は修正が必要になります。 ということを把握したところで先日は時間がなくなってしまいました。grammar.xmlの解説だけでも結構ボリュームがあるので、これを翻訳できると自分を含めうれしい人はいるかなあと思いました。

tty emacsで表示できない絵文字をゲタで代替表示

普段emacsをputtyからUTF-8で使うことが多いのですが、最近よくとある絵文字の表示に遭遇すると表示が崩れるという事態にあったので、なんとかできないかと対処してみました。 .@knok izonmoji-mode.elが参考にならないでしょうか。Emacsで独自 coding systemを定義する方法は私も知りたい。cp5022x.elが参考になるのだろうか? Wanderlustで iso-2022-jpを cp50220に置き変えてる。 — ふみやす%シェルまおう的なにか@通販生活 (@satoh_fumiyasu) 2015, 4月 2 前提として、https://github.com/kachie/emacs.d/blob/master/init.d/w32-init.elと同様の設定をしています。この設定では、いくつかの文字をputtyのUTF-8設定で正しく表示できるようなutf-8-for-puttyというエンコーディングを定義しています。その中でcp5022x.elという機種依存文字を表示させるためのelispも使われているのですが、その中身を見ながら見よう見まねで書いてみました。 変換テーブルの定義 置き換えの対象となる絵文字のテーブルを自作します。 #x1F440は絵文字の目玉で、これをゲタ(〓 #x3013)に置き換えるテーブルです。 エンコーディングの定義 utf-8-for-puttyエンコーディングの定義部分を修正します。 表示幅の定義 オリジナルのset-east-asian-ambiguous-widthテーブルに目玉のコードポイントを追加します。 これで自分が望む出力が得られるようになりました。 非tty Emacsの場合 今回の目的には合いませんでしたが、画像が表示できる環境ではemoji.elが良いようです。こちらを使うと、絵文字がイメージとして表示されます。キャリア別の絵文字も扱えるようで、なかなか高機能です。 @knok emoji.el はどうでしょうか? https://t.co/XwaX0Ta2wo — Shinichiro HIDA (@_shinichiro_) 2015, 4月 2  

将軍の名前を補完するshogun-completion

かつて、どこかで「プログラマが徳川将軍の名前を覚えられないのはshellで補完できないからだ」という言説を見かけ、それを解消するべくbashで徳川幕府の将軍名を補完できるshogun-completionというものを作成しました。 当時課題だった問題2点に対応すべく、このたび改良版を公開しました。 鎌倉幕府対応 以前の実装では徳川幕府のみにしか対応していなかったので、オプションにkamakuraを入力した上で補完をすると、鎌倉幕府の歴代将軍を補完できるように修正しました。 shogunコマンドの実装 以前の実装は実在しないshogunコマンドに対する補完、という実装だったのですが、このたびshogunコマンドも実装しました。開発言語にはgolangを用いています。ごーしょーぐんとでも呼んでください。 shogunコマンドの引数に将軍名を入力すると、何代目であるかの情報を出力します。これで以前の実装では対応できなかった、「当該将軍が何代目であるかわからない」という問題にも対応できました。 bashにおける補完の限界 実のところ、zshの補完であればもっと高度なことができます。将軍名を補完する際にそれが何代目であるかの情報を表示するといったことも可能です。 じゃあbashでできないかというと、頑張ればできそうではあります。”bash autocompletion: add description for possible completions“というStackOverflowのやり取りに一例がありました。以下はそのコードの転載です。 ローカル変数completionsに行区切りで補完したい情報を代入しています。その結果を、IFSに改行のみを設定したうえで、compgenに処理させています。これで、入力にマッチする補完候補のみが配列としてCOMPREPLYに代入されます。さらに配列の数が1つだった場合、文字列” – “以下の部分を切り取って返します。その結果、この例では入力したいIPアドレスのみが実際には補完される、というわけです。 このアプローチを自分も試してみたのですが、bashのバージョンのせいか、IFS=”\n”とした状態でもcompgenの出力全体を代入された1個の配列にしかなりませんでした。IFSを使わない方法も試してみたのですが、思うように複数個の配列にできなかったのでそこで断念しました。あと、当該記事にも書かれていますが、IFSの変更は影響が大きいのでむやみにやらない方がいいようです。

CMakeで自動生成ファイルを出力させる

あるソースコードにテーブルが配列として含まれているという状況で、メンテナンス性を高めるためにその内容を別のファイルに分離して、機械処理で配列として出力し、それをincludeするという手段を考えました。 これをautomakeで実現するのはそれほど難しくないのですが、autotools依存を避けるためにCMakeで実現する方法を調べてみました。 最終的に、以下のようなCMakeLists.txtで実現できました。 src/kk2.cがk2rom_hepburn.cをincludeすることを前提としています。k2rom_hepburn.cはperl scriptを通してtable/k2rom_hepburn.txtから生成します。 生成するスクリプトはadd_custom_commandを使います。OUTPUTに出力するファイル名、COMMANDに実行するコマンドを記述します。行が長くなる場合には、改行を任意の箇所で入れられるようです。 出力するファイルk2rom_hepburn.cを直接ターゲットとして扱えないようなので、add_custom_targetでgenerate_headersというターゲットを明示して作成し、その依存関係としてk2rom_hepburn.cを記述します。 最後に、作成したターゲットを依存関係へ組み込むためadd_dependencyを使います。これで、libkakasiの生成にgenerate_headersが依存することを明記しました。 以上のステップを踏むことによって、k2rom_hepburn.txtからk2rom_hepburn.cが生成された上でkk2.cがコンパイルされ、それをもとにlibkakasi.soが出力されます。

ruby+selenium+chromeでgmail送信

rubyのselenium-webdriverでgmailの操作を試みた結果、なんとか動くものができたのでメモしておきます。 同様のことをC#でやっている事例はあったのですが、rubyのCSSセレクタが思うように動きませんでした。結局、CSSではなくIDを指定してボタンの操作をしました。 URLはこんな感じの組み合わせでメールの内容を指定できます。 https://mail.google.com/a/?view=cm&tf=1& (定型?) to=nokubi@gmail.com& (toに送信先アドレスを指定) su=test& (suにサブジェクト) body=test (bodyに本文) これは”Mailto: for GMail“の拡張を参考にしました。

OpenCVのテンプレートマッチング

3DSのプチコン3号をどうにかしたいという目標を持ったので、3DSタッチパネルディスプレイに表示されるソフトウェアキーボードを、OpenCVを使って位置を認識させる、ということを試してみました。 まずはあらかじめ、キーボードの写真を撮っておきます。撮影にはNexus5の内蔵カメラを使いました。画像が若干傾いていたので、撮った写真をgimpで加工し、ほぼ長方形になるよう調整しました。 この画像から、キーを一つ切り出します。とりあえず「1」のキーを切り取ってみました。 キーボードの全体画像からこの「1」のキーを検出する方法として、テンプレートマッチングを行ってみます。OpenCVのサイトにそのものずばりのC++サンプルが掲載されています。自分はどこかのページで見たコードを参考に以下のようなものを作成しました。 search_imgを対象画像とし、tmp_imgにマッチする領域をtemplateMatch関数で算出します。アルゴリズムの選択には、特に何も考えずサンプルのまま(CV_TM_CCOEFF_NORMED/Collection coefficient)にしました。これにより、演算結果がresult_imgに代入されます。 その結果から、minMaxLoc関数を用いてresult_imgから値が最大となる位置を算出します。cv::Matは行列データ型であり、minMaxLocは行列内の最大値、最小値を持つ座標とその値を取得する関数です。今回の場合、必要なのは最大値の座標だけです。 サンプルプログラムではrectangle関数で該当する矩形領域に線を引いています。 自分はC++に不慣れなので、同じことをPythonでやってみようとしました。いろいろと試行錯誤の結果、以下のようなコードになりました。 rectangleに与えるべき引数がC++とはちょっと異なります。C++では矩形の起点と高さ+幅を与えるのに対し、Pythonでは2つの対角上の頂点の座標を指定します。最初この違いに気付かなくて、思うような結果にならず悩みました。 今回はスケールが同一の画像でのテンプレートマッチングだったので期待通りの結果を得ることができましたが、実際の利用をする場合には異なるサイズや、若干傾いた画像、他に余計なものが写りこんでいる画像などを相手にする必要があるので、もっといろいろな下処理などが必要になるものと思われますが、まずは第一歩を進めることができました。

壊れた仮想ディスクを扱う

立ち上げっぱなしのWindowsマシンが、気が付くとBSoDで落ちてしまいました。原因は追加したHDDにあるようで、Linuxで観測してみると不定期にSATAのエラーを吐きます。 再起動したら、HDD自体が認識されず困ったので、testdiskでパーティションテーブルの復元を試みました。幸いなことに、これでNTFSパーティションの復旧はできました。このtestdiskはSystemRescueCD辺りも含まれているので、普段から起動ディスクとして用意しておくと安心です。 しかしこれだけでは不完全で、NTFSパーティション自体がdirtyになっていたので、回復コンソールからchkdsk /fを実施することでファイルシステムレベルの修正できました。 しかし不幸はこれだけでは終わりませんでした。対象HDD内においていた仮想マシンが起動しなくなり、grubのプロンプトに落ちるという状況になりました。Debianのインストーラーrescueモードで見ると、そもそもext2/3としてのsignatureまで壊れてしまっているようでした。 使っていた仮想マシンのディスク形式はVMware Playerからimportしてきたvmdk形式だったので、いったんqemu-imgでraw形式に変換し、kpartxでパーティションを認識、fsckを実施しました。 大量のエラーが発生し、lost+foundにかなりのデータがおいやられたものの、なんとかfsckは完走しました。/homeごとなくなってしまいましたが… 幸い、以前変換したときの元のイメージも残っていたので、現在はそちらをraw形式に変換してデータの復元を試みています。重要なデータはlost+foundの方を参照することにします。

VMware PlayerからVirtualBoxへの移行

最近のスラッシュドットの記事から、VMware Playerのライセンスにおける「非商用の解釈がかわった」という記事(ライセンス解釈が変わったようです。)を目にし、この機会にVirtualBoxへの移行を実施しました。 移行対象は違いますが、既にやってみた人の記事(VMware Fusion 5からVirtualBox 4.3への移行)があったので、作業自体はそれほど難しくはありませんでした。ただ、この記事にもあるようにストレージをSCSIからIDEに変更する、という作業はVMware Playerでも同様に必要でした。 自分の場合、もう一点「ホストオンリーネットワークもそのまま移行したい」という希望があったので、そのために若干の作業が発生しました。参考にしたのは「VirtualBoxのネットワーク設定とCentOS6.5のインストール」です。こちらの記事はMacOSXのようですが、Windowsでも行うことはあまり変わりません。 VirtualBox マネージャーからメニューの「ファイル」「環境設定」を選択し、ダイアログを開きます。さらに「ネットワーク」「ホストオンリーネットワーク」を選択し、ネットワークを追加します。ネットワークレンジやネットマスクを、これまでVMware Playerで使っていたものと同じになるよう設定します。 ここで一点注意があります。VMware Playerが入ったままだと、VMwareの仮想ネットワークインターフェースが設定として保持されたままになるので、vmnetcfg.exeで設定を変更するか、VMware Playerをアンインストールする必要があります。そうしないと、同一のネットワーク設定が存在しているとWindows側に認識され、リンクローカルなネットワークが強制的に割り当てられてしまいます。 あとはOVF化した仮想マシンをVirtualBoxからインポートし、ネットワーク設定を先ほど作成したホストオンリーネットワークに指定して、実際に通信ができれば完了です。 NATネットワークの移行も、ほぼ同様の手順で実行し、Window側のネットワーク共有を使えばできると思います。