keisukeのブログ

***乱雑です!自分用のメモです!*** 統計や機械学習の勉強と、読み物を書く練習と、備忘録用のブログ

IPython notebook (Jupyter) と IPython qtconsole を同じIPythonのカーネルで動かす

2015-05-23追記:
IPython Notebook version 3 (Jupyter)からは、どうやらカスタムjavascriptが変わったようですので、%username%\.ipython\profile_default\static\customにある、custom.jsの末尾に挿入するコードは次のようになります:

require([
     'base/js/namespace',
     'base/js/events'
 ], function(IPython, events) {
     events.on('app_initialized.NotebookApp', function(){
         IPython.toolbar.add_buttons_group([
             {
                 'label'   : 'run qtconsole',
                 'icon'    : 'fa-terminal fa', // select your icon from http://fortawesome.github.io/Font-Awesome/icons
                 'callback': function () {
                     IPython.notebook.kernel.execute('qtconsole')
                 }
             }
             // add more button here if needed.
             ]);
     });
     events.on('app_initialized.NotebookApp', function() {
        IPython.CodeCell.options_default.cm_config.autoCloseBrackets = false;
     });
 });

+追記終わり+

2015/03/11追記:
ipython notebookが立ち上がった時に読むjavascriptを編集することで、簡単にqtconsoleを同じカーネルで動かすことが出来ます。 具体的には、
%username%\.ipython\profile_default\static\customにある、custom.jsというファイルの末尾に次を挿入:

$([IPython.events]).on('app_initialized.NotebookApp', function(){
    IPython.toolbar.add_buttons_group([
        {
             'label'   : 'run qtconsole',
             'icon'    : 'icon-terminal', // select your icon from http://fortawesome.github.io/Font-Awesome/icons
             'callback': function () {
                 IPython.notebook.kernel.execute('%qtconsole')
             }
        }
        // add more button here if needed.
        ]);
});

これだけです。 ただし、%username%\.ipython\profile_default\static\customの場所は異なる場合があります。デフォルトでなにもいじっていないなら問題無いと思います。

上記の設定をしたあとにいつも通りipython notebookを起動すれば、画面上部のツールバーに「>_」というアイコンが増えているはずです。 そのボタンを押せば、同じカーネルでqtconsoleが立ち上がり、notebookを汚さずに変数の中身やヘルプのチェックがおこなえます。f:id:kaisk:20150311221620p:plain

ちなみに、設定をいっさいしなくても、notebookに%qtconsoleと打ち込んで実行すれば同じカーネルでqtconsoleが立ち上がります。

+追記終わり+


IPython notebook はとても便利なアプリで,ブラウザ上でインタラクティブPythonのシェルを叩けます. しかし,基本的にはコードのブロックを次々と入力していき,しかも出力や入力のログが残り続けます. 「この変数はなんだっけ?」と思ってprint(x)などとだけ書いたコードを評価すると,そのprint(x)とその出力のログは残り続けるのです(というか,入力と出力のログを残すことがnotebookの役割なので正しいわけですが).

しかし,やっぱり変数の中身だけちょっと見てみたいとか,「これで決定!」なわけではないコードを試してみたいとか,とにかく「ログに残ってほしくない」ようなとき,shellで動くIPythonの便利さが欲しくなります. だからといって新たにshellでIPythonを起動しても,notebook上の変数を調べるためにはnotebookの入力を全部取ってきてshellにコピペしなければなりません.

そこで,同じカーネルでnotebookとqtconsole*1を動かすことを考えます. notebookもqtconsoleも,実はそれ自体はpythonのコードを実行してはいません.裏で動いている「カーネル」と呼ばれるpythonのプロセスに入力を渡して実行してもらい,その出力を受け取って表示しているだけです.よって,その「カーネル」を共有してnotebookとqtconsoleで使えば良いわけです.

まずはipython notebookを起動しましょう.ファイル名を指定して実行,またはshell(terminal, cmd.exe, powershellなど)上で

ipython notebook

とします. おなじみの画面がブラウザに表示され,同時にカーネル用のウィンドウ(シェルのウィンドウ)が立ち上がると思います. なにかnotebookを開いて,カーネルを起動させます. notebookを開くと,先ほどのシェルのウィンドウに「Kernel started: ***」のように表示されます.この状態で,今までどおりnotebookは編集できます. さらに同じカーネルを使ったqtconsoleを立ち上げるには,ファイル名を指定して実行,またはshell上で

ipython qtconsole --existing

とします. --existingオプションは,ipythonを最後に起動したカーネルを使って立ち上げるという意味です. もしもすでにカーネル複数立ち上がっていて(=複数のnotebookを編集していて),どのカーネルを使ってqtconsoleをたちあげたいか指定するときには,使いたいカーネルのウィンドウに表示されている「Kernal started:」のあとの文字列と同じものを使って

ipython qtconsole --existing kernel-文字列.json

とします. もしもカーネルの番号がわからなくなった場合,notebookで

%connect_info

を実行すれば教えてくれます."key"がそうです.

*1:shell上のipythonみたいなものです.shellよりちょっと強力な機能がついています.

事前確率,事後確率,尤度,...

確率と統計の基礎(事前確率,事後確率,尤度,ベイズの法則,...)を勉強していると,何度も何度も見たことがある説明がなんとなく理解しづらく,難しく思えることがよくあります.

例えば,

x が与えられたとき y となる確率を p(y\mid x) と書き,事後確率と呼ぶ.」

という文章はあらゆるところで見かけると思います. もちろん確率を勉強した人であれば何度も見た文章なので理解が難しいものではないのですが,それでもこの文章に「尤度」やら「事前確率」やら「カルバックライブラダイバージェンス」やらが混じり始めると,どこかで理解のほつれが出始めることがよくあると思います.

先ほどの文章の何が問題かというと,p(y\mid x) は「yの事後確率」であって「xの事後確率」ではない点です. xに事後確率が存在しないわけではなく,この場合だと p(x\mid y) はxの事後確率です.

丁寧に説明されている場合は,

x が与えられたとき y となる確率を p(y\mid x) と書き,y事後確率と呼ぶ.」

とわざわざ書いてくれますが,基礎向け以外の解説だとよく省略して単に「事後確率」と呼ばれています.

というのも,統計では普通 xy ではなく,観測データ x とパラメータ \theta で話を進めるからです. 統計では,

「パラメータ \theta により決まっているらしい確率分布 p から得られたデータ x をもとに,その背後にある法則(=パラメータ)を予測せよ」

という問題を解くことが基本なので,x は与えられるもの,観測されるものという前提が暗黙にあります. よって,単に「事後確率」と言った場合,「xに関する事後確率 p(x\mid \theta)ではなく\thetaに関する事後確率p(\theta\mid x)*1を指します.

では p(x\mid \theta) がなんと呼ばれているかというと,「(\thetaの)尤度」です. つまり,\thetaが与えられた時のxの事後確率は\thetaの尤度です.

一方,p(\theta)は,「\theta の事前確率」とは呼ばずに,単に「事前確率」と呼びます.

これらの暗黙の了解が頭にしっかりと入っていないと*2,急に出てきた「事後確率」「事前確率」などの言葉に混乱して「どっちの変数が与えられてて,どっちの変数が変数のままなのか」問題を処理できていないまま数式を読み進めることになり理解が追いつかなくなります.

まとめると,
観測データx
パラメータ\thetaと書くとき,
暗黙に
「事後確率」は「データxが与えられた時のパラメータ\thetaの事後確率 p(\theta\mid x)」を指す.
「尤度」は「パラメータ\thetaを決めたときのデータxの事後確率 p(x\mid\theta)」を指す.
「事前確率」は「パラメータ\thetaの事前確率p(\theta)」を指す.

*1:x与えられた時の\thetaの確率

*2:

Windowsバージョン予測

世間はWindows10のニュースで賑わっていますが,なぜWindows8のあとが9ではなく10なのでしょうか. 「Windows9」にすると,これまでのWin9xシリーズであるWindows95Windows98などを検出するためのコードにWindows9も引っかかってしまい,後方互換に問題が出るのでWindows10にしたという噂もどこかで聞きましたがよくは知りません.

ところで,次のグラフはWindowsの製品に含まれる数字を縦軸,発売日を横軸にとったものです:

f:id:kaisk:20150125192806p:plain

2000系列が明らかに外れ値となっています.おそらくMicrosoftの方が命名を間違えたのでしょう.98の次が2000になるのは小学生が見ても誤りですね.98年の二年後が2000年なんて馬鹿なことはありません.98年の二年後は100年です.そこで,Microsoftの方が命名を間違えた製品を除いたグラフは:

f:id:kaisk:20150125193114p:plain

このようになります.まだ95や98が外れ値になっていますが,2000と比べればエラーが小さくなったのでそこまで気にしなくてもいいでしょう.98→2000に比べれば,3.1→98は大した間違いではありませんね.

さて,では本当にWindows10という命名は妥当だったのでしょうか?これまでのWindowsの製品の命名から,2015年に発表されるべきWindowsの製品名を予測しましょう. 線形回帰します.

f:id:kaisk:20150125193911p:plain

Ridgeの線が見えませんが,Lassoのものと重なっています.これまでのWindowsの製品名から予測した結果,2015年に発売されるべきWindowsは,OLSによると「Windows 17.346」,Ridge・Lassoによると「Windows 14.913」のようです.実際に発売されるであろうWindowsが「10」であることを考えると,あまり良い予測ではありあません.そこで,やはりMicrosoftの方が製品名を付け間違えたであろう「Windows95」「windows98」をデータから除いて,同じことをしました:

f:id:kaisk:20150125194318p:plain

今度は3つの線がすべて重なっています.予測された2015年のWindowsの製品名は「Windows 7.678」です.8より小さくなってしまいました.しかし,データを見ると,1995年のあたりで明らかに傾向が変わっています.これは,その頃にMicrosoftの方が命名の規則を忘れたのでしょう.そこで,WindowsNT系列のデータを切り出します:

f:id:kaisk:20150125194619p:plain

だいぶいい感じになってきました.予測された製品名は「Windows 8.638」,四捨五入すれば「Windows 9」です!やはりwindows10ではなく9が正統な製品名なのでしょう.参考までに,最後のデータによって予測された2020年の製品名は「Windows 9.90」,つまりWindows10は5年くらい時代を先取りしているようです.ちなみにWindows98が発売されるのは西暦2400年ごろ,Windows2000が発売されるのは西暦10000年ごろのようです.

なお,Windowsの製品名ではなくちゃんと内部バージョン名を見てみるともっとちゃんとできそうです(やりませんけど). 現在主流のNT系の内部バージョン名のグラフを見ると,どうやら線形ではなく頭打ちのlog曲線を描いているように見えますね(やりませんけど). 今回はネタなので製品名にはいっている数字を予測しましたが,ちゃんと内部バージョン名を予測しようとしたらけっこうよい精度が出そうです.

f:id:kaisk:20150125195425p:plain

f:id:kaisk:20150125195501p:plain

f:id:kaisk:20150125195504p:plain

f:id:kaisk:20150125195507p:plain

【Python】sliceの速度

listのsliceの速度がどれくらいかと思って実験してみました.

問題

長さlのlistがあるとします.その連続したr要素の和の最大値を求めます:

x = [1,2,3,10,11,2]  # l = 6: 長さ6のlist
# sum([1,2,3])  # r = 3: 連続した3要素の和
# sum([2,3,10])
# sum([3,10,11])
# sum([10,11,2])
# 一番大きな値はどれ?

方法1 (sliceと呼ぶことにします)

まず,r要素のsliceをとってsumを取る方法:

def slice_sum_max(x, r):
    max_val = -np.inf
    for i in range(len(x)-r+1):
        sum_val = sum(x[i:i+r])
        if sum_val > max_val:
            max_val = sum_val 
    return max_val

方法2 (squeezeと呼ぶことにします)

先頭を押し出して,新しい要素を足していく方法:

def squeeze_sum_max(x, r):
    max_val = sum(x[:r])
    sum_val = max_val
    last_ele = x[0]
    for i in range(len(x)-r+1):
        sum_val -= last_ele
        last_ele = x[i+r]
        sum_val += last_ele
        if sum_val > max_val:
            max_val = sum_val
    return max_val

結果

長さ10000の場合:

f:id:kaisk:20150125021320p:plain

長さ100000の場合:

f:id:kaisk:20150125021328p:plain

どっちも変わらない結果になりました. なお,listのまま関数に渡すか,np.ndarrayに変換してから関数に渡すかを変えていますが,やはりndarrayに要素アクセスすると遅くなるようです.

xargsメモ

xargsで実行したいコマンドに2回引数を渡したいときは,
例えば画像をpngからjpgへ変換したいときは,

$ find *.png | xargs -I{} convert {} {}_.jpg

などとするとよい.
この例では,convert前の画像の名前と,後の画像の名前を指定しなければならないので,-I{}によって{}という変数に一旦格納している.

【scikit-learn】変数やメソッドの命名規則

参考:http://scikit-learn.org/stable/developers/#apis-of-scikit-learn-objects

自分の理解の整理も兼ねて、scikit-learnの公式ドキュメントの、特に
「規則」の部分について日本語でまとめます。
全体的にscikit-learnではAPIや命名が非常によく規則だっており、異なる種類の処理を同じコードで記述するのが容易です。

メソッドAPI

sckit-learnのobjectsは次のメソッドを持つべきである*1

Estimatorのオブジェクト - fit()

データからパラメータの学習をおこなう。
Supervisedの場合

obj.fit(data, targets)

Unsupervisedの場合

obj.fit(data)
Predictorのオブジェクト - predict()

学習したパラメータを使って、新しいデータの処理をおこなう。

obj.predict(new_data)

タスクがClassificationの場合、定量化された値を返すために、

obj.predict_proba(new_data)

が用意されていることがある。この場合、返り値はそれぞれのクラスに属する確率を表すリストとなる。
また、確率ではないような場合、

obj.decision_function(new_data)

でリストを返すこともある。

Transformerのオブジェクト - transform()

フィルタリングをおこなったり、次元の削減をしたりして、データの表現を変換する。

transformed_data = obj.transform(data)

ちなみに、もしもfit()を事前におこなっているなら、fit()を分離して記述するよりも、fit_transform(data)を用いて同時にEstimatorとTransformerに仕事をさせたほうが(基本的に)効率が良い。

Modelのオブジェクト - score()

学習したパラメータがどれだけ新しいデータを表現できているかを測る。

score = obj.score(new_data)

尤度とか二乗誤差の和とか。高いほど良いモデルとなるように正規化されている。つまり、二乗誤差の和の場合、マイナスをかける(または逆数をとる)などしてひっくり返しているので、ユーザは常にscoreが高いモデルを良いモデルだと思えば良い。細かいことはscikit-learnがやってくれる。

初期化(ハイパーパラメータ)

ハイパーパラメータ(学習により決定できないので、事前に決めないといけないパラメータ)*2は、objectsの初期化の時に指定する:

obj = SVC(C=2)

など。ちなみに、obj = SVC(); obj.set_params({'C':2})としても良いが、fit()したあとにハイパーパラメータを変えたりするのは混乱のもとなので初期化の段階でハイパーパラメータは全て指定するのが好ましい。*3

fit()の引数 X, y, kwargs

Estimatorのオブジェクトはfit(X, y, kwargs)というAPIでフィッティングができる(Unsupervisedなら y は省略する。省略する代わりに y = None でも良い。kwargsはオプション)。Xは、[N, D]次元の行列(Nサンプル、D特徴)。yは[N]のリスト*4(Nサンプル)。kwargsはフィッティングのときに指定できるオプション。別になくても良いように設計はされている。特別なことがしたいなら指定する*5

学習したパラメータ

学習したパラメータは、underscore(下線、「_」のこと)を最後につけることで区別する。つまり、fit()によって得られたパラメータはobj.coef_などとして参照できる(obj.coefではない)。

*1:ここでいう「objects」とは、例えば obj = linear_model.LinearRegression()などのこと。

*2:とはいっても交差検定などで候補から(実験的に)最も良さそうなものを選べることが多い

*3:set_params()は交差検定の自動化などで便利

*4:リストというかnumpy.array

*5:Perceptronのfit()などにkwargsがあったりする

メタ構文変数,意味のない言葉のリスト

参考:http://dev.classmethod.jp/etc/metasyntactic-variables/

メタ構文変数」とは,fooやbar,日本ではhogeやfugaなど,「意味のない」ことを意味する言葉たちです.一時的に何かをしたいときや,読者に対して「意味がない言葉である」ことを示すためにあえて使われたりします.

上記の記事でいろいろな例が挙げられており,また,メタ構文変数に備わっているべき性質も議論されています.それは「順番がわかるべき」「覚えやすく,判別しやすい」「ある程度の量が用意されている」などです.「順番がわかるべき」というのは,fooのあとはbarを使う,のようにメタ構文変数に順位があることです.これがあることで何番目の変数なのか,前にいくつ意味のない変数があるのかの把握が容易になります.また,「覚えやすく,判別しやすい」というのはそのままの意味です.もしもメタ構文変数としてhoge, honge, holge, horge, hodgeを使ったら,自分でもどれがどれかわからなくなる上に,口頭で人に伝えづらくなります.「ある程度の量が用意されている」というのは,意味のないシリーズの単語が3つで終わり,ではなくちゃんと9個10個ありますか?ということです.3個程度では足りなくなることがあります.

以上を踏まえて,次のようなメタ構文変数が上記の記事で挙げられています:

  • foo, bar, baz, qux, quux, corge, grault, garply, waldo, fred, plugh, xyzzy, thud
    • 順番はfoo,bar,bazあたりまでわかる.しかし,baz以降を覚えている人はあまりいないのでは・・・判別性も微妙.
  • hoge, fuga, piyo, (hogehoge, mogera)...
    • 順番はほぼ無い.私が日本人なので,覚えやすさはある.判別性もそこそこ.しかし,日本でしか通じない.
  • spam, ham, eggs, ...
    • 朝食シリーズ.順番は一応ある.Pythonでよく使われるらしい.種類が少なすぎる?
  • Alice, Bob, Eve, ...
    • 人名シリーズ.暗号界隈で使われる.人物AがAlice,人物BがBob,盗聴者(Eavesdropper)がEveなので順番があるし覚えやすい.判別性もよい.ただし,意味がない文字になっているかと言われると微妙.AliceさんもBobさんも存在するし・・・

他にもいろいろ考えてみました.日本でしか通用しそうにないものも混じっていますが・・・(もし他にあったらぜひ教えてください!):

  • 甲・乙・丙・丁・戊・己・庚・辛・壬・癸
    • 十干シリーズ.順番完璧.種類も10個と多い.しかし,おそらく日本(と中国?)でしか通じない.あと,判別性も微妙.庚と癸に至っては,どちらも「き」である.
  • いろはにほへと...
    • いろはシリーズ.順番完璧.種類も多い.日本でしか通じない.あと,あまりにも単語ひとつが短すぎるので微妙.
  • 丑寅卯辰巳午未申酉戌亥
    • 十二支シリーズ.十干シリーズと同じメリットデメリットを抱える.
  • ドレミファソラシ (Do Re Mi Fa Sol La Si)
    • 音階シリーズ.日本以外でも通じそう(ドレミ,じゃなくてCDE,なことも多いけど).ただし,「ソ」だけ3文字で気に食わないのと,「Do」がやばすぎる*1ので非推奨っぽい.
  • 月火水木金土日 (Mon, Tue, Wed, ...)
    • 曜日シリーズ.世界どこでも通じそう.ただ,意味のない言葉かと言われると微妙.
  • H, He, Li, Be,...
  • 水金地火木土天海 (Mercury, Venus, Earth)
    • 太陽系シリーズ.順番良し.判別性よし.ただ,ちょっと単語ひとつが長いかも.ただし,オシャレである.

*1:doは多くのプログラミング言語で意味を持つ単語である