01 深度学习的基础概念
深度学习是机器学习的一个分支,也是目前最主要的一个研究方向,相对于传统的机器学习而言,深度学习会简单一些。在神经网络中传递的向量就是特征,模型学得好不好,就看特征提取得好不好。在传统的特征工程中,需要从现有数据创建特征、转换和插补缺失或无效的特征、通过例如主成分分析 (PCA)的方法降低数据维度,以及根据重要性分数和相关矩阵选择最相关的特征进行模型训练,非常的繁琐,而在深度学习中,特征的提取都是自动完成的,我们只需要采集输入数据,设计神经网络结构和定义期望的结果即可。
在深度学习中,搞清楚下面几个核心概念之间的关系,就成功了大半了。
本文会介绍这些概念的用途以及举例解释,希望读者能够快速的对这些有个基本了解。
一、神经元(Neurons)
在深度学习的神经网络中,其最基础组成部分就是神经元,一个神经元只处理一种特征,成千上万的神经元组成了智慧,这与计算机如出一辙,明明只有 0 和 1 ,却组成了绚丽多彩的计算世界。下图为一个神经元计算示意图
其数学公式为: $$ \begin{align} z & =W^T = \sum^3_{i=1}x_iw_i+b = w_1x_1+w_2x_2+w_3x_3+b \\ y & = f(z) \end{align} $$ 它的计算过程可以理解为,首先有一个会处理 M 特征的神经元, 它能够提取 M 特征的权重向量去提取输入数据里有关 M 的特征值(用权重去过滤),给到激活函数进行判断,如果输入数据跟 M 有关,向量内积就会很大,也就会导致这个神经元属于激活状态。
神经元是如何工作的?
现在,假设我们要设计一个温感神经元,它需要能处理温度这种特征,能够感觉 烫
。由于温度是一个只有一维的输入,这个时候公式为 $f(x)=a(wx+b)$。 $x$ 作为神经元的输入的温度了,$w$ 是控制这个神经元对于温度的敏感程度,假如 $w$ 小,那么随着 $x$ 的增加,$wx$ 增加就比较缓慢了,它对温度就不那么敏感。$b$ 就是偏置,可以理解多大的温度才会感觉 烫
,比如这个神经元对比 20 ℃没感觉,40℃也没感觉,但是从 60 度开始就感觉到烫了,70 度非常烫,那么这个偏置就是 60 ℃。下面这个公式可以表示一个温感神经元:
$$
f(x)=\text{ReLU}(1\times x -60)=\max(0, (1\times x - 60))
$$
其中 $w=1$,$b=-60$,激活函数 $a$ 就是 ReLU 了。ReLU 会将负数都转为 0 ,正数保持不变。这样就可以做到 20℃ 没感觉,输出是 0,一直到超过 60℃,$f(x)$ 才会大于 0。
另外,值得一提的是,虽然我们能够在很多相关例子中看到矩阵乘积运算,注意这只是为了方便数学表示和利用矩阵批量计算提升速度,其本质还是向量的内积。
二、激活函数(Activation Function)
激活函数是神经网络中一个重要的组成部分,它承担一个逻辑判断的功能,对神经元接收到的信号,进行综合的判断决定神经元是否需要处于激活状态,通常认为其输出值大于 0 表示,该神经元处理激活状态。
激活函数分为线性激活函数如 $f(x)=x$ 和非线性激活函数如 ReLU、Sigmoid 。
然而,我们并不会用线性激活函数,因为它并没有增加网络复杂度,而且也可以被化简掉。举个例子,我们有个神经元 $y=a(wx+b)$,激活函数就是线性激活函数 $a=3x$,然后让 $w$ 为 2,b 为 1。接下来完整的公式就是 $y=3(2x+1)$ ,然后化简 $y=6x+2$。这种情况激活函数就没了,可以直接让 $w$ 为 6,$b$ 为 2。
所以,一般说的激活函数,都是指非线性激活函数。非线性的激活函数使得深度神经网络成为可能,没有这些非线性的激活函数,多层神经网络与单层神经网络就没有差别了。
三、神经网络的组成
一个神经网络(通常也会称其为模型)中,我们可以看成由输入层,隐藏层以及输入层组成,每层里包含若干神经元。
-
输入层(Input Layer)
输入层负责接收数据输入的,由一组线性无关的小数组成。因为如果数据线性有关的话,一组数据可以被另一组数据乘以一个系数代替,那就没有意义了。
-
对于图形来说,就是 $W\times H \times (RGB)$,宽和高以及每个像素点 RGB 三原色的值(0~255)。神经网络并不喜欢整数,实际上会对数据执行标准化变成小数。
-
对于自然语言中文来说,就是每个字编码得到一个固定的整数索引,然后用这个索引去向量表里取 N 维的词向量,词向量也是由一组小数组成。
我们要知道模型并不关心实际的数据的具体数值,就跟声音一样,以频率表达信号,振幅大小不关心,它只是表现为声音的大小,用大声念一段句子和用小声念一样能表达同一个意思。
-
-
输出层(Ouput Layer)
输出层就是定义我们期望的输出结果。
-
对于需要预测明天会不会下雨的概率,那么我们输出层只要一个神经元就好了,然后我们可以用 sigmoid 激活函数把它成 $0\sim1$ 之间的概率值。sigmoid 函数可以负无穷到正无穷之间的任意值变成 $0\sim 1$。
假如是生成图像,这一个神经就是代表一个颜色通道的一个像素,将其乘以 255,就得到该通道的颜色值了。
-
对于我们需要区分手写的数字是哪个,那么我们的输出层就要 10 个神经元,分别代表 $0\sim9$ 每个数字。在经过多分类常用的激活函数 softmax 之后,就会变成每个数字概率,这 10 个神经元输出的总和为1。 比如第 1 个神经元输出的数值最大,那输入的图形,模型预测的结果就是数字 $0$ 了。
同理,在自然语言中的文本生成。假设我们的词汇表有 N 个汉字,那么就需要 N 个神经元,一个神经元代表一个汉字输出的可能性。
-
-
隐藏层(Hidden Layers)
除了输入层和输出层之外的就是隐藏层,这也是可以由开发者进行自由设计的层,输入层已经由输入的数据定了,输出层也由输出目标定了,唯有这个隐藏层才有发挥空间。
在深度学习中,通常认为靠近输入层的网络层提取的都是低级特征,每个神经元只可以看到一小部分输入数据,越靠近输出层,其提取的特征就越高级,越抽象,能看到全局的信息,做出的预测越准。前面提到了一个神经元只处理一种特征,理论上隐藏层的层数和每层的神经元的个数是越多越好,这样它就能处理各种各样的特征了。但是,在实践中,通常是根据要处理的数据复杂度 / 特征量决定的。假如要解决的问题简单,就可以把网络设置浅一点,神经元的数量少一点。这样也有利于提升模型收敛的速度,以更少的时间完成训练。
神经网络是个黑盒,但是关于隐藏层的设计还是有技巧的。比如:
- 假如输入数据是有时序特征的,我们希望模型能够感知到这个时序,这个时候就可以用循环神经网络 RNN。
- 假如输入数据图像,而图像是有点、线、面这样的底层特征组成的,这样可以用卷积神经网络 CNN 扫描整个来提取这些特征。
- 目前主要用于提取特征的更多是模仿人类的注意力机制,它即可用于自然语言理解,又可以用于图像识别。
四、权重初始化(Weight Initialization)
一个神经网络模型由两部分组成模型算法 + 权重。在前面的神经元部分提到,最简单的神经元的表达形式是 $f(x)=a(wx+b)$,这里的 $w$ 和 $b$ 就是神经网络的可学习参数,它是可以通过训练来找到其最佳值的。虽然 $w$ 和 $b$ 一个叫权重,一个叫偏置,但在整个神经网络中,习惯上统称权重。
权重的初始化非常重要,如果初始化不得当,模型可能会无法收敛。比如权重值过大,在矩阵乘法叠加,可能会造成后续神经元的数值过大,造成梯度爆炸问题;权重值过小会造成数值过小而导致梯度消失。一般采用贴近零的随机小数(如:正态分布,均值约为0,标准差为1)进行初始化,这样每个神经元都有不同的值,即可以有不同的梯度,这样才能差异化演化出不同的职能的神经元来处理不同的特征。了解这一点即可,像是 PyTorch 框架,都有默认的初始化方法,一般直接用就好。
五、损失函数(Cost/Loss Function)与目标函数(Objective Function)
损失函数是衡量模型预测输出与我们期望输出(真实标签数据)之间的差距,神经网络的优化目标是降低这个差距。虽然标题说了有损失函数与目标函数两种说法,但是两者是可以转化等同的。损失函数优化方向是最小化,而目标函数是需要最大化。目标函数一般用于强化学习中,定义一个目标,让智能体在与环境交互中最大化奖励。在实际实现上,都会把目标函数加上一个负号,把变成最小化问题,这样就跟损失函数一样了。
六、反向传播(Backpropagation)
深度学习中数据的流向分为正向传播和反向传播,正向传播就是数据从输入层到隐藏层再到输出层,而反向传播就是要把损失函数计算出的误差以相反的方向逐层传播修正神经元的参数。每个神经元每次更新的幅度是由梯度和学习率共同决定的,学习率是全局定义的,但是梯度是由每个神经元的当前权重决定的,因此每个神经元会以不同更新幅度进行权重参数的更新。
注意,不管多复杂的神经网络,它的反向传播总逐个处理的,你可以把它想象成一个一元一次方程 $y=kx+b$,$y$ 的输出与期望相差大,误差大,调整幅度大点。
七、学习率
学习率是控制模型在反向传播中,一次参数更新的幅度,学习率过大,虽然更新的快,但是容易无法找到最优点,学习率过小,容易陷入局部优先点,而无法找到全局最优点。
在实际模型训练的时候,可以根据损失下降的不同情况来调整训练的学习率。
- 如果出现,损失缓慢平滑的下降,这个时候可以增加学习率。
- 如果出现,损失下降不稳定,一会上一会下,浮动较大,可能总是跨过最优点,这个时候可以适当降低学习率。
八、优化器
神经网络能够进行参数的自动化更新靠的是梯度下降算法,根据梯度慢慢调整网络中神经元的参数。优化器就是采用各种算法来提速模型训练收敛速度,一个好的优化器,事半功倍。
-
Basic Gradient Descent
这是最基础的梯度下降算法,直接学习率乘以梯度进行更新。
但这把一次性计算了所有的样本,计算量大,比较慢,所以就衍生出来改进版本。
-
随机梯度下降(Stochastic Gradient Descent,SGD)
针对前面的缺点,不计算全部了,改成随机采样了,Pytorch 中也有学习率衰减参数的实现。
-
Adam 优化器
这是目前用的最多的优化器。它就比前面提到更高级了,结合了物理运动的惯性。这就像是在一个 U 型曲面,在顶端放一个球,让其顺坡滚下,它一开始速度是加速,越来越快,越过最低点会冲到对面去,但是在往复多次,由于摩擦力能量损耗的因故,速度会逐渐降下来,最后会在 U 型曲面的最底部停下,也就是损失值最低点。这跟 Basic Gradient Descent 不一样,Basic Gradient Descent 慢悠悠的,不懂根据斜率加速,模型收敛耗费的时间太久了。
九、数据标准化(Normalization)
数据标准化或者归一化,可以提升加速模型训练收敛的速度。激活函数大多都在 0 点附近工作得比较好,假如偏离 0 点过远,会造成梯度消失或者梯度爆炸的问题,而数据标准化可以通过缩放或者平移,重新调整数据的到最适宜模型训练的范围。这也再次佐证了,前面提到的,模型不关心输入数据具体数值,关心的是数据的分布特征。
目前 Normalization 已经是各类模型的标配了,有其是当前流行的自然语言大模型,基本上每层网络,计算前或者计算后都会进行一次 Normalization。
十、总结
本文介绍深度学习中几个非常重要的概念,这些概念支撑起了整个深度学习。传统机器学习的特征工程中,需要手工做特征,配置不同特性权重大小,而在深度学习这一切都是自动的。我们只需要准备好数据,以及期望的输出标签数据,设计好合适的网络结构,模型可以自动从这些数据中学习。要注意的是,输入的数据和标签之间映射,要有规律,不能是无规律的噪音数据(输入 X 有对应好几个输出标签 Y),这样模型才能学习到其特征。还有个比较棒的点就是,允许存在少量的异常数据,保证有大多数正确数据,模型也能从中学得到。