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

前言

我们在感知机中采用了梯度下降的方式实现了参数的优化(手动实现感知机),但是感知机对于较为复杂的问题就显得力不从心了,所以我们需要用到多层感知机,即神经网络。此时的梯度下降就需要通过反向传递来实现了

最简单的反向传递

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

乘法层

公式
我们假设x*y=z, 损失函数为L,那么我们分别对z求关于x和y的偏导得
∂ z ∂ x = y \frac{ \partial z}{\partial x}=y xz=y
∂ z ∂ y = x \frac{\partial z}{\partial y}=x yz=x
得到结论乘法层的偏导为两个乘数互换位置

∂ L ∂ z ∂ z ∂ x = ∂ L ∂ z ⋅ y \frac{ \partial L}{\partial z}\frac{ \partial z}{\partial x}=\frac{ \partial L}{\partial z} \cdot y zLxz=zLy
∂ L ∂ z ∂ z ∂ y = ∂ L ∂ z ⋅ x \frac{ \partial L}{\partial z}\frac{\partial z}{\partial y}=\frac{ \partial L}{\partial z} \cdot x zLyz=zLx
代码实现
在反向传递时要遵循链式法则,所以在这里我们每个偏导都要乘以后面一层反向传递来的偏导数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, 损失函数为L那么我们分别对z求关于x和y的偏导得
∂ z ∂ x = 1 \frac{ \partial z}{\partial x}=1 xz=1
∂ z ∂ y = 1 \frac{\partial z}{\partial y}=1 yz=1

∂ L ∂ x = ∂ L ∂ z ∂ z ∂ x = ∂ L ∂ z \frac{\partial L}{\partial x}= \frac{\partial L}{\partial z} \frac{\partial z}{\partial x}= \frac{\partial L}{\partial z} xL=zLxz=zL

∂ L ∂ y = ∂ L ∂ z ∂ z ∂ y = ∂ L ∂ z \frac{\partial L}{\partial y}= \frac{\partial L}{\partial z} \frac{\partial z}{\partial y}= \frac{\partial L}{\partial z} yL=zLyz=zL
代码实现

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 = { x , x > 0 0 , x < = 0 y=\begin{cases} x, & {x>0} \\ 0, & {x<=0} \end{cases} y={x,0,x>0x<=0
d y d x = { 1 , x > 0 0 , x < = 0 \frac{dy}{dx}=\begin{cases} 1, & {x>0} \\ 0, & {x<=0} \end{cases} dxdy={1,0,x>0x<=0

d L d x = d L d y d y d x = { d L d y , x > 0 0 , x < = 0 \frac{d L}{d x}= \frac{dL}{dy}\frac{dy}{dx}=\begin{cases} \frac{dL}{dy}, & {x>0} \\ 0, & {x<=0} \end{cases} dxdL=dydLdxdy={dydL,0,x>0x<=0
代码

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 = 1 1 + e − x y=\frac{1}{1+e^{-x}} y=1+ex1
d y d x = e − x ( 1 + e − x ) 2 = 1 1 + e − x ⋅ e − x 1 + e − x = 1 1 + e − x ⋅ ( 1 − 1 1 + e − x ) = y ⋅ ( 1 − y ) \frac{dy}{dx}=\frac{e^{-x}}{(1+e^{-x})^2}=\frac{1}{1+e^{-x}} \cdot\frac{e^{-x}}{1+e^{-x}} \\ =\frac{1}{1+e^{-x}} \cdot(1-\frac{1}{1+e^{-x}})=y \cdot (1-y) dxdy=(1+ex)2ex=1+ex11+exex=1+ex1(11+ex1)=y(1y)

d L d y d y d x = d L d y ⋅ y ⋅ ( 1 − y ) \frac{dL}{dy} \frac{dy}{dx}=\frac{dL}{dy} \cdot y \cdot (1-y) dydLdxdy=dydLy(1y)
代码

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
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艾醒(AiXing-w)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值