自顶向下,整体到细节
一、首先找到对应的是属于哪个范式的论文
机器学习(特征工程),神经网络(结构工程)、预训练(目标函数)、预训练提示预测(提示),每个范式都有相应的典型代表 目前预训练比较典型,分为,编码、解码、编码-解码
二、严格按照步骤执行
三 tokenizer也是整体到细节
一共这几个类型,每个都有相应的代表 sentencepiece wordpiece bpe
考虑到对外网的屏蔽直接在colab,本地写好代码直接上操作即可
tokenizer对齐
"""
colab上运行安装包
!pip install datasets transformers[sentencepiece] paddlenlp paddlepaddle-gpu paddlepaddle
"""
import paddlenlp
import transformers
import numpy as np
text = 'welcome to use paddlenlp'
torch_tokenizer = transformers.LongformerTokenizer.from_pretrained('allenai/longformer-base-4096')
torch_tokenizer_rt = transformers.RobertaTokenizer.from_pretrained('roberta-base')
paddle_tokenizer = paddlenlp.transformers.RobertaTokenizer.from_pretrained('roberta-wwm-ext')
torch_inputs = torch_tokenizer(text)
torch_inputs_rt = torch_tokenizer_rt(text)
paddle_inputs = paddle_tokenizer(text)
print(torch_inputs_rt)
print('*****************************************************')
print(torch_inputs)
print('*****************************************************')
print(paddle_inputs)
model对齐
import paddlenlp
import transformers
import torch
import paddle
import numpy as np
text = 'welcome to use paddlenlp'
torch_tokenizer = transformers.AlbertTokenizer.from_pretrained('albert-base-v2')
paddle_tokenizer = paddlenlp.transformers.AlbertTokenizer.from_pretrained('albert-base-v2')
torch_inputs = torch_tokenizer(text)
paddle_inputs = paddle_tokenizer(text)
torch_inputs = {k: torch.tensor([v]) for (k, v) in torch_inputs.items()}
paddle_inputs = {k: paddle.to_tensor([v]) for (k, v) in paddle_inputs.items()}
torch_model = transformers.AlbertModel.from_pretrained('albert-base-v2')
torch_model.eval()
torch_outputs_logits = torch_model(**torch_inputs)[0]
torch_array = torch_outputs_logits.cpu().detach().numpy()
paddle_model = paddlenlp.transformers.AlbertModel.from_pretrained('albert-base-v2')
paddle_model.eval()
paddle_outputs_logits = paddle_model(**paddle_inputs)[0]
paddle_array = paddle_outputs_logits.numpy()
diff = torch_array - paddle_array
print(np.amax(abs(diff)))
数据类型的各种直观的转换
各种数据类型的转换,一开始文本转换成id的类型,是python数据类型字典,列表这些 后面输入到模型需要把python数据类型转换成tensor数据类型 模型输出的还是tensor,最后转换成numpy数据类型做进一步比对运算
模型转换说明
都是字典而已
PyTorch和Paddle都是通过序列化和反序列化模型的 state dict (状态字典)来进行参数权重的存储和加载的。 state dict 从数据结构上来看就是一个字典(比如Python中的dict), 其中key是模型参数的名称(数据类型为string),而value则为key所对应的值(数据类型为Tensor)。 参数存储时,先获取目标对象的 state dict ,然后将 state dict 存储至磁盘; 参数载入时,先从磁盘载入保存的 state dict ,然后通过 set_state_dict() 方法配置到目标对象中。
只是一个名字
按照约定俗成的命名规则,Paddle框架保存的模型文件名一般后缀为 ‘.pdparams’ , PyTorch框架保存的模型文件名一般后缀为 ‘.pt’ 、 ‘.pth’ 或者 ‘.bin’ 。 虽然后缀并不影响模型的保存和加载,但我们一般都会遵循这个命名规范。
一个预训练模型的结构一定包含预训练阶段所有的模型结构,把预训练的任务弄清楚
pooler 模块 pooler模块在最后一层encoder之后,是我们对最后一层encoder输出的池化操作,
cls 模块 cls模块是我们计算mlm(masked language model)和next sentence prediction(nsp)任务的结构。 'cls.predictions’开头的参数是我们做mlm任务时的参数,'cls.seq_relationship’开头的参数是我们做nsp预测任务时的参数。
pytorch模型参数是ordered_dict,paddle是dict
两者的存储是相似的,PyTorch里使用的是python中的ordered_dict来存储模型的参数状态, 在Paddle中则使用的是python中的dict来来进行存储。
paddle的线性层是和pytorch是互为转置的
bert.encoder.layer.0.intermediate.dense.weight 和 bert.encoder.layers.0.linear1.weight 这两个keys是相对应的一组参数名,但是他们的values形状却不相同;前者是 [3072, 768] , 后者是 [768, 3072] ,两者刚好是一个转置的关系。这是因为PyTorch对于 nn.Linear 模块的保存是将权重的shape进行转置后保存的。 所以在我们进行 state dict 转换的时候,需要注意做好shape的转换(例如将PyTorch模型里 nn.Linear层对应的参数权重转置处理后生成Paddle的参数权重)。
注意还存在参数一对多和多对一的情况
有些模型结构可能在实现时对参数的处理有差异导致存在参数的拆分或者合并等操作, 此时我们需要进行参数多对一或者一对多的映射,同时将对应的values拆分或者合并。
注意对齐是预测前向而已,一定要配置eval状态,排除dropout
前向精度的对齐十分简单,我们只需要保证两者输入是一致的前提下,观察得到的输出是否一致。 这里有几个注意事项,我们运行推理时需要打开eval模式,设置dropout为0等操作去除随机性造成的影响。
配置文件,就是相关的超参数配置是一样的
除了得到的模型权重文件,我们还需要准备模型配置文件。将模型权重文件(model_state.pdparams)和模型配置文件(model_config.json) 这两个文件放在同一个路径下,我们就可以进行模型前向精度的对齐验证,下面提供了bert模型对齐前向精度的代码示
pytorch是直接tokenizer就得到张量,可以直接输入模型
torch_inputs = torch_tokenizer(text, return_tensors="pt")
torch_outputs = torch_model(**torch_inputs)
paddle得到的是python dict类型,所以要做转换
paddle_inputs = paddle_tokenizer(text)
paddle_inputs = {k:paddle.to_tensor([v]) for (k, v) in paddle_inputs.items()}
paddle_outputs = paddle_model(**paddle_inputs)
{'input_ids': [0, 605, 42557, 7, 304, 21775, 8476, 39031, 2], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]} <class 'transformers.tokenization_utils_base.BatchEncoding'>
*****************************************************
{'input_ids': [101, 12759, 8228, 11927, 10257, 8168, 11407, 10986, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0]} <class 'dict'>
!wget https://cdn-lfs.huggingface.co/bert-base-uncased/097417381d6c7230bd9e3557456d726de6e83245ec8b24f529f60198a67b203a
通过上面的路径方式在colab上快速下载和验证,能达到1秒10多M
权重转换
import paddle
import torch
import numpy as np
torch_model_path = "pytorch_model.bin"
torch_state_dict = torch.load(torch_model_path)
paddle_model_path = "bert_base_uncased.pdparams"
paddle_state_dict = {}
# State_dict's keys mapping: from torch to paddle
keys_dict = {
# about embeddings
"embeddings.LayerNorm.gamma": "embeddings.layer_norm.weight",
"embeddings.LayerNorm.beta": "embeddings.layer_norm.bias",
# about encoder layer
'encoder.layer': 'encoder.layers',
'attention.self.query': 'self_attn.q_proj',
'attention.self.key': 'self_attn.k_proj',
'attention.self.value': 'self_attn.v_proj',
'attention.output.dense': 'self_attn.out_proj',
'attention.output.LayerNorm.gamma': 'norm1.weight',
'attention.output.LayerNorm.beta': 'norm1.bias',
'intermediate.dense': 'linear1',
'output.dense': 'linear2',
'output.LayerNorm.gamma': 'norm2.weight',
'output.LayerNorm.beta': 'norm2.bias',
# about cls predictions
'cls.predictions.transform.dense': 'cls.predictions.transform',
'cls.predictions.decoder.weight': 'cls.predictions.decoder_weight',
'cls.predictions.transform.LayerNorm.gamma': 'cls.predictions.layer_norm.weight',
'cls.predictions.transform.LayerNorm.beta': 'cls.predictions.layer_norm.bias',
'cls.predictions.bias': 'cls.predictions.decoder_bias'
}
for torch_key in torch_state_dict:
paddle_key = torch_key
for k in keys_dict:
if k in paddle_key:
paddle_key = paddle_key.replace(k, keys_dict[k])
if ('linear' in paddle_key) or ('proj' in paddle_key) or ('vocab' in paddle_key and 'weight' in paddle_key) or ("dense.weight" in paddle_key) or ('transform.weight' in paddle_key) or ('seq_relationship.weight' in paddle_key):
paddle_state_dict[paddle_key] = paddle.to_tensor(torch_state_dict[torch_key].cpu().numpy().transpose())
else:
paddle_state_dict[paddle_key] = paddle.to_tensor(torch_state_dict[torch_key].cpu().numpy())
print("torch: ", torch_key,"\t", torch_state_dict[torch_key].shape)
print("paddle: ", paddle_key, "\t", paddle_state_dict[paddle_key].shape, "\n")
paddle.save(paddle_state_dict, paddle_model_path)
在权重转换好的基础上,还有底层tokenizer和model代码已经编写完的情况下,就可以验证代码编写和权重转换是否正确了。
随机一个输入验证是否转换对齐
text = "Welcome to use paddle paddle and paddlenlp!"
torch_model_name = "bert-base-uncased"
paddle_model_name = "bert-base-uncased"
# torch output
import torch
import transformers
from transformers.models.bert import *
# torch_model = BertForPreTraining.from_pretrained(torch_model_name)
torch_model = BertModel.from_pretrained(torch_model_name)
torch_tokenizer = BertTokenizer.from_pretrained(torch_model_name)
torch_model.eval()
torch_inputs = torch_tokenizer(text, return_tensors="pt")
torch_outputs = torch_model(**torch_inputs)
torch_logits = torch_outputs[0]
torch_array = torch_logits.cpu().detach().numpy()
print("torch_prediction_logits shape:{}".format(torch_array.shape))
print("torch_prediction_logits:{}".format(torch_array))
# paddle output
import paddle
import paddlenlp
from paddlenlp.transformers.bert.modeling import *
import numpy as np
# paddle_model = BertForPretraining.from_pretrained(paddle_model_name)
paddle_model = BertModel.from_pretrained(paddle_model_name)
paddle_tokenizer = BertTokenizer.from_pretrained(paddle_model_name)
paddle_model.eval()
paddle_inputs = paddle_tokenizer(text)
paddle_inputs = {k:paddle.to_tensor([v]) for (k, v) in paddle_inputs.items()}
paddle_outputs = paddle_model(**paddle_inputs)
paddle_logits = paddle_outputs[0]
paddle_array = paddle_logits.numpy()
print("paddle_prediction_logits shape:{}".format(paddle_array.shape))
print("paddle_prediction_logits:{}".format(paddle_array))
# the output logits should have the same shape
assert torch_array.shape == paddle_array.shape, "the output logits should have the same shape, but got : {} and {} instead".format(torch_array.shape, paddle_array.shape)
diff = torch_array - paddle_array
print(np.amax(abs(diff)))
下游任务fine-tuning验证(可选)
当我们对齐前向精度时,一般来说我们的模型转换就已经成功了。我们还可以运行下游任务fine-tuning进行double check。 同样的,我们需要设置相同的训练数据,相同的训练参数,相同的训练环境进行fine-tuning来对比两者的收敛性以及收敛指标。这个就是复现原论文了。
一、复现这还是第一阶段,二、更改网络是第二阶段,三、自己创造网络结构就是第三阶段了
|