昨天從母校圖書館借到下面這本齋藤康毅的好書 :
Source : 博客來
這本書上回借時看完前兩章, 這次我打算邊看邊測編寫筆記, 這樣才能紮實地吸收這本書的精華. 這本書的目標是只使用少數必要函式庫 (Numpy & Matplotlib) 從零打造精簡版的深度學習框架 DeZero, 與 PyTorch, TensorFlow 一樣具有 Define-by-Run 功能 (定義好運算圖連結即可執行), 不僅可在 Colab 雲端跑, 也可以在手機上跑.
DeZero 已在 PyPi 上架, 如果要直接使用 DeZero 可用 pip install 安裝 (先不用) :
pip install dezero
打造 DeZero 需要用到 Numpy 與 Matplotlib :
pip install numpy
pip install matplotlib
此書將打造 DeZero 框架的 60 個步驟分成五個階段 (一章就是一個步驟) :
- 步驟 1~10 : 建立 DeZero 的基礎 (自動微分)
- 步驟 11~24 : 使用 Pythonized 語法撰寫 DeZero
- 步驟 25~36 : 計算二階微分以進行反向傳播
- 步驟 37~51 : 用 DeZero 建立類神經網路
- 步驟 52~60 : 用 DeZero 搭建 CNN 與 RNN 模型
總之, 透過這種徒手打造深度學習框架的過程, 可以深刻理解深度學習的原理與框架結構.
以下是 Ch1~2 關於建立變數與函數的筆記.
1. 建立變數 :
變數就是資料的容器 (箱子), 在 DeZero 中使用名為 Variable 的類別來作為存放變數的容器 :
class Variable:
def __init__(self, data):
self.data=data
這是標準的 Python 類別用法, 類別名稱首字元大寫符合 Python PEP8 原則, 函式 __init__() 用來對物件 (類別的實體) 進行初始化, 傳入參數 self 代表物件本身, 後面跟著的是使用者參數, 此處 data 為一個 Numpy 陣列 (ndarray 物件), 所以一個 Variable 物件就是用來儲存 ndarray 物件用的容器 :
>>> class Variable:
def __init__(self, data):
self.data=data
先建立一個 Numpy 陣列 :
>>> import numpy as np
>>> data=np.array(1.0)
>>> data
array(1.0)
>>> type(data)
<class 'numpy.ndarray'>
將陣列傳給 Variable() 建構式, 它會呼叫 __init()__ 初始化物件 :
>>> x=Variable(data)
>>> x.data
array(1.0)
可見 Numpy 陣列 data 已存入 x 物件的 data 屬性中.
1. 建立函數 :
在數學上, 函數用來描述兩組變數之間的對應關係, 在 DeZero 框架裡, 則是定義一個名為 Function 的類別來實作函數, 它會定義一個 __call__() 函式, 接受一個 Variable 物件變數輸入, 然後將演算結果 (另一個 Variable 變數) 傳回作為輸出, 例如下面的平方函數 :
class Function:
def __call__(self, input):
x=input.data
y=x ** 2
output=Variable(y)
return output
__call__() 為 Python 特殊函式, 定義此方法的類別可以把它的實體名稱當作函式直接呼叫, 例如 :
>>> x=Variable(10) # 建立 Variable 變數物件
>>> f=Function() # 建立 Function 函數物件
>>> type(f)
<class '__main__.Function'>
>>> y=f(x) # 呼叫函式物件並傳入資料
>>> type(y) # 傳回 Variable 物件
<class '__main__.Variable'>
>>> y.data
100
>>>
不過上面這個 Function 類別被固定做平方運算, 應該將特定運算 (例如平方) 交給 Function 的子類別 Square 的函式 forward() 來執行, 這樣 Function 就可泛化為可執行任何函數的類別, 改寫如下 :
class Function:
def __call__(self, input):
x=input.data
y=self.forward(x)
output=Variable(y)
return output
def forward(self, x): # 讓子類別覆蓋的空方法
raise NotImplementedError() # 預防子類別沒有實作此方法時出現例外
class Square(Function): # 繼承 Function 並覆蓋其 forward() 方法
def forward(self, x):
return x ** 2
>>> x=Variable(10)
>>> f=Square() # 建立函數的類別
>>> y=f(x) # 建立函數類別的實體並傳入 x 求值
>>> type(f)
<class '__main__.Square'> # f 是 Square 物件
>>> type(y)
<class '__main__.Variable'> # f(x) 傳回值為 Variable 物件
>>> y.data
100
結果相同, 但此處的 Function 比上面更一般化了, 我們可以定義任何函數類別掛接到 Function 來求值, 例如求立方的函數類別 :
>>> class Cube(Function): # 繼承 Function 並覆蓋其 forward() 方法
def forward(self, x):
return x ** 3
>>> x=Variable(10)
>>> f=Cube()
>>> y=f(x)
>>> y.data
1000
沒有留言 :
張貼留言