1.1 为什么要加入标准化层
深度学习每层网络结构都会造成数据分布的变化,当网络很深时,这种变化出现累加,顶层网络的输入数据分布变动很大。称为ICS(内部协方差偏移)。标准化层的出现就是为了解决ICS问题。
1.2 ICS带来的问题
过大的数据波动,会导致数据在经过激活函数后,落在饱和区,产生非常小的梯度,影响模型训练速度
过大的数据波动,上层网络需要不断调整去适应下层网络,因此学习率不宜设置过大
1.3 解决ICS常用的方法
采用非饱和的激活函数,比如relu
更小的学习率
更细致的参数初始化方法
数据白化(PCA计算量大,BN更合适)
1.4 常见的标准化层
BatchNorm, LayerNorm, InstanceNorm
从NLP的角度分析三种标准化的不同,数据格式为[batch_size * seq_length * embed_dim]。BN从batch维度做标准化,取第一条文本第一个字和第二条文本第一个字,计算均值和方差。LN从seq_length维度做标准化,取每一条文本计算。IN从token维度,也就是拿每个字的embedding计算
值得注意的是bert,transformer的LayerNorm,虽然调用的torch.nn.LayerNorm,但实际操作维度是IN
1.5 为什么bert用LN不用BN
一般来说CV适合BN,NLP适合LN,这是由数据特性决定的,LN更适合处理变长的序列,且不受batch_size影响。
2.位置编码
2.1 为什么要有位置编码
解决自注意力机制不能处理词序问题,让模型能够理解词的顺序信息。
2.2 位置编码有哪些
1.固定的绝对位置编码,正余弦形式(也称三角式)
2.可学习的绝对位置编码,直接给不同位置加一个position embedding,作为参数参与训练
3.相对位置编码:attention中建模token两两之间的相对距离,即下图的B
3.CRF
3.1概念
条件随机场
Bert的输出是在每个字上的分布概率,称发射矩阵,也是CRF的输入
结构是一个可学习的矩阵,称为转移矩阵
训练过程:更新转移矩阵的参数,使得损失最小
损失构成:真实路径的分数/所有路径的分数 , 其中真实路径分数是所有路径中最高的
优化算法:拟牛顿法(二阶导数下降快)
3.2和HMM对比
不需要满足HMM的两大假设:齐次马可夫假设(任一时刻隐藏状态只跟上一时刻隐藏有关),观测独立性假设(观测只取决于当前时刻的隐藏状态)
4.1结构
1.输入模块
源文本嵌入层 + 位置编码器
目标文本嵌入曾 + 位置编码器
2.输出模块
线性层 + softmax
3.编码器模块
由N层堆叠而成
每个编码器包括两个子层连接结构
第一个子层包括 多头自注意力层 + 规范化层 + 残差连接
第二个子层包括 前馈全连接子层 + 规范化层 + 残差连接
4.解码器模块
由N层堆叠而成
每个解码器层包括三个子层结构
第一个子层包括 带mask的多头自注意力层 + 规范化层 + 残差连接
第二个子层包括 多头注意力层 + 规范化层 + 残差连接
第三个子层包括 前馈全连接层 + 规范化层 + 残差连接
4.2 多头自注意力机制
1.注意力机制
注意力机制是注意力计算规则能够应用在深度学习网络的载体,除了注意力计算规则外,还包括一些必要的全连接层以及相关张量处理,使其与应用网络融为一体。
2.为什么要除根号dk
对于较大的dk来说,在完成Q·K转置后将会得到很大的值,而这将导致在经过sofrmax操作后产生非常小的梯度,不利于网络的训练。除根号dk能把输出重新拉回均值0方差1.
3.为什么要多头
解决应用注意力机制时,模型在对当前位置的信息进行编码时,会过度的将注意力集中于自身的位置的问题
5.量化
1.静态量化:静态量化是在模型训练完成后进行的,一般会对整个模型的所有参数和激活值进行量化。这种方法不需要对模型进行重新训练,但可能会导致一定程度的精度损失。
2.动态量化:动态量化是在模型推理阶段进行的,只对模型的权重进行量化,而激活值则是在每次推理时动态量化的。这种方法可以在保持精度的同时,减少模型的内存占用和计算量。大小减半以上,速度加倍,不能gpu运行
6.onnx onnx_runtime
onnx_runtime是微软的推理框架,可在cpu和gpu运行。把模型图切分成更小的子图,使用onnxruntime核心进行算子的优化.cpu上速度快3倍
onnx是模型的中间件,模型转其他部署工具可以用上。onnx格式也可以用runtime部署。
import torch
import os
import numpy as np
import time
import onnxruntime
device = torch.device("cpu")
from transformers import AutoTokenizer, AutoModel
tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
model = AutoModel.from_pretrained("bert-base-chinese").to(device)
# print(model)
text = "那些坚持996的员工最后会猝死么?"
inputs = tokenizer(text)
print(inputs)
inputs_ids = torch.LongTensor([inputs["input_ids"]]).to(device)
attention_mask = torch.LongTensor([inputs["attention_mask"]]).to(device)
time1 = time.time()
out = model(input_ids=inputs_ids, attention_mask=attention_mask)
print(time.time()-time1)
print(out["last_hidden_state"].size())
print(out["pooler_output"].size())
# 动态量化
# quantized_model = torch.quantization.quantize_dynamic(
# model, {torch.nn.Linear}, dtype=torch.qint8
# )
# print(quantized_model)
#
# torch.save(quantized_model.state_dict(), "temp.p")
# # 打印持久化文件的大小
# print('Size (MB):', os.path.getsize("temp.p") / 1e6)
# # 移除该文件
# os.remove('temp.p')
#
# torch.save(model.state_dict(), "temp.p")
# # 打印持久化文件的大小
# print('Size (MB):', os.path.getsize("temp.p") / 1e6)
# # 移除该文件
# os.remove('temp.p')
# time1 = time.time()
# text = "中国高铁怎么样?"
# inputs = tokenizer.encode(text)
# print(inputs)
# inputs = torch.LongTensor([inputs]).to(device)
# print(inputs)
# out = quantized_model(input_ids=inputs)
# print(out.logits)
# print(time.time()-time1)
example = {"input_ids":inputs_ids, "attention_mask":attention_mask}
example2 = {"input_ids":inputs_ids.cpu().numpy(), "attention_mask":attention_mask.cpu().numpy()}
torch.onnx.export(model, example, 'model.onnx', export_params=True, opset_version=11,
input_names=['input_ids', 'attention_mask'],
output_names=['last_hidden_state','pooler_output'])
onnx_session = onnxruntime.InferenceSession('model.onnx')
t1 = time.time()
pred_onnx = onnx_session.run(None, example2)
print(time.time()-t1)
print(pred_onnx[0].shape)
print("-------------------")
print(pred_onnx[1].shape)
7.LLM-finetune
https://zhuanlan.zhihu.com/p/625896377 这些训练方式理解比较难,我抽象一下,都是为了减少训练参数,在原模型上打的补丁
7.1 prefix-tuning
固定模型参数, 在输入token前面加入虚拟tokens,在训练时只更新这部分tokens
7.2 prompt-tuning
7.3 lora
固定模型参数,旁路增加降维矩阵A,升维矩阵B,输出时将AB与模型参数相加,用高斯随机初始化A,0初始化B
一般取QKV-attention增加旁路
r(矩阵的秩)一般取1,2,4,8,领域与原模型差距越大,r取值越大。同时也需要更多的语料。
A不能全为0,否则会导致B的梯度始终为0,无法更新参数