計算圖
用計算圖求解
-
eg.1 太郎在超市買了2個100日元/個的蘋果,消費稅10%,計算支付金額的過程可以表示成
也可以表示成如下形式
-
eg.2 太郎買了2個100日元/個的蘋果和3個150日元/個的橘子,消費稅10%,計算支付金額的過程
圖中新增了加法節(jié)點,構(gòu)建計算圖后從左向右進行計算,到達最后一個節(jié)點后計算結(jié)束
- 綜上,用計算圖解題時需要按照如下流程
- 構(gòu)建計算圖
- 在計算圖上自左向右進行計算
這里"自左向右進行計算"叫做正向傳播,從計算圖的出發(fā)點到結(jié)束點的傳播.
如果從圖中自右向左看的方式叫做"反向傳播",反向傳播可以用于導數(shù)計算
局部計算
"局部"表示與自己相關(guān)的范圍,無論全局其他地方發(fā)生了什么,局部計算只能根據(jù)和自己相關(guān)的信息輸出接下來的結(jié)果.
eg.3 太郎買了2個蘋果很其他很多東西
這里各個節(jié)點的計算都是局部計算,意味著圖中(1)處只需要關(guān)心4000和200的求和運算,至于4000是怎么來的不需要關(guān)心.所以計算圖可以集中精力于局部計算,也意味著可以將其他部分的運算結(jié)果作為整體緩存起來.這里我們設蘋果的單價為x,支付金額為L,偏導數(shù)可以通過反向傳播求解
結(jié)果是2.2
鏈式法則
計算圖的反向傳播
假設存在函數(shù)
將這個局部導數(shù)乘以上游傳過來的值(E),然后傳給前面的節(jié)點
鏈式法則的定義
復合函數(shù):可以認為由多個函數(shù)構(gòu)成的函數(shù)
復合函數(shù)求導法則:
現(xiàn)在我們將這個函數(shù)用計算圖表示
即
由于,可知
反向傳播
加法節(jié)點的反向傳播
對于,,即加法節(jié)點的反向傳播只乘以1,輸入值會原封不到地(反向)傳給下一個節(jié)點
一個具體的例子,如果某次計算圖一個加法節(jié)點的正向傳播如左,從上游兩個節(jié)點分別傳來10和15正向傳播傳遞15給下游;反向傳播如右,上游傳來1.3,則改節(jié)點將1.3和1.3傳給下游2個節(jié)點
乘法節(jié)點的反向傳播
這里我們考慮z=xy,導數(shù)如下
乘法節(jié)點進行反向傳播計算時需要傳入正向傳播時的輸入信號值
指數(shù)函數(shù)和對數(shù)函數(shù)的反向傳播
指數(shù)函數(shù)
對數(shù)函數(shù)
反向傳播計算蘋果的例子
如上可知,對于總支付金額L,蘋果價格的偏導是2.2,蘋果個數(shù)的偏導是110,消費稅的偏導是200
對于蘋果和橘子的例子,正向傳播和反向傳播的流程如下
簡單層的實現(xiàn)
class MultiLayer:
def __init__(self):
self.x = None
self.y = None
def forward(self, x, y):
self.x = x
self.y = y
return x * y
def backward(self, dout):
dx = dout * self.y
dy = dout * self.x
return dx, dy
class AddLayer:
def __init__(self):
self.x = None
self.y = None
def forward(self, x, y):
self.x = x
self.y = y
return x + y
def backward(self, dout):
dx = dout
dy = dout
return dx, dy
激活函數(shù)層的實現(xiàn)
ReLU
class Relu:
def __init__(self):
self.x = None
self.mask = None
def forward(self, x):
self.mask = (x <= 0)
return x * (1 - self.mask)
def backward(self, dout):
return dout * (1 - self.mask)
sigmoid函數(shù)
實現(xiàn):
class Sigmoid:
def __init__(self):
self.y = None
def forward(self, x):
y = (1 + np.exp(-x)) ** (-1)
self.y = y
return y
def backward(self, d_out):
dx = d_out * (1. - self.y) * self.y
return dx
Affine層的實現(xiàn)
矩陣仿射變換
的計算圖如下(假設n=2,o=3)
偏導如下:
帶反向傳播的計算圖
batch版本的仿射變換
class Affine:
def __init__(self, W, b):
self.W = W
self.b = b
self.X = None
self.dW = None
self.db = None
def forward(self, X):
self.X = X
return X.dot(self.W) + self.b
def backward(self, d_out):
dx = d_out.dot(self.W.T)
dW = self.X.T.dot(d_out)
db = np.sum(d_out, axis=0)
self.dW = dW
self.db = db
return dx
softmax-with-loss
一個典型的2層神經(jīng)網(wǎng)絡結(jié)構(gòu)如下
softmax和entropy層的正向和反向傳播結(jié)構(gòu)如下
代碼實現(xiàn):
class SoftmaxWithLoss:
def _softmax(x):
if x.ndim == 2:
x = x.T
x = x - np.max(x, axis=0)
y = np.exp(x) / np.sum(np.exp(x), axis=0)
return y.T
x = x - np.max(x) # 溢出對策
return np.exp(x) / np.sum(np.exp(x))
def _cross_entropy_error(y, t):
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
# 監(jiān)督數(shù)據(jù)是one-hot-vector的情況下趁舀,轉(zhuǎn)換為正確解標簽的索引
if t.size == y.size:
t = t.argmax(axis=1)
batch_size = y.shape[0]
return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
def __init__(self):
self.loss = None
self.y = None
self.t = None
def forward(self,x,t):
self.t = t
self.y = self._softmax(x)
self.loss = self._cross_entropy_error(self.y,self.t)
def backward(self,dout=1):
batch_size = self.t.shape[0]
dx = (self.y - self.t) / batch_size
## 反向傳播實現(xiàn)二層神經(jīng)網(wǎng)絡