# _*_ coding:UTF-8 _*_
# 文件名:Error_BackPropagation_02.py
# 开发工具:PyCharm
import numpy as np
"""
在01中讲到了计算节点的反向传播的计算方式,下面来说说激活函数节点的反向传播计算方式
对于ReLU函数(x大于0时输出x,小于0时输出0),我们对其求导数可以很容易得到当大于0时导数值为1,小于等于0时导数值为0
所以当上游传过来导数时,若这个节点正向传播时的x大于0,那么导数原封不动向下游传过去,反之为0
x大于0 x小于0
x-------》 -------》y x-------》 -------》0
relu relu
d《------ 《------ 导数值d 0《------ 《------ 导数值d
"""
class Relu:
"""Relu的反向传播和正向传播实现"""
def __init__(self):
self.mask = None
def forward(self, x):
self.mask = (x <= 0) # 小于0的地方会变True,大于0的地方会变False
out = x.copy()
out[self.mask] = 0 # True的地方会变0,也就是小于0的地方会变0
return out
def backward(self, dout):
dout[self.mask] = 0 # 将原来小于0的节点的传来导数值变为0,其他的不变
dx = dout
return dx
"""
sigmoid函数的反向传播
x-------》 -------》y
sigmoid
d*y*(1-y)《------ 《------ 导数值d
"""
def sigmoid(x):
return 1 / (1 + np.exp(-x))
class Sigmoid:
def __init__(self):
self.out = None
def forward(self, x):
out = sigmoid(x)
self.out = out
return out
def backward(self, dout):
dx = dout * (1.0 - self.out) * self.out
return dx
"""
Affine层实现反向传播
对于矩阵乘法顺便加上偏置的节点
"""
class Affine:
def __init__(self, W, b):
"""
初始化矩阵
:param W: 权值
:param b: 偏置
"""
self.W = W
self.b = b
self.x = None
self.dW = None
self.db = None
def forward(self, x):
"""
前项传播
:param x: 输入,矩阵
:return: 矩阵乘法和偏置相加后结果
"""
x = x.reshape(x.shape[0], -1)
self.x = x
out = np.dot(self.x, self.W) + self.b
return out
def backward(self, dout):
dx = np.dot(dout, self.W.T)
self.dW = np.dot(self.x.T, dout)
self.db = np.sum(dout, axis=0)
return dx
"""
实现soft-with-loss层
"""
def softmax(x):
x = x - np.max(x, axis=-1, keepdims=True)
return np.exp(x) / np.sum(np.exp(x), axis=-1, keepdims=True)
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
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 = cross_entropy_error(self.y, self.t)
return self.loss
def backward(self, dout=1):
batch_size = self.t.shape[0]
if self.t.size == self.y.size:
dx = (self.y - self.t) / batch_size
else:
dx = self.y.copy()
dx[np.arange(batch_size), self.t] -= 1
dx = dx / batch_size
return dx