初学者都能懂的深度学习之神经网络(一)

声明:本人仔细学习 3Blue1Brown 中提供的深度学习视频,将其整理出此文档。因文章的篇幅和本人的理解能力有限,若有条件的可以观看 3Blue1Brown 在b站上传的深度学习的三个视频以及相关内容。此文档会加入一些自己学习的感悟和理解,初次使用 markdown,公式格式可能比较乱。若有误烦请诸位指正。

第一部分 深度学习之神经网络的结构

本节仅介绍最简单的多层感知器MLP。其他类型的神经网络例如: 卷积神经网络 (Convolutional neural network) 擅于图像识别长短期记忆网络 (Long short-term memory network) 擅于语音识别

神经网络 (Neural network) 之名来源于人的大脑结构,neurons神经元,何为神经元?又是如何连接起来构成网络的呢?neuron 理解成装有一个数字的容器。

为了更方便的的理解,举出了一种数字识别的案例,更形象的见下图:一个28*28的输入图像里的每一个像素看作一个神经元,即共计784个神经元,神经元中的数字代表对应像素的灰度值(0 代表纯黑像素,1 代表纯白像素)(这里我有一个想法:在 markdown 中实现GUI。该案例的目标是通过784个像素点来识别 0、1、2、3、4、5、6、7、8、9数字。

上述的每个神经元中的数字,我们称之为 “激活值 (Activation)” 。显然,激活值越大,那么这个神经元也就越亮,反之则越暗。因此,由784个神经元组成的图像组成了神经网络的第一层。然后我们先来了解网络的最后一层,这一层的十个神经元分别代表 0 到 9 这十个数字,同理这十个数字的激活值也都是在 0 到 1 之间。需要注意的是:这些值并不是说是否被判断为某一个确定的数字,而是输入的图像对应十个数字的可能性!

除此之外,在神经网络的输入层 (784个神经元)与输出层 (10个神经元)之间还有几层 “隐含层 (Hidden layers) ”,隐含层进行处理识别数字的具体工作。在上述的案例中,我们选择两层隐含层(选择的原因),确定每层隐含层包含 16 个神经元(选择的原因:显得好看)(实际应用中,在网络的结构上有很大的调整实验余地)。上面讲到了 784 个神经元的灰度值,该输入层的激活值的图案会让下层的激活值产生某些特殊的图案,然后第一层隐含层的 16 个神经元的激活值再让第二层隐含层的 16 个神经元的激活值产生特殊的图案,最终在输出层得到某种结果,在输出层最亮的那个神经元就表示神经网络认为的输入图像的数字。具体的传递过程见下面的动图。

相信很多初学者(俺也是)都会提出一个问题:为什么我们就能认为这种层状结构就可以作出智能判断?就是说,为什么要有中间层的存在,通过 784 个像素点(用一堆 if 语句)直接判断最后的数字结果不就好了嘛,何必还要两层中间层?它们存在的意义是什么?

背景:当我们在识别数字的时候,我们其实实在组合数字的各个部件:9 便是一个圈加一个竖线;8 可以看作由两个圈组成;4 可看作由三个竖线组成。

那么在理想的情况下,既然要得到最后的输出层十个数字的激活值(在该层也可以理解为概率值),那么我们希望倒数第二层中的各个神经元能够分别对应上一个笔画部件。这样一来,当我们输入例如 9 或者 8 这种带圈的数字时,某一个神经元中的激活值就会接近 1。需要指出:这里的带圈数字的圈的形状并不是固定的,而希望位于图像顶部的圆圈图案都能够点亮这个神经元。那么,从第二层隐含层到输出层,网络只需要学习哪些部件能组合出哪个数字即可。此时你可能又会有问题了:构建隐含层都够麻烦了,识别这些部件不是更难吗?如何判断哪些部件才是我们想要的呢?

与将十个数字分解为 16 个部件的思想相同,识别圆圈的任务可以拆分成更细微的部件的问题:首先识别出数字图形中更小的边,例如把圆圈切分成五个甚至更多个不同方向的短边组成的部件;1、4、7中的长竖线可以看作是多个短竖线。

因此我们希望第一层隐含层的各个神经元能够对应上各种各样的短边。那么当图像输入进来的时候,能把关联短边的八到十个小部件神经元都点亮(这里的点亮不是1),接着就能点亮对应大部件的神经元,最后就能点亮对应数字的神经元。这就是我们希望的层状结构解决识别问题的目标,这样的转化为抽象元素,一层层抽丝剥茧的思想,也同样应用于各种人工智能任务,比如说语音识别(从原音频中识别出特殊的声音,组合成特定的音节,组合成单词,再组合成短语,以及更加抽象的概念)。

设计上一层中的激活值到底会如何决定下一层中的激活值?我们需要设计一个机制,可以把像素拼成短边,然后把短边组成图案,或者把图案拼成数字等等。

首先我们拿一个神经元来举例:设计中间层第一层的一个神经元,它的目标是实现小部件的识别,我们规定它能够正确识别出图像中的某一指定区域里是否存在一条边。现在我们需要知道这个网络的参数,应该如何调整网络上的旋钮开关才能让它表示出某一种规定图案(例如几条边组合成圆圈的图案)?

这里需要注意的是:对于某一确定的输入图像,我们显然不能改变 784 个输入神经元的激活值,给这个神经元和第一层所有神经元间的每一条接线都赋上一个权重值然后将第一层所有的激活值和它们对应权重值一起算出它们的加权和。为了方便理解,可以把这些权重值看作一个表格,正的权重值标记为绿色,负的标记为红色,颜色越暗表示权重值越接近 0 。如果把关注区域的权重赋为正值,而其他所有权重值一律赋为 0。这样一来,对所有的像素值取加权和,就只会累加我们关注区域的像素值了!更进一步,如果想识别出这里是否存在一条边,只需要在周围一圈的像素赋予负的权重,这样当中间像素亮,周围像素暗时,加权和就能达到最大为啥还要设置周围的权重为负?这样加权和不是减小了吗?
w 1 a 1 + w 2 a 2 + w 3 a 3 + w 4 a 4 + ⋅ ⋅ ⋅ + w n a n 其中 , w i 为权重值; a i 为神经元激活值 w_1a_1+w_2a_2+w_3a_3+w_4a_4+\cdot\cdot\cdot+w_na_n\\其中,w_i为权重值;a_i为神经元激活值 w1a1+w2a2+w3a3+w4a4++wnan其中,wi为权重值;ai为神经元激活值

这样计算出来的加权和可以是任意大小,但在这个网络中,我们需要激活值在 0 到 1 之间。在高数中我们知道,sigmoid 函数可以将任意实数压进 0 到 1 这个区间内(激活函数 不仅只有 sigmoid 函数,其他也可以自行了解:(8条消息) 机器学习中的数学——激活函数(一):Sigmoid函数_von Neumann的博客-CSDN博客_sigmoid函数计算器)。
S i g m o i d F u n c t i o n : σ ( x ) = 1 1 + e − x 其中, x ∈ [ − ∞ , + ∞ ] Sigmoid Function:\sigma(x)=\frac{1}{1+e^{-x}}\\其中,x\in[-\infty,+\infty] SigmoidFunction:σ(x)=1+ex1其中,x[,+]
所以这个神经元的激活值实际上就是对其加权和到底有多少的评价。但有时,即使加权和大于 0 时,也不想把神经元点亮(譬如当加权和大于 10 时,激发才有意义),此时就需要加上一个偏置值 (bias),以保证神经元不被随便激发,将偏置值送进 sigmoid 映射函数即可。

这里我们做个总结:权重是告诉你第二层的神经元关注什么样的像素图像,而偏置则是告诉你加权和有多大才能让神经元的激发变得有意义

上面便解释完了其中一个神经元,但第二层的 16 个神经元都会和输入层的 784 个神经元相连接,每一个第二层神经元的 784 个接线上都带着一个权重,同样的,每个神经元都会计算自己的加权和并加上自己的偏置,最后再通过 sigmoid 输出自己的结果。此时,你肯定会想到我们需要给出 784 * 16 个权重值和 16 个偏置值,而这仅仅是输入层和第二层之间的连接,第二层和第三层以及第三层和输出层之间的连接还有它们分别自带的权重和偏置,因此整个网络参数总计有 13002 个!这相当于网络上有 13002 个旋钮开关可以调整,从而带来不一样的结果。所以,当我们讨论机器如何学习的时候,其实就是在讲,电脑应该如何设置这一大坨的数字参数,才能让它正确地解决问题。

你想哈,对于仅只有一个隐含层以及输入层输出层的神经元个数较少时,可以选择手调权重和偏置,但上面的 13002 个参数,手调?仿佛在想 pitch。。。(你再想想,如果识别一张图,就要手调这么多参数,但在机器学习里面,要识别几千上万张图,怎么调?)

通过上述的思想,可以表示单个神经元如下:
a 0 ( 1 ) = σ ( w 0 , 0 a 0 ( 0 ) + w 0 , 1 a 1 ( 0 ) + w 0 , 2 a 2 ( 0 ) + ⋅ ⋅ ⋅ + w 0 , n a n ( 0 ) + b 0 ) 其中, a i ( j ) 表示编号为 j 层的 i 号神经元的激活值; w m , n 表示 j + 1 层的 m 号神经元和 j 层的 n 号神经元之间的权重值; b k 表示 k 号神经元的偏置值。 a_0^{(1)}=\sigma(w_{0,0}a_0^{(0)}+w_{0,1}a_1^{(0)}+w_{0,2}a_2^{(0)}+\cdot\cdot\cdot+w_{0,n}a_n^{(0)}+b_0)\\其中,a_i^{(j)}表示编号为j层的i号神经元的激活值;\\w_{m,n}表示j+1层的m号神经元和j层的n号神经元之间的权重值; \\b_k表示k号神经元的偏置值。 a0(1)=σ(w0,0a0(0)+w0,1a1(0)+w0,2a2(0)++w0,nan(0)+b0)其中,ai(j)表示编号为j层的i号神经元的激活值;wm,n表示j+1层的m号神经元和j层的n号神经元之间的权重值;bk表示k号神经元的偏置值。
用矩阵形式来表达网络两层之间的权重、偏置等的运算:
[ w 0 , 0 w 0 , 1 ⋯ w 0 , n w 1 , 0 w 1 , 1 ⋯ w 1 , n ⋮ ⋮ ⋱ ⋮ w k , 0 w k , 1 ⋯ w k , n ] [ a 0 ( 0 ) a 1 ( 0 ) ⋮ a n ( 0 ) ] + [ b 0 b 1 ⋮ b k ] = [ w 0 , 0 a 0 ( 0 ) + ⋅ ⋅ ⋅ + w 0 , n a n ( 0 ) + b 0 w 1 , 0 a 0 ( 0 ) + ⋅ ⋅ ⋅ + w 1 , n a n ( 0 ) + b 1 ⋮ w k , 0 a 0 ( 0 ) + ⋅ ⋅ ⋅ + w k , n a n ( 0 ) + b k ] \begin{bmatrix} w_{0,0} & w_{0,1} & \cdots & w_{0,n}\\ w_{1,0} & w_{1,1} & \cdots & w_{1,n} \\ \vdots & \vdots & \ddots & \vdots \\ w_{k,0} & w_{k,1} & \cdots & w_{k,n} \end{bmatrix} \begin{bmatrix} a_0^{(0)} \\ a_1^{(0)} \\ \vdots \\ a_n^{(0)} \end{bmatrix} + \begin{bmatrix} b_0 \\ b_1 \\ \vdots \\ b_k \end{bmatrix}= \begin{bmatrix} w_{0,0}a_0^{(0)}+\cdot\cdot\cdot+w_{0,n}a_n^{(0)}+b_0 \\ w_{1,0}a_0^{(0)}+\cdot\cdot\cdot+w_{1,n}a_n^{(0)}+b_1 \\ \vdots \\ w_{k,0}a_0^{(0)}+\cdot\cdot\cdot+w_{k,n}a_n^{(0)}+b_k \end{bmatrix} w0,0w1,0wk,0w0,1w1,1wk,1w0,nw1,nwk,n a0(0)a1(0)an(0) + b0b1bk = w0,0a0(0)++w0,nan(0)+b0w1,0a0(0)++w1,nan(0)+b1wk,0a0(0)++wk,nan(0)+bk
下面的可视化 GIF 展示的是层间的激活值计算过程:

然后将每个该层的加权和以及偏置值计算后的值通过激活函数 sigmoid 压缩

[ a 0 ( 1 ) a 1 ( 1 ) ⋮ a k ( 1 ) ] = σ ( [ w 0 , 0 w 0 , 1 ⋯ w 0 , n w 1 , 0 w 1 , 1 ⋯ w 1 , n ⋮ ⋮ ⋱ ⋮ w k , 0 w k , 1 ⋯ w k , n ] [ a 0 ( 0 ) a 1 ( 0 ) ⋮ a n ( 0 ) ] + [ b 0 b 1 ⋮ b k ] ) \begin{bmatrix} a_0^{(1)} \\ a_1^{(1)} \\ \vdots \\ a_k^{(1)}\end{bmatrix}= \sigma(\begin{bmatrix} w_{0,0} & w_{0,1} & \cdots & w_{0,n}\\ w_{1,0} & w_{1,1} & \cdots & w_{1,n} \\ \vdots & \vdots & \ddots & \vdots \\ w_{k,0} & w_{k,1} & \cdots & w_{k,n} \end{bmatrix} \begin{bmatrix} a_0^{(0)} \\ a_1^{(0)} \\ \vdots \\ a_n^{(0)} \end{bmatrix} +\begin{bmatrix} b_0 \\ b_1 \\ \vdots \\ b_k \end{bmatrix}) a0(1)a1(1)ak(1) =σ( w0,0w1,0wk,0w0,1w1,1wk,1w0,nw1,nwk,n a0(0)a1(0)an(0) + b0b1bk )

这里,用一个非常清晰简洁明了的方式来表达网络两层之间的激活值运算:
a ( 1 ) = σ ( W a ( 0 ) + b ) 其中, W 为权重矩阵; a ( 0 ) 为上一层神经元激活矩阵; a ( 1 ) 为该层神经元激活矩阵; b 为该层神经元偏置矩阵。 a^{(1)}=\sigma(Wa^{(0)}+b)\\其中,W为权重矩阵;a^{(0)}为上一层神经元激活矩阵;\\a^{(1)}为该层神经元激活矩阵;b为该层神经元偏置矩阵。 a(1)=σ(Wa(0)+b)其中,W为权重矩阵;a(0)为上一层神经元激活矩阵;a(1)为该层神经元激活矩阵;b为该层神经元偏置矩阵。
这样的表示方式,使我们在编写程序时简便了很多(因为很多库在矩阵乘法方面做了十足的优化,譬如 numpy )

神经网络的架构就是这样。非常感谢你你能够耐心看完,但最后我还要补充和强调的两点是:

  • 在理解激活值这个概念时,它其实完全是由输入图像来决定的。
  • 把神经元看作是一个函数才更加准确,它的输入是上一层所有的神经元的输出进行运算后的值,而它的输出是一个 0 到 1 之间的值。(其实整个网络就是一个输入量为 784,输出量 10 的函数,不过这个函数极为复杂(小声bb:这货不复杂可不行,不然怎么显得牛X呢?)

好了,深度学习之神经网络结构也就结束了。神经网络的梯度下降法和反向传播法两部分的视频我还在加班加点写文档!注:俺只是个研一的算法 ”搬运工“。

我的联系方式:[email protected]

猜你喜欢

转载自blog.csdn.net/HISJJ/article/details/126750270