cgiにおける文字コードの扱いとか

久々にcgi書いてたら文字コード周りでハマったので備忘録。

Perlで日本語処理を書いてると文字コード周りでハマるのは誰もが一度は経験するはず。
慣れてくると、ちゃんとuse utf8 or encodingとbinmodeしてればいいんでしょ、となると思うんだけど、今回の敵はcgiでした。

今回の教訓は、utf8フラグ付いている文字列と付いていない文字列でeqしてもtrueが返ってくること。
そして、もう1つはwebにあるコードもきちんと意味を考えて使いましょうということ。

CGI.pmでutf8フラグの扱いがおかしい話は前から知っていて、このページCGI.pmとUTF8 flag : blog.nomadscafe.jpを参考に、

use utf8;
binmode STDOUT, ":utf8";
use Encode;
for my $p ($q->param) {
my @v = map {Encode::decode('utf8',$_)} $q->param($p);
$q->param($p,@v);
}

としていました。ところがどうもうまく行かないなと悩んでいたのですが、これパラメータの値の方だけutf8フラグをきちんと処理してて、キーの方は修正してないんですよね…
cgiのキーの方に日本語とか使う人がどれくらいいるかは知りませんが、その場合には

use utf8;
binmode STDOUT, ":utf8";
use Encode;
for my $p ($q->param) {
my @v = map {Encode::decode('utf8',$_)} $q->param($p);
$q->delete($p);
$p = Encode::decode('utf8',$p);
$q->param($p,@v);
}

とする必要があるみたいです。

正規表現での置換の際に配列を利用する

個人用メモ。

perl正規表現を用いた置換の際に、$1などの特殊変数が使えるのは常識レベルの話だと思います。
今回は特殊変数で、配列の内容が参照できるかを調べてみました。


my @map = ("a","b","c");
my $input = "2";
say $input;
$input =~ s/(\d)/$map[$1]/g;
say $input;

というソースを動かすと

2
c

という結果が帰ってきて、無事置換できていることが分かります。

他にも試してみたところ、ハッシュは無事置換できました。
リファレンスを通すのは危ないかなと思いましたが、これもいけました。
ただ、関数は置換できず、

&map(2)

という結果になりました。

配列で置換できると、シリアライズされたデータを元に戻す際に便利そうなので、有用性は結構あると思います。

python-twitterでFriendsを全て獲得する

python-twitterにはGetFriends()というメソッドがあり、Friendsを獲得することができます。
しかし、apiの制限上最大100件までしかFriendsを獲得することができません。
そこで、このGetFriendsを変更することで、全てのFriendsを獲得することができるようにしました。

まず、twitterapiの仕様を確認すると、pageというパラメータを投げることで、指定したページ(page=2なら101-200人目)を獲得できると書いてあります。
まずはGetFriendsにpageという引数を追加し、pageを指定できるようにしてみました。
ところが何故かpageを指定しても、最初の100人しか獲得できませんでした。
原因はよく分かりませんが、http://code.google.com/p/python-twitter/issues/detail?id=113&q=GetFriendsによると、pgaeはうまく機能しないようです。

上記のページによるとcursorというパラメータを投げることで対応するのがいいようです。
上記のページの解説では、GetFollowersの返り値から、next_cursorの値を取り出して、これをGetFllowersに引数として与えています。
しかし、現在のpython-twitterの仕様では、GetFriendsやGetFollowersはUserのインスタンスのリストを返してくるので、この方法では実現できません。
仕方ないので、python-twitterを書き換えることになります。

まあ、ここまで分かってしまえば、後は簡単で

    parameters['cursor'] = cursor    
    friends = []
    
    while  parameters['cursor'] != 0:
      json = self._FetchUrl(url, parameters=parameters)
      data = self._ParseAndCheckTwitter(json)
      friends += [User.NewFromJsonDict(x) for x in data['users']]
      parameters['cursor'] = data['next_cursor']
    return friends

とやってしまえば、全部のFriendsが返ってきます。
まず、apiに投げるパラメータのcursorは、この位置から100件返しなさい、ということになっています。-1の場合は先頭になります。ただし、このcursorの値はuse_idらしく、普通には扱いが面倒です。
一方、apiでFriendsを習得すると返されるJSONの中には、next_cursorやprev_cursorという値があります。これは次や前のリストが存在する場合にそのcursorの位置を教えてくれるようです。
そこで、このnext_cursorの値を次のcursorに与えてやれば、今回取得したリストの次のリストを獲得できます。
あとは、毎回獲得したFriendsをリストに突っ込んでおいて、最後にreturnすればOKです。

あんまりしっかりは確認してませんが、GetFollowersも同様の手順でうまくいくはずです。

windows で emacs shellを使う

先日からpython勉強し始めたのだが、なんとなくcygwin環境で開発していました。
実は今までcygwinはxで接続する用とrsyncするためぐらいにしか使っていませんでした。
開発環境として使うのは今回が初めて。
で、cygwinというかDOSプロンプトの都合上、横幅を大きくするのすら一苦労…さすがにこの環境は辛い。
cygwin上でのXも試してみたのですが今一使いづらい。
そもそも、windows用のemacs入れてるんだから、そのshell使うのが一番だよね、というのが今回の出発点。

ところが、emacs起動してM-x shellとやっても起動しない。child processがどうたらとか言われてしまいました。
そこでhttp://sakito.jp/emacs/emacsshell.htmlを参考に、windowsemacsでshellモードを設定してみました。

最初にやったのは、fakecygpty(http://www.meadowy.org/meadow/browser/trunk/nt/fakecygpty.c)をコンパイルする+パスの通った場所に置く+名前変えたのを作るです。

gcc -o fakecygpty.exe fakecygpty.c
cp fakecygpty.exe f_zsh.exe
cp fakecygpty.exe f_bash.exe
cp fakecygpty.exe f_ssh.exe

ですね。これらを適当なパスの位置に置きます。これがwindowsのなのかcygwinのなのか分からなかったので、チキンにもどっちもパスが通ってる位置に置きました。
あとは以下を、.emacsに書いて終了。

(defun skt:shell ()
(or (executable-find "f_zsh") ;; Emacs + Cygwin を利用する人は Zsh の代りにこれにしてください
(executable-find "f_bash") ;; Emacs + Cygwin を利用する人は Bash の代りにこれにしてください
(executable-find "cmdproxy")
(error "can't find 'shell' command in PATH!!")))

;; Shell 名の設定
(setq shell-file-name (skt:shell))
(setenv "SHELL" shell-file-name)
(setq explicit-shell-file-name shell-file-name)

(set-language-environment 'utf-8)
(prefer-coding-system 'utf-8)

M-x shellで無事起動できました。
本当は他にも設定しなければいけないこととかあるみたいなんですが、とりあえず動くので気にしないことにします。
コーディングとテストぐらいならshellの機能をバリバリに使ってということもないですしね。

python-twitter使ってみた

たまには趣味のプログラミングするかってことでtwitter関連で何か作ってみることにした。
せっかくなので、最近興味のあったpythonに手を出してみることに。

今日はとりあえず、python-twitterを使えるようになるところまでのお話。

まずはpython-twitter(http://code.google.com/p/python-twitter/)からダウンロード。
依存関係にある

http://cheeseshop.python.org/pypi/simplejson
http://code.google.com/p/httplib2/
http://github.com/simplegeo/python-oauth2

をインストールして、

python setup.py build
python setup.py install

でインストール完了。

ここまではあっさり行ったんだけど、こっからが大変だった。
まず、python-twitterで検索すると、大体

api = twitter.Api(username = 'hoge',password = 'piyo')

みたいになっているけれど、これはbasic認証時代のものなので使えません。
usernameなんて変数ないよ、みたいなこと言われてしまいました。

仕方なくOAuthを使うことに。ところがこれが全然分からん。
概念とかはなんとなく分かるんだけど、じゃあどうすればいいの?状態。
こういう時は手を動かすのが一番、ということでとりあえずやってみることに。
とりあえず、https://dev.twitter.com/に言って適当に登録してみることに。
そしたらconsumer_keyとconsumer_secretが表示されて一安心。
で、下の方にaccess token create みたいなことが書いてあったのでクリックしたら、無事access tokenとaccess token secretをゲット。
まあ、どうやらこれ自分のアプリ専用みたいなこと書いてあって、他の人が使う時はきちんとやらないといけないのだろうけど、とりあえず自分用のアプリ作るのが目的なので気にしないことにする。
余裕があればOAuthについてちゃんと勉強することにしよう。

あとは

api = twitter.Api(consumer_key='consumer_key',consumer_secret='consumer_secret',
access_token_key='access_token_key',access_token_secret='access_token_secret')

とやって終了。
これで色んなサイトに載っていたサンプルコードも無事動くようになりました。

あとは自分用に適当なアプリの開発でもしてみようかと思っています。

日本語は本当に語順が自由な言語なのか?

最近twitterを見ていて気になったのが、
日本語は語順が自由だから…
という表現。
確かに日本語の場合、英語の文型のような語順に対する強い制約は存在しない。

  • 太郎が花子に本を貸した。
  • 花子に太郎が本を貸した。
  • 花子に本を太郎が貸した。

このように語順を入れ替えても特に問題はなく意味が通じる。
しかし、日本語の場合は1番上の例ように、ガニヲの順が一番受容性が高いと言われており、完全に自由というわけではない。
「勉強をする」のような「サ変名詞ヲする」型の場合や「手を焼く」のような慣用的表現の場合にはより顕著に語順が制約されてくる。

また、複雑な文の場合は係受けの曖昧性がなくなるような語順の方が好まれる。

  • 太郎が解けなかった問題を花子が解いた。
  • 花子が太郎が解けなかった問題を解いた。

では「解いた」の格要素ではヲがガより前くる上の例の方が自然と言えるだろう。
一般的には長い格要素を前に持ってくることで、係受けの曖昧性がなくなることが多いので、そのような語順になることが多い。

さらに前文などの文脈がある場合にはその影響も受ける。

  • 太郎に問題を出された。その正解を花子に教えてもらった。
  • 太郎に問題を出された。花子にその正解を教えてもらった。

では若干だが上の例の方が受容性が高い。
基本的に前文に出現する要素に関連する語が先頭に来る方が受容性が高いと言われている。

とりあえずガヲニに限ってみたが、実際の日本語ではこれ以外の格要素や「ハ」など多様な問題が絡んでくる。
それでも多くの場合格要素の順番を入れ替えても意味は通じることが多いが、しっくりこないだろう。
結論?としては「日本語は語順が自由だから…」というのは、自然言語処理の現状がそれ以上の違和感を扱うところまで到達していない、ということなのではないかと。

素性選択について

最近、素性選択について少し悩んでます。
素性選択とは、機械学習に素性を与える際に、利用できるものを全て使うのではなく、有効そうなもののみを利用することを言います。

実は素性選択って機械学習において計算量以外でのメリットがあまりないのではないかと考えていました。
そもそも有効でない素性であれば学習時にほとんど重みが割り振られないと考えられるからです。
しかし、実際に今取り組んでいるタスクで実験をしてみると、直感的には分類に寄与しないと考えられる素性に結構重みが割り振られていて、誤りの原因になっているようです。

考えられる原因としてはタスクの性質があります。今回のタスクでは、正例には明らかに効きそうな素性があり、負例にはほとんど特徴がない、という性質があります。加えて、事例数は負例の方が数十倍となっています。
おそらく、分離超平面の位置を調整するバイアス項ではなく、正例・負例ともに出現する素性に対して負の重みを与えることで、負例の方が多いという現象を再現しようとしているのだと考えられます。



実際に素性選択をしてみたいことにはハッキリとしたことは言えませんが、素性選択は分類結果に対しても、有効に働くのではないかと考えられます。
では逆に素性選択を行なう場合には、どのような手法がいいのでしょうか?

1つ目に考えられるのは、PMI(自己相互情報量)や情報利得を利用して自動で選択する方法です(言語処理のための機械学習入門にも書かれていますし)。
ですが、機械学習というある種の素性選択で有効ではない素性を選択している以上、これらの自動選択の手法の有効性には疑問が残ります。

2つ目は人手による選択です。
この場合、明かに効きそうな素性については選択が可能ですが、人間が予期しない隠れた関係を発見できる機械学習のメリットは大幅に失なわれてしまう気がします。
また、選択に恣意性が生じるため理論的な一貫性が欠けてしまうのではないでしょうか。

個人的には、効きそうな素性をより大きな枠で捉えて設計し(形容詞が効きそうだと思ったら、品詞ごと素性にしてしまう)、具体的に何が効くかは機械学習に任せる、というアプローチが好きなのですが、実験結果に合わせて考え方を変えるのも大事なのかなと思う、今日この頃です。