buq’s blog

覚えておきたいけど覚えておけなさそうなことを書きます?

Django vs Rails : which is growing more?

ウェブアプリ開発のフレームワークをちょっと勉強したい.私は普段から Python を書いているから Python 上のフレームワークである Django が使いたい(知らない言語で知らない技術を勉強するのは,わからないことの原因究明がストレスフル)が,周りには Ruby を使ったフレームワークである Rails のユーザが多いのでこちらのほうが困ったときに質問しやすいように思う.

そもそも世の中的に,Django が伸びているなら Django で勉強しようかな−… と思ったので stackoverflow に [rails] created:2013-01-01..2014-01-01 みたいなクエリを投げて,ヒット数からどっちが成長しているか調べた.

期間 通年 Rails Django
2013 111,650 48,793
2014 103,947 48,526
2015 100,079 54,021
2016 35,091 20,300

(2016年は検索時点での結果)

年度ごとに比較できるように 1月1日から5月1日まででクエリを投げた結果は下記:

期間 5月1日まで Rails Django R/D
2013 36,495 16,310 2.24
2014 39,842 17,639 2.26
2015 32,069 16,299 1.97
2016 34,457 19,919 1.73

DjangoRails 食ってるっぽい?(本当は他のフレームワークも調べるべきだ.) Django 2013年比で 1.22 倍, Rails は 0.95 倍になっている.Rails/Django は小さくなっている.

そもそも Ruby コミュニティは縮小傾向・ Python コミュニティは拡大傾向にあるようだ:

期間 5月1日まで Ruby Python R/P
2013 25,957 79,668 0.33
2014 29,271 108,759 0.27
2015 24,808 108,926 0.23
2016 24,088 140,224 0.17

Rails の本読みかけだけど Django の本にのりかえよう


ところで 2014 年に Ruby 界で何かバーストが起きていますね.なんだろうこれ.

🎉

pulp のバグ:目的関数が零のとき valid() の返り値が正しくない

python の LP/MIP ソルバー(のラッパー)である pulp のバグをみつけたのでメモ. GitHub - coin-or/pulp: A python Linear Programming API

具体的には,目的関数がゼロの場合,ダミー変数が現れて solve() 後の valid() (現在の解が実行可能かどうか)の挙動が正しくなくなる.

サンプル

import pulp

mip1 = pulp.LpProblem('****** MIP1 ******')
x = pulp.LpVariable('x')
mip1 += (0 * x)
mip1 += (x >= 1)

mip1.solve()
print mip1
print 'x', x.value()
print mip1.valid()
print

mip2 = pulp.LpProblem('****** MIP2 ******')
y = pulp.LpVariable('y')
mip2 += (1 * y)
mip2 += (y >= 1)

mip2.solve()
print mip2
print 'y', y.value()
print mip2.valid()

出力(# 以降は注釈)

****** MIP1 ******:
MINIMIZE
0*__dummy + 0
SUBJECT TO
_C1: x >= 1

VARIABLES
__dummy = 0 Continuous # 自分で定義していないダミー変数が現れている
x free Continuous

x 1.0
False # validでないと判定される.しかし,x = 1.0 は明らかに実行可能.

****** MIP2 ******:
MINIMIZE
1*y + 0
SUBJECT TO
_C1: y >= 1

VARIABLES
y free Continuous

y 1.0
True

理由はダミー変数 __dummy の値が零でなく None になるから.

for v in mip1.variables():
    print v, v.value()

出力:

__dummy None
x 1.0

LpProblem.valid() は問題に現れるすべての変数,制約が valid であれば True を返すが, 変数の値が None のとき,変数は valid ではないという実装になっているのが問題. おそらく正しい解決は,ダミー変数の値のデフォルトを 0 にすること? ダミー変数のポリシーとかよく分かっていないのでプルリクを送るには至っていない.

4月8日追記 issueたてた

カルマンフィルタの導出

カルマンフィルタのベイズじゃない説明の意味がわからなかったので,ベイズっぽく導出した.それのメモ.

状態変数が  X_t であり,観測量が  Z_t がである次の離散時間システムを考える:

 X_t = AX_{t-1} + Bu_{t-1} + W_{t-1}

 Z_t = HX_{t} + V_{t}

ここで  u は系への入力であり, w, v はそれぞれ平均が零,共分散行列が  Q, R である正規分布に従うノイズである.ノイズは全て独立とする.(確率変数と行列を大文字でかいてある.)

例えば  x が地表の点で,  A, H がともに単位行列であれば,場所の観測にノイズが乗っていて,かつ速度ベクトルを自分で決められるときの位置の推定問題である.

平均  x_0, 分散  P の確率変数  X_0 を初期値とする上記のシステムで, p(X_1|Z_1) を求めることができればよい.すなわち, Z_1 = z_1 を観測したときの  X_1 を求めればOK.

これは条件つき確率の定義に従って分母を無視することで簡単に計算できる.  p(X_1|Z_1) = p(X_1 = x_1 | Z_1 = z_1) \propto p(AX+Bu+W = x_1 \cap V = z_1-Hx_1)
 AX+Bu+W V は独立な正規分布だから,確率は積をとればOK. 定数項を上手に無視して,  x_1 に関するところだけピックアップし,行列版の平方完成をすれば,条件付きの分布が正規分布となり,その平均および分散が

平均:  (\Pi^{-1} + H^\top R^{-1} H)^{-1} (\Pi^{-1}\xi + H^\top R^{-1} z_1)
分散:  (\Pi^{-1} + H^\top R^{-1} H)^{-1}

となることがわかる.ここで  \Pi = APA^{\top}+Q, \xi=Ax_0+Bu_0

おまけ: ここで1次元の問題を考えて, A=H=1, P=p, Q=q, R=r とすると,

平均:  ((p+q)^{-1} + r^{-1} )^{-1} ((p+q)^{-1}\xi +  r^{-1} z_1)
分散:  ((p+q)^{-1} + r^{-1} )^{-1}

平均は精度(分散の逆数)で重み付けした2つの推定の内分となり,精度は2つの推定の精度の和になる.

chainer に GPU 計算をさせるときは model を GPU に移してから optimizer と紐付ける

OK なコード

model = Regression(Model())
if gpu >= 0:
    xp = cuda.cupy
    cupy.cuda.Device(gpu).use()
    model.to_gpu()
else:
    xp = np
opt = optimizers.Adam() # モデルを gpu に移したあとで
opt.setup(model)        # optimizer と紐付けるとOK

NGなコード

model = Regression(Model())
opt = optimizers.Adam() # モデルを gpu に移す前に
opt.setup(model)        # optimizer と紐付けると悲しい
if gpu >= 0:
    xp = cuda.cupy
    cupy.cuda.Device(gpu).use() 
    model.to_gpu()
else:
    xp = np

NGなコードは,動作はしてしまうが一歩も学習が進まない

epoch 0
trainingloss 1249.54880859
testloss  1482.03295898
epoch 1
trainingloss 1249.54880859
testloss  1482.03295898

GPUも動くだけ動いちゃうので,reluでも勾配が消えうるのかと思って1日溶かしました👀