#ゼロから作る Deep Learning を読む
ゼロから作る Deep Learning という本が素晴らしい。Python で学ぶディープランニングという副題が示す通り Python でゼロから Deep Learning のアプリ(?アルゴリズム?)を作ることで、理解を深めましょうという本です。
この本では最初に AND, OR, NAND, XOR を作っています。本末転倒の気もしますが、これらを Polyphony でコンパイルしてハードウェア化してみましょう。
なおソースは github の以下の URL からとってこれます。
https://github.com/ryos36/polyphony-tutorial/
簡単に試したい人は polyphony と iverilog をインストールし上記 URL を clone して simu.py で各ソースを実行してみてください。
> pip3 install polyphony
<適当に iverilog をインストール>
> git clone https://github.com/ryos36/polyphony-tutorial/
> cd polyphony-tutorial/DeepLearning
> ../bin/simu.py and.py
pyvenv を使った環境構築はこちらにまとめました。
http://qiita.com/ryos36/items/7e7fce9078a79f782380
2章のパーセプトロンから
2.3.1 の例にある AND ゲートを作ってみます。
from polyphony import testbench
def AND(x1, x2):
w1, w2, theta = 5, 5, 7
tmp = x1*w1 + x2*w2
if tmp <= theta:
return 0
elif tmp > theta:
return 1
@testbench
def test():
print(AND(0, 0))
print(AND(1, 0))
print(AND(0, 1))
print(AND(1, 1))
test()
本の中では 0.5, 0.5, 0.7 のパラメータになっていますが、Polyphony 用に 5, 5, 7 の整数値に変更します。(今考えたら 2,2,3 の方がよかったかも)
結果は次の通りです。
[test-0.2.2] Persimmon:polyphony-tutorial> cd DeepLearning/
[test-0.2.2] Persimmon:DeepLearning> ../bin/simu.py and.py
0:AND_0_in_x1= x, AND_0_in_x2= x, AND_0_out_0= x
110:AND_0_in_x1= 0, AND_0_in_x2= 0, AND_0_out_0= x
160:AND_0_in_x1= 0, AND_0_in_x2= 0, AND_0_out_0= 0
0
180:AND_0_in_x1= 1, AND_0_in_x2= 0, AND_0_out_0= 0
0
250:AND_0_in_x1= 0, AND_0_in_x2= 1, AND_0_out_0= 0
0
320:AND_0_in_x1= 1, AND_0_in_x2= 1, AND_0_out_0= 0
370:AND_0_in_x1= 1, AND_0_in_x2= 1, AND_0_out_0= 1
1
期待通りです。なお、勝手に出力されている謎の情報のコロンの左の数字は時間(クロック数)です。おおよその性能の目安になります。
Python のリストを使う
こんどは Python のリストを使ってみましょう。
from polyphony import testbench
def list_mul(lst_r, lst_a, lst_b):
for i in range(len(lst_r)):
lst_r[i] = lst_a[i] * lst_b[i]
def sum(lst):
tmp = 0
for i in range(len(lst)):
tmp = tmp + lst[i]
return tmp
def AND(x1, x2):
lst_r = [0, 0]
lst_a = [x1, x2]
lst_b = [5, 5]
b = -7
list_mul(lst_r, lst_a, lst_b)
tmp = sum(lst_r) + b
if tmp <= 0:
return 0
else:
return 1
@testbench
def test():
print(AND(0, 0))
print(AND(1, 0))
print(AND(0, 1))
print(AND(1, 1))
test()
Polyphony にリストの掛算やsumはないので、関数を定義しています。
[test-0.2.2] Persimmon:DeepLearning> ../bin/simu.py and2.py
0:AND_0_in_x1= x, AND_0_in_x2= x, AND_0_out_0= x
110:AND_0_in_x1= 0, AND_0_in_x2= 0, AND_0_out_0= x
550:AND_0_in_x1= 0, AND_0_in_x2= 0, AND_0_out_0= 0
0
570:AND_0_in_x1= 1, AND_0_in_x2= 0, AND_0_out_0= 0
0
1030:AND_0_in_x1= 0, AND_0_in_x2= 1, AND_0_out_0= 0
0
1490:AND_0_in_x1= 1, AND_0_in_x2= 1, AND_0_out_0= 0
1930:AND_0_in_x1= 1, AND_0_in_x2= 1, AND_0_out_0= 1
1
リストを使うことで抽象性が増しましたが、演算に for 文を使っているためスピードが遅くなってしまいました。
同様に or.py や nand.py を作ることが出来ます。コピペになっているのがちょっと悲しいソースです。
XOR をつくってみる
これらを踏まえて XOR を作り実行します。
from polyphony import testbench
def list_mul(lst_r, lst_a, lst_b):
for i in range(len(lst_r)):
lst_r[i] = lst_a[i] * lst_b[i]
def sum(lst):
tmp = 0
for i in range(len(lst)):
tmp = tmp + lst[i]
return tmp
def AND(x1, x2):
lst_r = [0, 0]
lst_a = [x1, x2]
lst_b = [5, 5]
b = -7
list_mul(lst_r, lst_a, lst_b)
tmp = sum(lst_r) + b
if tmp <= 0:
return 0
else:
return 1
def OR(x1, x2):
lst_r = [0, 0]
lst_a = [x1, x2]
lst_b = [5, 5]
b = -2
list_mul(lst_r, lst_a, lst_b)
tmp = sum(lst_r) + b
if tmp <= 0:
return 0
else:
return 1
def NAND(x1, x2):
lst_r = [0, 0]
lst_a = [x1, x2]
lst_b = [-5, -5]
b = 7
list_mul(lst_r, lst_a, lst_b)
tmp = sum(lst_r) + b
if tmp <= 0:
return 0
else:
return 1
def XOR(x1, x2):
s1 = NAND(x1, x2)
s2 = OR(x1, x2)
y = AND(s1, s2)
return y
@testbench
def test():
print(XOR(0, 0))
print(XOR(1, 0))
print(XOR(0, 1))
print(XOR(1, 1))
test()
コピペプログラムの集大成のようなプログラムになっていますが、動きます。動くことが重要です。次のように結果も正しいようです。
[test-0.2.2] Persimmon:DeepLearning> ../bin/simu.py xor.py
0:XOR_0_in_x1= x, XOR_0_in_x2= x, XOR_0_out_0= x
110:XOR_0_in_x1= 0, XOR_0_in_x2= 0, XOR_0_out_0= x
1440:XOR_0_in_x1= 0, XOR_0_in_x2= 0, XOR_0_out_0= 0
0
1450:XOR_0_in_x1= 1, XOR_0_in_x2= 0, XOR_0_out_0= 0
2780:XOR_0_in_x1= 1, XOR_0_in_x2= 0, XOR_0_out_0= 1
1
2790:XOR_0_in_x1= 0, XOR_0_in_x2= 1, XOR_0_out_0= 1
1
4130:XOR_0_in_x1= 1, XOR_0_in_x2= 1, XOR_0_out_0= 1
5460:XOR_0_in_x1= 1, XOR_0_in_x2= 1, XOR_0_out_0= 0
0
Python のクラスを使う
コピペを回避するためにクラスを使いましょう。だいぶ見通しがよくなりました。
from polyphony import testbench
class BitOp:
def __init__(self, w0, w1, b):
self.w0 = w0
self.w1 = w1
self.b = b
def eval(self, x0, x1):
tmp0 = self.w0 * x0
tmp1 = self.w1 * x1
tmp = tmp0 + tmp1 + self.b
if tmp <= 0:
return 0
else:
return 1
def AND(x1, x2):
op = BitOp(5, 5, -7)
return op.eval(x1, x2)
def OR(x1, x2):
op = BitOp(5, 5, -2)
return op.eval(x1, x2)
def NAND(x1, x2):
op = BitOp(-5, -5, 7)
return op.eval(x1, x2)
def XOR(x1, x2):
AND = BitOp(5, 5, -7)
OR = BitOp(5, 5, -2)
NAND = BitOp(-5, -5, 7)
s1 = NAND.eval(x1, x2)
s2 = OR.eval(x1, x2)
y = AND.eval(s1, s2)
return y
@testbench
def test():
print(XOR(0, 0))
print(XOR(1, 0))
print(XOR(0, 1))
print(XOR(1, 1))
test()
リストを使うのをやめたので、実行時間は早くなりました。
[test-0.2.2] Persimmon:DeepLearning> ls
and.py and2.py c_xor.py nand.py or.py t_and.py xor.py
[test-0.2.2] Persimmon:DeepLearning> ../bin/simu.py c_xor.py
0:XOR_0_in_x1= x, XOR_0_in_x2= x, XOR_0_out_0= x
110:XOR_0_in_x1= 0, XOR_0_in_x2= 0, XOR_0_out_0= x
280:XOR_0_in_x1= 0, XOR_0_in_x2= 0, XOR_0_out_0= 0
0
290:XOR_0_in_x1= 1, XOR_0_in_x2= 0, XOR_0_out_0= 0
460:XOR_0_in_x1= 1, XOR_0_in_x2= 0, XOR_0_out_0= 1
1
470:XOR_0_in_x1= 0, XOR_0_in_x2= 1, XOR_0_out_0= 1
1
650:XOR_0_in_x1= 1, XOR_0_in_x2= 1, XOR_0_out_0= 1
820:XOR_0_in_x1= 1, XOR_0_in_x2= 1, XOR_0_out_0= 0
0
おまけ: タプル版の and
Polyphony を 0.3.0 にすると(普通に pip3 install すると現時点 2017.3.27 では 0.2.2 がインストールされる) タプル版も動くよ。
from polyphony import testbench
def t_mul2(t_a, t_b):
a0, a1 = t_a
b0, b1 = t_b
return (a0 * b0, a1 * b0)
def t_sum2(t_a):
a0, a1 = t_a
return a0 + a1
def AND(x1, x2):
para = (5, 5)
b = -7
t_r = t_mul2((x1, x2), para)
tmp = t_sum2(t_r) + b
if tmp <= 0:
return 0
else:
return 1
@testbench
def test():
print(AND(0, 0))
print(AND(1, 0))
print(AND(0, 1))
print(AND(1, 1))
test()
XOR は出来たけど
XOR 出来たけど、、、そもそも FPGA でできるの当たり前では?
Deep Learning マスターへの道はまだまだ遠い