« 2009年02月 | メイン | 2009年04月 »

2009年03月 アーカイブ

2009年03月01日

Java servletをPerl CGIに移植してみる

Javaのservletは、HTTPリクエストを受けてテキストを返す場合(HttpServlet)に限ると、役割はCGIと同じである。実行プロセスが常駐している以外は、CGIと大差が無い。CGIでも、例えばApache+mod_perlを使えばPerlのモジュールをapacheのプロセスに常駐させることができ、さらにJava servletと差が無くなる。従って、Java servletにすべきかCGIか、またCGIならどの言語を選ぶべきかは、動作速度と作り易さにかかっているのではないか…と思っていた。

先日、3択首都当てクイズのservletのコードを見直していて、文字列処理等でJava特有の面倒臭さがあり、PerlのCGIならかなり楽に書けるんじゃないか、と思った。
筆者はPerlは結構昔から使っているのだが、PerlのCGIを真面目に作ったことが無かったので、CGI.pmの勉強を兼ねて、3択首都当てクイズのサーバーサイドプログラムのCGI版を作ってみた。

CGIのソースコード
対応するクライアント(Java applet)側コード
対CGI版首都当て3択クイズの起動ページ

確かに幾分楽に書けたが、常駐型ではないことが前提になっている。当然、極端に遅い。速度を確保するには常駐型にする必要があるが、作ってみて思ったが、これを常駐型にするのは厄介である。
筆者はPerlのCGIを常駐型にする方法としてApache+mod_perlしか知らないので、これを前提に書くが、PerlのCGIはJavaと違って使用メモリ量を制限するのが容易ではない。このwebサーバーにもmod_perlを導入しているが、メモリに関しては結構苦労があった。適切なタイミングでガベッジコレクションさせる術が無いため、何回か毎にリセットするしか無いのである。1つのコンテキストで処理していると、リセット中のスループットが低下するため、2つのコンテキストで処理するようにしたいが、次にデータベースとの接続の管理方法が問題になる。もしTomcatのようなコネクションプールの仕組みが使えるとしても、そこまでしてマルチスレッドにしたいか?と悩んでしまう。
筆者はApache+mod_perlは重要な選択肢の1つだと考えているが、CGIをmod_perlでインターネットに公開するのは、怖くてできない。一般論として、CGIを常駐型で動かすのは、強引で裏技的な手段であり、好んで採用するものでは無いと思う。やはり、常駐型ならJavaのサーブレットで、非常駐ならCGIで作るのが良いのだろう、と考え直した。

続きを読む "Java servletをPerl CGIに移植してみる" »

2009年03月04日

Apache1.3+mod_perlのコンパイル

偶然にも、mod_perlという名前を久々にここに書いた日の夜に、このサーバーのmod_perlが動かなくなった。いつも通りに、Debianのaptitudeを使って"Upgradable"なパッケージを全てアップグレードしたら、mod_perlが消えてしまったのである。
先月半ばにDebianの5.0がリリースされ、Apache 1.3がサポート対象外になったのと関係しているものと思われるが、Apache 1.3自体は"Obsoleted package"に分類されながらも残っていたのに、なぜmod_perlだけ消えてしまったのだろう。Perlがバージョンアップされたからだろうか。アップグレードに伴い、依存関係の解消のために消されるパッケージとして表示されていたのだろうが、今回は"Upgradable"が多量だったのでろくに見なかったのが失敗だった。

仕方なく、面倒だがこの際Apache2+mod_perl2に移行するかと思って、mod_perl2をインストールして動かしてみると、mod_perlで動かしたいMovableTypeがmod_perl 1.xにしか対応していないことがわかった。

何としてもmod_perl 1.xをインストールしようと思って、Debianのapache1.3+mod_perlのパッケージを拾ってインストールしようとすると、それがおびただしい数のパッケージに依存していることがわかった。ついでに、依存関係にあるパッケージを1つ1つインストールすると、次々にそれらが"Obsoleted packages"に入れられてしまい、完全にやる気をそがれた。

という訳で、Apache 1.3とmod_perlのソースコードをコンパイルするしか無くなった。
このサーバーではDebianのaptitude頼りでほとんどオープンソースのコンパイルというものをしなかったので、簡単にコンパイルできるとは思っていなかったが、予想に反してあまりにも簡単にコンパイルできた。
感動したので、その手順を記録する。


1. 適当な所から以下の2つのソースを取得、展開
apache_1.3.41.tar.gz
mod_perl-1.0-current.tar.gz

2. mod_perl-1.XXディレクトリに移動して、以下を実行

perl Makefile.PL APACHE_SRC=../apache_1.3.41/src DO_HTTPD=1 USE_APACI=1 EVERYTHING=1 ADD_MODULE=proxy
make
make install
cd ../apache_1.3.41/
make install

3. Apache::Request, Apache::Cookieをインストール

perl -MCPAN -e shell
> install Apache::Request


以上である。
Apache::Cookieは、Apache::Requestをインストールすると自動的にインストールされる。
mod_perlをmakeすると、自動的にapacheもコンパイルされる。

Makefile.PLのオプションは、mod_perlのページの例を参考にした。例の通りではmod_proxyが有効にならなかったので、ADD_MODULE=proxyを追加した。

続きを読む "Apache1.3+mod_perlのコンパイル" »

2009年03月14日

統計学復習メモ10: なぜ共分散行列の固有ベクトルが単位主成分なのか

前項に書いた通り、主成分分析における主成分の単位ベクトルは、共分散行列の固有ベクトルとして求まる。そのこと自体に昔から興味があったので、主成分分析の復習ついでに考察してみる。

まず、最小2乗法で考えてみる。簡単のために2次元で考える。n個のサンプルデータを

とし、第1主成分の単位ベクトルを

とすると、Xに対応する主成分軸上の第1主成分Yは
Y=P^TX
であり、そのYを元の座標系に戻したものX~は
¥tilde{X}=PY=PP^TX
である。このことは、高校で習った一次変換を思い出してやってみるとわかる。このX~が、Xを第1主成分の軸上に射影したものであり、これとXとの距離が、最小にしたい誤差ということになる。その誤差Eを、Xを直交座標とした場合の距離の2乗とすると、
E=(¥tilde{X}-X)^T(¥tilde{X}-X)
であり、p12+p22=1に注意すると、これは
=¥sum_{i=1}^n((p_1(p_1x_{1i}+p_2x_{2i})-x_{1i})^2+(p_2(p_1x_{1i}+p_2x_{2i})-x_{2i})^2)
=¥sum_{i=1}^n(p_2x_{1i}+p_1x_{2i})^2
と計算できる。

このp1とp2の2次関数であるEの最小値ををp12+p22=1の条件下で求めるので、「ラグランジュの未定乗数法」というやつを使う。すると、G=p12+p12-1と置くと、

である。これを整理すると、

となる。これの最初の行列は、よく見ると、Xの共分散行列

の逆行列のスカラー倍であるので、上の方程式は、両辺に左側からCov(X)をかけると

(aはスカラー)となり、a/λをλと置き直すと、

が得られる。従って、固有ベクトルの定義より、PはCov(X)の固有ベクトルであることがわかる。


さて、Xが2次元の場合については辛うじて求まったが、Xが3次元になると、この方法ではEが複雑になり、非常に辛い。筆者はMaximaに式の変形をさせながら3次元についても同じであることを調べようとしたが、E-λGを偏微分した所で変数が目測で200個以上ある状態になり、なんとなく雰囲気はあったが挫折した。この方法ではXをm次元に一般化するのはかなり困難か、何か特殊な技術が必要だと思う。

そこで、次に分散最大で考えてみる。こちらは幾分容易である。
n個のサンプルデータを

とし、第1主成分の単位ベクトルを

として、第1主成分

の分散
V=¥frac{1}{n}YY^T=¥frac{1}{n}(P^TX)(P^TX)^T=¥frac{1}{n}P^TXX^TP
を最大にするPを求めることを考える。Xのi行目とj行目の共分散をcov(Xi,Xj)と書くと、
V=¥sum_{i=1}^{m}¥sum_{j=1}^{m}p_ip_jcov(X_i,X_j)
とも書ける。このp1...pmの2次関数であるVの最大値をp12+p22+...+pm2=1の条件下で求めるので、やはりラグランジュの未定乗数法を使う。すると、G=p12+p22...+pm2-1と置くと、

である。これを整理すると、

すなわち

が得られ、PがCov(X)の固有ベクトルであることがわかる。

第2主成分以降についても、それより前の主成分と直交するという条件を加えて、同様の方法で解くと、やはりCov(X)の固有ベクトルであることがわかり、第1主成分から順番に分散最大の固有値が取られていくので、第k主成分はCov(X)のk番目に大きい固有値に対応する固有ベクトルだと求まる。

続きを読む "統計学復習メモ10: なぜ共分散行列の固有ベクトルが単位主成分なのか" »

2009年03月20日

統計学復習メモ11: なぜ特異値分解で主成分が求まるのか

前々項に書いたように、主成分分析は特異値分解を使っても計算できる。なぜだろうか。
それについても答えを得たので、ついでにメモしておく。

n個のサンプルデータを横に並べたものを

(m<n)とし、Pを単位主成分を横に並べたもの

とし、YをXの主成分とすると、
Y=P^TX
である。

特異値分解定理により、Xは何らかの正規直交行列U,Vと、m行m列部分が対角行列でそれ以外が0である行列Σによって、
X=U¥Sigma V^T
と分解される。それを応用すると、
XX^T=(U¥Sigma V^T)(U¥Sigma V^T)^T=U¥Sigma V^TV¥Sigma^TU^T=U¥Sigma¥Sigma^TU^T...(1)
が成り立つ。XXTはm×mの正方行列なので、ΣΣTもm×mの正方行列であり、
X=U¥Sigma V^T...(2)
と置くことができる。(σ1≧σ2≧...≧σmである)

前項に書いたように、Yの共分散行列は
Cov(Y)=P^TCov(X)P
であり、従って
Cov(X)=P\;Cov(Y)P^T
であり、Cov(Y)はCov(X)の固有値を対角成分に持つ行列である。Cov(Y)=Λを

1≧λ2≧...≧λm)と置くと、
...(3)
となる。一方、
Cov(X)=¥frac{1}{n}XX^T
なので、(1)と(2)を使うと、
...(4)
という形にすることができ、(3)と(4)を見比べると、PもUも正規直交行列なので、U=P、
すなわち ¥frac{¥sigma_k^2}{n}=¥lambda_k
の関係があることがわかる。さらに、
Y=P^TX=U^TU¥Sigma V^T=¥Sigma V^T
なので、Xの主成分YはXを特異値分解したものの一部として求められることがわかる。

続きを読む "統計学復習メモ11: なぜ特異値分解で主成分が求まるのか" »

2009年03月29日

簡易MMLプレーヤー

javax.sound.midiを使うと割と簡単に音階を鳴らせることがわかったので、簡単なMML(Music Macro Language)プレーヤーを作ってみた。BASICのPLAY文でお馴染みの、ドレミファソラシドがCDEFGABCで表されるあれだ。

Simple MML Playerの起動ページへ
ソースコード

とりあえず、CDEFGとでも入れてPlayを押すと、何か鳴ると思う。

以下の文字をサポートしている。(大文字と小文字の区別は無し、[]は省略可、xは数字を表す)
・C,D,E,F,G,A,B: ドレミファソラシド
 形式:(C,D,E,F,G,A,Bのどれか)[+/-][x][.][&]
 +/-は#/♭、xは長さ(全音符の何分の1か)、"."は符点(長さ1.5倍指定)、&はタイ/スラー
 例)C4 --- 四分音符のド、D+2. --- 符点二分音符の#レ
  B2&B8 --- 二分音符+八分音符の長さのシ
・R: 休符
 形式:R[x][.]
 xは長さ(全休符の何分の1か)、"."は符点
・O[x]: オクターブ(デフォルトは4)
・L[x]: 長さ省略時の音符/休符の長さ(1-64、デフォルトは4)
・V[x]: 音の大きさ(0-100、デフォルトはチャンネル0が100、チャンネル1が75)
・T[x]: テンポ、1分当たりの4分音符の数(デフォルトは120)
・@[x]: 音色(0-127、デフォルトは0(グランドピアノ))(参考資料
・<,>: オクターブを1つ下がる/上がる
・(): 和音
 形式:([C/D/E/F/G/A/B/+/-/>/<]の羅列)[x][.][&]
 例)(CEG)4 --- 四分音符の長さでドミソを同時に鳴らす
  (A>C+E) --- ラ、1つ上の#ド、ミを同時に鳴らす
・","(カンマ): チャンネル区切り(チャンネルは2つのみ)
 例)C8D8E8D8C4,E4F4G4 --- チャンネル0でドレミレド、チャンネル1でミファソ

もちろん独自仕様である。いわゆる作者 is Godである。

1行の中に","で区切って2つのMMLを書くと、それぞれが別のチャンネルで同時に鳴る。
同じ行の2つのMMLは必ず同時に始まる。一方が短い場合、長い方が終わるまで次の行が演奏されない。

エラー処理は極めて曖昧である。というか、ほとんど何もしていない。不正な文字列を入力や30和音などの無茶な指定をした場合にきちんと鳴るかどうかは無保証である。

ブラウザ上でなく、ローカルのファイルシステムから実行すると、演奏する前にMIDIデータ(SMF形式)がsimple_mml.midというファイルに保存される。

スライドバーの音量設定は、次の演奏から有効になる。


以下は、javax.sound.midiに関する覚え書きである。
・概略
SequenceにTrackを作って、Track#add()で時系列のMidiEventを登録して、Sequencerに渡してSequencer#start()を呼ぶとMIDIシーケンスが演奏される。
MidiEventは、ShortMessageのNOTE_ONがある音階の鳴り始め、NOTE_OFFが鳴り終わりである。ピアノの1つの鍵を押すのと離すのに相当する。音階は0〜127の整数で、60が基準のド、61がド#、62がレ…である。

・テンポについて
JDK 1.6のドキュメント(API spec, jdk-6-docs/technotes/guides/sound/)にもMIDI 1.0の仕様書(MIDI 1.0 Spec, SMF spec)にも見つけられなかったので正しいやり方かどうかわからないが、筆者のSun JRE1.6の環境では、javax.sound.midiでもmeta eventのFF 51で四分音符の長さを設定できるようである(デファクトスタンダードの類?)。MetaMessageで登録する場合は、type=0x51とし、 dataはbig endianの3バイトで、四分音符の長さをμ秒で指定する。
このメタイベントを使う場合は、Sequenceを作る時のdivisionTypeをPPQとする。
このメタイベントを使う以外の方法としては、divisionTypeをSMPTE_30、resolutionを100とかにしてtickの長さを絶対時間にして(この引数だと1 tickが1/3000秒になる)テンポに応じて音の長さをtickの整数倍で指定していく方法しか思いつかなかった。この方法だと誤差が大きいし、tickの値がすぐ大きくなるのでMIDIシーケンスのデータが長くなってしまうので、好ましくない。

・演奏停止について
Sequencer#stop()を呼んでも、既に鳴り始めた音は鳴り止まないことがある。確実に音を止めるために、Sequencer.close()を呼ぶようにした。
また、シーケンスが終了しても最後の音が鳴り続けることがあるので、シーケンスの最後にメタイベントのFF 2F(トラック終端)を付けて、それをMetaEventListenerで受けて、音を止めるようにした。

続きを読む "簡易MMLプレーヤー" »

About 2009年03月

2009年03月にブログ「Weblog on mebius.tokaichiba.jp」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2009年02月です。

次のアーカイブは2009年04月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Powered by
Movable Type 3.35