傀儡師のプログラミング日記
CaboCha を ctypes で使ってみようとして玉砕 (2003/11/29)日本語係り受け解析器 CaboCha/南瓜: Yet Another Japanese Dependency Structure Analyzerを Python から使うのに,ctype から使えるようにしてみようかと思いました。以下は,その玉砕過程です。 Yasushi Masudaさんが 日本語訳されているのをまず参考に大体の使い方を見まして,MeCabLib というのも公開されているので,これを参考にやってみることにしました。MeCab C ライブラリ仕様と,CaboCha C ライブラリ仕様と,を見比べて,mecab_new → cabocha_new, mecab_new2 → cabocha_new2 と,このあたりは,単純に置換していくだけで同じようなものだからいけるそう。cabocha_sparse_tostr, cabocha_sparse_tostr2, cabocha_sparse_tostr3, strerror,destroy も MeCabLib からぱくってみます。MeCab の方は n-best やlock, unlockといった関数がないので,これらは削除してしまいます。さくさくと編集して試してみたのですが,エラーでダメでした。 ということで Python のコマンドプロンプト上で手打ちでひとつひとつ試してみることにしました。まずは,MeCab でやってみます。
ちゃんと動くので,基本的な ctypes の使い方は,一応理解できた?ようです。ここで同じようにして,CaboCha を使ってみます。
そうすると
と落ちてしまいます...。同じようなことをやっているので,基本的には間違っていないと思うのですが,エラーになる理由が良く分かりません。ctypes なら簡単に使えると思ったので,試してみたのですが,なぜエラーになるか分からないので,あっけなく玉砕です。素直に Visual Studio .NET C++ インストールして,CaboCha に付属している標準の Python ライブラリ使うことにしようかと思います(^^;;Traceback (most recent call last): File "<stdin>", line 1, in ? WindowsError: exception: access violation [this] Windows 版の time.time() は精度が悪い (2003/11/24)
次の短いプログラムを Windows と Linux で実行してみると面白いことが分かります。
Windows 版の場合、出てくる値と値の間がそれぞれ0.0010秒程度(100,000ループさせているのに32回しかそもそも値が表示されません)。これに対して、Linux で試すと 0.000017秒~0.000020 秒程度ごとに表示されるのです(100,000行表示される)。つまり、Linux で試すと、そもそも time.time() が 0.0000020秒(より短い)ぐらいで実行できるので、ループが回ってくるたびにちゃんと異なる時間を返してくれています。そうしたわけで、実行環境によってもやや状況は変わるかもしれませんが、Windows 版の Python だと time.time() を使うと、あまり時間を正確に測ることができないということになるようです。
ちなみに、次のように、単純に time.time() を 100000 回繰り返すだけのプログラムを実行してみると、Windows 0.231000065804秒、Linux 0.182734966278秒と、やや Linux の方が速いです。といっても、実は Windows の方が Linux のマシンより CPU 自体が遅いので正確ではありません。それでも、1.3 倍にも満たないわけで、オーダーがぜんぜん違うことの説明になりません。Windows 版の Python の time.time() はダメということになります。速度が遅いというわけではなく、精度が粗いということになります。
なぜこういうことになってしまうのでしょう。Windows が OSレベルでダメなのでしょうか。こういうレベルのことは、今まで関心がなかったので気にしていませんでした。そこで、少し調べてみることにしました。運よく、RunTime: LinuxおよびWindows 2000におけるハイパフォーマンス・プログラミング手法にちょうどよい記事が見つかりました。 Windows 2000には、時間を測定する2つのAPIがあります。1つはGetTickCount()です。この関数は、システムがブートされてから経過した時間をミリ秒単位で報告します。GetTickCount()には、「クロック・ティック」精度があります。つまり、システム・クロックが刻まれたときだけ更新されるということです。Windowsの場合は、10ミリ秒ごとに更新されるだけです。そのためクロック・ティック精度は、最高でも10ミリ秒すなわち 10,000マイクロ秒程度です。 という理由によって、GetTickCount() を使っているようであれば、まず、その点で問題がありだということになります。GetTickCount を使っている限り、10ミリ秒単位でしか計測できないということになるからです。もう少し見てみると、 Windows 2000にはまた、64ビット高精度パフォーマンス・カウンターの現行値を取り出すQueryPerformanceCounter() APIがあります。QueryPerformanceCounter()に対するコールの結果に含まれるそれぞれの「ティック」が表す内容は、 QueryPerformanceFrequency()が返す値で決まります。頻度数は、1秒当たりのカウンター増加数であり、したがって秒は次のように表されます。 QueryPerformanceCounter を使って「カウンタの値/1秒あたりのカウンタ増加数」のような感じになっていれば、この記事と同様の精度になってくれるはずです。 同じハードウェア上でWindows 2000のQueryPerformanceCounter()システム・コールは、Linuxのgettimeofday() APIよりも速度がはるかに遅いということです。今回の目的には、タイミング・ルーチンの2マイクロ秒という精度で十分です。 Windows でも、一応、2マイクロ秒の精度で、タイミング・ルーチンが書けるということになります。ということは、Python のソースで、GetTickCount を使っているか、QueryPerformanceCounter を使っているかをまずチェックしてみればよいと思い、Python のソースを grep してみると、GetTickCount は使っていないようです。QueryPerformanceCounter は _hotshot.c と、timemodule.c で使われています。Linux の場合は gettimeofday が使われています。そうであれば、理屈上 Linux の倍程度ではあっても、もう少し Windows 版の時間の粒度が細かくなってもよさそうなものなのに、なぜなのでしょう。_hotshot.c を見ると、MS_WIN32 の場合、次のようになっているので、上記の記事と同じような手法を使っているように見えます。 #ifdef MS_WIN32 #include time_clock(PyObject *self, PyObject *args)など見ながら、ふと考えてみると時間の間隔を測りたいわけではないのだから、time.clock() を使えばいいと気がつきました。これで試してみると、0.0000282秒程度で、100,000行ちゃんと表示されるようになりました。ということで、これから、Windows で時間測定をするときは、time.clock() でプロセス開始からの時間をチェックするようなやり方をしようと思う。しかし、なぜ time.time() だと精度がそんなに違うのでしょう。time_time(PyObject *self, PyObject *args)を見てみると、secs = floattime(); に問題がありそうな気がしてきます。
(2) に流れてしまっているようです。しかし、今頃、バグということはないでしょう。ということで、また検索をしてみると Python ライブラリリファレンス 10.10 timeitが見つかりました。 Windows の場合、 time.clock() はマイクロ秒の精度がありますが、 time.time() は 1/60 秒の精度しかありません。一方 Unixの場合、time.clock() でも 1/100 秒の精度があり、 time.time() はもっと正確です。いずれのプラットフォームにおいても、デフォルトのタイマ関数は CPU 時間ではなく通常の時間を返します。つまり、同じコンピュータ上で別のプロセスが動いている場合、タイミングの衝突する可能性があるということです。ここに最初から答えがありました。考えてみると、QueryPerformanceCounter はあくまでカウンタだから時間をとることができません。ですから、時刻をとる場合は、ftime() を使わざるを得ないということなのでしょう。しかし、起動時の時刻を保存しておいて、カウンタから求めた時間を足してやったらどうなのだろうと思って試してみることにしました。
このように time.time を上書きして置き換えてしまえば、呼び出しごとに確実に異なる時間が返ることが保証できそうです。ただし、100000回の呼び出しテストをしてみる time.time() < time.clock() < _mytime() の順にと、0.241000056267 , 0.340696932152, 0.514129638672 となって、ほんものの time.time() にくらべて偽ものは倍の実行時間がかかってしまいます。それでも、時刻としてできるだけきめ細かくとりたいときは、そうするしかないのではないかと思います。1回呼び出しするときのコストは、0.00002秒程度と換算できます。ただ、パフォーマンステストで時間を細かくとるなら、時刻ではなく時間の差分だけ分かればよいのですから time.clock でいいわけでしょう。
それ以前に、パフォーマンスの測定したいなら不精をして time.time() で時刻の差分をとるなどせずに、プロファイラをちゃんと使えば、よいという話もあります。Python 2.3 でプロファイラも改善されているようです。しかし、なんなのでしょうね、このオチは。Python のマニュアルを最初から全部読んでいれば、問題はなかったのに。 [this] IndexedCatalog について (2003/09/18)
IndexedCatalog 0.6 RC1 がリリースされたので、ちょっと IndexedCatalog に
ついてまとめてみることにしました。詳しくは、IndexedCatalog のオリジナルページを参照してください
このページは、IndexedCatalog のページから主として情報を取り出したものです。
IndexCatalog とは何かIndexCatalog は、Zope のオブジェクトデータベース(Zope Object Database (ZODB)) を使用することを前提としたもので、ZODB に格納されたオブジェクトの一覧を作成し (カタログ化)し、このカタログに格納されたオブジェクトを、属性値などを検索キー として高速に検索できるように索引(インデックス)をつける仕組みです。つまり、ZODB を拡張し、オブジェクトの属性値などのすべてのフィールドの値を string/integer/float のいずれかの型の値をキーとした取り出しやすい形式で ZODB 内に格納し(つまり索引を作成し)、これに対して検索うインターフェイスを 備えたものということになるでしょう。まだ、こなれた説明になっていませんが、 気にしないことにして、先に進みます。 ZODB のカタログとはいっても、Zope ではこのパッケージは使用できません。 IndexCatalog は、ZODB を使ったスタンドアロンのその他のパッケージ用に 現状で開発されているものです。ライセンスは Lesser GNU Public Licence (LGPL)です。 IndexCatalog を機能から見ると次のようになります 以上、IndexedCatalog のアバウトに訳したもの 例として、まず IndexedCatalog の例を参考にし、これをちょっと変形して 単語を登録できるようなものを作成してみました。 # 実行してみると とりあえずは、なんとなく、動いているような感じなので、 次は、もう少し実用的なものにチャレンジしてみることにしよう。 [this] Pyblosxom を調べてみる (2) (2003/08/02)
とりあえず、pyblosxom をインストールしてみました。実は、まだぜんぜん分かっていません。ということで、ちょっとずつ中身を確認しながら、いじっていこうかと思います。ちなみに下記の内容は、pyblosxom 0.7beta1 を見たときにすでに書いてしまったので、今動かしているのは、pyblosxom 0.8rc1 なので若干違うかもしれません。
CGI の本体の pyblosxom.cgi を見ると、sys.path の追加などを行ったあとに、libs.pyblosxom の PyBlosxom クラスをインポートしています。そして、libs.Request クラスをインポートしていますが、これはブラウザからのリクエストを格納するためのものでしょう。そして、os と cgi に関しては、通常の Python で CGI を書く場合のお決まりのパターンでしょう。 そして、ここからが本筋にあたるところです。Request() でリクエストクラスをインスタンス化して、reg.addConfiguration(config.py) で設定ファイルを読み込んで、それをリクエストクラスのオブジェクトに反映させているようです。from libs.pyblosxom import PyBlosxom from libs.Request import Request import os, cgi そして、次に空のディクショナリを作って、変数 d に代入します。req = Request() req.addConfiguration(config.py)
次に、["HTTP_HOST" ....] のリストの各要素をキーとして、os.environ.get(mem, "") で環境変数から、
それぞれの値を拾ってディクショナリに入れています。
このディクショナリを req オブジェクトに addHttp メソッドを使って追加し、さらにこれに、Python の CGI では定番の cgi.FieldStorage() を、"form" というキーでディクショナリに追加しています。フォーム変数にアクセスするときには、これを使います。
そうしたら、この req オブジェクトを引数として、PyBlosxom クラスをインスタンス化してやります。PyBlosxom がメインのオブジェクトなわけです。そして、p.startup() でたぶん、内部的に起動した状態を作り出して(初期化して)、p.run() で実際に何らかの動作を行うといったことなのでしょう。このように、pyblosxom.cgi 自体は、普通に Python で CGI を書くときのように単純なものです。
さらに Request.py を見てみると、Request クラスは、リクエストや Pyblosxom 自体の設定を格納しておくためのクラスであることが改めて確認できます。このリクエストオブジェクトを参照しながら Pyblosxom クラスが表示するための HTML を作り出すようです。def __init__ (初期化)のところを見ると、self._configuration = {}、self._http = {}、self._data = {} と単に3つの空のディクショナリを作ってやっているだけで初期化はやはり単純です。設定、HTTPリクエスト、データと3つに分けて格納する場所をつくってやります。その後ろも基本的に、このオブジェクトのプロパティに値を設定したり、取り出したりだけの単純なものです。 肝心の pyblosxom.py を見てみると、これは多少大きいのですが、それでもたったの338行。コアの部分は非常にシンプルなプログラムのようです。p = PyBlosxom(req) p.startup() p.run() [this] Pyblosxom を調べてみる (1) (2003/07/26)
ここに書き込むのは、ずいぶん久しぶりになってしまいました。というのも、編集用の
パスワードを忘れてしまって、それっきりになっていたからです。
でも、こちらに移行しようと最近また思い始め、Pyblosxom を使ってみようと思っているところです。
それにしても、TinyBlog を捨てようと思ったその矢先にパスワードを見つけてしまうとは、
なんとも皮肉なものです。これは簡単に捨てないでくれという啓示かもしれないので、
とりあえず Pyblosxom に移行するまでのメモを TinyBlog を使って残しておくことにしようと思います。
新バージョンの pyblosxom を使うと、こんな感じになるようです。移行するまでには時間がかかると思うので(私の時間的な問題で)、最新バージョンの pyblosxom ですこしずつ試しながら、その情報をここに載せておこうかと思うしだいです。 今日の作業まずは、SourceForge Project の pyblosxom のページから、pyblosxom-0.7beta1.tar.gz をダウンロードしました。そして、圧縮されたファイルを解凍すると、ChangeLog, INSTALL, README.contrib, config.py, README.plugins, xmlrpc.cgi, pyblosxom.cgi, ReadMeForPlugins.py の各ファイルと、contrib, libs, flavour_examples のディレクトリがあったので、とりあえず、INSTALL を開いてみます。INSTALL ファイルには、具体的に分かりやすくインストールが書かれているので、これを読み進めながら、インストール方法を検討してみることにします。「1.1 Simple Installation」 では、pyblosxom.cgi, config.py と libs/ ディレクトリを cgi-bin/ に置けばいいと書かれています。libs/ を cgi-bin に置くのに抵抗がある場合は、
を pyblosxom.cgi の4行目に入れればいいということなので、この方法で試すことにします。Blogger API に対応させたい場合は、xmlrpc.cgi も cgi-bin/ に置かなければならないようです。 「1.2 Using .htaccess」には、もうひとつのインストール方法として、.htaccess を使ったものがあり、http://www.example.com/cgi-bin/pyblosxom.cgi のような URL より、http://www.example.com/blog がいい場合は、.htaccess を使ったインストール方法もある書かれています。ということで、やはり、この方法にしようかと方向転換しました。この場合、まず pyblosxom.cgi の動作を確認した上で blog というファイル名に変更し、.htaccess に次の行を入れればよいようです。 http://www.example.com だけでアクセスさせたいなら、blog ではなく index にすればよいようなので、最終的には、この方式にしようかと思います。 「1.3 Denying access to config.py or the INI file」には、アクセス制限の方法が書かれています。libs/ や config.py をウェブからアクセスできるようなディレクトリに置きたくない場合、/home/joe/www に html ファイルが置いてあるとすれば、/home/joe/pyblosxom に config.py と libs を置くことができるので、安全のためにこの方法をとることにします。ということで、XREA では、ホームディレクトリ上に pyblosxom というディレクトリを作ってそこに config.py と libs を置き、www に pyblosxom.cgi をとりあえず、そのままの名前で置くことにします。そして、次の行を pyblosxom.cgi の先頭の方に追加します。Options ExecCGI <Files blog> ForceType application/cgi-script SetHandler cgi-script </Files>
最後に、ひとつにディレクトリにすべて放り込んでおきたい場合は、.access に次の行を入れて、config.py にアクセスされないようにするという手が紹介されていました。
基本的なインストールとしては、これだけで済むようなので、いたって簡単であることが分かりました。今日のところは、ここまでにしておきます。<Files config.py> order deny, allow deny from all </Files> [this] |
Blog
TinyBlog について 傀儡師の館(楽天) pyblosxom テスト中 自然言語処理について 人工無能について Python について |
Copyright (c) 2003, Kugutsushi