写点什么

基于 Mindspore2.0 的 GPT2 预训练模型迁移教程

  • 2023-03-10
    中国香港
  • 本文字数:11780 字

    阅读完需:约 39 分钟

基于Mindspore2.0的GPT2预训练模型迁移教程

本文分享自华为云社区《MindNLP-基于Mindspore2.0的GPT2预训练模型迁移教程》,作者: Super_WZB。

前言

动机


大家好,我是 Super_WZB,最近 MindSpore 快要上线 2.0 版本了,由于之前主要是参与 MindSpore 的开发工作,一直想找机会多用一用。而自春节开始也是参与到了一项基于 MindSpore 的迁移工作,积攒了一些经验,所以最近蹭蹭 ChatGPT 的热度,搞了一下 GPT2 的模型迁移工作。目前初步实现了 GPT2Model 最基础模型的推理,输出精度能够和 hugging face 中基于 Pytorch 的实现完全对标。整个流程我感觉非常的顺利,并且也切实的体会到 MindSpore 目前已经可以说是从“可用”进化到了“易用”的阶段。出于 MindSpore 布道师的职责,同时更是自己想要分享 MindSpore2.0 的使用感受,写下这篇基于 MindSpore2.0 的模型迁移教程,供大家参考。

目的


这篇文章主要目的是为了让大家能够清楚如何用 MindSpore2.0 来进行模型的迁移,因此更加注重整体的开发流程介绍,针对迁移中代码的编写不会详细讲解,但是会给出样例以及供查阅的文档链接。最终希望读者能够了解迁移模型需要做什么,每一步应该怎么做,做完了应该怎么验证。话不多说,直接开始:

1、前期准备


本章节介绍开发的前期准备工作,简要介绍环境配置、MindSpore 安装和寻找迁移参考代码的途径,每一部分的详细操作大家可以百度搜索一下,相关博客非常多,这里就不赘述。这一章节非常的基础,如果已经是老手可以直接跳过。

1.1 寻找参照样例


既然是迁移工作,那么第一件事肯定是确定自己想要迁移的模型,然后找到该模型的开源代码,提供以下几个途径供大家寻找源码,基本上比较知名的模型通过以下几种方式都是可以找到相应代码的:


  • 模型论文源码:一般知名的模型和机构发布的论文和模型都是有开源的,可以直接在 github 搜这个模型的名字或者论文的题目,找到对应机构的仓库即可



  • Paper With Code:Paper With Code 是一个整合论文和相应开源代码的网址,其中可以查到绝大多数论文以及多框架版本的实现,如果觉得官方的看不懂的话,可以找一些高引用的其他框架版本作为参考源码。



  • Hugging Face:Hugging face 起初是一家总部位于纽约的聊天机器人初创服务商,他们本来打算创业做聊天机器人,然后在 github 上开源了一个 Transformers 库,虽然聊天机器人业务没搞起来,但是他们的这个库在机器学习社区迅速大火起来。目前已经共享了超 100,000 个预训练模型,10,000 个数据集,变成了机器学习界的 github。那其实看起来这个网站和 PaperWithCode 很类似,但是它胜在有模型使用教程,同时有演示窗口,并且调用非常方便。


1.2 Git 操作


找到了参考代码之后,大家会发现这些源码基本上都是保存在 GitHub 上的,因此为了更方便的查阅我们需要迁移的代码,以及跳转和搜索可能存在的依赖函数,我建议大家把参考代码 Clone 到本地,然后进行对照开发:



常用的 git 操作可以参考这篇博客:Git的下载、安装与使用

1.3 环境配置

1.3.1 自有硬件


如果大家自己有硬件资源,比如 CPU、GPU、服务器,那么可以在自己的电脑本地配置神经网络的运行环境,主要包括以下步骤:


  • 安装 anaconda:anaconda 是一个非常便捷好用的包管理工具,基本上大家都使用它来管理 python 运行环境,安装流程参考:anaconda安装-超详细版-CSDN博客

  • 创建并激活虚拟环境:安装好了 anaconda 之后就可以创建虚拟环境了,之所以要创建自己的虚拟环境是因为不同的项目和开发者需要的运行环境不同,需要的包不同,即使需要同样的包可能需求的版本也不同,因此最好是对每一个项目创建一个单独的虚拟环境,以免影响他人使用,创建虚拟环境流程参考:使用 Anaconda 创建 Python 虚拟环境-CSDN博客

  • 安装依赖包:python 开发的一大优势就是有非常多可以直接安装调用的依赖包,这些包中包含了大量奇奇怪怪非常有用的接口,可以大大简化 python 项目的开发难度。因此大部分基于 python 的项目都导入了非常多的依赖包,在运行之前必须将它们都安装好否则会报错,依赖包通常采用 pip 安装的方式,可以从以下几个网址下载:

    PyPI · The Python Package Index(官网,可能需要科学上网,不该镜像的话默认从这里下载)

    pip更换软件源方法-CSDN博客(如果官网下载异常,可以参考博客切换以下国内镜像)

    http://mirrors.aliyun.com/pypi/simple/ (阿里云镜像)https://pypi.mirrors.ustc.edu.cn/simple/ (中国科技大学镜像)http://pypi.douban.com/simple/ (豆瓣镜像)https://pypi.tuna.tsinghua.edu.cn/simple/ (清华大学镜像)http://pypi.mirrors.ustc.edu.cn/simple/(中科科学技术大学镜像)

1.3.2 启智社区


OpenI 启智 新一代人工智能开源开放平台


启智 AI 协作平台,简称启智社区 ,是一个开源在线 Web 应用,旨在为人工智能算法、模型开发提供在线协同工作环境,它提供了代码托管、数据集管理与共享、免费云端算力资源支持(GPU/NPU)、共享镜像等功能。



启智平台是可以直接在线创建网络运行环境的平台,里面可以白嫖 GPU/NPU 资源,配置也非常容易,个人感觉非常的好用,如果大家没有自己的硬件资源的话可以创建一个账号,用启智来进行调试:


GPU 调试参考:OpenIOSSG/MNIST_PytorchExample_GPU - OpenI - 启智 AI 开源社区提供普惠算力!


NPU 调试参考:OpenIOSSG/MNIST_Example NPU - OpenI - 启智 AI 开源社区提供普惠算力!

1.4 安装 MindSpore

1.4.1 MindSpore 简介


安装之前,请允许我先介绍和宣传一下 MindSpore:MindSpore 官网介绍


昇思 MindSpore 是一个全场景深度学习框架,旨在实现易开发、高效执行、全场景覆盖三大目标。


其中,易开发表现为 API 友好、调试难度低;高效执行包括计算效率、数据预处理效率和分布式训练效率;全场景则指框架同时支持云、边缘以及端侧场景。


昇思 MindSpore 总体架构如下图所示:



  • ModelZoo(模型库):ModelZoo 提供可用的深度学习算法网络,也欢迎更多开发者贡献新的网络(ModelZoo地址)。

  • Extend(扩展库):昇思 MindSpore 的领域扩展库,支持拓展新领域场景,如 GNN/深度概率编程/强化学习等,期待更多开发者来一起贡献和构建。

  • Science(科学计算):MindScience 是基于昇思 MindSpore 融合架构打造的科学计算行业套件,包含了业界领先的数据集、基础模型、预置高精度模型和前后处理工具,加速了科学行业应用开发。

  • Expression(全场景统一 API):基于 Python 的前端表达与编程接口,支持两个融合(函数/OOP 编程范式融合、AI+数值计算表达融合)以及两个统一(动静表达统一、单机分布式表达统一)。

  • 第三方前端:支持第三方多语言前端表达,未来计划陆续提供 C/C++、华为自研编程语言前端-仓颉(目前还处于预研阶段)等第三方前端的对接工作,引入更多的第三方生态。

  • Data(数据处理层):提供高效的数据处理、常用数据集加载等功能和编程接口,支持用户灵活地定义处理注册和 pipeline 并行优化。

  • Compiler(AI 编译器):图层的核心编译器,主要基于端云统一的 MindIR 实现三大功能,包括硬件无关的优化(类型推导、自动微分、表达式化简等)、硬件相关优化(自动并行、内存优化、图算融合、流水线执行等)、部署推理相关的优化(量化、剪枝等)。

  • Runtime(全场景运行时):昇思 MindSpore 的运行时系统,包含云侧主机侧运行时系统、端侧以及更小 IoT 的轻量化运行时系统。

  • Insight(可视化调试调优工具):昇思 MindSpore 的可视化调试调优工具,能够可视化地查看训练过程、优化模型性能、调试精度问题、解释推理结果。

  • Armour(安全增强库):面向企业级运用时,安全与隐私保护相关增强功能,如对抗鲁棒性、模型安全测试、差分隐私训练、隐私泄露风险评估、数据漂移检测等技术。

    对昇思 MindSpore 感兴趣的开发者,可以参与昇思 MindSpore 的社区并一键三连(Watch/Star/Fork)


1.4.2 MindSpore 安装


MindSpore 官网就有非常详细的安装教程,大家可以按照官网的步骤进行安装:


MindSpore 安装指南


2、网络迁移


神经网络其实可以理解为搭积木,而不同框架就可以理解为不同品牌的积木包,比如有乐高、森宝、启蒙等等,不同品牌的积木包中肯定有非常多的积木是类似可代替的。


比如 A 品牌推出了一个 Super Mario 超级马里奥的积木套装,而我们手头有 B 品牌的零散积木,只要我们有了这个 Super Mario 的搭建步骤图,我们同样可以用 B 品牌的积木构造出一个基本相同的 Super Mario。


那么我们将品牌 A 替换为框架 A品牌 B 替换为 MindSpore积木替换为需要用到的 API 接口构建图替换为 GPT2 的论文。那么:


我们有了基于框架 A 的 GPT2 模型,而我们手头有 MindSpore 中大量的可调用接口,那么我们只需要参照 GPT2 的网络结构图和原论文,就可以用 MindSpore 写出一个基本相同的 GPT2 模型,这个过程就是模型迁移


经过上面的例子,大家应该大致了解网络迁移是在干个什么事情,而实际上网络的迁移工作也非常简单,主要考验开发者对于网络模型构建以及多种深度学习框架的熟悉情况。不过不同的模型网络结构肯定是不相同的,因此本章节只会介绍迁移流程和每一步应该做什么,具体怎么迁移就需要大家读懂源码,然后参照我给出的 api 映射表具体问题具体分析。


首先介绍一下后续迁移讲解用到的资源情况:


2.1 源码下载

2.1.1 参考源码下载


前往 huggingface/transformers 仓库下载 transformers 包



之后找到迁移需要用到的 configguration_gpt2.py 配置文件和 modeling_gpt2.py 模型文件(没有后缀的一般是 pytorch 实现,带 tf 的是 tensorflow 实现,我个人对于 pytorch 更熟悉一些所以选择 pytorch 版本进行迁移)



使用 pycharm 远程连接,可以直接访问文件代码:


2.3.1 MindNLP 仓库下载


由于迁移之后的源码是需要合入到 MindNLP 仓库的,因此大家需要去MindNLP官方GitHub仓库进行一键三连(watch+fork+star)



目前 MindNLP 中已经有了 Bert 模型的迁移代码,因此我们是可以将这个 bert.py 与 hugging face 中的 bert 代码进行对比来学习应该如何迁移的:



下载好之后同样用 pycharm 打开,刚下载的 mindnlp 打开图示的 models 界面是没有 gpt2 这个文件夹的,这个是大家需要根据自己的模型创建的,用于存放之后迁移之后的相关文件。我的是 GPT2 所以创建为 gpt2,其他模型同理。



之后在该文件夹下新建 init___.py、config_gpt2.py、gpt2.py 三个文件,作用分别是:



  • 使得当前文件夹可以被识别为一个模块用于 import 和调用

  • GPT2 参数文件

  • GPT2 模型文件

2.2 API 映射


下载好了参考源码和 MindNLP 仓库之后我们就可以正式开始网络迁移了,经过上面搭积木的例子,大家其实应该知道我们需要做的其实就是把参考源码中所使用框架(我参考的是 pytorch,之后都以它来讲解)的 API 替换为 MindSpore 中的 API 即可。

2.2.1 直接 API 映射


下面举一个非常简单的例子:


GPT2MLP 的迁移:



得益于 MindSpore 中 API 命令的规范化和统一化,我们可以发现从左边基于 pytorch 的实现迁移到右边基于 mindspore 的实现基本上可以直接复制粘贴,图中的 Conv1D 和 ACT2FN 是左边 hugging face 源码自己封装的类别,后面会讲解。而这个 GPT2MLP 中其他的代码基本上是直接照搬即可,唯一的差异就是这个 nn.Dropout()中的参数有些许不同,这个在 2.3API 差异中会介绍。


通过这一个例子大家会发现其实迁移还是非常简单的,只要把代码逻辑甚至直接把代码搬过来就行了。这得益于目前 MindSpore 完善的 API 接口库,大部分神经网络需要用到的接口都是有的,并且对于输入输出等参数的设置也是向大众的一致标准靠齐的,所以会用其他框架就一定能很快的上手 MindSpore(打波小广告哈哈哈)。


下面是更多直接 API 映射的例子:



大家会发现,这些直接 API 映射的例子里面存在一些参数或者名字不对应的情况,这将会在 2.3 API 差异中为大家讲解。

2.2.2 hugging face 自封装类别和函数迁移


还是以 GPT2MLP 举例,其中的 Conv1D 类别是 hugging face 实现 GPT2 时自己封装好的类别:



那我们需要做的其实也很简单,把这个类别也迁移过来就好了。而关于这个类别该迁移到哪个文件,这个可以选择迁移到自己的模型文件(即 gpt2.py),也可以参照 hugging face 中的文件路径在 MindNLP 中相应路径新建文件来保存。我这里以迁移到 gpt2.py 为例:



迁移之后呢,这个 hugging face 自定义的 Conv1D 类别我们也可以直接使用啦:


2.3 API 差异


接下来讲一下迁移中出现的 API 名字或者参数存在差异的问题,API 差异主要包括 API 命名差异、API 参数差异、API 功能差异。

2.3.1 命名差异


命名差异就是说 MindSpore 某个接口和 pytorch 等其他框架的功能是一致的,但是 API 的名字不同,这时候我们就需要查询 pytorch 等其他框架中某个 API 在 MindSpore 的名字叫啥,而这就需要用到 MindSpore 官方给出的 pytorch/tensorflow API 映射表:


可以看到其中收纳了绝大多数常用的 API 接口,我们只需要在网页中搜索原来 pytorch/tensorflow 的 API 名就可以找到 MindSpore 这边对应的 API 名字,并且 MindSpore 这边还非常细致的给出了每个 API 映射之间的关系,是完全一致、还是存在差异,点赞


PyTorch 与 MindSpore API 映射表


TensorFlow 与 MindSpore API 映射表


这两张映射表非常重要,是迁移的基础,一定要收藏、一定要收藏、一定要收藏



比如这张图中的差异 nn.ModuleList 是因为 Pytorch 中网络都继承了 nn.Module 类别,而 MindSpore 网络继承的是 nn.Cell 类别,因此命名有些不同:



这个差异是可以在映射表中查到的,并且没有显示存在差异,所以我们直接给它替换掉就解决啦:


2.3.2 参数差异


(1)参数值差异


参数值差异是指 pytorch/tensorflow 与 MindSpore 中 API 的名字相同,但是一些参数的名字或者参数的含义不同,导致 API 在使用时功能会产生差异,比如最经典的 nn.Dropout§差异



PyTorch 中默认输入 nn.Dropout(0.2)时代表有每个参数有 20%的概率被丢弃,而如果在 MindSpore 中不指定参数名直接输入 nn.Dropout(0.2)的话代表每个参数有(1 - 0.2)即 80%的概率被丢弃。这就是一个非常经典,如果大家有长期使用 MindSpore 的话肯定知道的差异,当然最新的版本中已经提示这种默认写法将会删除,之后就也可以直接使用 nn.Dropout(0.2)啦:



(2)参数初始化差异


这一块主要是有一些网络 API 的参数在初始化时存在不同,不要小看初始化的差异,有时候网络结构都是对的,但是结果就是对标补上,很有可能就是某些网络的参数初始化不一样,导致结果大相径庭。


比如将 PyTorch 线性层 nn.Linear()映射为 MindSpore 线性层 nn.Dense():


从 Pytorch 的官网来看,他的 nn.Linear 线性层中的 weight 和 bias 应该是用均匀分布初始化的



而 MindSpore 中的 nn.Dense 线性层中 weight 使用 normal 初始化的,而 bias 使用 zeros 初始化的


2.3.3 功能差异


其实大部分的功能差异都是因为 2.3.2 中参数没设置好,但是也存在一小部分 API 确实是功能有差异,这里举一个很简单的例子:


torch 中的 Tensor.transpose 在 MindSpore 中应该是 swapaxes



而 MindSpore 中的 Tensor.transpose 实际上对应 torch.Tensor.permute



所以如果不仔细检查,看到 mindspore 中有 transpose API 就直接迁移过来的话最后的结果往往是不正确。因此大家在迁移时一定要仔细核对每一步迁移的 API 是否是正确的映射,多查表、多查表、多查表!!!

2.4 API 缺失


极少出的情况会出现 pytorch/tensorflow 中的 API 在 MindSpore 查不到的情况:


  • 如果缺失的 API 是非常重要并且常用,那么大家可以在mindspore官方gitee仓库中提交 issue 申请补充该 API(但是一般常用的 APi 都已经有了,很少有这种情况)

  • 如果该 API 在 numpy 中有相应实现,我们可以调用 numpy 的 API 生成数据之后再用 mindspore.Tensor 包装起来:

    比如 mindspore 中没有这个 finfo API



我们可以用 numpy.finfo 得到相同的数据之后包装成 mindspore.Tensor



  • 如果该 API 使用频率不高,并且 numpy 中也没有相应实现,那么我们可以自己用 python 写一个类来实现该 API 的功能。即写一个类,实现其初始化函数和运行函数,然后调用即可,具体实现方法这里就不赘述。

2.5 注意事项


  • 如果存在 Dropout、BatchNorm 等训练和预测阶段行为不一致的,或存在随机性的,需要将模块设置为预测模式:

    Pytorch:module.eval()

    MindSpore: cell.set_train(False)

  • MindSpore 和 Pytorch 的参数命名可能不同,如:

    Pytorch: nn.Embedding.weight, MindSpore: nn.Embedding.embedding_table

    Pytorch: nn.BatchNorm1d.weight, MindSpore: nn.BatchNorm1d.gamma

    Pytorch: nn.BatchNorm1d.bias, MindSpore: nn.BatchNorm1d.beta

  • 由于框架机制不同,Pytorch 部分操作无需转换,直接删除即可:

    Tensor.contiguous()

    Tensor.to(device)

3、迁移验证


清楚了网络迁移应该干什么,以及如何查找对应的 API 之后,我们就可以对自己迁移的网络进行验证了,验证主要包括两个方面:输出 shape 验证、输出精度验证,验证流程从小到大依次为单模块验证、整网验证、checkpoint 验证。

3.1 单模块验证


单模块验证就是对网络中每个单独的模块进行验证,比如对于迁移好的 GPT2MLP:



我们需要对它进行测试验证,那怎么做呢?实际上网络说复杂了是网络,说简单点就是一堆函数的拼接,我们测试的一个模块就是一个小的函数,只不过它是一个类别的正向运行函数(forward/construct,只是命名差异,pytorch 中叫 forward,mindspore 中叫 construct)罢了。所以想要验证迁移结果是否正确,我们只需要实例化迁移前和迁移后的两个类别,然后给他们的正向运行函数输入相同的数值,再对标两个函数的输出结果即可。以下给出简单的实现:


import numpy as npimport modeling_gpt2, gpt2, configuration_gpt2, config_gpt2
if __name__ == "__main__": config_pt = configuration_gpt2.GPT2Config() // 获取pytorch的配置 config_ms = config_gpt2.GPT2Config() // 获取mindspore的配置 pt_net = modeling_gpt2.GPT2MLP(config_pt) // 实例化pytorch的GPT2MLP模块 ms_net = gpt2.GPT2MLP(config_ms) // 实例化mindsproe的GPT2MLP模块 input_np = np.random.randint(0, 10, (2, 512)) // 使用numpy随机生成一个shape为(2, 512)的numpy.array pt_input = torch.tensor(input_np) // 将numpy.array转化为pytorch.Tensor ms_input = mindspore.Tensor(input_np) // 将numpy.array转化为mindspore.Tensor
pt_out = pt_net(pt_input) // 调用pytorch正向函数GPT2MLP.forward()计算结果 ms_out = ms_net(ms_input) // 调用mindspore正向函数GPT2MLP.construct()计算结果 assert pt_out.size() == ms_out.shape // 对比pytorch和mindspore输出的shape,必须相同否则迁移出错 loss = 1e-3 // 精度误差一般为1e-5,最大为1e-3,必须小于1e-3否则迁移出错 assert np.allclose(pt_out.detach().numpy(), ms_out.asnumpy(), loss, loss) // 将结果全部转成array然后对比精度
复制代码


最终我们的目的就是要给 pytorch 和 mindspore 的两个模块输入相同的数据,他们的输出 shape 完全一致,精度误差在 1e-3 之内就代表该模块基本迁移成功了。每个模块都这样子验证正确之后,我们就可以尝试把整个网络搭建起来然后进行验证了

3.2 整网验证


整网验证其实和每个模块测试验证没啥区别,网络说白了就是个大函数,所以就把 GPT2MLP 改成 GPT2Model 其实就差不多了,无非就是输出可能多几个。


当然我说的仅仅只是测试代码很好写,和模块测试没啥区别,但是整个网络连起来之后可能会出现单模块测试时未出现的 bug,这也很正常,如果出现 bug 一点点 debug 检查就好了。


import numpy as npimport modeling_gpt2, gpt2, configuration_gpt2, config_gpt2
if __name__ == "__main__": config_pt = configuration_gpt2.GPT2Config() // 获取pytorch的配置 config_ms = config_gpt2.GPT2Config() // 获取mindspore的配置 pt_net = modeling_gpt2.GPT2Model(config_pt) // 实例化pytorch的GPT2MLP模块 ms_net = gpt2.GPT2Model(config_ms) // 实例化mindsproe的GPT2MLP模块 input_np = np.random.randint(0, 10, (2, 512)) // 使用numpy随机生成一个shape为(2, 512)的numpy.array pt_input = torch.tensor(input_np) // 将numpy.array转化为pytorch.Tensor ms_input = mindspore.Tensor(input_np) // 将numpy.array转化为mindspore.Tensor
pt_out = pt_net(pt_input) // 调用pytorch正向函数GPT2MLP.forward()计算结果 ms_out = ms_net(ms_input) // 调用mindspore正向函数GPT2MLP.construct()计算结果 assert pt_out.size() == ms_out.shape // 对比pytorch和mindspore输出的shape,必须相同否则迁移出错 loss = 1e-3 // 精度误差一般为1e-5,最大为1e-3,必须小于1e-3否则迁移出错 assert np.allclose(pt_out.detach().numpy(), ms_out.asnumpy(), loss, loss) // 将结果全部转成array然后对比精度
复制代码


最终我们需要达到的目的和模块验证一致,向 pytorch 和 mindspore 的整个网络输入相同的数据,最终要求网络输出的个数相同、shape 一致、精度误差在 1e-3 以内。满足以上要求我们就可以进行最后的 checkpoint 验证了。

3.3 checkpoint 验证


以上的验证都是在检查网络的流程以及计算是否正确,而其中网络的参数都是随机初始化的,而为了达到迁移的最终目的:”直接调用训练好的预训练模型,可以达到与原论文相同的结果“。我们必须将预训练好的模型参数 checkpoint 导入进来,然后在”指定参数“的情况下再进行一次整网验证,如果也能够满足网络输出的个数相同、shape 一致、精度误差在 1e-3 以内的要求,那么我们的 checkpoint 验证也就成果啦,这就说明这个 GPT2Model 真正迁移成功了。下面我简要介绍一下应该如何进行 checkpoint 验证

3.3.1 checkpoint 下载


一般 NLP 这边的大模型官方是有预训练的参数的,但是有些官方放出来的网站死活就是打不开,因此我还是推荐大家使用 hugging face 中来下载 checkpoint:


以 GPT2 为例,我们前往 GPT2 的 hugging face 网址 gpt2 at main (huggingface.co),点击其中的 Files and version,这个界面存放了 gpt2 不同版本的配置文件以及模型预训练参数,我使用的是 pytorch 版本,因此我下载 pytorch_model.bin 以及 pytorch 和 tensorflow 通用的 config.json 配置文件。



将 pytorch_model.bin 和 config.json 上传到服务器的同一个文件夹内:



接下来进行 checkpoint 的导入和转换

3.3.2 checkpoint 导入与转换


由于我们手上的是 pytorch 的预训练参数,所以我们先参照 hugging face 中提供的使用样例将这个 pytorch_model.bin 导入


(1)导入 pytorch 预训练参数


import torchfrom transformers import GPT2Model, GPT2Config
model_name = '/home/xxxxxx/wzb/mindnlp/pt_pretrained' // pytorch checkpoint存放路径model_config = GPT2Config.from_pretrained(model_name) // 导入GPT2配置pt_net = GPT2Model.from_pretrained(model_name, config=model_config) // 导入GPT2 checkpoint中的参数
复制代码


(2)创建 MindSpore 的 GPT2Model 模型


import mindsporefrom mindnlp.models.gpt2 import gpt2, config_gpt2
ms_config = config_gpt2.GPT2Config() // 获取mindspore GPT2配置ms_net = gpt2.GPT2Model(config=ms_config) // 创建mindspore GPT2Model
复制代码


(3)核对参数是否对应


获取 pytorch 和 mindspore 的网络参数字典,而由于 pytorch 和 mindspore 中有部分网络参数的命名不同,所以我们需要核对一下两边的参数是不是都能对应的上:


pt_dict = pt_net.state_dict()                                       // 获取pytorch整网参数字典ms_dict = ms_net.parameters_dict()                                  // 获取mindspore整网参数字典
复制代码


常见参数命名差异对比



获取了 pt_dict 和 ms_dict 之后我们可以将他们打印出来看看参数是否能够对应


for pt_key in pt_dict:                                               // 打印pytorch所有参数名    print(pt_key)print("+++++++++++++++++++++++++++++++++++++++++")                   // 分界线for ms_key in ms_dict:                                               // 打印mindspore所有参数名	print(ms_key)
复制代码


打印出来之后自己人眼核对那可太累了,我推荐大家使用 excel 来进行比对。由于 print()会自动换行,所以我们将 pytorch 和 mindspore 的参数复制之后直接粘贴到 excel 表格中的两列,复制好之后第一件事就是直接看一下两边的参数个数是否相同(查看这两列的行数是否相同):




粘贴之后,由于我们知道存在一些命名的差异,因此我们点击 mindspore 这一列然后 ctrl+f 之后选择替换,将 gamma 换成 weight,将 beta 换成 bias,得到:




之后我们利用 Excel 的 Exact()函数直接比较 pytorch 和 mindspore 每一行的字符串是否相同:



可以看到,除了前两行,后面的参数都是一致的,而前两行不一致其实也是正常的,因为只有一个 embeeding 层,所以我就没有将 embedding_table 替换为 weight,实际上是对的,至此参数全部对应正确后核对结束。


(4)参数导入


参数对应一致后,我们需要将 pytorch 网络的参数导入 mindspore 的网络,同时需要注意对名称不一致参数的替换处理:


for key, parameter in ms_net.parameters_and_names():                // 获取ms模型的参数名和数值    if 'embedding_table' in key:                                    // 参数名中的embedding_table替换为weight        key = key.replace('embedding_table', 'weight')    elif 'gamma' in key:        key = key.replace('gamma', 'weight')                        // 参数名中的gamma替换为weight    elif 'beta' in key:        key = key.replace('beta', 'bias')                           // 参数名中的beta替换为bias    // 依据key获取pytorch中相应参数的数值并赋给mindspore当前参数parameter,上面替换参数名就是为了get(key)的时候不会找不到    parameter.set_data(mindspore.Tensor(pt_dict.get(key).detach().numpy()))
复制代码


参数全部正确导入之后我们就可以进入最终的 checkpoint 整网验证了

3.3.3 checkpoint 整网验证


获取了 Pytorch 和 MindSpore 导入了参数的网络后,我们就可以和之前的 3.2 整网验证一样,构造输入然后验证输出是否对标,最终整体代码如下:


import torchimport mindsporeimport numpy as npfrom transformers import GPT2Model, GPT2Config
from mindnlp.models.gpt2 import gpt2, config_gpt2
if __name__ == "__main__": model_name = '/home/xxxxxx/wzb/mindnlp/pt_pretrained' model_config = GPT2Config.from_pretrained(model_name) pt_net = GPT2Model.from_pretrained(model_name, config=model_config) ms_config = config_gpt2.GPT2Config() ms_net = gpt2.GPT2Model(config=ms_config)
pt_dict = pt_net.state_dict() ms_dict = ms_net.parameters_dict() for key, parameter in ms_net.parameters_and_names(): if 'embedding_table' in key: key = key.replace('embedding_table', 'weight') elif 'gamma' in key: key = key.replace('gamma', 'weight') elif 'beta' in key: key = key.replace('beta', 'bias') parameter.set_data(mindspore.Tensor(pt_dict.get(key).detach().numpy()))
input_ids = np.random.randint(0, 10, (2, 512)) pt_input = torch.tensor(input_ids) ms_input = mindspore.Tensor(input_ids)
pt_out = pt_net(pt_input) ms_out = ms_net(ms_input)
assert pt_out.size() == ms_out.shape print("shape对标通过")
loss = 1e-3 assert np.allclose(pt_out.detach().numpy(), ms_out.asnumpy(), loss, loss) print("精度对标通过,误差:%f", loss)
复制代码


如果最终输出个数、shape 和精度全部通过,那么恭喜你网络 GPT2Model 迁移成功,之后你只需要重复以上的操作,把其他的 GPT2 变形全部迁移成功,本次 MindNLP 的预训练模型迁移工作就做完成了,完结撒花!!!

总结


通过本文的阅读,大家应该是能够了解 MindNLP 的预训练模型迁移工作需要做什么,怎么做以及怎么验证结果。而如果大家能够独立完成一个 Model 的迁移工作,就会发现目前的 MindSpore2.0.0 实际上已经比较好用了,API 丰富并且映射表格非常详细,对于差异的描述也非常清晰,报错信息也比之前精准多了(当然还是需要努力)。看来在大家共同的努力下,MindSpore 还是取得了非常显著的提升,当然距离最初设想的动静统一目标还是有不小的差距,还是需要不断的查漏补缺。


综合来说,国产深度学习框架的发展道阻且长、任重而道远,很开心自己能够为其贡献自己的一份力。同时作为昇思 MindSpore 的布道师我想说:从未使用过 MindSpore 的同学可以基于这篇文章来体验一下 MindSpore,曾经使用过但是因为各种原因“退坑”了的同学也不妨试一下 MindSpore2.0,真的比以前的体验好了很多!


点击关注,第一时间了解华为云新鲜技术~

发布于: 2023-03-10阅读数: 4
用户头像

提供全面深入的云计算技术干货 2020-07-14 加入

生于云,长于云,让开发者成为决定性力量

评论

发布
暂无评论
基于Mindspore2.0的GPT2预训练模型迁移教程_人工智能_华为云开发者联盟_InfoQ写作社区