paddle2.x版roformer
-
这周花了点时间将pytorch版的roformer转化成了paddle版本的。
https://github.com/PaddlePaddle/PaddleNLP/pull/804
写个帖子记录一下提交PR的过程及过程中遇到的坑。坑
- 坑1:PaddleNLP库不像huggingface/transformers有个贡献指南文档。
因此在贡献的时候,我参考了下paddlepaddle的贡献指南文档。 - 坑2:提交的时候报错yapf错误。
后来发现pre-commit的时候,会使用yapf进行更新项目中的代码,然后还需要重新git add .
才能记录上被yapf修改过的文件,这样才能提交成功。
过程
# 首先fork https://github.com/PaddlePaddle/PaddleNLP # 然后克隆到本地 git clone https://github.com/【你的用户名】/PaddleNLP cd PaddleNLP # 使用 git checkout -b 创建并切换到新分支。 git checkout -b 【分支的名字】 # 安装pre-commit pip install pre-commit # 给本地分支安装pre-commit pre-commit install # 修改文件后先pre-commit格式化文件规范 pre-commit # 添加并commit修改的文件 git add . git commit -m "fix xxx" # 推送到远程分支 git push
由于paddlepaddle2.x版本与pytorch版本的API差不多一致,因此我们模型转换的时候其实很简单。可以对着pytorch的代码直接对应修改。
例如:
pytorch roformer中的@staticmethod def apply_rotary_position_embeddings( sinusoidal_pos, query_layer, key_layer, value_layer=None ): # https://kexue.fm/archives/8265 # sin [batch_size, num_heads, sequence_length, embed_size_per_head//2] # cos [batch_size, num_heads, sequence_length, embed_size_per_head//2] sin, cos = sinusoidal_pos.chunk(2, dim=-1) # sin [θ0,θ1,θ2......θd/2-1] -> sin_pos [θ0,θ0,θ1,θ1,θ2,θ2......θd/2-1,θd/2-1] sin_pos = torch.stack([sin, sin], dim=-1).reshape_as(sinusoidal_pos) # cos [θ0,θ1,θ2......θd/2-1] -> cos_pos [θ0,θ0,θ1,θ1,θ2,θ2......θd/2-1,θd/2-1] cos_pos = torch.stack([cos, cos], dim=-1).reshape_as(sinusoidal_pos) # rotate_half_query_layer [-q1,q0,-q3,q2......,-qd-1,qd-2] rotate_half_query_layer = torch.stack( [-query_layer[..., 1::2], query_layer[..., ::2]], dim=-1 ).reshape_as(query_layer) query_layer = query_layer * cos_pos + rotate_half_query_layer * sin_pos # rotate_half_key_layer [-k1,k0,-k3,k2......,-kd-1,kd-2] rotate_half_key_layer = torch.stack( [-key_layer[..., 1::2], key_layer[..., ::2]], dim=-1 ).reshape_as(key_layer) key_layer = key_layer * cos_pos + rotate_half_key_layer * sin_pos if value_layer is not None: # rotate_half_value_layer [-v1,v0,-v3,v2......,-vd-1,vd-2] rotate_half_value_layer = torch.stack( [-value_layer[..., 1::2], value_layer[..., ::2]], dim=-1 ).reshape_as(value_layer) value_layer = value_layer * cos_pos + rotate_half_value_layer * sin_pos return query_layer, key_layer, value_layer return query_layer, key_layer
paddle roformer
@staticmethod def apply_rotary_position_embeddings(sinusoidal_pos, query_layer, key_layer, value_layer=None): # https://kexue.fm/archives/8265 # sin [batch_size, num_heads, sequence_length, embed_size_per_head//2] # cos [batch_size, num_heads, sequence_length, embed_size_per_head//2] sin, cos = paddle.chunk(sinusoidal_pos, 2, axis=-1) # sin [θ0,θ1,θ2......θd/2-1] -> sin_pos [θ0,θ0,θ1,θ1,θ2,θ2......θd/2-1,θd/2-1] sin_pos = paddle.reshape( paddle.stack( [sin, sin], axis=-1), sinusoidal_pos.shape) # cos [θ0,θ1,θ2......θd/2-1] -> cos_pos [θ0,θ0,θ1,θ1,θ2,θ2......θd/2-1,θd/2-1] cos_pos = paddle.reshape( paddle.stack( [cos, cos], axis=-1), sinusoidal_pos.shape) # rotate_half_query_layer [-q1,q0,-q3,q2......,-qd-1,qd-2] rotate_half_query_layer = paddle.reshape( paddle.stack( [-query_layer[:, :, :, 1::2], query_layer[:, :, :, 0::2]], axis=-1), query_layer.shape, ) query_layer = query_layer * cos_pos + rotate_half_query_layer * sin_pos # rotate_half_key_layer [-k1,k0,-k3,k2......,-kd-1,kd-2] rotate_half_key_layer = paddle.reshape( paddle.stack( [-key_layer[:, :, :, 1::2], key_layer[:, :, :, 0::2]], axis=-1), key_layer.shape, ) key_layer = key_layer * cos_pos + rotate_half_key_layer * sin_pos if value_layer is not None: # rotate_half_value_layer [-v1,v0,-v3,v2......,-vd-1,vd-2] rotate_half_value_layer = paddle.reshape( paddle.stack( [-value_layer[:, :, :, 1::2], value_layer[:, :, :, 0::2]], axis=-1), value_layer.shape, ) value_layer = value_layer * cos_pos + rotate_half_value_layer * sin_pos return query_layer, key_layer, value_layer return query_layer, key_layer
RoFormer
模型简介
RoFormer (RoFormer: Enhanced Transformer with Rotary Position Embedding)是一个带有旋转位置嵌入(RoPE)的MLM预训练语言模型。 RoPE是一种相对位置编码方法,具有良好的理论特性。其主要思想是根据绝对位置将上下文嵌入(transformer中的 q,k)乘以旋转矩阵。可以证明上下文嵌入的内积将仅取决于相对位置。
RoPE 是唯一可用于线性注意力的相对位置嵌入。更多详情请参考论文或原博客。EleutherAI还发布了一篇博客,其中包含有关 RoPE 的直观解释和实验。本项目是RoFormer在 Paddle 2.0上的开源实现,包含了
THUCNews分类任务
的微调代码。快速开始
THUCNews分类任务数据
THUCNews分类任务所含数据集已在paddlenlp中以API形式提供,无需预先准备,使用
run_thucnews.py
执行微调时将会自动下载。执行Fine-tunning
启动Fine-tuning的方式如下:
unset CUDA_VISIBLE_DEVICES python -m paddle.distributed.launch --gpus "0" run_thucnews.py \ --model_type roformer \ --model_name_or_path roformer-chinese-base \ --max_seq_length 256 \ --batch_size 64 \ --learning_rate 2e-5 \ --num_train_epochs 3 \ --logging_steps 1 \ --save_steps 500 \ --output_dir ./tmp/ \ --device gpu \ --use_amp False
其中参数释义如下:
model_type
指示了模型类型,使用RoFormer模型时设置为roformer即可。model_name_or_path
指示了某种特定配置的模型,对应有其预训练模型和预训练时使用的 tokenizer。若模型相关内容保存在本地,这里也可以提供相应目录地址。注:roformer-chinese-base
等对应使用的预训练模型转自huggingface/transformers,具体可参考当前目录下converter中的内容。max_seq_length
表示最大句子长度,超过该长度将被截断。batch_size
表示每次迭代每张卡上的样本数目。learning_rate
表示基础学习率大小,将于learning rate scheduler产生的值相乘作为当前学习率。num_train_epochs
表示训练轮数。logging_steps
表示日志打印间隔。save_steps
表示模型保存及评估间隔。output_dir
表示模型保存路径。device
表示训练使用的设备, 'gpu’表示使用GPU, 'xpu’表示使用百度昆仑卡, 'cpu’表示使用CPU。use_amp
指示是否启用自动混合精度训练。
基于
roformer-chinese-base
在THUCNews分类任务上Fine-tuning后,在验证集上有如下结果:Task Metric Result THUCNews Accuracy 0.98 - 坑1:PaddleNLP库不像huggingface/transformers有个贡献指南文档。