《神经网络和深度学习》笔记

一、前言

笔者入门深度学习看的是吴恩达的公开课《神经网络和深度学习》。这门公开课纯粹的入门,讲述的东西并不多,主要是以逻辑回归为例子,将逻辑回归使用神经网络的形式表示出来,通过这个例子来讲述前向计算、后向传播、向量化计算等基础知识。因为网上已经有资料是对视频笔记的详细记录,文中不会对视频讲解的每个部分进行阐述,只将自己在学习过程中遇到的难点和重点以笔记的形式记下来。希望能够帮助到各位读者,如果文章有错漏的地方,还请各位读者海涵指正。

二、模型公式说明

我们先把各种符号术语给讲述明白先,视频使用的是逻辑回归作为例子

2.1、输入矩阵

首先是输入矩阵 X

横方向是样本的数量,竖方向是每个样本的特征,一共有个(为了简介,在上面的矩阵中没有表示),对于每个样本都有,即有维特征

2.2、样本标签矩阵

Y 样本的标签矩阵如下,同输入矩阵一样表示样本数量

2.3、假设函数

假设函数 定义如下:

是我们的预测输出。通常希望假设函数输出的是概率,但线性函数无法做到,我们可以再次基础上加入sigmoid函数从而使得其变成非线性函数。

2.4、损失函数

我们可以使用平方差来作为损失函数,根据教学所说,这样的损失函数可以使为题成为非凸问题,即可以找到全局最优解

但逻辑回归使用的是对数似然函数来作为损失函数,关于这一点不明白的读者可以参考我的《最大熵模型》一文,所以我们真正使用的损失函数如下

对于单个样本的损失函数如上式所示,一般算法的代价函数是对个样本的损失求和再除以

三、计算图

视频使用计算图来表示一个神经网络的计算过程,这里不对计算图进行描述,各位读者可以通过视频习得,我们直接讲述逻辑回归的计算图。计算图有前向计算和后向传播 2 个计算过程,单层前向计算过程比较简单,我们这里不做说明,我们主要说明后向计算需要注意的地方及难点。后向传播是为了求出梯度下降的值,我们也不对梯度下降进行讲述,这部分视频已经有所讲解且思路清晰,这里笔者就不对基础的数学知识进行说明了,我后面也会有别的文章对梯度下降进行讲解

3.1、符号公式

我们先对逻辑回归的公式进行拆解,可以得到如下公式



其中我们需要对函数进行求导,我们直接写出它的求导结论

上面结论可以使用倒置函数+链式法则推导而出,详细过程参考附录《Sigmoid函数的求导证明》

3.2、链式法则推导

在讲解计算图的后向传播过程前,我们先简单讲一下链式法则。
链式法则可以让求导变得更加简单,其证明过程如下:

设函数在点的邻域有定义
在点可导 在点可微,即

我们定义有

对应于自变量的一个增量

那么有下面的推导
\begin{aligned} \Delta y&=f(g(x+\Delta x))-f(g(x ))\\ &=f(u+\Delta u)-f(u)\\ &=f'(u)\Delta u+\omicron (\Delta u) \\ &=f'(u)(g'(x)\Delta x+\omicron (\Delta x))+\omicron (g'(x)\Delta x+\omicron (\Delta x))\\ &=f'(u)g'(x)\Delta x+f'(u)\omicron (\Delta x)+\omicron (g'(x)\Delta x+\omicron (\Delta x))\\ \end{aligned}
后面两项

表示一族函数,当Δx趋近0时,这些函数和Δx的比值也趋近于0
可得当时后面两项无穷小
所以在可导且

3.3、单层计算图

3.3.1、单层计算图讲解


如上图所示,单层计算图比较简单,我们可以通过链式法则一步一步求出损失函数对参数和的导数公式,从而使用梯度下降来更新这 2 个参数。

我们最终需要求出的是下面的 2 个导数公式

计算过程请各位读者注意结合3.1小节的《符号公式》
第一步求出损失函数对的导数,根据损失函数的定义和导数的求导法则,可以直接得到

第二步是根据第一步的结果来计算,集合函数的求导公式,可以得到:

第三步同理,跟的过程是一样的

经过上面的推导我们可以得到我们想要的导数公式了,有了上面的公式,我们可以直接使用程序来求我们的参数进行迭代,程序的伪代码如下,作用是完成一轮的迭代计算

For i in m:













3.3.2、向量化单层计算图

上一小节是针对实数进行的求导过程,其缺点在视频中已经有讲解,计算复杂且性能低下。在处理神经网络的过程中我们一般使用矩阵来进行计算,从而加快我们的计算过程,节省时间,这一小节我们将使用矩阵来计算计算图,也就是向量化我们的输入和各种参数,然后通过矩阵计算来得出我们想要的结果。关于向量化以及 python 的广播这里不做讲述,各位读者可以从视频中学习

上面的单层计算图我们可以将表示为一个列向量并表示为(是列向量这个结论是笔者的理解,不知道是否正确。当然了,这个只是向量的表现形式,不必太过纠结,能够理解运算过程即可),同理表示为一个列向量并表示为,一般将输入特征表示为列向量,那么就有

这里是一个行向量,因为进行了转置
向量化参数后我们就可以使用python的广播更方便的进行运算,对于单层的计算图笔者不多做赘述,简单地在这里记录一下

3.4、多层计算图

多层计算图有一些符号规定我们需要说明一下,使用等来表示第一层的,也就是说上标方括号用来表示第 x 层的值,表示为,其余参数同理。下标表示的是对应节点的值,更一般地我们表示为就是第个节点的值,推广开来即第 n 层的第 m 个节点的值,其余参数同理

关于矩阵的运算请参考附录《矩阵乘法》和《矩阵的运算及其运算规则》

如下图所示,这是一个简单的 2 层逻辑回归计算图,一个输入有 3 个特征,分别是

多层

3.4.1、多层计算图前向计算

node是第一层,而output是第二层。第一层的计算公式也在图中表示出来了,一共有 2 个计算,跟我们之前的单层计算图是一样的

需要注意矩阵的点乘和乘是不一样,关于矩阵的点乘和乘参考附录《点乘和矩阵乘》

这里要注意的是是一个列向量,我们的也是一个列向量,两者进行乘法时,我们需要对进行转置得到

那么针对所有的node我们可以得到下面的公式
z_1^{[1]}=w_1^{[1]T}x+b,a_1^{[1]}=\sigma{(z_1^{[1]})}\\ z_2^{[1]}=w_2^{[1]T}x+b,a_2^{[1]}=\sigma{(z_2^{[1]})}\\ z_3^{[1]}=w_3^{[1]T}x+b,a_3^{[1]}=\sigma{(z_3^{[1]})}\\ z_4^{[1]}=w_4^{[1]T}x+b,a_4^{[1]}=\sigma{(z_4^{[1]})}\\接下来我们可以对所有node的参数表示为一个矩阵,每一行都是对应着node的参数,比如表示的是node1的参数,每一行的参数数量都与输入特征的数量相同,在这里例子中我们有 3 个输入,那么这里矩阵每一行就对应 3 个参数。
我们使用(m,n)来表示这个矩阵有m行n列,即维度。那么这里的矩阵就是(4,3)维度即4行3列。
如果我们抽出其中的一行来距离的话,则是

参数同理,但参数的维度是(4,1)


所有的node组成了第一层,那么对于第一层,我们可以使用矩阵表示为下面的形式,下面的乘是矩阵乘法
\overbrace{ \begin{bmatrix} {\cdots}&{w_1^ {[1]T}}&{\cdots}\\ {\cdots}&{w_2^ {[1]T}}&{\cdots}\\ {\cdots}&{w_3^ {[1]T}}&{\cdots}\\ {\cdots}&{w_4^ {[1]T}}&{\cdots}\\ \end {bmatrix} }^{W^{[1])}(4,3)} \overbrace{ \begin{bmatrix} {x_1}\\ {x_2}\\ {x_3}\\ \end {bmatrix} }^{input}+ \overbrace{ \begin{bmatrix} {b_1^{[1]}}\\ {b_2^{[1]}}\\ {b_3^{[1]}}\\ {b_4^{[1]}}\\ \end {bmatrix} }^{b^{[1]}}= \overbrace{ \begin{bmatrix} {z_1^{[1]}}\\ {z_2^{[1]}}\\ {z_3^{[1]}}\\ {z_4^{[1]}}\\ \end{bmatrix} }^{z^{[1]}}
我们还需要继续计算我们的函数,这里是对逐个元素进行计算

经过上面的矩阵运算,我们完成了第一层前向计算过程
同理的,output的计算过程如下,最后的就是我们的输出

output


从上面可以看出的维度是(1,4),而和而都是(1,1)即一个实数。
最后我们对进行一次计算得到,同理也是对逐个元素进行计算:

在整个计算过程的最后加入损失函数即可简化为如下的图


3.4.2、多层计算图后向传播

后向传播在视频中描述得并不详尽,这里笔者将整个推导过程给各位读者罗列出来。在此之前先需要说明一下我们的激活函数使用的是函数。我们可以先不管激活函数是什么,并不影响这里的理解,后面的章节会简单的说明激活函数

我们依旧按照单层计算图的方法一步一步计算。
第一步

第二步,根据函数的推导公式,直接得到下面的式子,这里的计算结果依旧是一个实数即(1,1)
\frac{da^{[2]}}{dz^{[2]}}=a^{[2]}(1-a^{[2]}) \\ \frac{dL(a^{[2]},y)}{dz^{[2]}}=\frac{dL(a^{[2]},y)}{da^{[2]}}\frac{da^{[2]}}{dz^{[2]}}=(-\frac{y}{a}+\frac{1-y}{1-a^{[2]}})[a^{[2]}(1-a^{[2]})]=a^{[2]}-y

第三步,这里需要转置的原因是是一个行向量,而我们的是一个列向量,所以需要进行转置从而匹配的维度
另外计算出来的结果是一个(1,4)的矩阵,而也是一个(1,4)的矩阵,这样就能够进行梯度下降的减法计算。到了这里我们就完成了output层的后向传播运算,接下来我们需要对node层也就是第一层进行后向传播运算。
\frac{dz^{[2]}}{dW^{[2]}}=a^{[1]T} \\ \frac{dz^{[2]}}{db^{[2]}}=1 \\ \frac{dL(a^{[2]},y)}{dW^{[2]}}=\frac{dL(a^{[2]},y)}{dz^{[2]}}\frac{dz^{[2]}}{dW^{[2]}}=(a^{[2]}- y)a^{[1]T} \\ \frac{dL(a^{[2]},y)}{db^{[2]}}=\frac{dL(a^{[2]},y)}{dz^{[2]}}\frac{dz^{[2]}}{db^{[2]}}=a^{[2]}- y
第四步,注意一下,因为是一个矩阵,所以是对矩阵的求导,其求导结果也是一个(4,1)维度的矩阵,而也是一个(4,1)维度的矩阵。关于矩阵的求导请参考附录《矩阵求导》。
注意下面的号,这里表示的是矩阵的点乘,也就是两个矩阵相乘就是元素逐个相乘,所以计算出来的结果也是个(4,1)维度的矩阵
为什么使用点乘在后面笔者用一个小节来进行说明
\frac{dz^{[2]}}{da^{[1]}}=W^{[2]T} \\ \frac{da^{[1]}}{dz^{[1]}}=a^{[1]}(1-a^{[1]}) \\ \frac{dL(a^{[2]},y)}{dz^{[1]}}=\frac{dL(a^{[2]},y)}{dz^{[2]}}\frac{dz^{[2]}}{da^{[1]}}*\frac{da^{[1]}}{dz^{[1]}}=(a^{[2]}-y)W^{[2]T}*[a^{[1]}(1-a^{[1]})]\\
第五步,这里的转置按照笔者的理解依旧是为了匹配维度
\frac{dz^{[1]}}{dW^{[1]}}=x^T \\ \frac{dz^{[1]}}{db^{[1]}}=1 \\ \frac{dL(a^{[2]},y)}{dW^{[1]}}=\frac{dL(a^{[2]},y)}{dz^{[1]}}\frac{dz^{[1]}}{dW^{[1]}}=\frac{dL(a^{[2]},y)}{dz^{[1]}}x^T \\ \frac{dL(a^{[2]},y)}{db^{[1]}}=\frac{dL(a^{[2]},y)}{dz^{[1]}}\frac{dz^{[1]}}{db^{[1]}}=\frac{dL(a^{[2]},y)}{dz^{[1]}}
我们上面说是一个(4,1)维度的矩阵,我们将表示如下:

而是一个行向量,表示如下:

那么可以得到下面的式子

根据最简单的矩阵乘法规则,一行乘一列,得出来的是一个(4,3)维度的矩阵,同的维度是一样的。而我们可以直接得到,所以到这里,整个后向传播的求导过程我们完成了,这样我们就能够直接使用求导的结果进行梯度下降来更新参数

3.5、激活函数

线性方程和线程方程的组合还是线性方程,而很多时候模型都是非线性的,我们需要将模型编程非线性模型,而激活函数是为了让模型成为非线性的。
在上一小节的后向传播中,我们注意到我们会对激活函数进行求导,所以激活函数某种程度上也会影响模型的学习效率。
视频中指出了下面几种激活函数:

根据视频概括总结有:

  • 总是优于
  • 可以使激活函数的梯度总是为 1,避免了当太大时梯度为 0
  • 工业界常用作为隐藏层的几乎偶函数
  • 函数可以在二分类的输出层中作为激活函数使用

关于激活函数的内容视频讲解得非常清楚,相信给读者可以理解,笔者这里就不多做赘述

3.6、为什么使用点乘

我们在 3.5小节 中的第四步用到了点乘,但为什么是用点乘呢?最直接的看法就是因为两个维度为(4,1)的矩阵无法使用矩阵乘法,但这似乎并不是本质,下面笔者使用一个简单的例子来说明
首先,我们可以直接得到的公式

选择一种特殊的情况,比如我们对进行求导,然后这个导数来求解,公式如下:

公式中上面的花括号(1,1)表示花括号所指定的项的维度是(1,1)即1*1的矩阵,那么对于其他的我们可以得到其他的公式,完整的公式如下:
\overbrace{\frac{dL(a,y)}{dz^{[2]}}}^{(1,1)}\overbrace{\frac{dz^{[2]}}{da_1^{[1]}} }^{[1,1]} \overbrace{\frac{da_1^{[1]}}{dz_1^{[1]}}}^{(1,1)}=\overbrace{\frac{dL(a,y)}{dz_1^{[1]}}}^{(1,1)}\\ \overbrace{\frac{dL(a,y)}{dz^{[2]}}}^{(1,1)}\overbrace{\frac{dz^{[2]}}{da_2^{[1]}} }^{[1,1]} \overbrace{\frac{da_2^{[1]}}{dz_2^{[1]}}}^{(1,1)}=\overbrace{\frac{dL(a,y)}{dz_2^{[1]}}}^{(1,1)}\\ \overbrace{\frac{dL(a,y)}{dz^{[2]}}}^{(1,1)}\overbrace{\frac{dz^{[2]}}{da_3^{[1]}} }^{[1,1]} \overbrace{\frac{da_3^{[1]}}{dz_3^{[1]}}}^{(1,1)}=\overbrace{\frac{dL(a,y)}{dz_3^{[1]}}}^{(1,1)}\\ \overbrace{\frac{dL(a,y)}{dz^{[2]}}}^{(1,1)}\overbrace{\frac{dz^{[2]}}{da_4^{[1]}} }^{[1,1]} \overbrace{\frac{da_4^{[1]}}{dz_4^{[1]}}}^{(1,1)}=\overbrace{\frac{dL(a,y)}{dz_4^{[1]}}}^{(1,1)}\\

这个时候让我们尝试将它们使用点乘合并一下,思路很简单,将式子左边的第二项和第三项分别用维度为(4,1)的矩阵表示,式子的右边也用维度(4,1)的矩阵表示。再看看上面的式子,其实式子左右的第二项和第三项就是矩阵各个元素的乘积了,那还不简单,直接使用矩阵的点乘操作来简化就行了,所以可以得到

注意,符号表示的是点乘的意思

附录

Sigmoid函数的求导证明:https://www.jianshu.com/p/d4301dc529d9
Deeplearning课程矩阵维度总结:https://blog.csdn.net/sansherlock/article/details/82262470
矩阵乘法:http://www.ruanyifeng.com/blog/2015/09/matrix-multiplication.html
矩阵的运算及其运算规则:http://www2.edu-edu.com.cn/lesson_crs78/self/j_0022/soft/ch0605.html
矩阵求导:https://blog.csdn.net/daaikuaichuan/article/details/80620518
矩阵点乘和乘法:https://www.cnblogs.com/liuq/p/9330134.html

你可能感兴趣的:(《神经网络和深度学习》笔记)