単純パーセプトロンの学習python vs julia
juliaで単純パーセプトロン
特にやる理由もなかったんですが、 前回単純パーセプトロンをpythonで構築したんで、その比較ついでにjuliaでも実装。
まずはpythonでデータを作っちゃいます。 前回と同じ3次元ですがデータ数を10000に増やします。
の前に、結構長くなっちゃったので結論をこちらに先に書いておく。
実行環境 | エポック数 | 時間 |
---|---|---|
python with numpy(float 64) | 217 | 53.9s |
pure python (float32) | 163 | 7.17s |
julia(素) | 113 | 3.16s |
julia(関数) | 113 | 0.85s |
同じ数値、同じ演算をしているのに、 epochのばらつきがあるのは一体…? (型をfloat32に揃えても一緒でした) それはさておき、juliaがepochを考慮しても30倍の速さ(numpy比)を出してくれました。 pure pythonが意外と健闘…。
以下本編。
データ作成
# w0 * x0 + w1 * x1 + w2 * x2 + b = 0 # x2 = -(w0/w2)*x0 - (w1/w2)*x1 - b/w2 = 0 N = 10000 w0 = 5 w1 = -3 w2 = 2 b = 1 # ラベル1 x0_rand_posi = stable_rand.rand(int(N/2)) x1_rand_posi = stable_rand.rand(int(N/2)) x2_rand_posi = -(w0/w2)*x0_rand_posi - (w1/w2)*x1_rand_posi -b/w2 + stable_rand.rand(int(N/2)) # ラベル0 x0_rand_nega = stable_rand.rand(int(N/2)) x1_rand_nega = stable_rand.rand(int(N/2)) x2_rand_nega = -(w0/w2)*x0_rand_nega - (w1/w2)*x1_rand_nega -b/w2 - stable_rand.rand(int(N/2)) train_x = np.array([np.append(x0_rand_posi,x0_rand_nega,axis=0),np.append(x1_rand_posi,x1_rand_nega,axis=0),np.append(x2_rand_posi,x2_rand_nega,axis=0)]).T train_y = np.append(np.ones(int(N/2)),np.zeros(int(N/2)))
これでtrain_xとtrain_yにデータが格納されました。 juliaでも使えるようにcsvに吐き出しておきます。
np.savetxt('train_x.csv',train_x,delimiter=',') np.savetxt('train_y.csv',train_y,delimiter=',')
# 重み初期化 w = np.ones(3) # バイアス初期化 b = np.ones(1) # ステップ関数 def step(x): return 1 * (x > 0) # 予測関数 def predict(w,x,b): return step(np.dot(w, x) + b) # 学習 # ipythonの%%timeで計測 %%time eps = .01 epoch_counter = 0 while True: epoch_counter += 1 fin_flag = True for i in range(len(train_y)): if i % 2 == 0: idx = int(i/2) else: idx = int((i-1)/2 + N/2) delta_w = eps * (train_y[idx] - predict(w,train_x[idx],b)) * train_x[idx] delta_b = eps * (train_y[idx] - predict(w,train_x[idx],b)) w += delta_w b += delta_b # 一回でも誤り判定があればTrueになる fin_flag *= all(delta_w == 0) * (delta_b == 0) if fin_flag: break print(epoch_counter) print(w) print(b)
さて、実行結果。 あと、w,bも出力
217 [ 1.74633386 -1.04860002 0.69887095] [0.35] Wall time: 53.9 s
numpyを使ってやってしまいましたが、 一応pure pythonでも単純パーセプトロンをコーディング データはさっきのを流用。
# numpy arrayは使用禁止のためリストへ train_x = train_x.tolist() train_y = train_y.tolist() # 重みとバイアスを初期化 w = [1,1,1] b = 1 # ステップ関数 def step(x): return 1 * (x > 0) # 内積関数(np.dotを使えないため) def dot(a,b): ab = 0 for ai,bi in zip(a,b): ab += ai*bi return ab # 予測 def predict(w,x,b): return step(dot(w, x) + b) # 学習 %%time eps = .01 epoch_counter = 0 while True: epoch_counter += 1 fin_flag = True for i in range(len(train_y)): if i % 2 == 0: idx = int(i/2) else: idx = int((i-1)/2) + 500 delta_b = eps * (train_y[idx] - predict(w,train_x[idx],b)) delta_b_ite = [delta_b for i in train_x[idx]] delta_w = list(map(lambda b,x : b * x , delta_b_ite, train_x[idx])) w = list(map(lambda w1,w2: w1+w2, w,delta_w)) b += delta_b fin_flag *= all(wi == 0 for wi in delta_w) * (delta_b == 0) if fin_flag: break print(epoch_counter) print(w) print(b)
はてさて結果は
163 [1.0707060146288323, -0.6444864547742656, 0.43073711329007247] 0.2199999999999993 Wall time: 7.17 s
なんやこれ…? numpy使うより収束も速ければ、 処理速度も速い!?
よくわからない…。 でも、重みもバイアスも約0.614倍になってるだけなので正しそう….。
npはデフォルトでfloat64になるので、 numpy側を明示的にfloat32にして実行してみる。
データをこんな感じで修正
train_x = np.array([np.append(x0_rand_posi,x0_rand_nega,axis=0),np.append(x1_rand_posi,x1_rand_nega,axis=0),np.append(x2_rand_posi,x2_rand_nega,axis=0)],dtype=np.float32).T train_y = np.append(np.ones(int(N/2),dtype=np.float32),np.zeros(int(N/2),dtype=np.float32)) w = np.ones(3,dtype=np.float32) b = np.ones(1,dtype=np.float32)
再度numpyで学習!
%%time eps = .01 epoch_counter = 0 while True: epoch_counter += 1 fin_flag = True for i in range(len(train_y)): if i % 2 == 0: idx = int(i/2) else: idx = int((i-1)/2 + N/2) delta_w = np.array(eps * (train_y[idx] - predict(w,train_x[idx],b)) * train_x[idx],dtype=np.float32) delta_b = np.array(eps * (train_y[idx] - predict(w,train_x[idx],b)),dtype=np.float32) w += delta_w b += delta_b # 一回でも誤り判定があればTrueになる fin_flag *= all(delta_w == 0) * (delta_b == 0) if fin_flag: break print(epoch_counter) print(w) print(b)
116 [ 1.4922788 -0.89688647 0.5975643 ] [0.30000067] Wall time: 30.4 s
ううーん…? epoch回数は減ったけど…。それでもpure pythonのほうが速いし…。 わからん。
まぁ一旦おいておく。(誰か教えてエロい人)
さて、juliaで同じように。
using CSV import Base: * , > train_x = convert(Array,CSV.read("train_x.csv", header=false, delim=',')) train_y = convert(Array,CSV.read("train_y.csv", header=false, delim=',')) # 学習率 l_rate = 0.01 # 重み初期化 w = ones(3) # バイアス初期化 b = 1 # ステップ関数 function fn_step(x) return 1 * (x > 0) end # 予測 function fn_predict(w,x,b) return fn_step(w' * x +b) end
さて、こっから学習
epoch_counter = 0 @time while true global epoch_counter += 1 local fin_flag = true for i in 1:size(train_y)[1] if i % 2 == 0 idx = Int((i)/2) + 500 else idx = Int(i+2/2) end delta_w = l_rate * (train_y[idx] - fn_predict(w,train_x[idx,:],b)) * train_x[idx,:] delta_b = l_rate * (train_y[idx] - fn_predict(w,train_x[idx,:],b)) global w += delta_w global b += delta_b fin_flag *= (delta_w == zeros(size(delta_w))) * (delta_b == 0) end if fin_flag println(epoch_counter) println(w) println(b) break end end
はてさて結果は
113 [1.24965, -0.749514, 0.49939] 0.24999999999999933 3.161580 seconds (37.60 M allocations: 1.272 GiB, 4.11% gc time)
速い。…が10倍程度。 まだjuliaの本気ではないですね。 やはり関数化が必要な模様。 学習用関数を作ります。
function fit(x,y,w,b,epoch_counter=0,learning_rate = 0.01) while true epoch_counter += 1 local fin_flag = true for i in 1:size(y)[1] if i % 2 == 0 idx = Int((i)/2) + 500 else idx = Int(i+2/2) end delta_w = learning_rate * (y[idx] - fn_predict(w,x[idx,:],b)) * x[idx,:] delta_b = learning_rate * (y[idx] - fn_predict(w,x[idx,:],b)) w += delta_w b += delta_b fin_flag *= (delta_w == zeros(size(delta_w))) * (delta_b == 0) end if fin_flag println(epoch_counter) return w,b break end end end
さて実行
w = ones(3) b = 1 @time fit(train_x,train_y,w,b)
…?
113 0.850472 seconds (24.86 M allocations: 1.061 GiB, 11.68% gc time) ([1.24965, -0.749514, 0.49939], 0.24999999999999933)
例によって一瞬ですね…。
早すぎィ!
Juliaデータサイエンス―Juliaを使って自分でゼロから作るデータサイエンス世界の探索
- 作者: Anshul Joshi,石井一夫,岩中公紀,太田博三,大前奈月,兼松正人,古徳純一,菅野剛,高尾克也,中村和敬
- 出版社/メーカー: エヌ・ティー・エス
- 発売日: 2017/09/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
現場で使える!NumPyデータ処理入門 機械学習・データサイエンスで役立つ高速処理手法 (AI & TECHNOLOGY)
- 作者: 吉田拓真,尾原颯
- 出版社/メーカー: 翔泳社
- 発売日: 2018/11/19
- メディア: 単行本
- この商品を含むブログを見る