NLP相关知识点集

18 阅读14分钟

传统的Seq2Seq架构有独立的Encoder和Decoder。编码器的结果是一个唯一的语义张量,不利于解码器端应用注意力机制。采用Transformer进行改进后,会有巨大的提升。

causual Decoder:

自回归语言模型,预训练和下游应用完全是一致的,严格遵守只有后面的token才能看到前面的token的规则。

训练效果高,zero-shot能力更强,具有涌现能力。

将数据写入tfrecord中的流程:

1,指定要写入的数据集路径;

2,获取所有的XML标注文件;

3,指定tfrecord的存储位置;

4,获取图像的路径

5,将数据写入到tfrecord文件中

权重矩阵参数初始化4种方式:

1,标准初始化(均匀分布):

权重参数初始化 从区间 均匀随机取值。即在(-1/sqrt(d), 1/sqrt(d))均匀分布中生成当前神经元的权重,其中d为每个神经元的输入数量。

2,随机初始化(正态分布/高斯分布):

随机初始化从 均值为0、标准差是1 的高斯分布中取样,使用一些很小的值 对参数W进行初始化。

3,Xavier初始化(Xavier正态分布初始化器):

基本思想:各层的激活值和梯度的方差 在传播过程 保持一致。

以0为中心,标准差为stddev = sqrt(2 / (fan_in + fan_out))的正态分布中抽取样本,其中fan_in是输入神经元的个数,fan_out是输出的神经元个数。

4,He初始化: 

基本思想:正向传播时激活值方差不变,反向传播时状态值梯度方法不变。


一般在训练模型时,加上model.train(), 这样会正常使用BN和Dropout

一般在测试的时候,选择model.eval(),固定模型参数,同时不会使用BN和Dropout

word2vec编码:

词袋模型两种:

CBOW模型的任务是根据上下文词汇来预测目标词汇;

Skip-gram模型是根据给定词汇预测上下文词汇

在一段文本序列中,n个词或字同时出现且相邻,这样的文本特征,称为:n_gram特征。fast_text工具中一般设置n=2;

传统的word2vec一旦训练好向量,就永久不变,且没有参考上下文的语境操作。

TF-IDF:基于决策树

TermFrequency指的是词在文本中出现的频率,IDF(Inverse-document-frequency)指的是逆向文档频率,表示词是否具有强区分度。

它表示的是某个词在当前文章中的重要性程度,经常被用于文本分类、垃圾邮件识别等场景中。

主要思想:

如果某个词或短语 在一篇文章中 出现的概率高,并且在其他文章中很少出现,则认为此词或短语具有很好的类别区分能力,适合用来分类。

TF-IDF作用:用以评估一个字词对于一个文件或一个语料库中的其中一份文件的重要程度。

FastText模型:

功能: 训练词向量、文本分类、词向量迁移

模型调优方式: 数据优化、调整训练次数、调整学习率、调整n_gram、修改损失函数的计算方式(由softmax 改为 层次softmax)、自动超参数调优

优势:结构简单、使用了层softmax(使用了霍夫曼树)、添加了n_gram特征(为了弥补模型简单而无法捕捉词序特征的缺陷)

评估指标都做了哪些优化?

1,迁移词向量

2,对数据进行增强

3,根据文本分类目标和业务要求修改损失函数。

项目中,可以考虑 使模型初始化参数为 迁移参数,这样模型训练时,可以提升起始的验证准确率。而且 再对 正负样本数据进行 回译增强,在原有的数据集基础上,再分别上采样了 正负样本各1000条,有效扩展 数据特征维度,将验证准确率再提升

加载和使用 预训练模型流程:

1,确定需要加载的预训练模型

2,加载预训练模型的映射器Tokenizer

3,加载 带头或不带头 的预训练模型:

a, 不带头的模型; b, 带有语言模型头; c, 分类模型头;d, 问答模型头

4,使用模型 获得 输出结果。

迁移学习实践:

下载微调脚本 + 配置微调脚本参数 + 运行并检测效果。

两种微调类型:

1,使用指定任务类型的微调脚本 微调预训练模型,然后使用带有 输出头的预定义网络 输出结果

2,直接加载预训练模型进行 输入文本的特征表示,后接自定义网络 进行微调 输出结果。

如何训练自己的模型?

1,在https://huggingface.co/join上创建一个账户

2,在服务器终端使用transformer-cli登陆

3,使用transformer-cli上传模型

4,使用pytorch的hub加载模型并使用


Transformer:整体特征,引入语境,加入向量权重,Q、K、V是根据输入的X训练得到,Q和K是权重,V是提取特征

SA;Multi-Head(多头)、多层堆叠、位置编码、并行加速训练

MLP:多层感知机,多个全连接层+激活函数,特征汇总,不改变维度

Q、K、V都是通过同一组输入特征x通过全连接层(会分别设置一组权重参数Wq、Wk、Wv)W·x+b的方式得到,每个词的Q会跟整个序列中每一个K计算得分,然后基于得分再分配特征

位置表达信息:在SA中每个词都会考虑整个序列的加权,所以其出现位置并不会对结果产生什么影响,相当于放哪无所谓,但这跟实际并不符合,因此模型需要加入对位置的识别,进而引入‘位置编码’,进行对每个词的位置标识,增加位置信息敏感度。

Encoder-Decoder注意力机制中的Masking:

在Encoder-Decoder注意力机制中,Masking的作用是确保Decoder在每个时间步只能关注到Encoder输入序列中已经处理的部分信息,避免未来信息的泄漏。即使attention只会attend on已经产生的sequence。这很合理,没有产生出来的东西不存在,就无法做attention。

数学实现:在训练时,通过将Decoder的每个时间步输出作为Q查询与Encoder层的输出作为Key和Value,用Q和K进行点积计算,然后将要被mask的位置对应的注意力分数设置负无穷大,使得softmax函数在计算注意力权重时,将这些位置的注意力分数为接近于0的值,从而屏蔽了未来的信息。在推理时,同样需要使用解码器掩码确保模型只能关注已经生成的部分序列信息。

Attention推理阶段: 缓存预填充 + KV-Cache

KV-Cache显存占用计算: 在推理过程,以FP16保存KV cache

seq_length * batch_size * [d_head * n_head] * layer * 2 * 2

序列长度 * 批数 * [多头数 * 每头权重矩阵维度] * transformer的层数 * 2个变量(K、V)* 2(float16存储,每个占2字节)

FeedForward层在训练的时候做了什么?

FFN层在训练过程中,通过 【特征提取】+【非线性映射】 来学习 输入序列的表示,从而为模型的下游任务提供更好的输入特征。 在训练过程中,FFN的参数是通过【BP反向传播】算法和【梯度下降优化】方法来学习的,通过【最小化模型】在训练集上的损失函数,模型会自动调整FFN层的权重和偏置。

Transformer的两个mask机制:

两种掩码:

序列掩码:用于屏蔽输入序列的填充padding部分,确保这些位置不影响自注意力的计算。

查找掩码:用于解码器中防止未来信息泄露,确保在预测下一个词时 只能使用之前的词。

降低Transformer的FFN参数数量的方式:

减少隐藏层维度、减少隐藏层神经元数量、卷积替换全连接、矩阵分解技术(SVD分解:将权重矩阵分解为多个较小的矩阵,从而减少参数数量)

从根本上限制Transformer能力的是:

数据质量和数量、计算资源、超参数选择、任务复杂度、模型结构和设计。

当有新的数据来训练Transformer模型时,如何实现模型的增量训练?

保存模型参数:

进行增量训练之前,首先需要保存当前模型的参数和状态。这样可以确保在增量训练过程中能够从先前的模型状态开始。

准备新数据 -> 加载模型 ->微调模型 -> 调整学习率 -> 评估验证 -> 保存模型

Transformer三种类型的 non-linear非线性 操作:

激活函数、LN层归一化、FFN前馈神经网络

Transformer为何只用Attention机制就解决了CNN、LSTM、RNN等能解决的一切问题?

全局依赖关系、并行计算、位置编码、多头注意力机制。

Transformer的Attention计算为什么需要进行Scaling操作:

Scaling操作的目的是 为了控制 注意力分布 的范围,防止softmax函数的输入值过大过小。

数值稳定性:

将注意力分数缩放到一个合适的范围内,提升数值稳定。

梯度稳定性:

在BP反向传播中,梯度的大小可能会受到输入值的影响,输入值过大或过小可能导致梯度消失或爆炸的问题。因而有助于稳定梯度的计算。

均匀分布:

使注意力分数分布 更加均匀,避免其中部分值 过大或过小。

Beam Search算法在Transformer的引用:

Beam Search算法是一种用于生成序列的搜索算法。

其核心思想:

在每个时间步选择最优可能的几个候选词,然后在下一个时间步继续考虑这些候选词,直到生成 完成的序列 为止。

候选词选择:根据当前时间步的概率分布,选择最有可能的K个候选词作为下一个时间步的输入。这些候选词通常是通过对概率分布进行排序或采样得到的。重复如此,直到生成的序列达到预定的长度或遇到终止标记为止。

数学原理:

局部最优解、剪枝策略、留概率最高的K个候选词。

——————————————————————————————————

当内存大小限制连一个instance的训练都无法容纳时,可以采取以下方式:

将训练数据分割成更小的批次或片段、迭代训练、保存和加载模型状态(防止训练意外情况中断,而下次可从保存点继续训练)、优化模型结构和参数(减少模型大小、调整超参数)、分布式训练(多台机器)、内存优化

ROPE:旋转位置编码。不作用在embedding输入层,参与attention计算。

具体操作流程:对序列中的每个Token,先计算其对应query和key向量,后对每个token位置计算对应的旋转位置编码,然后对每个token的query和key向量的元素应用旋转变换。最后将旋转后的query和key向量相乘后就纳进了相对位置编码。

loss spike 损失刺突的出现与:梯度更新幅度、e大小、batch大小这三个田间密切相关。

——————————————————————————————————

LSTM: 遗忘门、细胞状态C、输入状态、输出状态;

细胞状态C控制参数,决定什么样的信息会被保留, 什么样的会被遗忘。

lstm会将数据转为三维矩阵,(Batch_size, Max_length:序列当中最大长度,序列长度是固定的,对应着有多少时间步; 每个时间步输入的向量维度),每个epoch轮训练,只会以最后一个训练结果为基准,即第Max_length-1的结果;因为第2个输入(是只看前面两个词)训练得到的预测的结果会包含第1个词训练预测的特征,那么最后一个词训练的结果,必然包含所有前面的词的训练结果(特征、分类),所以中间结果是不需要的。

RNN:短序列的比较好,长序列的不太好。

NLP中每个词所在的位置,所代表的词性,所表达的语义不一定是相同的,所以不能用BN。BN的作用是为了让单个数据向中央靠拢,多用于CNN上;用BN的前提是数据在每个位置上所表达的信息尽可能类似。

batch_size是用于控制每次迭代训练时用到的样本数;

BN处理是一种用于【加速 深度神经网络 训练】的技术,通过对每个小批量数据进行归一化处理,使得每个数据的特征尽量在均值为0、方差为1附近;

BN&LN :

相同点: 都是对数据进行正规化,将输入数据归一到正态分布,加速收敛,提高训练的稳定性;

BN:一个batch的向量,同一维度的数据做正规化。

BN的缺点:对变长数据无法处理,这也就导致不同长度的语义数据无法处理,便有了LN。

LN:序列向量中,不同时刻的向量做正规化。

更深的理解:对于BN,在训练时,是对每一批训练数据做归一化,也即用每一批数据的均值和方差,把训练末期的各个batch的均值方差统计上来求平均作为推理使用。在测试时,是进行一个样本的预测,就并没有batch的概念,因此,在这个时候用的均值和方差是 全量训练数据的 均值和方差。

BN算法防止过拟合,在网络训练中,BN的使用使得一个minibatch中所有样本都被关联起来,因此网络不会从某一个训练样本中生成确定的结果,即同样一个样本的输出不再仅仅取决于样本的本身,也取决于跟这个样本同属一个batch的其他样本,而每次网络都是随机取batch,这样就会使得整个网络不会朝一个方向学习,一定程度上避免了过拟合。

LN不依赖于batch的大小和输入sequence的长度,因此可以用batchsize=1和RNN的sequence的normalize操作。padding会对Batch Normalization的seq-len这个维度有影响,计算的时候会把padding也算进去。

LN有两种结构:

Post-Norm: LN在残差结构之后;对参数正则化的效果更强,进而模型的鲁棒性也会更好。

Pre-Norm: LN是在残差结构中的主干线上,则残差结构旁支这一部分参数不需要进行正则化,正好可以防止模型的梯度爆炸和梯度消失。

如果层数少,post-Norm的效果其实要好一些,如果要把层数加大,为了保证模型的训练,pre-norm更好。

ps:Post-Norm中LN必须在F全连接之前。LN之后的数据特征符合正态分布,数值分布在期望值附近,且范围较小,如果放在F全连接之后,再进行相加,这就导致模型会被残差旁支的数据主导,则降低或失去次操作的意义。

附:三种注意力机制实现源码

#numpy实现注意力机制
def softmax(x):
    ex = np.exp(x-np.max(x))
    return ex/np.sum(ex,axis=-1,keepdims=True)

def scale_dot_product(q, k, v, mask):
    dk = k.shape[-1]
    score = np.dot(q, k.T)/np.sqrt(dk)
    weights = softmax1(score)
    weights_out = np.dot(weights,v)
    return weights,weights_out
    
#torch实现注意力机制  
def scale_dot_product(q, k, v, mask):
    dk = k.shape[-1]
    score = torch.matmul(q, k.T)/torch.sqrt(torch.tensor(dk,dtype=torch.float))
    if mask is not None:
        score += mask
    weights = torch.softmax(score, dim=-1)
    weights_out = torch.matmul(score, v)
    return weights, weights_out

#torch.einsum实现
def scaled_dot_product_attention(q,k,v,mask=None):
    #计算注意力分数
    scores = torch.einsum('...qd,...kd->...qk', q,k)/torch.sqrt(q.shape[-1])
    #可选的mask
    if mask is not None:
        scores += mask
    #计算注意力权重
    attention_weights = torch.softmax(scores, dim=-1)

    #对值进行加权平均
    output = torch.einsum('...qk,...kv->...qv', attention_weights, v)
    return output, attention_weights