美文网首页神经网络
Lecture 4 介绍神经网络

Lecture 4 介绍神经网络

作者: HRain | 来源:发表于2019-01-09 18:23 被阅读20次

    我们已知梯度计算有数值法解析式法,当损失函数非常复杂时我们可能无法直接写出损失函数L对参数W的梯度计算表达式,所以可以引入中间节点,当前节点的梯度计算之和当前节点有关。例如下图是之前的损失函数计算图,卷积神经网络会更复杂。实践为了进行参数更新,通常也只计算参数W,b的梯度。然而x_i的梯度有时仍然是有用的:比如将神经网络所做的事情可视化便于直观理解的时候,就能用上。(作业中会要求写这段代码。)

    图1 损失函数L计算图

    本课重点

    • 反向传播算法

    • 神经网络

    1 反向传播算法

    反向传播是利用链式法则递归计算表达式的梯度的方法。理解反向传播过程及其精妙之处,对于理解、实现、设计和调试神经网络非常关键。

    1.1 标量的反向传播

    1.1.1 引例

    先从简单的例子开始说明,比如f(x,y,z) = (x + y) z。初值 x = -2, y = 5, z = -4。虽然这个表达足够简单,可以直接微分,但是在此使用一种有助于直观理解反向传播的方法。下图是整个计算的线路图,绿字部分是函数值,红字是梯度。(梯度是一个向量,但通常将对x的偏导数称为x上的梯度。)

    图2 梯度计算线路图
    解释如下:

    公式可以分为两部分,q = x + yf = q z。这两部分都很简单可以直接写出梯度表达式:

    fqz的乘积, 所以 \frac{\partial f}{\partial q} = z=-4; \frac{\partial f}{\partial z} = q=3
    qxy 相加,所以 \frac{\partial q}{\partial x} = 1,\frac{\partial q}{\partial y} = 1

    然而我们并不关心 q 上的梯度,因为 \frac{\partial f}{\partial q} 没有用处。我们关心的只有 f 对于 x,y,z 的梯度。链式法则指出将这些梯度表达式链接起来的正确方式是相乘,比如

    \frac{\partial f}{\partial x} = \frac{\partial f}{\partial q} \frac{\partial q}{\partial x} =-4 同理,\frac{\partial f}{\partial y} =-4

    还有一点是 \frac{\partial f}{\partial f}=1

    前向传播从输入计算到输出(绿色),反向传播从尾部开始,根据链式法则递归地向前计算梯度(显示为红色),一直到网络的输入端。可以认为,梯度是从计算链路中回流

    上述计算的代码如下:

    # 设置输入值
    x = -2; y = 5; z = -4
    
    # 进行前向传播
    q = x + y # q 是 3
    f = q * z # f 是 -12
    
    # 进行反向传播:
    # 首先回传到 f = q * z
    dfdz = q # df/dz = q, 所以关于z的梯度是3
    dfdq = z # df/dq = z, 所以关于q的梯度是-4
    # 现在回传到q = x + y
    dfdx = 1.0 * dfdq # dq/dx = 1. 这里的乘法是因为链式法则。所以df/dx是-4
    dfdy = 1.0 * dfdq # dq/dy = 1.所以df/dy是-4
    
    '''一般可以省略df'''
    

    1.1.2 直观理解反向传播

    反向传播是一个优美的局部过程。以下图为例,在整个计算线路图中,会给每个门单元(也就是f结点)一些输入值x,y并立即计算这个门单元的输出值z,和当前节点输出值关于输入值的局部梯度(local gradient)\frac{\partial z}{\partial x}\frac{\partial z}{\partial y}。前向传播中,门单元的这两个计算是完全独立的,它不需要知道计算线路中的其他单元的计算细节。然而,一旦前向传播完毕,在反向传播的过程中,门单元将获得整个网络的最终输出值在自己的输出值上的梯度\frac{\partial L}{\partial z}。链式法则指出,门单元应该将回传梯度乘以它的输出对输入的局部梯度,从而得到整个网络的输出对该门单元的每个输入值的梯度\frac{\partial L}{\partial x}\frac{\partial L}{\partial y}。这两个值又可以作为前面门单元的回传梯度。

    图3 反向传播门单元 因此,反向传播可以看做是门单元之间在通过梯度信号相互通信,只要让它们的输入沿着梯度方向变化,无论它们自己的输出值在何种程度上升或降低,都是为了让整个网络的输出值更高。比如引例中x,y梯度都是-4,所以让x,y减小后,q的值虽然也会减小,但最终的输出值f会增大。(当然损失函数要的是最小。)
    加法门、乘法门和max门

    引例中用到了两种门单元:加法和乘法。
    乘法求偏导:f(x,y) = x y \hspace{0.1in} \rightarrow \hspace{0.1in} \frac{\partial f}{\partial x} = y \hspace{0.1in} \frac{\partial f}{\partial y} = x加法求偏导:f(x,y) = x + y \hspace{0.1in} \rightarrow \hspace{0.1in} \frac{\partial f}{\partial x} = 1 \hspace{0.1in} \frac{\partial f}{\partial y} = 1除此之外,取最大值操作也是常用的:f(x,y) = \max(x, y) \hspace{0.1in} \rightarrow \hspace{0.1in} \frac{\partial f}{\partial x} = \mathbb{1}(x >= y) \hspace{0.1in} \frac{\partial f}{\partial y} = \mathbb{1}(y >= x)上式是说,如果该变量比另一个变量大,那么梯度是1,反之为0。

    图4 加法门、乘法门和max门 加法门单元是梯度分配器,输入的梯度都等于输出的梯度,这一行为与输入值在前向传播时的值无关;乘法门单元是梯度转换器,输入的梯度等于输出梯度乘以另一个输入的值,或者乘以倍数a(ax的形式乘法门单元);max门单元是梯度路由器,输入值大的梯度等于输出梯度,小的为0。

    乘法门单元的局部梯度就是输入值,但是是相互交换之后的,然后根据链式法则乘以输出值的梯度。基于此,如果乘法门单元的其中一个输入非常小,而另一个输入非常大,那么乘法门会把大的梯度分配给小的输入,把小的梯度分配给大的输入。在线性分类器中,权重和输入进行点积w^Tx_i,这说明输入数据的大小对于权重梯度的大小有影响。例如,在计算过程中对所有输入数据样本x_i乘以1000,那么权重的梯度将会增大1000倍,这样就必须降低学习率来弥补。这就是为什么数据预处理关系重大,它即使只是有微小变化,也会产生巨大影响。对于梯度在计算线路中是如何流动的有一个直观的理解,可以帮助调试神经网络。

    1.1.3 一个更复杂的例子

    下面看一个复杂一点的例子:f(w,x) = \frac{1}{1+e^{-(w_0x_0 + w_1x_1 + w_2)}}
    这个表达式需要使用新的门单元:
    f(x) = \frac{1}{x} \hspace{0.15in} \rightarrow \hspace{0.16in} \frac{df}{dx} =- \frac{1}{x^2}\\ f_c(x) = c + x \hspace{0.2in} \rightarrow \hspace{0.1in} \frac{df}{dx} = 1 \\ f(x) = e^x \hspace{0.3in} \rightarrow \hspace{0.2in} \frac{df}{dx} = e^x \\ f_a(x) = ax \hspace{0.3in} \rightarrow \hspace{0.15in} \frac{df}{dx} = a计算过程如下:

    图5 计算过程
    对于1/x门单元,回传梯度是1,局部梯度是 图6 输入和输出都是4096维的max函数 这个max函数对输入向量x的每个元素都和0比较输出最大值,因此输出向量的维度也是4096维。此时的梯度是雅可比矩阵,即输出的每个元素对输入的每个元素求偏导组成的矩阵。假如输入x是n维的向量,输出y是m维的向量,则 那么这个例子的雅克比矩阵是[4096x4096]维的,输出有4096个元素,每一个都要求4096次偏导。其实仔细观察发现,这个例子输出的每个元素都只和输入相应位置的元素有关,因此得到的是一个对角矩阵。实际应用的时候,往往100个x 同时输入,此时雅克比矩阵是一个[409600x409600]的对角矩阵,当然只是针对这里的f函数。(实际上,完全写出并存储雅可比矩阵是不可能的事,因为维度非常大。)

    一个例子

    目标公式为:f(x,W)=\vert \vert W\cdot x \vert \vert ^2=\sum_{i=1}^n (W\cdot x)_{i}^2
    其中x是n维的向量,W是n*n的矩阵。设q=W\cdot x,于是得到下面的式子:

    可以看出:
    • \frac{\partial f}{\partial q_i}=2q_i
      从而得到f对q的梯度为2q

    • \frac{\partial q_k}{\partial W_{i, j}}=1\{i=k\}x_j
      \frac{\partial f}{\partial W_{i, j}}=\sum_{k=1}^n\frac{\partial f}{\partial q_k}\frac{\partial q_k}{\partial W_{i, j}}=\sum_{k=1}^n(2q_k)1\{i=k\}x_j=2q_ix_j
      从而得到f对W的梯度为2q\cdot x^T

    • \frac{\partial q_k}{\partial x_i}=W_{k,i}
      \frac{\partial f}{\partial x_i}=\sum_{k=1}^n\frac{\partial f}{\partial q_k}\frac{\partial q_k}{\partial x_i}=\sum_{k=1}^n(2q_k)W_{k,i}
      从而得到f对x的梯度为2W^T\cdot q

    下面为计算图: 图7 向量化计算图
    代码实现:
    import numpy as np
    
    # 初值
    W = np.array([[0.1, 0.5], [-0.3, 0.8]])
    x = np.array([0.2, 0.4]).reshape((2, 1))  # 为了保证dq.dot(x.T)是一个矩阵而不是实数
    
    # 前向传播
    q = W.dot(x)
    f = np.sum(np.square(q), axis=0)
    
    # 反向传播
    # 回传 f = np.sum(np.square(q), axis=0)
    dq = 2*q
    # 回传 q = W.dot(x)
    dW = dq.dot(x.T)  # x.T就是对矩阵x进行转置
    dx = W.T.dot(dq)
    

    注意:要分析维度!不要去记忆dW和dx的表达式,因为它们很容易通过维度推导出来。权重的梯度dW的尺寸肯定和权重矩阵W的尺寸是一样的(为什么呢?上面有个例子不是输出4096,输入4096,梯度是[4096x4096]吗?这里又为什么 梯度和原输入尺寸一致呢?其实并不矛盾,因为这里的 f 输出是1个实数,所以dW和W的形状一致。如果考虑dq/dW的话,如果按照雅克比矩阵的定义,dq/dw应该是2x2x2维,为了减小计算量,就令其等于x。其实完全不用考虑那么复杂,因为最终的损失函数一定是一个实数,所以每个门单元的输入梯度一定和原输入形状相同。关于这点的说明,可以点击这里,官网进行了详细的推导。),而这又是由x和dq的矩阵乘法决定的,总有一个方式是能够让维度之间能够对的上的。例如,x的尺寸是[2x1],dq的尺寸是[2x1],如果你想要dW和W的尺寸是[2x2],那就要dq.dot(x.T),如果是x.T.dot(dq)结果就不对了。(dq是回传梯度不能转置!)

    2 神经网络简介

    2.1 神经网络算法介绍

    在不诉诸大脑的类比的情况下,依然是可以对神经网络算法进行介绍的。在线性分类一节中,在给出图像的情况下,是使用Wx来计算不同视觉类别的评分,其中W是一个矩阵,x是一个输入列向量,它包含了图像的全部像素数据。在使用数据库CIFAR-10的案例中,x是一个[3072x1]的列向量,W是一个[10x3072]的矩阵,所以输出的评分是一个包含10个分类评分的向量。

    一个两层的神经网络算法则不同,它的计算公式是s = W_2 \max(0, W_1 x)。其中W1的含义是这样的:举个例子来说,它可以是一个[100x3072]的矩阵,其作用是将图像转化为一个100维的过渡向量,比如马的图片有头朝左和朝右,会分别得到一个分数。函数max(0,-)是非线性的,它会作用到每个元素。这个非线性函数有多种选择,后续将会学到。但这个形式是一个最常用的选择,它就是简单地设置阈值,将所有小于0的值变成0。最终,矩阵W2的尺寸是[10x100],会对中间层的得分进行加权求和,因此将得到10个数字,这10个数字可以解释为是分类的评分。注意非线性函数在计算上是至关重要的,如果略去这一步,那么两个矩阵将会合二为一,对于分类的评分计算将重新变成关于输入的线性函数。这个非线性函数就是改变的关键点。参数W1,W2将通过随机梯度下降来学习到,他们的梯度在反向传播过程中,通过链式法则来求导计算得出。

    一个三层的神经网络可以类比地看做s = W_3 \max(0, W_2 \max(0, W_1 x)),其中W1,W2,W3是需要进行学习的参数。中间隐层的尺寸是网络的超参数,后续将学习如何设置它们。现在让我们先从神经元或者网络的角度理解上述计算。

    两层神经网络代码示例,中间层使用sigmoid函数:

    import numpy as np
    from numpy.random import randn
    
    N, D_in, H, D_out = 64, 1000, 100, 10
    # x 是64x1000的矩阵,y是64x10的矩阵
    x, y = randn(N, D_in), randn(N, D_out)
    # w1是1000x100的矩阵,w2是100x10的矩阵
    w1, w2 = randn(D_in, H), randn(H, D_out)
    
    # 迭代10000次,损失达到0.0001级
    for t in range(10000):
        h = 1 / (1 + np.exp(-x.dot(w1)))  # 激活函数使用sigmoid函数,中间层
        y_pred = h.dot(w2)
        loss = np.square(y_pred - y).sum()  # 损失使用 L2 范数
        print(str(t)+': '+str(loss))
    
        # 反向传播
        grad_y_pred = 2.0 * (y_pred - y)
        grad_w2 = h.T.dot(grad_y_pred)
        grad_h = grad_y_pred.dot(w2.T)
        # grad_xw1 = grad_h*h*(1-h)
        grad_w1 = x.T.dot(grad_h*h*(1-h))
    
        # 学习率是0.0001
        w1 -= 1e-4 * grad_w1
        w2 -= 1e-4 * grad_w2
    

    2.2 神经网络与真实的神经对比

    神经网络算法领域最初是被对生物神经系统建模这一目标启发。大脑的基本计算单位是神经元(neuron)。人类的神经系统中大约有860亿个神经元,它们被大约1014-1015突触(synapses)连接起来。下面图表的左边展示了一个生物学的神经元,右边展示了一个常用的数学模型。每个神经元都从它的树突(dendrites)获得输入信号,然后沿着它唯一的轴突(axon)产生输出信号。轴突在末端会逐渐分枝,通过突触和其他神经元的树突相连。

    在神经元的计算模型中,沿着轴突传播的信号(比如x_0)将基于突触的突触强度(比如w_0),与其他神经元的树突进行乘法交互(比如w_0 x_0)。其观点是,突触的强度(也就是权重w),是可学习的且可以控制一个神经元对于另一个神经元的影响强度(还可以控制影响方向:使其兴奋(正权重)或使其抑制(负权重))。在基本模型中,树突将信号传递到细胞体,信号在细胞体中相加。如果最终之和高于某个阈值,那么神经元将会激活,向其轴突输出一个峰值信号。在计算模型中,我们假设峰值信号的准确时间点不重要,是激活信号的频率在交流信息。基于这个速率编码的观点,将神经元的激活率建模为激活函数(activation function)f ,它表达了轴突上激活信号的频率。由于历史原因,激活函数常常选择使用sigmoid函数\sigma ,该函数输入实数值(求和后的信号强度),然后将输入值压缩到0-1之间。在本节后面部分会看到这些激活函数的各种细节。

    图8 神经元与数学模型对比
    这里的激活函数f采用的是sigmoid函数,代码如下:
    class Neuron:
        # ...
        def neuron_tick(self, inputs):
            # 假设输入和权重都是1xD的向量,偏差是一个数字
            cell_body_sum = np.sum(inputs*self.weights) + self.bias
            # 当和远大于0时,输出为1,被激活
            firing_rate = 1.0 / (1.0 + np.exp(-cell_body_sum))
            return firing_rate
    

    此外,注意这种模型非常粗糙,真实的神经元可要复杂的多,所以一定要严谨谦逊,免得让生物专业的人笑的肚子疼。

    常用的激活函数
    图9 常用的激活函数

    2.3 神经网络结构

    对于普通神经网络,最普通的层的类型是全连接层(fully-connected layer)。全连接层中的神经元与其前后两层的神经元是完全成对连接的,但是在同一个全连接层内的神经元之间没有连接。在网络中不允许循环,因为这样会导致前向传播的无限循环。下面是两个神经网络的图例,都使用的全连接层:

    图10 全连接神经网络 左边是一个2层神经网络,隐层由4个神经元(也可称为单元(unit))组成,输出层由2个神经元组成,输入层是3个神经元(指的是输入图片的维度而不是图片的数量)。右边是一个3层神经网络,两个含4个神经元的隐层。注意:层与层之间的神经元是全连接的,但是层内的神经元不连接。当我们说N层神经网络的时候,我们没有把输入层算入。因此,单层的神经网络就是没有隐层的(输入直接映射到输出)。也会使用人工神经网络(Artificial Neural Networks 缩写ANN)或者多层感知器(Multi-Layer Perceptrons 缩写MLP)来指代神经网络。此外,输出层的神经元一般不含激活函数。

    用来度量神经网络的尺寸的标准主要有两个:一个是神经元的个数,另一个是参数的个数。用上面图示的两个网络举例:

    • 第一个网络有4+2=6个神经元(输入层不算),[3x4]+[4x2]=20个权重,还有4+2=6个偏置,共26个可学习的参数。
    • 第二个网络有4+4+1=9个神经元,[3x4]+[4x4]+[4x1]=32个权重,4+4+1=9个偏置,共41个可学习的参数。

    现代卷积神经网络能包含约1亿个参数,可由10-20层构成(这就是深度学习)。

    三层神经网络代码示例

    不断重复的矩阵乘法与激活函数交织。将神经网络组织成层状的一个主要原因,就是这个结构让神经网络算法使用矩阵向量操作变得简单和高效。用上面那个3层神经网络举例,输入是[3x1]的向量。一个层所有连接的强度可以存在一个单独的矩阵中。比如第一个隐层的权重W1是[4x3],所有单元的偏置储存在b1中,尺寸[4x1]。这样,每个神经元的权重都在W1的一个行中,于是矩阵乘法np.dot(W1, x)+b1就能作为该层中所有神经元激活函数的输入数据。类似的,W2将会是[4x4]矩阵,存储着第二个隐层的连接,W3是[1x4]的矩阵,用于输出层。完整的3层神经网络的前向传播就是简单的3次矩阵乘法,其中交织着激活函数的应用。

    import numpy as np
    
    # 三层神经网络的前向传播
    # 激活函数
    f = lambda x: 1.0/(1.0 + np.exp(-x))
    
    # 随机输入向量3x1
    x = np.random.randn(3, 1)
    # 设置权重和偏差
    W1, W2, W3 = np.random.randn(4, 3), np.random.randn(4, 4), np.random.randn(1, 4),
    b1, b2= np.random.randn(4, 1), np.random.randn(4, 1)
    b3 = 1
    
    # 计算第一个隐藏层激活 4x1
    h1 = f(np.dot(W1, x) + b1)
    # 计算第二个隐藏层激活 4x1
    h2 = f(np.dot(W2, h1) + b2)
    # 输出是一个数
    out = np.dot(W3, h2) + b3
    

    在上面的代码中,W1,W2,W3,b1,b2,b3都是网络中可以学习的参数。注意x并不是一个单独的列向量,而可以是一个批量的训练数据(其中每个输入样本将会是x中的一列),所有的样本将会被并行化的高效计算出来。注意神经网络最后一层通常是没有激活函数的(例如,在分类任务中它给出一个实数值的分类评分)。反向传播过程可参考作业1中的内容。

    全连接层的前向传播一般就是先进行一个矩阵乘法,然后加上偏置并运用激活函数。

    理解神经网络

    理解具有全连接层的神经网络的一个方式是:可以认为它们定义了一个由一系列函数组成的函数族,网络的权重就是每个函数的参数。拥有至少一个隐层的神经网络是一个通用的近似器,神经网络可以近似任何连续函数。虽然一个2层网络在数学理论上能完美地近似所有连续函数,但在实际操作中效果相对较差。虽然在理论上深层网络(使用了多个隐层)和单层网络的表达能力是一样的,但是就实践经验而言,深度网络效果比单层网络好。

    在实践中3层的神经网络会比2层的表现好,然而继续加深(做到4,5,6层)很少有太大帮助。卷积神经网络的情况却不同,在卷积神经网络中,对于一个良好的识别系统来说,深度是一个非常重要的因素(比如数十(以10为量级)个可学习的层)。对于该现象的一种解释观点是:因为图像拥有层次化结构(比如脸是由眼睛等组成,眼睛又是由边缘组成),所以多层处理对于这种数据就有直观意义。

    总结

    1. 前向传播与反向传播、标量与向量形式,核心思想都是分段计算和链式法则;
    2. 神经网络简介,与神经元对比,激活函数,神经网络结构,理解神经网络

    相关文章

      网友评论

        本文标题:Lecture 4 介绍神经网络

        本文链接:https://www.haomeiwen.com/subject/opzprqtx.html