温馨提示:这篇文章已超过465天没有更新,请注意相关的内容是否还可用!
摘要:本文介绍了深度学习领域的八股文,这些文章涵盖了深度学习的基本概念、技术进展、应用领域和未来趋势等方面。通过学习和了解这些内容,读者可以更好地理解深度学习的原理和应用,掌握最新的技术进展和发展趋势。这些文章对于人工智能领域的从业者、研究人员和爱好者都具有重要的参考价值。
Bert旨在通过联合左侧和右侧的上下文,从未标记文本中预训练出一个深度双向表示模型。因此,BERT可以通过增加一个额外的输出层来进行微调,就可以达到为广泛的任务创建State-of-the-arts 模型的效果,比如QA、语言推理任务。Bert的构成:由12层Transformer Encoder构成。bert的基本任务:mask language model 和 next sentence predict,mask language model的遮盖⽅式:选择15%的token进⾏遮盖,再选择其中80%进⾏mask,10%进⾏随机替换,10%不变
- 被随机选择15%的词当中以10%的概率⽤任意词替换去预测正确的词,相当于⽂本纠错任务,为BERT模型赋予了⼀定的⽂本纠错能⼒;
- 被随机选择15%的词当中以10%的概率保持不变,缓解了finetune时候与预训练时候输⼊不匹配的问题(预训练时候输⼊句⼦当中有mask,⽽finetune时候输⼊是完整⽆缺的句⼦,即为输⼊不匹配问题)。
BLEU(Bilingual Evaluation understudy) 是一种流行的机器翻译评价指标,一种基于精度的相似度量方法, 用于分析候选译文和参考译文中n元组共同出现的程度
ROUGE(Recall-Oriented Understudy for Gisting Evaluation) 一种基于召回率的相似度量方法,和BLEU类似, 无Fmeans 评价功能,主要考察翻译的充分性和忠实性, 无法评价参考译文的流畅度, 其计算的时N元组(Ngram)在参考译文和待测评译文的共现概率.
Transformer encoder的组成:self-Attention和feed forward组成 中间穿插残差⽹络;残差⽹络的作⽤:缓解层数加深带来的信息损失,采⽤两个线性变换激活函数为relu,同等层数的前提下残差⽹络也收敛得更快。随着神经网络深度的不断增加,利用sigmoid激活函数来训练被证实不如非平滑、低概率性的ReLU有效,因为ReLU基于输入信号做出门控决策。研究者提出了一种新的非线性激活函数,名为高斯误差线性单元(Gaussian Error Linear Unit)。
- G E L U ( x ) = 0.5 x ( 1 + t a n h ( 2 π ( x + 0.044715 x 3 ) ) ) GELU(x)=0.5x(1+tanh(\sqrt\frac2\pi(x+0.044715x^3))) GELU(x)=0.5x(1+tanh(π2 (x+0.044715x3)))
GELU与随机正则化有关,因为它是自适应Dropout的修正预期。这表明神经元输出的概率性更高,研究者发现,在计算机视觉、自然语言处理和自动语音识别等任务上,使用GELU激活函数的模型性能与使用RELU或者ELU的模型相当或者超越了它们。
-
import numpy as np import matplotlib.pyplot as plt def gelu_1(x): # 使用numpy实现 return 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * x ** 3))) def gelu_dao(inputs): return ((np.tanh((np.sqrt(2) * (0.044715 * inputs ** 3 + inputs)) / np.sqrt(np.pi)) + ((np.sqrt(2) * inputs * ( 0.134145 * inputs ** 2 + 1) * ((1 / np.cosh( (np.sqrt(2) * (0.044715 * inputs ** 3 + inputs)) / np.sqrt(np.pi))) ** 2)) / np.sqrt(np.pi) + 1))) / 2 def plot_gelu(): x1 = np.arange(-8, 8, 0.1) y1 = gelu_1(x1) plt.plot(x1,y1) y2 = gelu_dao(x1) plt.plot(x1, y2) plt.show() if __name__ == '__main__': plot_gelu()
self-Attetion的组成和作⽤
- 对每⼀个token分别与三个权重矩阵相乘,⽣成q,k,v三个向量,维度是64
- 计算得分,以句⼦中的第⼀个词为例,拿输⼊句⼦中的每个词的 k 去点积 q1,这个分数决定了在编码第⼀个词的过程中有多重视句⼦的其他部分
- 将分数除以键向量维度的平⽅根(根号dk),这可以使梯度更稳定。假设q和k中的向量都服从⾼斯分布,即均值为0,⽅差为1,那可qk点积都的矩阵均值为0,⽅差为dk,此时若直接做softmax,根据公式,分⺟是ex的相加,分⺟较⼤,会导致softmax的梯度变⼩,参数更新困难
- softmax将分数归⼀化,表示每个单词对编码当下位置的贡献
- ⽤v乘softmax分数,希望关注语义上相关的单词,并弱化不相关的词
多头机制类似于“多通道”特征抽取,self attention通过attention mask动态编码变⻓序列,解决⻓距离依赖(⽆位置偏差)、可并⾏计算;
- 类似于CNN中通过多通道机制进⾏特征选择;
- Transformer中先通过切头(spilt)再分别进⾏Scaled Dot-Product Attention,可以使进⾏点积计算的维度d不⼤(防⽌梯度消失),同时缩⼩attention mask矩阵。
FFN 将每个位置的Multi-Head Attention结果映射到⼀个更⼤维度的特征空间,然后使⽤ReLU引⼊⾮线性进⾏筛选,最后恢复回原始维度。
Self-Attention的输出会经过Layer Normalization,为什么选择Layer Normalization⽽不是Batch Normalization?此时,我们应该先对我们的数据形状有个直观的认识,当⼀个batch的数据输⼊模型的时候,⼤⼩为(batch_size, max_len, embedding),其中batch_size为batch的批数,max_len为每⼀批数据的序列最⼤⻓度,embedding则为每⼀个单词或者字的embedding维度⼤⼩。
⽽Batch Normalization是对每个Batch的每⼀列做normalization,相当于是对batch⾥相同位置的字或者单词embedding做归⼀化,Layer Normalization是Batch的每⼀⾏做normalization,相当于是对每句话的embedding做归⼀化。显然,LN更加符合我们处理⽂本的直觉。
BERT基于“字输⼊”还是“词输⼊”好?如果基于“词输⼊”,会出现OOV(Out Of Vocabulary)问题,会增⼤标签空间,需要利⽤更多语料去学习标签分布来拟合模型。随着Transfomer特征抽取能⼒,分词不再成为必要,词级别的特征学习可以纳⼊为内部特征进⾏表示学习。
BERT的词嵌入由符号嵌入(Token Embedding)、片段嵌入(Segmentation Embedding)和位置嵌入(Position Embedding)合成得到,表示为
E w o r d = E t o k e n + E s e g + E p o s E_{word}=E_{token}+E_{seg}+E_{pos} Eword=Etoken+Eseg+Epos
三个嵌入分量都可以表达为“独热”(one-hot)编码表示输入与嵌入矩阵的乘积形式,即
E w o r d = O t o k e n W t o k e n ∣ V ∣ ∗ H + O s e g W s e g ∣ S ∣ ∗ H + O p o s W p o s ∣ P ∣ ∗ H E_{word}= O_{token}W_{token}^{|V|*H} +O_{seg}W_{seg}^{|S|*H} +O_{pos}W_{pos}^{|P|*H} Eword=OtokenWtoken∣V∣∗H+OsegWseg∣S∣∗H+OposWpos∣P∣∗H
其中, O t o k e n O_{token} Otoken︰依据符号在词典中位置下标、对输入符号构造的one-hot编码表示; O s e g O_{seg} Oseg∶依据符号在两个序列中隶属标签(更一般的为符号属性)下标、对输入符号构造的one-hot编码表示; O p o s O_{pos} Opos :以符号在句子位置下标、对输入符号构造的one-hot编码表示: W t o k e n ∣ V ∣ ∗ H , W s e g ∣ S ∣ ∗ H , W p o s ∣ P ∣ ∗ H W_{token}^{|V|*H},W_{seg}^{|S|*H},W_{pos}^{|P|*H} Wtoken∣V∣∗H,Wseg∣S∣∗H,Wpos∣P∣∗H分别为其对应的待训练嵌入参数矩阵;|V|、|S|和|P|分别为字典维度、序列个数(更一般的为符号属性)和最大位置数;H为嵌入维度。三个one-hot编码向量与嵌入矩阵相乘,等价于构造三个以one-hot编码向量作为输入,输入维度分别为|V|、|S和|P|,输出维度均为H的全连接网络。求和即为特征融合。
bert预训练任务的损失函数,两个任务是联合学习,可以使得 BERT 学习到的表征既有 token 级别信息,同时也包含了句⼦级别的语义信息。bert的损失函数组成:第⼀部分是来⾃ Mask-LM 的单词级别分类任务;另⼀部分是句⼦级别的分类任务;
在第一部分的损失函数中,如果被mask的词集合为M,因为它是一个词典大小V上的多分类问题,所用的损失函数叫做负对数似然函数(且是最小化,等价于最大化对数似然函数),那么具体说来有:
L 1 ( θ , θ 1 ) = − ∑ i = 1 M l o g p ( m = m i ∣ θ , θ 1 ) , m i ∈ [ 1 , 2 , . . . , ∣ V ∣ ] L_1(\theta,\theta_1)=-\sum_{i=1}^Mlogp(m=m_i|\theta,\theta_1),m_i\in[1,2,...,|V|] L1(θ,θ1)=−i=1∑Mlogp(m=mi∣θ,θ1),mi∈[1,2,...,∣V∣]
在第二部分的损失函数中,在句子预测任务中,也是一个分类问题的损失函数:
L 2 ( θ , θ 2 ) = − ∑ j = 1 N l o g p ( n = n i ∣ θ , θ 2 ) , n i ∈ [ i s n e x t , n o t n e x t ] L_2(\theta,\theta_2)=-\sum_{j=1}^Nlogp(n=n_i|\theta,\theta_2),n_i\in[isnext,notnext] L2(θ,θ2)=−j=1∑Nlogp(n=ni∣θ,θ2),ni∈[isnext,notnext]
自回归与自编码
- 单向特征表示的自回归预训练语言模型,统称为单向模型:ELMO、GPT、ULMFiT、SiATL
- 双向特征表示的自编码预训练语言模型,统称为BERT系列模型:BERT、RoBERTa、ERNIE1.O/ERNIE(THU)
- 双向特征表示的自回归预训练语言模型,XLNet:将自回归LM方向引入双向语言模型方面
- 自回归语言模: 优点∶文本摘要,机器翻译等,在实际生成内容的时候,就是从左向右的,自回归语言模型天然匹配这个过程。缺点:只能利用上文或者下文的信息,不能同时利用上文和下文的信息
- 自编码语言模型:优点︰能比较自然地融入双向语言模型,同时看到被预测单词的上文和下文;缺点∶主要在输入侧引入[Mask]标记,导致预训练阶段和Fine-tuning阶段不一致的问题,因为Fine-tuning阶段是看不到[Mask]标记的
RNN的统—定义为
h t = f ( x t , h t − 1 ; θ ) h_t=f(x_t,h_{t-1};\theta) ht=f(xt,ht−1;θ)
其中 h t h_t ht是每一步的输出,它由当前输入 x t x_t xt和前一时刻输出 h t − 1 h_{t-1} ht−1共同决定,而 θ \theta θ则是可训练参数。在做最基本的分析时,我们可以假设 h t , h t − 1 , θ h_t , h_{t-1},\theta ht,ht−1,θ都是一维的,这可以让我们获得最直观的理解,并且其结果对高维情形仍有参考价值。要求我们定义的模型有一个比较合理的梯度。我们可以求得:
d h t d θ = δ h t δ h t − 1 d h t − 1 δ θ + δ h t δ θ \frac{d~ h_t}{d~ \theta}=\frac{\delta h_t}{\delta h_{t-1}}\frac{d~ h_{t-1}}{\delta \theta} +\frac{\delta h_t}{\delta \theta} d θd ht=δht−1δhtδθd ht−1+δθδht
所以步数多了,梯度消失或爆炸几乎都是不可避免的,我们只能对于有限的步数去缓解这个问题。
梯度爆炸︰设置一个梯度剪切阈值,然后更新梯度的时候,如果梯度超过这个阈值,那么就将其强制限制在这个范围之内。这可以防止梯度爆炸。梯度值变得非常大,从而对参数的更新产生巨大影响。这可能会导致模型无法收敛或收敛速度过慢。
可能引起梯度爆炸的原因(这其实就是根据反向传播的三个函数链式求导,一个是上一个神经元激活函数,一个是损失函数导数,一个是激活函数导数:
- 如果在神经网络中使用了具有饱和性质(如Sigmoid)的激活函数,并且权重初始化不当,则可能会出现数值上溢问题。当反向传播通过每一层传递时,sigmoid函数在中间区域的斜率很敏感变化很大,最终使得梯度变得异常大。
- 如果权重参数初始化过大,则在前向传播和反向传播过程中都容易造成数值溢出问题。特别是在深层神经网络中,在后面的层级上发生累积效应并放大了初始错误。
- 学习率决定了每次迭代更新参数时所采用的步长大小。如果学习率设置太大,每次更新时参数的变化就会非常剧烈,(即权重变大,数值上溢)可能导致梯度值爆炸。
饱和性质的激活函数是指在输入数据较大或较小时,激活函数的导数趋近于0,导致梯度消失或爆炸。这种情况下,神经网络可能会面临训练困难、收敛缓慢等问题。
常见的饱和性质的激活函数有Sigmoid函数和双曲正切(Tanh)函数。它们在输入接近极端值时,导数接近于0。对于Sigmoid函数而言,在输入非常大或非常小时,输出值会趋向于1或0,并且导数几乎为0;对于Tanh函数而言,在输入非常大或非常小时,输出值也会趋向于1或-1,并且导数同样几乎为0。
ReLU是一种简单但广泛使用的不饱和性质的激活函数。当输入为正时,ReLU将保持原始值作为输出;当输入为负时,则返回零作为输出。ReLU在实践中被证明可以有效地解决梯度消失问题,并提高神经网络模型的训练速度与效果。
Leaky ReLU是对ReLU的改进,它在输入为负时不返回零,而是返回一个小的非零值。这样可以避免ReLU中出现的“神经元死亡”问题(即某些神经元永远不会被激活),并且有助于增加模型的表达能力。
激活函数 表达式 导数 sigmoid f ( x ) = 1 1 + e x f(x)=\frac{1}{1+e^x} f(x)=1+ex1 f ′ ( x ) = f ( x ) ( 1 − f ( x ) ) f'(x)=f(x)(1-f(x)) f′(x)=f(x)(1−f(x)) tanh f ( x ) = e 2 x − 1 e 2 x + 1 f(x)=\frac{e^{2x}-1}{e^{2x}+1} f(x)=e2x+1e2x−1 f ′ ( x ) = 1 − ( f ( x ) ) 2 f'(x)=1-(f(x))^2 f′(x)=1−(f(x))2 Relu f ( x ) = m a x ( 0 , x ) f(x)=max(0,x) f(x)=max(0,x) f ′ ( x ) = s g n ( x ) f'(x)=sgn(x) f′(x)=sgn(x) Bnll f ( x ) = l o g ( 1 + e x ) f(x)=log(1+e^x) f(x)=log(1+ex) f ′ ( x ) e x 1 + e x f'(x)\frac{e^x}{1+e^x} f′(x)1+exex Gelu f ( x ) = 0.5 x ( 1 + t a n h ( 2 π ( x + 0.044715 x 3 ) ) ) f(x)=0.5x(1+tanh(\sqrt\frac2\pi(x+0.044715x^3))) f(x)=0.5x(1+tanh(π2 (x+0.044715x3))) swish f ( x ) = x ∗ s i g m o i d ( β ∗ x ) f(x)=x*sigmoid(\beta*x) f(x)=x∗sigmoid(β∗x) Leaky relu f ( x ) = { x if x>0 γ ∗ x if x0}\\ \gamma *x &\text{if xxγ∗xif x0if x0 α ( e x − 1 ) if x0}\\ \alpha(e^x-1) &\text{if xxα(ex−1)if x0if x = 1 , i = 1 , 2 , . . . , n \min_{w,b}\frac12||w||^2\\ y_i(w^Tx_i+b)>=1,i=1,2,...,n w,bmin21∣∣w∣∣2yi(wTxi+b)>=1,i=1,2,...,n 这是⼀个有约束条件的最优化问题,⽤拉格朗⽇函数来解决
min w , b max α = 1 2 ∣ ∣ w ∣ ∣ 2 + ∑ i = 1 m α i ( 1 − y i ( w T x i + b ) ) \min_{w,b}\max_\alpha=\frac12||w||^2+\sum_{i=1}^m\alpha_i(1-y_i(w^Tx_i+b)) w,bminαmax=21∣∣w∣∣2+i=1∑mαi(1−yi(wTxi+b))
不管直接在原特征空间,还是在映射的⾼维空间,我们都假设样本是线性可分的。虽然理论上我们总能找到⼀个⾼维映射使数据线性可分,但在实际任务中,寻找⼀个合适的核函数核很困难。此外,由于数据通常有噪声存在,⼀味追求数据线性可分可能会使模型陷⼊过拟合,因此,我们放宽对样本的要求,允许少量样本分类错误。这样的想法就意味着对⽬标函数的改变,之前推导的⽬标函数⾥不允许任何错误,并且让间隔最⼤,现在给之前的⽬标函数加上⼀个误差,就相当于允许原先的⽬标出错,引⼊松弛变量 εi≥0,公式变为:
min w , b , η 1 2 ∣ ∣ w ∣ ∣ 2 + ∑ i = 1 n η i \min_{w,b,\eta}\frac12||w||^2+\sum_{i=1}^n\eta_i w,b,ηmin21∣∣w∣∣2+i=1∑nηi
那么这个松弛变量怎么计算呢,最开始试图⽤0,1损失去计算,但0,1损失函数并不连续,求最值时求导的时候不好求,所以引⼊合⻚损失(hinge loss):
l h i n g e ( z ) = m a x ( 0 , 1 − z ) l_{hinge}(z)=max(0,1-z) lhinge(z)=max(0,1−z)
理解起来就是,原先制约条件是保证所有样本分类正确, y i ( w T x i + b ) ≥ 1 y^i (w^Tx_i+b) ≥1 yi(wTxi+b)≥1,现在出现错误的时候,一定是这个式子不被满足了,即 y i ( w T x i + b )
f ( x ) = { 1 2 ( y − f ( x ) ) 2 if |y−f(x)| ≤ δ δ ∣ y − f ( x ) ∣ − 1 2 δ 2 if |y−f(x)| > δ f(x) = \begin{cases} \frac12(y-f(x))^2 &\text{if |y−f(x)| ≤ δ}\\ δ|y-f(x)|-\frac12δ^2 &\text{if |y−f(x)| > δ}\\ \end{cases} f(x)={21(y−f(x))2δ∣y−f(x)∣−21δ2if |y−f(x)| ≤ δif |y−f(x)| > δ
XGBoost 是⼤规模并⾏ boosting tree 的⼯具,它是⽬前最快最好的开源 boosting tree ⼯具包
- 精度更⾼:GBDT 只⽤到⼀阶泰勒展开,⽽ XGBoost 对损失函数进⾏了⼆阶泰勒展开。XGBoost 引⼊⼆阶导⼀⽅⾯是为了增加精度,另⼀⽅⾯也是为了能够⾃定义损失函数,⼆阶泰勒展开可以近似⼤ᰁ损失函数;
- 灵活性更强:GBDT 以 CART 作为基分类器,XGBoost 不仅⽀持 CART 还⽀持线性分类器,(使⽤线性分类器的 XGBoost 相当于带 L1 和 L2 正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题))。此外,XGBoost ⼯具⽀持⾃定义损失函数,只需函数⽀持⼀阶和⼆阶求导;
- **正则化:**XGBoost 在⽬标函数中加⼊了正则项,⽤于控制模型的复杂度。正则项⾥包含了树的叶⼦节点个数、叶⼦节点权重的 L2 范式。正则项降低了模型的⽅差,使学习出来的模型更加简单,有助于防⽌过拟合;
- Shrinkage(缩减):相当于学习速率。XGBoost 在进⾏完⼀次迭代后,会将叶⼦节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后⾯有更⼤的学习空间;
- 列抽样:XGBoost 借鉴了随机森林的做法,⽀持列抽样,不仅能降低过拟合,还能减少计算;
- 缺失值处理:XGBoost 采⽤的稀疏感知算法极⼤的加快了节点分裂的速度;
- 可以并⾏化操作:块结构可以很好的⽀持并⾏计算。
- 虽然利⽤预排序和近似算法可以降低寻找最佳分裂点的计算ᰁ,但在节点分裂过程中仍需要遍历数据集;预排序过程的空间复杂度过⾼,不仅需要存储特征值,还需要存储特征对应样本的梯度统计值的索引,相当于消耗了两倍的内存。
-
还没有评论,来说两句吧...