Python深度学习基础(二)——反向传递概念透彻解析以及Python手动实现

前言

我们在感知器中使用梯度下降来优化参数(手动实现感知器),但是感知器对于更复杂的问题是不够的,所以我们需要使用多层感知器,也就是神经网络。这时就需要通过反向传递来实现梯度下降。

最简单的回传

我们在感知器中执行的最简单的操作是加法和乘法。这里我们先以乘除法为例,实现最简单的反向传递

乘法层

公式
我们假设x*y=z, 那么我们分别对z求关于x和y的偏导得
%5Cfrac%7B%20%5Cpartial%20z%7D%7B%5Cpartial%20x%7D%3Dy
%5Cfrac%7B%5Cpartial%20z%7D%7B%5Cpartial%20y%7D%3Dx
得出乘法层的偏导数是两个乘法器位置的交换
代码
在反向传递时要遵循链式法则,所以在这里我们每个偏导都要乘以后面一层反向传递来的偏导数dout才是应该传递给上一层的偏导数,下同。

class MulLayer:
    def __init__(self):
        self.x = None
        self.y = None

    def forward(self, x, y):
        self.x = x
        self.y = y                
        out = x * y

        return out

    def backward(self, dout):
        dx = dout * self.y
        dy = dout * self.x

        return dx, dy

添加剂层

公式
我们假设x+y=z, 那么我们分别对z求关于x和y的偏导得
%5Cfrac%7B%20%5Cpartial%20z%7D%7B%5Cpartial%20x%7D%3D1
%5Cfrac%7B%5Cpartial%20z%7D%7B%5Cpartial%20y%7D%3D1
代码

class AddLayer:
    def __init__(self):
        pass

    def forward(self, x, y):
        out = x + y

        return out

    def backward(self, dout):
        dx = dout * 1
        dy = dout * 1

        return dx, dy

激活函数的回传

Relu层

公式
y%3D%5Cbegin%7Bcases%7D%20x%2C%20%26%20%7Bx%3E0%7D%20%5C%5C%200%2C%20%26%20%7Bx%3C%3D0%7D%20%5Cend%7Bcases%7D
%5Cfrac%7Bdy%7D%7Bdx%7D%3D%5Cbegin%7Bcases%7D%201%2C%20%26%20%7Bx%3E0%7D%20%5C%5C%200%2C%20%26%20%7Bx%3C%3D0%7D%20%5Cend%7Bcases%7D
代码

class Relu:
    def __init__(self):
        self.mask = None

    def forward(self, x):
        self.mask = (x <= 0)
        out = x.copy()
        out[self.mask] = 0

        return out

    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout

        return dx

Sigmoid层

公式
y%3D%5Cfrac%7B1%7D%7B1%2Be%5E%7B-x%7D%7D
%5Cfrac%7Bdy%7D%7Bdx%7D%3D%5Cfrac%7Be%5E%7B-x%7D%7D%7B%281%2Be%5E%7B-x%7D%29%5E2%7D%3D%5Cfrac%7B1%7D%7B1%2Be%5E%7B-x%7D%7D%20%5Ccdot%5Cfrac%7Be%5E%7B-x%7D%7D%7B1%2Be%5E%7B-x%7D%7D%20%5C%5C%20%3D%5Cfrac%7B1%7D%7B1%2Be%5E%7B-x%7D%7D%20%5Ccdot%281-%5Cfrac%7B1%7D%7B1%2Be%5E%7B-x%7D%7D%29%3Dy%20%5Ccdot%20%281-y%29
代码

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

带交叉熵误差的SoftMax层

公式
这个函数反向传递的本质是返回实际值和预测值的差值
代码

def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
        
    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 # softmax的输出
        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

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2022年3月18日 下午12:59
下一篇 2022年3月18日 下午1:16

相关推荐