深度学习核心算法
从函数到神经网络
符号主义 x -> f(x) -> y 输入->规则->输出
联结主义 基于符号主义,放弃查找精确公式,简化函数、找到近似解
函数
前提:存在一个f(x)
训练:给定一组输入与对应的输出,找出函数
最终目的:找出函数后,给定任意输入得出正确的输出
理解函数
假设f(x)=wx+b
,求函数的过程其实就是在求权重w
和b
如何求:靠猜,根据已知的x和y,猜出w、b
对于非线性的函数:套一层激活函数,f(x)=g(wx+b) 常见的激活函数:
存在多个输入如何表示函数:
非线性不够灵活: 再进行一次线性变换、套上一个激活函数
前向传播: 输入层->隐藏层->输出层
计算损失函数
什么样的w和b是好的:能使函数的输出尽量拟合真实数据
如何判断拟合的好: 给定一个w、b,带入x后得出预测值ŷ, 即代表真实值与预测值的差异 那么对于所有的x,可计算出差异之和: 进一步调整可得均方误差(MSE): 均方误差为损失函数的一种,记作:
梳理下目前的问题: 数据: 线性模型: 损失函数:,其中 目标:求解让L最小的w和b的值
L(w,b)求解举例1: 假设线性模型为,且存在一组已知数据(1,1)(2,2)(3,3)(4,4) 将值带入损失函数后得: 损失函数的几何解释:损失函 数为开口向上的抛物线,求抛物线的最低点 一元二次函数求最小值:对L(w)求导:,使导数为0,求得w=1
L(w,b)求解举例2: 假设线性模型为 损失函数: 损失函数的几何解释:损失函数为三维中开口向上的碗状图形 二元函数求最小值,让每个函数的偏导为0:
线性回归:机器学习中最基本的一种分析方法,通过寻找一个线性函数来拟合x和y的关系
对于更复杂的非线性函数,如何求解损失函数L(w,b)呢? 也是一点点试,查看调整w和b后对损失函数的影响,每次将参数向着让损失函数变小的方向进行调整,直到让损失函数足够小
回到具体问题: 偏导数 和 表示的是:当 w 或 b 发生微小变化时,损失函数 L 的变化量 变化率:
- 如果 :增大 w 会增大 L(损失函数变差)
- 如果 :增大 w 会减小 L(损失函数改善) 目标是让 L 最小化,因此需要让参数朝着能降低 L 的方向调整:
- 如果 :说明 w 增大会让 L 增大,因此应该 减小 w
- 如果 :说明 w 增大会让 L 减小,因此应该 增大 w 统一操作:无论偏导数的符号如何,参数更新方向始终为 直观解释:梯度方向是函数上升最快的方向,因此其反方向就是下降最快的方向 为了控制变化的快慢,增加一个系数:, 该系数称之为学习率(理论上越小越好) WarmUp:加载别人的模型后,需要让学习率缓慢提升,然后再根据需求衰减 梯度下降:
基于梯度下降算法求解非线性函数: 举例: ->ŷ 链式法则: 由于我们可以从右向左依次求导,然后逐步更新每一层的参数,直到把所有的神经网络参数都更新一遍,在计算前一层时用到的一些偏导数的值后边也会用到,所以不用计算那么 多,而是让这些值从右向左一点点传播过来,这样一个过程称之为反向传播
前向传播:通过x计算出y 反向传播:计算出损失函数关于每个参数的梯度,然后每个参数都向着梯度的反方向变化一点点 这就构成了神经网络的一次训练,而神经网络经过多轮这样的训练,里边的参数会一点点的变化,直到让损失函数足够小
解决过拟合
在训练数据上拟合的很好但在没见过的数据上表现的很糟糕的现象,称之为过拟合。 在没见过的数据上的表现能力我们叫它泛化能力。
并不是模型越大越好,模型过于负责会产生过拟合,可以通过简化模型复杂度、增加模型的训练数据量解决。
通过对训练数据进行裁剪、翻转、镜像、加噪声等操作可以创造出更多的训练样本,这叫做数据增强。 数据增强可以让模型不因为输入的一点点小变化对结果产生过大的波动,这称之为增强了模型的鲁棒性。
尝试从训练过程处理过拟合: 在损失函数中引入参数本身的值 ,当参数调整后导致损失函数的变化很小,此时的调整就是不合适的,一定程度上可以抑制参数的野蛮生长,该项称之为惩罚项,抑制其野蛮增长的方法叫做正则化。 为正则化系数(超参数),控制惩罚力度。 L1正则化:新损失函数= 损失函数 + L2正则化:新损失函数= 损失函数 +
Dropout 在每次训练时,随机“关闭”(丢弃)一部分神经元(通常比例是 50%),迫使网络不依赖任何单个神经元,从而提高泛化能力。
相关概念补充
梯度消失
定义:网络越深,梯度反向传播时会越来越小,导致参数更新困难。
解决办法:用梯度裁剪来防止梯度的更新过大;用合理的网络结构,比如残差网络来防止深层网络的梯度衰减。
梯度爆炸
定义:梯度数值越来越大,参数的调整幅度失去了控制。
解决办法:用合理的权重初始化和将输入数据归一化,让梯度 分布更平滑。
收敛速度过慢
定义:比如可能陷入局部最优或者来回震荡。用动量法、RMSProp、Adam来加速收敛,减少震荡。
解决办法:
1.每次迭代之间不会共享经验,“踩过的坑下次迭代还会踩” 引入动量,基于上次迭代的结果会影响到下次迭代的方向,从而更快到达终点
计算开销过大
定义:数据规模量太庞大了,每次完整的前向传播和反向传播都非常耗时。
解决办法:用mini-batch把巨量的训练数据分割成几个小批次,来降低单次的计算开销。
z-score标准化
数据预处理的一种方法,基于原始数据的均值(mean)和标准差(standarddeviation)进行数据的标准化。使得数据在多个维度上的分布差异较小。
softmax分类
通过线性模式获取到输入的得分值,现在要将得分值转换为概率值
基于分类概率表,通过非线性函数放大差异(),进行归一化处理(分值占比),计算损失值(对数)
损失值:
CNN
一个最简单的神经网络:y=g(wx+b)
如果输入变成两个:
如果输入变成三个:
如果输出变成两个:
将参数变形为矩阵:
有:Y=g(WX+b) 可能存在多层神经元,所以使用字母a表示: 输入层为第0层,用表示;第一层用表示;以此类推...
通用描述公式:
全连接层(FC):每一层的神经元都与上一层的神经元完全相连
以图像识别为例:
全连接层存在局限,假设存在30x30的图像,那么输入层的则为900个神经元,假设下一层神经元的数量是1000个,那么下一层的总参数量达到了90w个
如何保存图像之间的空间关系? 在30x30的图像中,取3x3的矩阵,矩阵的值为像素的灰度值,假设为:
引入固定矩阵:
将两个矩阵的每个对应位置的值进行相乘求和(卷积):
0x46+(-1)x45+0x82 +(-1)x140+5x162+(-1x173) +0x169+(-1)x172+0x178
求得值为250
将这种运算方式遍历滑过图像的每个地方,用得出的数值形成一个新的图像,这种方式叫做卷积运算,引入的固定矩阵叫做卷积核。
传统的图像领域,卷积核是已知的,不同的卷积核可以将图像处理成不同的效果,如模糊、浮雕、轮廓、锐化效果等。
在深度学习领域,卷积核是未知的,与神经网络中的其他参数一样,是被训练出来的一组值。将一组全连接层替换为卷积层,可以大大减少权重参数的数量,可以高效捕捉到图片中的一些局部特征。从公式上看,也就是把矩阵乘法(叉乘)替换成了卷积运算:
通过滑动窗口,可以分析每个窗口内的像素分布,计算出所需特征的值。用于计算特征的是经过训练得到的一个个"向量",通过计算向量与滑动窗口的内积确定(内积等于零代表无关联关系)。
假设输入为7x7x3(3为通道数量),则卷积核为3x3x3(3x3为常见卷积核尺寸,3为对应通道的数量),计算滑动窗口与该卷积核三个矩阵的卷积值,然后加上偏置量的值,最终得到输出值。滑动窗口遍历完输入矩阵后,得到一个输出矩阵。此时,滑动窗口步长Stride越大得到的输出的特征矩阵越小,称之为下采样。一般卷积层不会改变特征矩阵的大小,特征矩阵降维一般由池化层负责。
在一次遍历中,每个滑动窗口与相同的卷积核进行计算,叫做滑动参数共享,目的是为了减少参数量。
当卷积核存在多个时,得到多个特征矩阵,常见卷积核个数为256、512等。 卷积层可能存在多个,感受野会越来越大,从获取到低层次特征逐步进化为获取高层次特征。也就是随着卷积进行,特征图变小、感受野变大,可以单个特征值描述的信息越来越丰富。
由于滑动窗口的特性导致输入矩阵的靠内的数值会经过多次计算,就显得对边缘值的注意力很少,此处引入边缘填充,即在原始输入矩阵加入一圈0值(padding)。
卷积结果计算公式:
长度:
宽度:
H1、W1为输入矩阵的长宽,H2、W2为输出特征矩阵的长宽,为卷积核尺寸,P为边界填充数量,S为步长
池化层可对卷积层后的特征图像进行降维,减少计算量的同时保留主要特征。
最大池化: 假设需要将4x4的特征矩阵降维至2x2的矩阵,那么可以设置步长为2、滑动窗口为2x2,遍历时获取每个滑动窗口内最大的特征值,最终输出降维后的2x2特征矩阵。
由多个卷积层、池化层、全连接层构成的,适用于图像识别领域的实际网络结构,称之为卷积神经网络CNN。
一般按Block划分处理步骤,如一个Block中含有CONV、RELU、CONV、RELU、POOL,整个CNN过程会重复三次Block加上一次FC,那么该CNN共有7层(6层CONV+1层FC)。在最终FC之前需要做转换,将特征矩阵拼接为一个大向量,然后参与线性变换。
CNN进化:
1.Alexnet
2.Vgg
堆叠小的卷积核所需的参数更少一些,并且卷积过程越多,特征提取也会越细致,加入的非线性变换也随着增多,还不会增大权重参数个数,这就是VGG网络的基本出发点,用小的卷积核来完成体特征提取操作。
3.Resnet
残差连接思想:引入一个“快捷连接”(shortcut connection)或“跳跃连接”(skip connection),允许数据绕过一些层直接传播。网络的某一层引入前面层的输出作为后面层的一部分输入,通过这种方式可以直接传播梯度,有助于深层网络的训练。
RNN
在自然语言处理领域,首先需要将文字进行编码,处理成计算机能识别的数字。
一种方式是只用一个数字标识,每个词用一个唯一的数字表示。缺点:维度太低,相当于一个一维的向量;数字对理解语言没有任何意义,无法衡量词之间的相关性。
一种方式是用一个大的列向量表示,每个词在向量中仅有一位是1,其余是0,该编码方式叫做one-hot。缺点是维度太高,词表稀疏;每个词都是正交的,无法找到相关性。
折中的方式是采用词嵌入word embedding,每个词有多个位置的值不为0。可以通过点积或 余弦相似度来表示向量之间的相关性,进而表示词之间的相关性。这就将自然语言之间的联系转化为了可用数学公式计算出来的方式。
把所有的词向量组成一个大矩阵,这个大矩阵就叫做嵌入矩阵,每一列代表一个词向量。这个矩阵并非手动计算出来,而是通过深度学习的方法训练出来的,如word2vec。
词向量的维度非常高,其所在的空间的维度也非常高,称之为潜空间,有一些工具可以将潜空间的向量降维,以可视化的方式表现词之间的距离关系。
现在词语已经完成编码,可以输入到神经网络中进行计算了。 将词向量导入到输入层面临的问题:每个词向量有300维,直接输入使得输入层太大;无法体现词语的先后顺序。 图像识别领域可以通过卷积层提取特征,那么自然语言领域该如何做呢?
假设一个场景,输入为一个单词,输出为词语的褒贬性:
第一个词: -> ->
第二个词: -> ->
为了体现出两个词之间的先后顺序,这里引入隐藏状态h:
第一个词经过非线性变换后,先输出到隐藏状态h1,然后再经过一个非线性变换得到输出y1;
第二个词和前一个词的隐藏状态h1一起参与运算,先输出到隐藏状态h2,然后再经过一个非线性变换得到输出y2;
...
这样的话,前一个词的信息不断向下传递,直到传到最后一句话的最后一个词那里。
这时矩阵需要进行区分:针对词向量的矩阵,针对隐藏状态的矩阵,最终计算输出结果的矩阵。
同理,对于偏置向量b也是如此。
这时,我们便有了循环神经网络RNN:
RNN存在的问题: 1.信息会随着时间步的增多而逐渐丢失,无法捕捉长期依赖,而有的语句恰恰是距离很远的地方,起到了关键性的作用 2.RN必须按顺序处理,每个时间不依赖上一个时间部的隐藏状态的计算结果。
那为了解决这些问题,人们使用GRU和LSTM改进了传统的RN,但是这些仍然是建立在让信息一点一点按照时间部传递的思路来解决,只能缓解而无法根治。那我们是否有一种可以彻底抛弃这种顺序计算,直接一眼把全部信息尽收眼底的新方案呢?有的,那就是transformer
Transformer
假设当前存在四个词向量
给每个词向量一个位置编码,表示该词在整个句子中的位置,将位置编码加入到原来的词向量中,此时的词向量就有了位置信息。
为了让每个词包含上下文信息,进行如下处理:
引入矩阵(query,要去查询的),与每个词向量相乘,得到维度不变的向量
引入矩阵(key,等着被查的),与每个词向量相乘,得到维度不变的向量
引入矩阵(value,实际特征信息),与每个词向量相乘,得到维度不变的向量
此时,词向量分别通过线性变换映射成了qkv,维度不变
让与做点积(),表示在第一个词的视角里,第一个词与第二个词的相似度是多少;
让与做点积(),表示在第一个词的视角里,第一个词与第三个词的相似度是多少;
让与做点积(),表示在第一个词的视角里,第一个词与第三个词的相似度是多少;
让与做点积(),表示在第一个词的视角里,和自己的相似度是多少。
此时得到四个相似度系数 ,将其分别和v向量相乘再相加,得到
此时代表在第一个词的视角下,按照和它相似度大小、按权重把每个词的词向量都加到了一块,这就把全部上下文的信息都包含在第一个词中了。也就是对于第一个词向量,经过上述处理后得到了新的词向量,其包含了位置信息、其他词的上下文信息。这就是注意力Attention机制做的事情。
进一步优化
有的时候一个词和另一个词的关系在不同的视角下是不同的,对注意力机制来说就是要多次计算相关性。
之前是每个词计算一组QKV,现在在原有QKV的基础上再经过两个权重矩阵变成两组QKV,给每个词两个学习机会。
对于第一个词: 变换成 , 变换成 , 变换成
对于第一个词: 变换成 , 变换成 , 变换成
其他词类推...
这里的每组qkv成为一个头,如 、 ... 就拼接组成了head1, 、 ... 就 拼接组成了head2
在每个头里的qkv仍然经过刚才的注意力层的运算,得到 和 ,将两个a向量拼接得到四个相似度系数: 拼接 得到 , 拼接 => ...
这种方式就叫做多头注意力Multi-Head Attention
如果词向量维度为300,存在三个头,那么每个头计算后得出的向量为100,三个头相加后为300,与词向量维度一致。
引入多头其实就是在丰富特征。
对于以上Transformer架构图:左侧叫做编码器Encoder,右侧叫做解码器Decoder
以翻译任务的训练过程举例:
左侧送入待翻译文本,进行词嵌入、引入位置编码,经过多头注意力、残差和归一化处理,送入全连接神经网络,再残差、归一化处理,结果送入右侧解码器的一个多头注意力机制的两个输入中,作为K、V矩阵
右侧解码器中送入翻译后的文本,同样进行词嵌入、引入位置编码,经过多头注意力、残差和归一化处理,送入多头注意力的一个输入中,作为矩阵Q,和刚刚编码器送入的K、V矩阵一起经过多头注意力(Cross Attention)、残差、归一化处理,再全连接神经网络、残差、归一化处理,最后经过一层线性变换的神经网络投射到词表向量中,最后用Softmax层转化为概率,这就代表预测的下一个词在词表中的概率分布。
右侧解码器中存在一个掩码Mask,用于在训练时将后续的词进行掩盖,便于模型进行自主推理(每个词只允许使用前边词的特征)。如果训练时有偏差,解码器输出的下一个词的概率不符合预期结果,那么就计算损失函数,再反向传播调整Transformer结构中的各种权重矩阵,直到学习好为止。
NLP相关概念:
基于变换器的双向编码器表示技术(Bidirectional Encoder Representations from Transformers,BERT),其实就是Transformer的Encoder部分。
训练方法1:输入语料,随机mask掉15%的词,用模型预测被mask的词汇。训练方法2:预测两个句子是否应该连在一起。
特点:通过预训练可以同时学习左右两侧的上下文。
适合领域:文本分类;命名实体识别(NER);关系抽取。
视觉Transformer Vit
图像识别领域,图像是不包含上下文的,需要手动 对图片做分割,生成上下文。
引入卷积,获取到分割图像的特征矩阵。
引入class token,cls-token会和所有token一起共同参与训练,在训练结束后,单独提取cls-token拿来做下游任务(分类、分割等)。
深度学习中常用的归一化技术:
1.Batch Normalization (BN) 核心思想
- 对 每个特征通道,计算当前 Batch 中所有样本的均值和方差,然后归一化
- 训练时维护全局均值和方差的移动平均,推理时直接使用
优点
- 加速 CNN 训练,缓解梯度消失/爆炸。
- 对初始化和学习率更鲁棒。 缺点
- 依赖 Batch Size:小 Batch 时(如 Batch=1),统计量估计不准,性能下降。
- 不适用于 RNN/Transformer:因为序列长度可变,Batch 内统计量无意义。
2. Layer Normalization (LN) 核心思想
- 对 每个样本的所有特征 计算均值和方差,独立于 Batch
- 训练和推理行为一致,无需存储统计量。
优点
- 不依赖 Batch Size:适合动态输入(如变长序列)。
- 在 Transformer 和 RNN 中表现优异。 缺点
- 对 CNN 效果不如 BN(因为空间维度未归一化)。
BN与LN如何选择:
- CNN(图像):优先用 BN(Batch Size ≥ 16)。
- RNN/Transformer(序列):必须用 LN。
- 小 Batch/动态输入:LN 或替代方案(Group Norm、Instance Norm)。