天天看点

深度学习入门(基于python实现)--第五章 误差反向传播 02

# _*_ 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

           

继续阅读