计算图
用计算图求解
-
eg.1 太郎在超市买了2个100日元/个的苹果,消费税10%,计算支付金额的过程可以表示成
太郎买苹果
也可以表示成如下形式
太郎买苹果2 -
eg.2 太郎买了2个100日元/个的苹果和3个150日元/个的橘子,消费税10%,计算支付金额的过程
太郎买苹果和橘子
图中新增了加法节点,构建计算图后从左向右进行计算,到达最后一个节点后计算结束
- 综上,用计算图解题时需要按照如下流程
- 构建计算图
- 在计算图上自左向右进行计算
这里"自左向右进行计算"叫做正向传播,从计算图的出发点到结束点的传播.
如果从图中自右向左看的方式叫做"反向传播",反向传播可以用于导数计算
局部计算
"局部"表示与自己相关的范围,无论全局其他地方发生了什么,局部计算只能根据和自己相关的信息输出接下来的结果.
eg.3 太郎买了2个苹果很其他很多东西
太郎买了2个苹果很其他很多东西
这里各个节点的计算都是局部计算,意味着图中(1)处只需要关心4000和200的求和运算,至于4000是怎么来的不需要关心.所以计算图可以集中精力于局部计算,也意味着可以将其他部分的运算结果作为整体缓存起来.这里我们设苹果的单价为x,支付金额为L,偏导数可以通过反向传播求解
反向传播计算支付金额对苹果单价的偏导数结果是2.2
链式法则
计算图的反向传播
假设存在函数
将这个局部导数乘以上游传过来的值(E),然后传给前面的节点
链式法则的定义
复合函数:可以认为由多个函数构成的函数
复合函数求导法则:
现在我们将这个函数用计算图表示
即
由于,可知
反向传播
加法节点的反向传播
对于,,即加法节点的反向传播只乘以1,输入值会原封不到地(反向)传给下一个节点
一个具体的例子,如果某次计算图一个加法节点的正向传播如左,从上游两个节点分别传来10和15正向传播传递15给下游;反向传播如右,上游传来1.3,则改节点将1.3和1.3传给下游2个节点
加法节点正反向传播的具体例子
乘法节点的反向传播
这里我们考虑z=xy,导数如下
乘法节点反向传播的具体例子
乘法节点进行反向传播计算时需要传入正向传播时的输入信号值
指数函数和对数函数的反向传播
指数函数
对数函数
反向传播计算苹果的例子
反向传播计算苹果的例子如上可知,对于总支付金额L,苹果价格的偏导是2.2,苹果个数的偏导是110,消费税的偏导是200
对于苹果和橘子的例子,正向传播和反向传播的流程如下
苹果和橘子的正向传播和反向传播
简单层的实现
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
激活函数层的实现
ReLU
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函数
实现:
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层的实现
矩阵仿射变换
的计算图如下(假设n=2,o=3)
偏导如下:
带反向传播的计算图
带反向传播的计算图
batch版本的仿射变换
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层神经网络结构如下
典型的2层神经网络
softmax和entropy层的正向和反向传播结构如下
softmax和entropy层结构
代码实现:
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)
# 监督数据是one-hot-vector的情况下,转换为正确解标签的索引
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
## 反向传播实现二层神经网络
网友评论