“深度学习”学习日记。误差反向传播法--Affine/Softmax层的实现

2023.1.17

Affine层:

在神经网络的正向传播中,为了计算加权信号的总和,使用矩阵乘积运算。

比如:

import numpy as np

x = np.arange(6).reshape(2, 3)  # (2,3)
w = np.arange(6).reshape(3, 2)  # (3,2)
b = np.arange(4).reshape(2, 2)  # (2,2)

y = np.dot(x, w) + b  # 要符合线性代数运算规则

print(y)
# [[10 14]
#  [30 43]]

然后y传递到激活函数层转换后在传递给下一层神经网络。

在深度学习中,把正向传播中进行的矩阵乘积运算称为“仿射变换”。称仿射变换的处理实现称为Affine层。

仿射变换:一次线性变换(加权乘积)和一次平移(偏置)

Affine层的计算图:观察正向传播和反向传播,反向传播时,我们要结合线性代数考虑矩阵的形状,求出“dot 节点”的推导式子。

“深度学习”学习日记。误差反向传播法--Affine/Softmax层的实现_第1张图片

1式: \frac{\partial L}{\partial x} = \frac{\partial L}{\partial Y} \cdot W^{T};

2式:   \frac{\partial L}{\partial W} =x^{T} \cdot \frac{\partial L}{\partial Y} ;

3式:   \frac{\partial L}{\partial Y}

关于加法节点的传播在这篇文章:https://blog.csdn.net/m0_72675651/article/details/128695488

代码实现:

import numpy as np


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
        out = np.dot(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


x = np.arange(6).reshape(2, 3)
w = np.arange(6).reshape(3, 2)
b = np.arange(4).reshape(2, 2)
a = Affine(w, b)
y = a.forward(x)
y1 = a.backward(y)
print(y, '\n', y1)

输出结果:

[[10 14]
 [30 43]] 
 [[ 14  62 110]
 [ 43 189 335]]

Softmax层: 

我们都知道soft函数会将输入的值正规化后再输出。回忆一下Softmax函数:y_{k}=\frac{\exp (a_{k})}{\sum_{k=1}^{n}exp(a_{k})}

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))

通过MNIST数据来演示: 

“深度学习”学习日记。误差反向传播法--Affine/Softmax层的实现_第2张图片

输入图像经过Affine层和ReLU层进行转换,因为MNIST数据有10类,输出层设计为Softmax层有10个输出,上层输入经过这里会有正规化过程。

在神经网络中,进行的处理有推理和学习两个阶段,其中推理阶段通常不需要softmax层,比如在上图中MNIST识别中以最后一个Affine层作为结果,其没有正规化,将这样的结果称为“得分”,也就是说,当神经网络的推理只需要一个答案(一个正确解标签)的情况下,我们只对“得分”的最大值感兴趣,所以不需要softmax层;

当我们在学习阶段时,我们需要softmax层,因为我们需要计算损失函数优化神经网络模型,我们就需要监督数据标签t和输出正确解标签y参与计算损失函数(y需要正规化) ;

Softmax-with-loss层: 

接下来实现Softmax层 ,但是这包含到损失函数,所以结合交叉熵误差函数,称为Softmax-with-loss层;交叉熵误差:y=-\sum_{k}^{}t_{k}\log y_{k} 。

(交叉熵误差函数参考这篇文章:https://blog.csdn.net/m0_72675651/article/details/128592167)

我们先用计算图先观察: 

为了简化而细致的展现,我们假设最后一层Affine层只传递给softmax层3个得分;

“深度学习”学习日记。误差反向传播法--Affine/Softmax层的实现_第3张图片

“exp 节点”:在正向传播时表示y=exp(x),由指数函数的数学解析式可得  \frac{\partial y}{\partial x}=exp\left ( x \right ) ; 

我们可以观察到最后传播得到了\left ( y_{1} -t_{1}\right ) 的结果,是softmax层输出结果与监督数据的差分。因此,这里有一个重要的性质,神经网络会把这样的差分传递给前面的层。

假如这里有一个监督标签(0,1,0),而softmax层的输出结果是(0.1,0.1,0.8),显然这是没有正确分类,而他的差分是(0.1,-0.9,0.8),这个大差分会向前面传播的层传播,所以前面的层也会学习到这个“大”内容;假如这里有一个监督标签(0,1,0),而softmax层的输出结果是(0.005,0.99,0.05),显然这是正确分类,而他的差分是(0.005,-0.01,0.05),这个小差分会向前面传播的层传播,所以前面的层也会学习到这个“小”内容;

import numpy as np
 
 
def cross_entropy_error(y, t):
    delta = 1e-7
    return -1 * np.sum(t * np.log(y + delta))
 
 
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))
 
 
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]
        dx = (self.y - self.t) / batch_size
 
        return dx

你可能感兴趣的:(深度学习,人工智能,python)