2018年10月21日 星期日

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

  這本由斎藤康毅著作,吳嘉芳翻譯的深度學習書,是為了湊齊購書免運而額外購買的。個人認為這本書不適合一般人閱讀,除了需要有Python語言的基礎知識,也需要有高中數學的能力(需要用到矩陣運算、微分等技巧,相信很多人的高中數學已經全部還給老師了),否則讀起來將會非常吃力;但若您是資訊工程師,或是擁有基本Python語言與高中數學概念的一般人士,皆會因為這本書,徹底了解深度學習,儘管本書以Python語言實作的內容皆為積木般的小規模試驗,難以大規模應用,但藉由這類組裝積木的過程,相當有助於我們了解深度學習技術的內涵,值得推薦。

第一章:Python入門

本書使用的程式

NumPy是計算數值用的函式庫
l   在命令提示字元輸入pip install numpy,下載並安裝NumPy
l   NumPy一維矩陣:
import numpy
x = numpy.array([1.0, 2.0, 3.0])
print(x)
à [1. 2. 3.]
print(type(x))
à <class 'numpy.ndarray'>
# x
矩陣的形狀
print(numpy.ndim(x))
à 1
print(x.shape)
à (3,)
print(x.size)
à 3
print(x.reshape(3, 1))
à [[1.]
 [2.]
 [3.]]
# x
矩陣的元素資料型態
print(x.dtype)
à float64
l   NumPy多維矩陣:
import numpy
x = numpy.array([[1, 2], [3, 4], [5, 6]])
print(x)
à [[1 2]
 [3 4]
 [5 6]]
print(type(x))
à <class 'numpy.ndarray'>
# x
矩陣的形狀
print(numpy.ndim(x))
à 2
print(x.shape)
à (3, 2)
print(x.size)
à 6
print(x.reshape(2, 3))
à [[1 2 3]
 [4 5 6]]
print(x.reshape(2, -1))
à [[1 2 3]
 [4 5 6]]
# x
矩陣的元素資料型態
print(x.dtype)
à int32
l   NumPy算術運算:
import numpy
#
對應元素(Element-wise),元素數量必須相同
x = numpy.array([1.0, 2.0, 3.0])
y = numpy.array([2.0, 4.0, 6.0])
print(x + y)
à [3. 6. 9.]
print(x * y)
à [ 2.  8. 18.]
a = numpy.array([[1, 2], [3, 4]])
b = numpy.array([[2, 4], [6, 8]])
print(a - b)
à [[-1 -2]
 [-3 -4]]
print(a / b)
à [[0.5 0.5]
 [0.5 0.5]]
l   NumPy算術運算-廣播(Broadcast)
import numpy
x = numpy.array([1.0, 2.0, 3.0])
print(x * 10.0)
à [10. 20. 30.]
a = numpy.array([[1, 2], [3, 4]])
b = numpy.array([10, 20])
print(a / b)
à [[0.1 0.1]
 [0.3 0.2]]
l   NumPy矩陣乘積:
import numpy
x = numpy.array([[1, 2], [3, 4], [5, 6]])
y = numpy.array([[7, 8, 9], [10, 11, 12]])
print(numpy.dot(x, y))
à [[ 27  30  33]
 [ 61  68  75]
 [ 95 106 117]]
l   NumPy矩陣轉換:
import numpy
x = numpy.array([[1, 2], [3, 4], [5, 6]])
# x
矩陣轉換為一維矩陣
print(x.flatten())
à [1 2 3 4 5 6]
# x
矩陣轉換為轉置矩陣
print(x.T)
à [[1 3 5]
 [2 4 6]]
# x
矩陣依指定順序轉換
print(x.transpose(1, 0))
à [[1 3 5]
 [2 4 6]]
l   NumPy存取元素:
import numpy
x = numpy.array([[51, 55], [14, 19], [0, 4]])
print(x[0])
à [51 55]
print(x[0][1])
à 55
print(x[0, [0, 1]])
à [51 55]
print(x[0, [1]])
à [55]
print(x[[0, 1]])
à [[51 55]
 [14 19]]
print(x[[1]])
à [[14 19]]
#
取出15以上的值
print(x > 15)
à [[ True  True]
 [False  True]
 [False False]]
print(x[x > 15])
à [51 55 19]
l   NumPy隨機取出元素:
import numpy
x = numpy.array([51, 55, 14, 19, 0, 4])
#
隨機取出3個值,引數replace決定是否重複取出
print(numpy.random.choice(x, 3, replace = True))
à [19 51 19]
#
隨機產出符合均勻分布的3*4矩陣
print(numpy.random.rand(3, 4))
à
[[0.5219055  0.79764018 0.31651997 0.60690332]
 [0.73189512 0.43294656 0.54283599 0.9800219 ]
 [0.60972101 0.29953073 0.39183258 0.22247915]]

Matplotlib是繪圖用的函式庫
l   在命令提示字元輸入pip install matplotlib,下載並安裝Matplotlib
l   繪製圖表:
import numpy as np
import matplotlib.pyplot as plt

#
06,以0.1為單位產生資料
x = np.arange(0, 6, 0.1)
y1 = np.sin(x)
y2 = np.cos(x)

plt.plot(x, y1, label = "sin")
plt.plot(x, y2, linestyle = "--", label = "cos")
plt.legend()
plt.xlabel("x")
plt.ylabel("y")
plt.title("sin & cos")
plt.show()
l   顯示影像:
import matplotlib.pyplot as plt
from matplotlib.image import imread

img = imread("test.png")
plt.imshow(img)
plt.show()

第二章:感知器

感知器
l   (AND)、反及(NAND)、或(OR)使用單層感知器,單層感知器在於只能表現用一條直線劃分的區域:
import numpy as np
def perceptron(x1, x2):
    x = np.array([x1, x2])
    w = np.array([0.3, 0.7])
    b = -0.7
    tmp = np.sum(x*w) + b
    if tmp <= 0:
        return 0
    else:
        return 1
l   互斥或(XOR)必須使用多層感知器:
def XOR(x1, x2):
    s1 = NAND(x1, x2)
    s2 = OR(x1, x2)
    y = AND(s1, s2)
    return y

第三章:神經網路

活化函數(Activation Function)
l   感知器採用了階梯(Step)函數做為活化函數(非線性函數),神經網路則採用了Sigmoid函數、ReLU函數等做為活化函數,用以處理神經元送來的訊號。
l   Step函數:
import numpy as np
def step(x):
    return np.array(x > 0, dtype = np.int)
l   Step函數的圖表:
import numpy as np
import matplotlib.pylab as plt
def step(x):
    return np.array(x > 0, dtype = np.int)
x = np.arange(-5.0, 5.0, 0.1)
y = step(x)
plt.plot(x, y)
plt.show()
l   Sigmoid函數:
import numpy as np
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
l   Sigmoid函數的圖表:
import numpy as np
import matplotlib.pylab as plt
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.show()
l   ReLU函數:
import numpy as np
def relu(x):
    return np.maximum(0, x)
l   ReLU函數的圖表:
import numpy as np
import matplotlib.pylab as plt
def relu(x):
    return np.maximum(0, x)
x = np.arange(-5.0, 5.0, 0.1)
y = relu(x)
plt.plot(x, y)
plt.show()

神經網路輸出層的活化函數
l   神經網路可以用來解決回歸問題與分類問題,回歸問題要使用恆等函數,分類問題要使用Softmax函數。
l   恆等函數是將輸入直接輸出。
l   Softmax函數的輸出是01之間的實數,且輸出總合為1,故Softmax函數的輸出可以解釋為機率:
import numpy as np
def softmax(a):
    #
變數c為防止計算過大數值而產生溢位
    c = np.max(a)
    expA = np.exp(a - c)
    sumExpA = np.sum(expA)
    y = expA / sumExpA
    return y

第四章:神經網路的學習

損失函數(Loss Function)
l   在神經網路的學習中,使用的指標稱作損失函數(Loss Function),一般而言主要使用均方誤差(Mean Squared Error)或交叉熵誤差(Cross Entropy Error)
l   均方誤差,ySoftmax函數輸出,t是訓練資料答案,均方誤差小較適合訓練:
import numpy as np
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
def meanSquaredError(y, t):
    return 0.5 * np.sum((y-t)**2)
print(meanSquaredError(np.array(y), np.array(t)))
l   交叉熵誤差,ySoftmax函數輸出,t是訓練資料答案,交叉熵誤差小較適合訓練:
import numpy as np
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
def crossEntropyError(y, t):
    #
變數delta是微小值,為防止發生負無限大的情形
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))
print(crossEntropyError(np.array(y), np.array(t)))

梯度法
l   神經網路的學習就是要讓損失函數產生的誤差達到最小,使用數值微分以利求極值:
import numpy as np
def numericalGradient(f, x)
    #
變數h是微小值
    h = 1e-4
    grad = np.zero_like(x)
    for idx in range(x.size):
        tmpVal = x[idx]
        x[idx] = tmpVal + h
        fxh1 = f(x)
        x[idx] = tmpVal - h
        fxh2 = f(x)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        x[idx] = tmpVal
    return grad
l   梯度法找出函數最小值,f是要最佳化的函數,initX是預設值,lr是學習率,stepNum是梯度法重複次數:
def gradientDescent(f, initX, lr = 0.01, stepNum = 100)
    x = initX
    for i in range(stepNum):
        grad = numericalGradient(f, x)
        x -= lr * grad
    return x

第五章:誤差反向傳播法

誤差反向傳播法
l   與權重參數有關的損失函數梯度,是利用數值微分計算出來,可改用誤差反向傳播法以較良好的效率計算權重參數梯度。
l   誤差反向傳播法的乘法層:
class MulLayer:
    def __init__(self):
        self.x = None
        self.y = None
    def forward(self, x, y):
        self.x = x
        self.y = y
        out = x * y
        return out
    def backward(self, dout):
        dx = dout * self.y
        dy = dout * self.x
        return dx, dy
l   誤差反向傳播法的加法層:
class AddLayer:
    def __init__(self):
        pass
    def forward(self, x, y):
        out = x + y
        return out
    def backward(self, dout):
        dx = dout * 1
        dy = dout * 1
        return dx, dy
l   誤差反向傳播法的Softmax-with-Loss層:
class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None
        self.y = None
        self.t = None
    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)
        self.loss = crossEntropyError(self.y, self.t)
        return self.loss
    def backward(self, dout = 1):
        batchSize = self.t.shape[0]
        dx = (self.y – self.t) / batchSize
        return dx

第六章:與學習有關的技巧

取代準確率梯度下降法(Stochastic Gradient Descent, SGD)
l   SGD形成無效率探索路徑的根本原因在於,梯度方向原本就不是指向最小值,因此本章介紹MomentumAdaGradAdam等三種取代SGD的方法:
一、Momentum-特色為使用物體速度的物理定律。
二、AdaGrad-特色為適應各個參數,一邊調整學習率一邊學習。
三、Adam-相當複雜,但直覺來說特色為融合上述兩種手法。

Batch Normalization演算法
l   在神經網路中加入Batch Normalization演算法,強制調整活性化的分布:
優點一-增加學習率,可以快速學習。
優點二-不會過度依賴XavierHe等權重預設值。
優點三-控制過度學習,可以減少Weight DecayDropout等方法的必要。

第七章:卷積神經網路

卷積神經網路(Convolutional Neural Network, CNN)
l   卷積神經網路的出現可以輸出入三維形狀的空間資料,比較一般神經網路與卷積神經網路組成的層級:
一般神經網路-Affine-ReLU - Affine-ReLU - ... - Affine-Softmax
卷積神經網路-Convolution-ReLU-Pooling - Convolution-ReLU-Pooling - ... - Affine-ReLU - Affine-Softmax,其中Pooling層用以縮小垂直與水平空間的運算,有時會省略

卷積運算
l   執行卷積運算,不使用for迴圈,改使用im2col()函數比較有效率,im2col()函數可以針對權重,輕鬆展開輸入資料,在CaffeChainer等深度學習框架中皆有im2col()函數,函數詳細內容可見本書作者Github,路徑為common/util.py
l   卷積層(Convolution)
import numpy as np
class Convolution:
    def __init__(self, W, b, stride = 1, pad = 0):
        self.W = W
        self.b = b
        self.stride = stride
        self.pad = pad
    def forward(self, x):
        FN, FC, FH, FW = self.W.shape
        N, C, H, W = x.shape
        #
計算輸出資料長寬
        out_h = int(1 + (H + 2 * self.pad - FH) / self.stride)
        out_w = int(1 + (W + 2 * self.pad - FW) / self.stride)
        #
展開輸入資料
        col = im2col(x, FH, FW, self.stride, self.pad)
        #
展開濾鏡,設定成-1,可以自動整合元素數量
        col_W = self.W.reshape(FN, -1).T
        out = np.dot(col, col_W) + self.b
        # transpose()
是更換多維陣列各軸順序的函數
        out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
        return out
l   池化層(Pooling)
import numpy as np
class Pooling:
    def __init__(self, pool_h, pool_w, stride = 1, pad = 0):
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stride = stride
        self.pad = pad
    def forward(self, x):
        N, C, H, W = x.shape
        #
計算輸出資料長寬
        out_h = int(1 + (H - self.pool_h) / self.stride)
        out_w = int(1 + (W - self.pool_w) / self.stride)
        #
展開、調整輸入資料形狀
        col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
        col = col.reshape(-1, self.pool_h * self.pool_w)
        #
最大值
        out = np.max(col, axis = 1)
        #
調整輸出資料形狀
        out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)
        return out

沒有留言:

張貼留言