顯示具有 Programming 標籤的文章。 顯示所有文章
顯示具有 Programming 標籤的文章。 顯示所有文章

2019年7月5日 星期五

倒退攄的煞鼓 -- 佮伊佮做伙

←前一篇 


到今,咱已經共所有倒退攄所需要的倒退攄攏搜揣出來,這馬是時機來共伊佮做伙。咱先來看伊的圖:


一棧一棧的實作,是囥佇 layers.py
伊主要的程式,是囥佇 two_layer_net.py

咱來斟酌解說一个:

    def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):
        # 開始攢好勢
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) 
        self.params['b2'] = np.zeros(output_size)

        # 共伊一棧一棧疊khi
        self.layers = OrderedDict()
        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
        self.layers['Relu1'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])

        self.lastLayer = SoftmaxWithLoss()

這前半段佮神經網路的學習siâng
第二段是使用一个特別的資料結構: OrderedDict 來共一棧一棧整--起來。
啥乜是 OrderedDict 咧? 簡單講,伊是一款字典 (Dictionary),毋過伊會共添入去的資料照順序排予好勢。原在字典內面的資料,是用 Khí(Key) 來揣出來,伊是無順序,由在 Python 家己去安排。按呢有啥乜好處? 小等你就知。


向前行


主程式是 gradient(),一開始就呼 (khoo/call) self.loss(x, t)

    def gradient(self, x, t):
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.lastLayer.backward(dout)
        
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        # 設定
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grads


這 loss() 就是共向前行(Forward propagation),softmax 和 error function 一擺做到底。

    # x:入力データ, t:教師データ
    def loss(self, x, t):
        y = self.predict(x)
        return self.lastLayer.forward(y, t)

loss() 分兩步: predict() 佮 lastLayer.forward(),也就是共上尾彼 SoftmaxWithLoss() 另外做。

    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)
        
        return x

predict() 嘛是有影簡單。一个 for 箍輾,照步來,去呼 layers 內面,逐棧的 forward() 函式。照頂懸 __init__() 所指定,就是照 Affine1, ReLU1, Affine2,一个一个呼伊的 forward(),共逐棧的輸出,成做會一棧的輸入。

這就是 OrderedDict 好用的所在,我毋免一个一个寫出來,用 for loop 就好。



倒退攄

佇 gradient() 內底,dout = 1 做參數開始,先呼上尾棧 lastLayer 的 backward()。紲落來這幾逝嘛是用  OrderedDict 的奇巧:

        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

因為伊是照順序,我共伊的值捎出來,共伊顛倒反 (layers.revers()),閣用 for 箍輾,伊就是倒退攄囉。

結束了後,咱欲愛的 W1 和 W2 的改變,是囥佇 Affine layer 內底 (參考 layers.py 的 Affine Clase), 所以愛共伊掠出來:

        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

擲轉去。


轉去 20181028 這工

 20181028 這工是咱頭一擺來訓練神經網路。gradient() 就是囥佇 train_neuralnet.py 內面使用,咱得著的 grads, 是予伊用來改變 network_parameters: 

    grad = network.gradient(x_batch, t_batch)
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]

紲落來才閣進行下一擺的訓練。

uì 20181028 講到今嘛年外,講甲落落長,伊上重要的觀念就是共正港的數學微分,伊的運算方式,改用倒退攄的方法。倒退攄,其實就是共微分个運算拍破,分解做上基本的神經元有法度算。按呢生會使予會算數速度加有夠緊,按呢才有實用的價值。

寫到遮,應該共神經網路上基本的原理講到一个崁站矣。這个系列嘛欲踮遮到煞鼓矣。這本書: 

Deep Learning|用Python進行深度學習的基礎理論實作 


後半段閣有袂少內容。毋過,完全共人用心血寫落來的書囥佇網路頂予人免費看,按呢嘛無好。向望逐家若是有興趣,會當去共伊交關一咧。

因為是利用做工課掠外,有閒的時間才寫,有當時閣會懶屍,所以進度真慢。總是,佇寫的時陣,才會了解台語欲寫專業的物件,會拄著啥乜困難,解決的方向是佗一爿? 加一个行業有人用台語寫作,就加一个行業的專有名詞會生--出來。各行各業攏有人用台語寫,台語才會健康生湠落去。特別是上新个智識,咱莫共家己母語限制佇厝內,抑是懷舊爾爾。

←前一篇 


Python Deep Learning 深學筆記 - Softmax 佮 Loss 的倒退攄

←前一篇     後一篇→


上尾, 咱欲討論輸出棧的倒退攄。

 uì 輸出棧 ê 一寡討論,知影分類問題用 softmax (Sòo-hu-mak-su)函數。所致,咱愛揣出 softmax 的倒退攄函式。
上尾,閣愛用 Loss 函式來算出精差是偌濟,親像這張圖:




因為這數學較複雜, 咱直接看伊的結果:


綠色是向前行, 藍色是倒退攄。
看著倒退攄是遐呢仔簡單的 y - t, 佮向前行 ê 時陣彼款複雜的算式比起來, 是夭壽簡單。會得著遮爾媠氣的結果,當然是揀著好用的 Cross Entropy Error 函式,佮 Softmax 會貼峇的結果。若有興趣,會當參考原書的附錄 A。

根據這結果,咱來看 Python 的 code 按怎寫, 參考 layers.py  中方的 class SoftmaxWithLoss:


class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None
        self.y = None # softmax 的輸出
        self.t = None # 正確的答案

    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)
        
        return self.loss

    def backward(self, dout=1):
        batch_size = self.t.shape[0]
        if self.t.size == self.y.size: # 這是 one-hot vector
            dx = (self.y - self.t) / batch_size
        else:
            dx = self.y.copy()
            dx[np.arange(batch_size), self.t] -= 1
            dx = dx / batch_size
        
        return dx


backward() 的 if 條件式內面, 就是 (y1 - t1), (y2 - y2), ... 的實做。毋過遮爾清氣的結果, 是佇
t 是 one-hot 的條件之下。啥乜是 one hot 呢? 咱來回想 MNIST 資料集中方, load_mnist() 介紹有紹介.

若是 else 彼爿就較複雜, 我現此時嘛毋知影。因為咱目前用的 MNIST 資料是 one hot, 就先按呢行落去。

上尾愛講予斟酌的所在是: backward(self, dout=1). dout = 1, 是因為當當咱算出 dL (delta L), 成做倒退攄的輸入的時陣,若是 dL 大, 就表示離標準答案差較遠。dL 細, 就較倚標準答案。所以咱會向望 dL 大的時陣,倒退攄的輸入嘛愛大,予伊刺激較強。所以根據得著的 dL,咱會用 dL/dL = 1 來做為倒退攄的輸入。



←前一篇     後一篇→





2019年5月5日 星期日

Python Deep Learning 深學筆記 - Affine 棧佮 Sigmoid

←前一篇    後一篇→


咱了解倒退攄演算法, 其實就是利用計算圖來共一个點的計算分拆, 成做上基本的數學式, 現此時就是愛共這寡基本運算的倒退攄公式揣出來, 才閣共 in 合起來。前一篇是分析,這篇是欲討論佮做伙。


A-hainn棧 (Affine Layer) 的觀念

Affine (華語: 仿射, 台語暫時用音譯: A-hainn) 是幾何學的名詞,Affine transformation 就是共一組點, 線, 抑是平面,對一個空間, 轉換到另一個空間。

這兩個空間是有關係的。

簡單舉一个例: 日落山的時陣,日頭光共你的影炤甲長ló-ló。嘛共你身軀邊的愛人的影炤甲長長長。日頭就是彼个 A-hainn  函式,共你佮你的愛人, 活佇真實世界的人,成做 (transform) 平面的影。

影佮你是有關係, 你振動, 伊嘛振動。你兩个徛做伙,影嘛黏牢牢。
影佮你是無仝--ê。伊無面無喙,干焦深淺無仝的烏,佇平面活動。

普通 A-hainn 運算,是矩陣 (Matrix)運算。

對另一個角度來講,就是共一組數字,透過固定的方式 (function, 函式), 成做另一組數字。
所以,神經網路對輸入,一棧一棧, 向前行的演算法,做 dot 運算閣加法:


X ● W1 + B1 = A1
h(A1) = Y

這款矩陣 Dot 運算, 也就是一種 A-hainn 運算。


Dot 運算的計算圖

加法真簡單, 咱來看  X* W = Y 就好.
透過計算圖來分拆:


伊的倒退攄是:





其中
  • Δ 是 diff 的意思。因為數學符號僫拍, 有時用字母 d 代替.
  • 細个 T 佇 W 抑是 X 的正手爿較懸, 是Matrix 的斜倒反(Transpose, 音譯: Tsuán-su-pho-su, 簡單寫做 Tsuán-su-pho)。有時為著簡單拍字, 嘛寫做 ^T.

愛注意: 佇 Dot 運算, 順序是要緊的. X ● W 袂使寫做 W   X. 當然, 你欲做甲真幼, 來推算 Dot 的倒退攄嘛是會使。彼是滿滿的數學.

啥乜是斜倒反咧? 咱回想 2 維陣列 ê 運算是有維度个, 準講 2x3 的 Mé-tshuì, 伊的倒反就是:
沿伊的對角線共伊反--過來, 成做 3x2 的 Mé-tshuì

你若閣有印象, 伊略略仔有成乘法的倒退攄: 就是對輸入 X 來講, 攄轉來的變化,是受著另一爿輸入 W 的影響(乘法時是乘以 W 倍, Dot 運算時是 dot)。仝理, W 嘛是受著 X 的影響。咱就無閣詳細分拆這過程。咱注心來看為啥物欲有這个 Transpose。
其實,這是 uì 陣列的 dot 運算,算倒轉來需要个: 咱回想 Dot 運算的這張圖:


咱若是欲算轉來, [ [50, 68], [122, 167]] 顛倒反算轉來 [[1, 2, 3], [4, 5, 6]] 欲按怎做呢? Mé-tshuì 無除法, 伊的逆運算是揣出一个 Mé-tshuì, 共結果 dot 這个 Mé-tshuì 會當得著原來的 Mé-tshuì,彼个型是:






X 的維度是偌濟呢? 咱 uì Matrix ê Dot 運算知影: 

  • dot 倒手爿Mé-tshuì 幾逝 (row)  , 等號 (=) 算出來的 Mé-tshuì  就幾逝
  • dot 正手爿Mé-tshuì 幾欄(column),  等號 (=) 算出來的 Mé-tshuì  就幾欄

所致,咱會當推算出來 X 的型是 2x3:
拄好就是原在 3x2 的斜倒反。當然,a1~a3, b1~b3 和原來的數字無仝,這你會使一个一个去算。
重點是: 咱佇遮欲解說的是: 倒退攄uì顛倒頭算轉來,因為輸入佮輸出的維度攏愛維持,中方使用著的 Mé-tshuì 就愛共伊對角反過來,這我共伊號做 "斜倒反(tshiâ-tó-ping)".


伊的寫法, 就是佇 layers.py 內面的 class Affine


Softmax 的倒退攄


這若是學過微積分 (Calculus), 就會曉知按怎共伊算出來. 咱佇遮共伊直接寫落來:


閣小可整理一个, 就成做:


當然, y, 就是佇寫過个 y = 1/(1+exp(-x))。為啥物欲做這款的變化? 因為按呢較簡單算。y 的值佇向前行 (forward propagation) 就算出來矣, 咱倒退攄時,干焦做乘法當然比平方(pîng-hong) 閣指數(kí-sòo) 加真簡單。參考 layers.py 的 class Sigmoid 的 backward() 函式, 是毋是真屧貼?


←前一篇    後一篇→



2018年12月25日 星期二

Python Deep Learning 深學筆記 - 基本運算个倒退攄

←前一篇    後一篇→


前一篇知影倒退攄演算法个概念,透過計算圖, 咱會當知影目的就是揣出 +, dot, sigmoid (抑是其他戛火函式) 个倒退攄運算。毋過, 事事項項攏愛uì上基本个所在來起造. 比如講: dot 運算本身乘(*)佮加(+) 整--起來.  這就是 "分而治之" 的手路.

加法倒退攄

若是  z = x + y:

伊的微分是 dz/dx = 1, dz/dy = 1。也都是 x 變 1, z 嘛綴伊仝款變 1. x (抑是 y) 變偌濟, z 就變偌濟。顛倒頭講, z  變偌濟 ,x (抑是 y) 就變偌濟,  所以伊的倒退攄是:




乘法倒退攄

若是 z = x * y, 伊的計算圖 :
伊的偏微分:   ∂z/∂x = y,  ∂z/∂y = x, 所以, 伊的倒退攄算法是:







咱會使按呢來了解: x 這爿來講, 伊小可變化, 對輸出个影響是會有 y 倍。仝款个道理, y 小可變化, 會有  x 倍个變化。咱舉一个實際个例, 比如講: 10 * 2 = 20:


咱若是用  1 來做倒退攄个輸入, 照咱推理模型:

x 爿是 2, y 爿是 10. 這意思是講:

若是 x 變做 10 + 1 = 11, z 會成做 20 + 2 = 22
若是 y 添 1 成做 2 + 1 = 3, z 會變做 20 + 10 = 30.

按呢敢著咧? 咱看覓: 本底 10 * 2 = 20

(10 + 1) * 2 = 11 * 2 = 22 = 20 + 2
10 * (2+1) = 10 * 3   = 30 = 20 + 10

這是完全對同。

到今咱會當了解: 倒退攄演算法, 窮實就是先共基本運算个微分模型算出來。

伊代表个意義是: 輸入个一絲絲仔改變, 會造成輸出偌濟變化! 這就是微分个概念, 精差普通微分是對倒爿算到正爿, 倒退攄是對正爿算轉去倒爿!



動手用 Python 來實作

理論分析了, 咱參考 layer_naive.py: 伊用 class 來共向前行(forward propagation) 佮倒退攄(backward propagation) 敆做伙, 成做一棧: AddLayer() 是加法棧, MulLayer() 是乘法棧. 伊的寫法真直觀, 家己看就知.


因為 ReLU 佮 Sigmoid 的倒退攄攏是數學, 我就干焦共in的算式寫落來. 詳細按怎算, 你會使去看原作者的書, 抑是其他數學的冊。


ReLU 棧的倒退攄

y = x (if x > 0)
y = 0 (if x < 0)
伊的倒退攄是:
dy/dx = 1 (if x > 0)
dy/dx = 0 (if x < 0)

伊的 Python 實作參考 layers.py 的 Relu Class。


Sigmoid的倒退攄

y = 1/(1+exp(-x))

伊的倒退攄是 (d表示 delta):
dx = y * ( 1 - y) * dL

中方  dL 是 y 彼爿的變化。當然,這咧公式較複雜,經過幾落改轉換。這寡轉換目的是愛佇最後,會使用 y 來表示。因為 y 是彼擺向前行算出來的值, 咱欲攄轉來的 dx 佮伊有關係。

伊的 Python 實作參考 layers.py Sigmoid Class


←前一篇    後一篇→






2018年11月11日 星期日

Python Deep Learning 深學筆記 - 倒退攄演算法簡介

←前一篇    後一篇→


退攄演算法 (Back Propagation) 是相對向前行演算法 (Forward Propagation) 个專有名詞. 伊是用來解決微分抑是梯度法傷過食時間个問題。


啥物是向前行演算法 (Forward Propagation)

向前行演算法, 就是咱一直講到今, uì 輸入, 第一棧, 第二棧, 到輸出个神經網路. 咱攏已經學過啊嘛奕過.

伊就是前一篇內面 predict() 函式做的代誌, 抑就是神經網路 uì 輸入 (input), 經過一棧一棧个神經元, 到輸出 (out) 這个過程, 咱若參考這篇: 

3 棧 ê 神經網路, 伊會使用這組函式來表示:

A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1)

A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)

A3 = np.dot(Z2, W3) + B3
y = identity_function(A3)

當然, 後來 identity_function() 是用 softmax() 函式來取代. 伊是 uì 輸入 X 行到輸出 y.
若是畫圖, 是按呢生:


伊箭頭个方向, 攏是 uì 倒爿行到正手爿, 這就是向前行演算法會計算圖(computational graph).

咱會使看著, 逐个圓箍仔, 攏是確定个數學算式, dot, +, sigmoid 這寡運算 (operation).


倒退攄演算法是想欲tshòng啥物咧?

咱翻頭來想咱為啥貨欲學微分梯度?

目的是當當(tng-tong) 咱uì一擺學習 X0 得著 Y0 這个結果. 咱若共 Y0, 變一屑屑仔, 咱共伊寫做 dY (delta Y), 閣揣出 dX (delta X), 咱就會得著 dY/dX  這个斜率/梯度. 咱就有法度決定對佇一个方向徙振動, 會予 Y 收縮, 行向咱想欲挃的值.

微分佮梯度是完全照數學理論, 干焦了解數學理論, 你就了解伊佇創啥貨. 毋過實際傷開時間, 無實用.

咱用另外一款思考, 有法度親像向前行演算法相siâng, 一節一節算過去, 按呢咧?






若有法度揣著內面逐點(圓箍仔) 个倒退攄个運算, 按呢咱是毋是就會當得著: dY/dX, dY/dW, dY/dB1, ..., 這寡微分/梯度咧? 當然, 咱上注心是 揣出 dW1, dW2 欲變uì佗位去.

內面逐點, 我加一个問號 "?", 就是咱紲落來个欲解決个問題: 除了用計算圖解說倒退攄演算法會用得, 閣愛一个一个揣出 dot, +, sigmoid, ... 等等个運算欲按怎倒退攄?


2018年10月28日 星期日

Python Deep Learning 深學筆記 - 完全照微分理論會拄著的問題

←前一篇    後一篇→

咱來走看覓

咱進前介紹个 5 个步序 , 踮 two_layer_net.py  共伊分別實作予好, 佇 train_neuralnet.py 共這五个步事整起來, uì 頭迵到尾, 行看覓:

$ python3 train_neuralnet.py 
train acc, test acc | 0.0903, 0.0899
train acc, test acc | 0.7732833333333333, 0.7781
train acc, test acc | 0.87545, 0.879
train acc, test acc | 0.89705, 0.8995
train acc, test acc | 0.907, 0.9103
train acc, test acc | 0.9141, 0.9184
train acc, test acc | 0.9193333333333333, 0.9221
train acc, test acc | 0.9239666666666667, 0.9249
train acc, test acc | 0.9285666666666667, 0.9291
train acc, test acc | 0.9303, 0.9318
train acc, test acc | 0.9334, 0.9352
train acc, test acc | 0.9364833333333333, 0.9377
train acc, test acc | 0.9385, 0.939
train acc, test acc | 0.9416166666666667, 0.942
train acc, test acc | 0.9425666666666667, 0.9417
train acc, test acc | 0.9447, 0.9435
train acc, test acc | 0.94585, 0.9454

紲落來閣彈出來一張圖,  看來真順利.

若有認真看原始碼 (Source code)


毋過, 咱斟酌看 train_neuralnet.py , 中方有

    grad = network.gradient(x_batch, t_batch)

這敢是咱佇介紹規半晡个 numerical_gradient()? 毋是呢, 伊是 gradient(), 毋是 numerical_gradient().

歡喜傷早囉!

閣看 two_layer_net.py 內面有 numerical_gradient(), 嘛有  gradient() 函式. 按呢斟酌看來,
train_nerual_net.py 並無用咱的講規半晡个 numerical_gradient().

按呢毋是佇咧裝痟个?

共伊改轉來 numerical_gradient() 奕看覓

我kā train_neuralnet.py 的 graidient() 改轉來:

    grad = network.numerical_gradient(x_batch, t_batch)

行看覓, 等真久, 干焦出現一逝:

train acc, test acc | 0.09736666666666667, 0.0982

就袂振袂動,時間堅凍.

發生啥物代誌咧?  一開始我是懷疑程式有蟲 (bug), 是毋是愛蟲(thāu-thâng)?

後來, 我用 timeit Má-tsìo 來小可看覓 numerical_gradient() 開偌濟時間, 按呢寫:

import timeit      
...
time_start = timeit.default_timer()
grad = network.numerical_gradient(x_batch, t_batch)
print("grad cal: {}".format(timeit.default_timer() - time_start))

結果是:

iter_per_epoch=600.0
grad cal: 46.7133047870002
train acc, test acc | 0.09736666666666667, 0.0982

頭一逝是我另外 iter_per_epoch 印出來. 咱佇遮看著:

行一遍 numerical_gradient() 愛四十五秒.

iter_per_epoch 是設定走幾輾, 印一擺 "train acc, test acc". 頭一擺先印, 紲落來就愛 600 擺 numerical_gradient() 了後才會閣印.

若是  45 * 600 = 27000 秒, 差不度是 7.5 點鐘才會印第二擺.

若是參考頂懸彼例使用  gradient(), 印 "train acc, test acc" 16 擺才收縮到滿意的程度 ,numerical_gradient() 就愛五工才有初步个結果。

以上个時間計算, 愛看你个電腦速度, 你算出來無一定佮我相仝 。

gradient()  佮 numerical_gradient() 這兩个方法,時間那會差遐爾濟?

gradient() 佮 numerical_gradient() 時間差蓋濟

咱若是仝款用 time_it 來算 gradient() 方法  (method) 个時間, 伊是量其約  0.01 秒, 和 45 比起來有 4000  外倍時間个精差.

gradient() 是按怎會遐爾仔緊, 伊就是用 倒退攄 (Back Propagation) 法啦!


←前一篇    後一篇→

2018年8月6日 星期一

Python Deep Learning 深學筆記 - 神經網路的學習

←前一篇     後一篇→


前兩篇紹介微分和梯度, 閣有 in 按怎收縮(convergence), 去揣著上細值梯度法, 是為著這篇做準備个: 透過梯度法, 共損失函式收縮到接近 0.

若按呢生, 伊的流程是:
  1. 先決定欲偌濟神經元, 幾棧
  2. 先予權重 (W) 和 B 一个初值. 普通這是用隨機 (random) 方式來起頭
  3. 用細批个資料, 來算出伊的損失函式 (loss function) 值.
  4. 計算損失函式佇彼的值的梯度, 伊會顯示行佗一个方向, 損失函式會減上濟
  5. 共權重(W) 換新算出來, 閣轉去 3, 4, 5, 重複運算, 一直到滿意的答案, 抑就是損失函式足倚零足倚零, 到咱會接受个戶橂.
當然, 3 內面的細批資料, 逐遍攏無仝!

下面, 咱若看若參考: two_layer_net.py


先決定欲偌濟神經元, 幾棧, 起頭的值

two_layer_net.py 內底,  __init__() 就是定義這个神經網路: 伊是兩棧ê神經網路:

a1 = W1 * x + b1
a2 = W2 * a1 + b2
y = softmax(a2)

咱直接揤頂仔彼咧連結, 去看 __init__(), 伊用 numpy ê randomn 函式來生出一个陣列, numpy 个簡介, 會用參考. 伊个大細, 是呼(khoo)个人, 佇ê 時陣交代落來个.

伊會用 Python 字典 (dictionary) 這種資料型態.

因為 x 是輸入, 佇 __init__() 是定義 W1, b1, W2, b2.
input_size 是 x 的數目. hidden_size 是中方彼棧个數目。
W1 个大細是(橫, 直) (input_size, hidden_size) 个陣列.
W2 个大細是(橫, 直)  (hidden_size, output_size) 个陣列.

    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
        self.params['b2'] = np.zeros(output_size)

這就是頂懸講个第二步.

計算損失函式

計算損失函式進前, 愛先根據一組輸入,來算出輸出, 這是進前介紹過个 predict(), 也就是真正行這个 predict() 函式:

a1 = W1 * x + b1
a2 = W2 * a1 + b2

就是這个 函式:

    def predict(self, x):
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
    
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)
        
        return y


紲落來,算出伊的 cross_entropy_error()  損失函式, 就是 y = softmax(a2)  个實作:

    def loss(self, x, t):
        y = self.predict(x)     
        return cross_entropy_error(y, t)

這是頂懸个第三步.

2018年7月17日 星期二

Python 程式的 Profiling 2 - cProfile Má-tsìo (Module)

cProfile Má-tsìo (Module)

cProfile 是共規个程式來分析, 程式內面个函式, 予人呼幾遍? 逐遍開偌濟時間? 予你真簡單就會使了解程式个關頭佇佗位? 紲落來个例, 有一寡是直接 uì 伊的官網提過來.

上完整个了解 cProfile, 當然是參考伊的官網.  佇遮, 我有閣參考這篇: Python 用 cProfile 測量程式效能瓶頸與 gprof2dot 視覺化分析教學 , 有興趣會使揤入去看.


簡單例

timeit 相siâng, 伊會使用佇命令列直接用抑是 Python 內底用 Má-tsìo 方式共伊 import 入來使用 。咱先介紹命令列, 用法是:
python -m cProfile [-o output_file] [-s sort_order] (-m module | myscript.py)

-s  : 是排序(pâi-sū) 个根據, 較捷捷用个有:
  • 'calls'            :予人呼个數目. 這是預設值
  • 'cumtine'      :粒積个時間
咱來起行 train_neuralnet.py 做一个例較清楚: (注意: 這个咧愛uì https://gitlab.com/Iunn/deep-learning-from-scratch/tree/master 全部搝落來才會振動)

$ python3 -m cProfile train_neuralnet.py
train acc, test acc | 0.09915, 0.1009
...
train acc, test acc | 0.9449333333333333, 0.9429
train acc, test acc | 0.9467, 0.9431
         887729 function calls (881058 primitive calls) in 237.808 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      571    0.001    0.000    0.001    0.000 :103(release)
      360    0.000    0.000    0.000    0.000 :143(__init__)
      360    0.000    0.000    0.004    0.000 :147(__enter__)
      360    0.000    0.000    0.001    0.000 :151(__exit__)
      571    0.002    0.000    0.003    0.000 :157(_get_module_lock)
      359    0.001    0.000    0.001    0.000 :176(cb)
      211    0.000    0.000    0.001    0.000 :194(_lock_unlock_module)
    479/7    0.000    0.000    0.403    0.058 :211(_call_with_frames_removed)
     3475    0.001    0.000    0.001    0.000 :222(_verbose_message)
        5    0.000    0.000    0.000    0.000 :232(_requires_builtin_wrapper)
....

伊會自動共上定定用个囥頭一个, 才那來若細排--落來. 欄位(nuâ-uī) 个意義是按呢:
  • ncalls        :予人呼幾擺
  • tottime      : 佇這个函式來底, 無共囝函式 (subroutine) 算在內, 開偌濟時間
  • percall      : tottime/ncall, 也就是一个 call 个時間。
  • cumtime   : 粒積个時間, 共囝函式个時間攏算入來
  • percall      :第二个 percall是 cumtime/ncalls
  • filename:lineno :函式名和第幾行
咱若是用 -s cumulative 來行, 伊就是按呢生:

$ python3 -m cProfile -s cumulative train_neuralnet.py
train acc, test acc | 0.09751666666666667, 0.0974
。。。。

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    318/1    0.009    0.000   36.585   36.585 {built-in method builtins.exec}
        1    2.237    2.237   36.585   36.585 train_neuralnet.py:2()
    70371   17.960    0.000   17.960    0.000 {built-in method numpy.core.multiarray.dot}
    10000    0.667    0.000   14.686    0.001 two_layer_net.py:55(gradient)
    10034    0.619    0.000   14.124    0.001 two_layer_net.py:18(predict)
    10000    0.035    0.000    7.642    0.001 two_layer_net.py:30(loss)
       34    0.043    0.001    7.193    0.212 two_layer_net.py:35(accuracy)
    40034    6.362    0.000    6.362    0.000 functions.py:13(sigmoid)

前兩個就差不多是規个 Má-tsìo, uì 第三个開始是咱的函式名. -s 个完整參數佇官網有. 你嘛會使看輸出个欄位名.


共輸出儉入去檔案

頂懸个命令, 干焦共結果 uì 標準輸出 (stdout)。咱嘛會使先共結果儉--起來,事後才閣用另一个Má-tsìo來分析. 按呢咱就會使用無仝个排序來斟酌分析結果, 毋免行幾落變程式。
若是欲共輸出儉佇檔案, 愛添 -o:
.
$ python3 -m cProfile -o train_neuralnet.pstats train_neuralnet.py

綴佇後壁  train_neuralnet.pstats 是檔案名.
徙落來, 咱會使用 -m pstats 來分析這个檔案:

$ python3 -m pstats -o train_neuralnet.pstats

你揤 ENTER 了後, 就入去一咧殼 (Shell), 這个殼裡, 是 pstats 的世界, 你會當落 pstats 个命令:

train_neuralnet.pstats % sort cumtime       # 用 cumtime 排序
train_neuralnet.pstats % stats              # 共伊印出來
Mon Jul 23 20:51:48 2018    ./out.pstate

         898211 function calls (891473 primitive calls) in 32.642 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    318/1    0.010    0.000   32.642   32.642 {built-in method builtins.exec}
        1    1.784    1.784   32.642   32.642 train_neuralnet.py:2()
    70612   14.804    0.000   14.804    0.000 {built-in method numpy.core.multiarray.dot}
    10000    0.541    0.000   12.327    0.001 /home/black/data/myRoadMap/myGit/my_python/my-deep-learning/deep-learning-from-scratch/ch04/two_layer_net.py:55(gradient)
    10034    0.476    0.000   11.870    0.001 /home/black/data/myRoadMap/myGit/my_python/my-deep-learning/deep-learning-from-scratch/ch04/two_layer_net.py:18(predict)
    10000    0.027    0.000    6.370    0.001 /home/black/data/myRoadMap/myGit/my_python/my-deep-learning/deep-learning-from-scratch/ch04/two_layer_net.py:30(loss)
       34    0.019    0.001    6.070    0.179 /home/black/data/myRoadMap/myGit/my_python/my-deep-learning/deep-learning-from-scratch/ch04/two_layer_net.py:35(accuracy)
    40034    5.631    0.000    5.631    0.000 ../common/functions.py:13(sigmoid)
.....
train_neuralnet.pstats % sort ncalls          # 用 ncalls 排序
train_neuralnet.pstats % stats 10             # 共伊印出來, 干焦印十行
Mon Jul 23 20:51:48 2018    ./out.pstate

         898211 function calls (891473 primitive calls) in 32.642 seconds

   Ordered by: call count
   List reduced from 3637 to 10 due to restriction <10>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    80644    1.161    0.000    1.161    0.000 {method 'reduce' of 'numpy.ufunc' objects}
74790/74786    0.038    0.000    0.042    0.000 {built-in method builtins.isinstance}
    70612   14.804    0.000   14.804    0.000 {built-in method numpy.core.multiarray.dot}
    50072    0.022    0.000    0.542    0.000 _methods.py:31(_sum)
    50068    0.146    0.000    0.720    0.000 fromnumeric.py:1778(sum)
    40034    5.631    0.000    5.631    0.000 functions.py:13(sigmoid)
    26000    0.005    0.000    0.005    0.000 {method 'append' of 'list' objects}
    20514    0.003    0.000    0.003    0.000 {method 'startswith' of 'str' objects}
    20167    0.013    0.000    0.540    0.000 _methods.py:25(_amax)
    20109    0.079    0.000    0.619    0.000 fromnumeric.py:2222(amax)

uì頂仔个例, 咱會使隨時換捌種排序个方式, 免閣重行規个程式. 咱嘛會寫落 help 來看命令ê用法.


train_neuralnet.pstats% help

Documented commands (type help ):
========================================
EOF  add  callees  callers  help  quit  read  reverse  sort  stats  strip

train_neuralnet.pstats% help sort
Sort profile data according to specified keys.
(Typing `sort' without arguments lists valid keys.)
train_neuralnet.pstats% sort
Valid sort keys (unique prefixes are accepted):
calls -- call count
ncalls -- call count
cumtime -- cumulative time
cumulative -- cumulative time
file -- file name
filename -- file name
line -- line number
module -- file name
name -- function name
nfl -- name/file/line
pcalls -- primitive call count
stdname -- standard name
time -- internal time
tottime -- internal time
train_neuralnet.pstats% 



Má-tsìo 方式 (Module)

就是用 import 共伊搝入來, 使用伊的 run() 方法:

import cProfile
import re
cProfile.run('re.compile("foo|bar")', 'restats')

佇頂仔个例, 咱對  re.compile("foo|bar") (re 是 regular expression)做 profile, 共結果儉入去restats  這个檔案中方.

咱會當用 pstats Má-tsìo 來共 restats 轉做人看有个型式:

import pstats

p = pstats.Stats('restats')
p.strip_dirs().sort_stats(-1).print_stats()

佇我的電腦 (Ubuntu 18.04, Python 3.6.5), 輸出:

Wed Jul 18 21:01:07 2018    restats

         199 function calls (194 primitive calls) in 0.001 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.001    0.001 :1()
        4    0.000    0.000    0.000    0.000 enum.py:265(__call__)
        4    0.000    0.000    0.000    0.000 enum.py:515(__new__)
        2    0.000    0.000    0.000    0.000 enum.py:801(__and__)
        1    0.000    0.000    0.001    0.001 re.py:231(compile)
....

咱會使看著: 伊是用名个順序來排先後.

排序个根據

若是欲改變排序个根據, 佇咧 sort_stats() 內底囥無仝个參數, 親像 'time', 'cumtime', 就會使.

>>> help(p.sort_stats)

>>> p.sort_stats('cumtime')

>>> p.print_stats()
Wed Jul 18 21:12:10 2018    restats

         199 function calls (194 primitive calls) in 0.001 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.001    0.001 {built-in method builtins.exec}
        1    0.000    0.000    0.001    0.001 :1()
        1    0.000    0.000    0.001    0.001 re.py:231(compile)
        1    0.000    0.000    0.001    0.001 re.py:286(_compile)

NOTE: 我是佇 Python 3.6.x 版,  猶原是用字串, 若是 3.7 版, 就愛 import  SortKey, 用伊的定義好个符號:

from pstats import SortKey
p.sort_stats(SortKey.NAME)


減省輸出

有當時, 咱干焦想欲看較重要个代誌, 無想欲全部个資訊攏摒摒--出來, 咱看甲目睭花, 咱會使佇 print_stats() 个參數添數字, 比如講 5 就是咱干焦想欲看上懸个五个函式:

>>> p.sort_stats('time').print_stats(5)
Wed Jul 18 21:12:10 2018    restats

         199 function calls (194 primitive calls) in 0.001 seconds

   Ordered by: internal time
   List reduced from 42 to 5 due to restriction <5>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.001    0.001 {built-in method builtins.exec}
      3/1    0.000    0.000    0.000    0.000 sre_parse.py:173(getwidth)
        1    0.000    0.000    0.001    0.001 re.py:286(_compile)
      3/1    0.000    0.000    0.000    0.000 sre_compile.py:64(_compile)
        2    0.000    0.000    0.000    0.000 sre_parse.py:470(_parse)


你嘛會使干焦看指定ê函式名:

>>> p.sort_stats('time').print_stats('__init__')

抑是看啥人呼伊? 抑是予人呼?

>>> p.print_callers(,'init')
...
>>> p.print_callees()

命令列用法和 Má-tsìo 用法 ê 討論

命令列用法 -m cProfile 是方便, 毋過, 伊是 uì 程式外爿開始做, 所以戲文本身嘛會算入來. 親像頂仔个例:

$ python3 -m cProfile -s cumulative train_neuralnet.py
...
...
        1    2.237    2.237   36.585   36.585 train_neuralnet.py:2()

若是用 Module 用法, 伊就是真正是戲文內面使用著个物件. 而且, 伊會使佇你干焦想欲 profiling 个所在做就好:

import cProfile

pr = cProfile.Profile()
pr.enable()
....                           # 想欲 profiling 个所在
pr.disable()
pr.print_stats(sort='time')

若是欲趕時間, 就是用命令列, 若是欲做較幼路个,  就是使用 Má-tsìo 用法.

用  gprof2dot 來生圖形

親像頂懸介紹个輸出, 是一逝一逝个表。雖罔有排序, 毋過對咱人來講, 也是較食力看. 若是會使用圖形, 較直接.

若是欲按呢做, 愛用著另外兩種工具, 一个是 pyhton 个 Má-tsìo (Module), 另一个是 Linux 頂懸个包袱仔 (package).

咱這馬來安 gprof2dot Má-tsìo:

sudo pip3 install gprof2dot

另外閣安 graphviz:
佇 Ubuntu/Debian Linux:

sudo apt-get install graphviz
佇 Fedora Linux:

sudo yum install graphviz

若是你的 sudo 袂喈, 你就愛成做 root 使用者.

咱用進前个 train_neuralnet.py 來生出 train_neuralnet.pstats

python3 -m cProfile -o train_neuralnet.pstats train_neuralnet.py

閣來才利用 pstats 來生出 png 圖檔:

python3 -m gprof2dot -f pstats train_neuralnet.pstats |dot -T png -o train_neuralnet-profile.png

頂懸个命令你先共伊看做公式, 除了輸入 train_neuralnet.pstats 佮輸出 train_neuralnet-profile.png 你會當家己指定, 賰咧攏固定. 你若是有興趣欲了解閣較濟, 會用个參考 Graphviz 官網. 這是伊的輸出:


若是欲放大, 點.
咱佇圖裡, 真簡單就會當看出來: gradient 39% 佔上濟, predict 38%. 毋過佇較低層內面, numpy 个 dot 佔欲 48%.

佇 Linux 來看GPX 檔案

最近定定有戶外活動。使用𤆬路機 (GPS) 來記錄行過的路線。普通我記錄路線,攏是用手機仔抑是專門个𤆬路機,罕得用電腦來看。 毋過,"仙人拍鼓有時錯,跤步踏差啥人無"。有一擺我無細膩,袂記得共一擺活動的路線收煞起來,閣直接開始記錄下一擺的活動,按呢共幾落...