2018年5月27日 星期日

Python Deep Learning 深學筆記 - 梯度

←前一篇     後一篇→

梯度法(Thui-tōo huat)


一維个微分, 佇兩維以上, 就號做梯度 (Gradient).


一維微分較簡單了解意義, 咱毋才先用伊做例, 了解切線 (tangent line), 斜率 (slope), 和 f(x) 變化个趨勢个關係. 利用這个趨勢, 咱會用咧寬寬仔倚近上懸點抑是上低點.

啥乜是偏微分?

若是維度是 2 以上, 就愛利用偏微分 (partial differential), 一擺對其中一个變數微分, 比如講: 

f(x0, x1) = x0**2 + x1**2

咱會看伊的生張: 運行 meshplot-x2y2.py: (咱無欲深入了解按怎共伊畫出來, 若是對伊有興趣, 遮有一寡討論)

現此時 f(x0, x1) 有兩个變數, 佇一个點, 都愛分開: 先 x1 固定數字, 對 x0 微分一擺, 才閣固定 x0, 對 x1 微分(詳細个理論當然愛去上課, 咱遮先抾來用), 掠準咱佇 (5, 6) 這點:

對 x0 偏微分, 先共 x1 固定佇咧 6:

def function_tmp1(x0):
    return x0 ** 2 + 6 ** 2.0

對 x1 偏微分, 先共 x0 固定咧 5:


def function_tmp2(x1):
    return 5 ** 2 + x1 ** 2.0

這兩的合起來, 就成做 f 函式佇 (5, 6) 這點个微分, 咱佇記做:
                            

用 Python 來寫二維个偏微分

咱理解了後, 欲按怎寫二維个偏微分咧? 參考 gradient_2d.py 內面个 numerical_gradient():

def _numerical_gradient_no_batch(f, x):
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x)
    
    for idx in range(x.size):
        tmp_val = x[idx]
        x[idx] = float(tmp_val) + h
        fxh1 = f(x) # f(x+h)
        
        x[idx] = tmp_val - h 
        fxh2 = f(x) # f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        
        x[idx] = tmp_val # 値を元に戻す
        
    return grad

共空逝算在內, 干焦 16 逝, 就會使做 2 維个數字微分 (numerical), 真奅!

佇遮, 輸入个參數兩个: f 是函式, x 是 numpy 陣列 (array). 這个 array 照理是 (x0, x1), idx == 0 是維度 0, idx == 1 是維度 2.咱用頭前个 (5, 6) 做例, x 就是 [x0=5, x1=6]. 你會使奕看覓, 使用 gradient_2d.py, 這个例, 起

python3 -m pdb gradient_2d.py

pdb 是 Python 个 debug module, 會使予你一步一步來掠蟲, 伊就是 Python 个 GDB, 你若是對 GDB 有熟, 伊對你就是零星个. 就算你袂曉, 你就照我紲落來寫个起字--落去就用得:

你看著:

-> import numpy as np
(Pdb)

表示你入去 pdb, 起 "b 59", 徙落去起 "l 58"

(Pdb) b 59
Breakpoint 1 at /..../deep-learning-from-scratch/ch04/gradient_2d.py:59
(Pdb) l 58
 53       x1 = np.arange(-2, 2.5, 0.25)
 54       X, Y = np.meshgrid(x0, x1)
 55   
 56       X = X.flatten()
 57       Y = Y.flatten()
 58   
 59 B     grad = numerical_gradient(function_2, np.array([X, Y]) )
 60   
 61       plt.figure()
 62       plt.quiver(X, Y, -grad[0], -grad[1],  angles="xy",color="#666666")#,headwidth=10,scale=40,color="#444444")
 63       plt.xlim([-2, 2])

(Pdb)

"b 59" 是叫伊踮 59 彼逝設擋點 (break point), "l 58" 只是予你看咱較實佇 59 有一个擋點.

紲落來, 起 "c", "interact":

(Pdb) c
> /.../deep-learning-from-scratch/ch04/gradient_2d.py(59)()
-> grad = numerical_gradient(function_2, np.array([X, Y]) )
(Pdb) interact
*interactive*
>>> 

"c" 是 continue, 就是叫程式起行, 伊會擋佇咧 59 逝, "interact" 是進入對話模式 (interactive mode), 這个咱會使用 numerical_graident() 和 function_2() 這个函式:

>>> numerical_gradient(function_2, np.array([5.0,6.0]))
array([ 10.,  12.])
>>> numerical_gradient(function_2, np.array([3.0,4.0]))
array([ 6.,  8.])
>>> numerical_gradient(function_2, np.array([0.0,2.0]))
array([ 0.,  4.])

咱看著 function_2 佇 [5, 6], [3, 4], [ 0, 2] 个梯度. 內面 [3, 4], [0, 2] 和書頂仔个答案仝款

使用 pdb, 就是我貧惰閣 copy numerical_gradient()/funcion_2 來做單元測試, 直接用 gradient_2d.py 寫便个來看伊的功能.

踮 gradient_2d.py 內面 numerical_gradient() 个疑問

佇課本伊的梯度圖是用 gradient_2d.py 共伊畫--出來:

圖看起來真媠嘛真合理: 箭頭較長的, 就是變動較大个, 上中方是上低點. 毋過, 我一直卡牢佇咧一个所在:

    X = X.flatten()
    Y = Y.flatten()
    
    grad = numerical_gradient(function_2, np.array([X, Y]) )

照道理, _numerical_gradient_no_batch 个第二个參數是 x 是 [x, y] 對, 佇遮就是 [ X, Y] 對, 分別對應著 x 杆, y 杆个值.

毋過佇 numerical_gradient() 呼 _numerical_gradient_batch() 个時,

def numerical_gradient(f, X):
    if X.ndim == 1:
        return _numerical_gradient_no_batch(f, X)
    else:
        grad = np.zeros_like(X)
        
        for idx, x in enumerate(X):
            grad[idx] = _numerical_gradient_no_batch(f, x)
        
        return grad

進前小節 用 Python 來寫二維个偏微分 咱使干焦使用

 numerical_gradient(function_2, np.array([5.0,6.0]))


是一維陣列, 伊的  X.ndim==1, 會行 if 彼个 case, 所以 X 是真正 (x, y), 一个 x 杆, 一个 y 杆, 傳入去 _numerical_gradient_no_batch(), 這無問題! (x0 對應著 X, x1 對應著 Y).


毋過, 因為這馬 X, Y 毋是孤一个數字, 是 array, 所致 X.ndim 會是 2, 行 else 這爿. 

若是經過 for idx, x in enumerate(X) 處理, 變成原在:

idx=0 是 原在个 X, 傳入去 _numerical_gradient_no_batch(function_2, X).
idx=1 是 _numerical_gradient_no_batch(function_2, Y)if 和 else 這兩條路線傳入去 _numerical_gradient_no_batch() 个參數意義無仝, 我踮遮就拍結矣!

我踮遮擋真久, 想袂通. 毋過, 無法度閣再拖落去, 就先共伊記錄起來, 後擺有機會才閣轉來解決一个問題!


2018年5月5日 星期六

Python matplotlib 欲按怎畫網仔圖 (Mesh plot)

佇做深學个筆記个時陣, 發現我畫袂出來 f(x0, x1) = x0**2 + x1**2 个圖, 所致家己揣資料來試看覓:

咱參考: meshplot-x2y2.py:


伊親像一个垂落來个網仔. 這个程式書頂仔無寫, 我是家己試--出來:


    x0 = np.arange(-3, 3, 0.1)
    x1 = np.arange(-3, 3, 0.1)
    X, Y = np.meshgrid(x0, x1)

    ## This is OK
    Z = X**2 +Y**2 

    ## This have errors
    # ValueError: shape mismatch: objects cannot be broadcast to a single shape
    # Z = function_2(np.array([X, Y])) 
    
    fig = plt.figure()
    ax = fig.gca(projection='3d')

    surf = ax.plot_surface(X, Y, Z)
    plt.show()

總講一句: 用 np.meshgrid() 產生 X, Y 來做底蒂, 才共你的 z = f(x, y) 用落去, 就是這幾步:
    X, Y = np.meshgrid(x0, x1)
    ... 你的函式來生出 Z
    fig = plt.figure()
    ax = fig.gca(projection='3d')
    surf = ax.plot_surface(X, Y, Z)

就會用之.

我拄著一个問題: 用 np.meshgrid(x0, x1) 轉出个 X, Y 無法度用咧咱寫好个 function_2. 毋過 Z = X**2 + Y**2 會使. 這當然是因為咱的 function_2 个寫法無支援, 聽候我有閒研究看按怎解較好勢!

2018年5月3日 星期四

Python Deep Learning 深學筆記 - 微分和揣上細值

←前一篇     後一篇→


微分(bî-hun)个觀念

微分个基礎觀念真簡單: 咱若有一个函式 y=f(x), 若是 x 變化真幼, 咱共伊標做 dx, 按呢 y 嘛綴 dx 變一點點矣, 咱共伊標做 dy, dy 親像這張圖:

y = f(x) 是一个函式, dy/dx 嘛是一个函式. in 兩的是啥物關係咧? 咱舉一个例: y = x**2, 這是真簡單个微積分, 咱先寫伊的表示:

def func_y(x):
     return x**2.0

紲落來, 欲按怎表示微分咧? 若是數學个微分理論, 愛處理連紲性(continuity) 和無限細个問題, 伊的答案號做解析 (analytic), 是完全正確--ê, 比如講: d(x**2)/dx = 2x, 就是解析. 伊佇 x = 0.5 个 dy/dx 就是 2*0.5 =1, 佇 x = 0 个 dy/dx 就是 2*0 = 0.

數值微分(Numerical differential)个觀念


若是電腦, 伊毋捌數彼款抽象思考, 數學家和電腦專家嘛發展出方法來處理微分, 彼就是數值微分(sòo-ta̍t-bî-hun). 咱用 Python 來奕看覓.

Python, 咱用一个真幼个數來逼倚, 比如講 1e-4 是 10 个負 4 次方:

def numerical_diff(f, x):
      h = 1e-4
      d =  (f(x+h) - f(x-h))/(2.0*h)
      return d

按呢共伊敆--起來:

#!/usr/bin/python3
def func_y(x):
    return x**2.0

def numerical_diff(f, x):
    h = 1e-4
    d =  (f(x+h) - f(x-h))/(2.0*h)
    return d

print(numerical_diff(func_y, 0.5))
print(numerical_diff(func_y, 0))

答案是:

0.9999999999998899
0.0

和 1 佮 0 精差是夭壽幼.  這款个方式, 號做數值微分 (numerical differentiation).

切線(tangent line) 和斜率 (slope)


佇 y=f(x), x=5 个彼點个微分, 就是彼个點个斜率 (tshiâ-lu̍t), 咱會使用彼斜率畫一條線迵過彼點, 這條線就是切線(tangent line), 咱來運行書頂的 ch04/gradient_1d.py, 伊的輸出是:

這的程式踮短短 31 逝, 就畫出一个函式佮伊的切線, 咱看伊的後半段:

def tangent_line(f, x):
    d = numerical_diff(f, x)
    print(d)
    y = f(x) - d*x
    return lambda t: d*t + y
     
x = np.arange(0.0, 20.0, 0.1)
y = function_1(x)
plt.xlabel("x")
plt.ylabel("f(x)")

tf = tangent_line(function_1, 5)
y2 = tf(x)

plt.plot(x, y)
plt.plot(x, y2)
plt.show()

函式 tagent_line(f, x) 是一个有影媠氣个寫法, 佇 4 逝就共函式 f 佇 x 點个切線函式生--出來. 伊利用一寡數學推算, 佮 lambda 這種 "生函式" 个方法.

閣紲落來就是使用 matplotlib.pylab 真勥(khiàng)个畫圖功能.

落梯法(lo̍h-thui-huat)


咱共這个函式小改一个, 親像這个程式: gradient_x2.py:

我予伊畫三條切線出來, 佇 x=5 伊的斜率是正个, x=0 伊的斜率拄拄好是 0, 佇 x=-5 伊的斜率是負个. 咱若是凊彩揣一點 x 想欲揣 f(x) 的上細值, 也就是 0 的所在, 欲按怎做咧?

咱會使那呢想: 斜率正个彼點, 咱 x 就向倒爿行. 斜率負个彼點, 咱就向正爿行, 若是伊有上細值, 伊的斜率就會那來若接近 0, 這就是上細點. 這種方法, 號做落梯法(gradient descent method).

共這个想法成做算式:

x = x - lr * df/dx

這就是逐擺 x 愛徙落去个下一步. 斜率正, df/dx > 0, 負號予 -n*df/dx 變細向倒爿. 斜率負 df/dx < 0, -n*df/dx 負負得正向正爿. n 是咱愛斟酌揀个一个數字, 傷大佮傷細攏會歹收縮. 咱用tha̍h-拄仔个 y=0.01*x**2 來做例, 參考: gradient_1_descent.py:

def gradient_descent(f, init_x, lr=0.1, step_num=10000):
    x = init_x

    for i in range(step_num):
        grad = numerical_diff(f, x)
        x -= lr * grad
    return x, f(x)


x1, y1 = gradient_descent(function_1, 10)
print(x1, y1)
x1, y1 = gradient_descent(function_1, -10)
print(x1, y1)

伊的結果是:
2.0202860902402727e-08 4.0815558864183276e-18
-2.0202860902402727e-08 4.0815558864183276e-18

雖然無仝, 毋過攏是倚 0, 彼差別幼微个程度, 會使共伊當做無差別.


佇 Linux 來看GPX 檔案

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