基於Mindspore2.0的GPT2預訓練模型遷移教程

華為雲開發者聯盟發表於2023-03-10
摘要: 這篇文章主要目的是為了讓大家能夠清楚如何用MindSpore2.0來進行模型的遷移。

本文分享自華為雲社群《MindNLP-基於Mindspore2.0的GPT2預訓練模型遷移教程》,作者: Super_WZB。

前言

動機

大家好,我是Super_WZB,最近MindSpore快要上線2.0版本了,由於之前主要是參與MindSpore的開發工作,一直想找機會多用一用。而自春節開始也是參與到了一項基於MindSpore的遷移工作,積攢了一些經驗,搞了一下GPT2的模型遷移工作。目前初步實現了GPT2 Model最基礎模型的推理,輸出精度能夠和hugging face中基於Pytorch的實現完全對標。整個流程我感覺非常的順利,並且也切實的體會到MindSpore目前已經可以說是從“可用”進化到了“易用”的階段。出於MindSpore佈道師的職責,同時更是自己想要分享MindSpore2.0的使用感受,寫下這篇基於MindSpore2.0的模型遷移教程,供大家參考。

目的

這篇文章主要目的是為了讓大家能夠清楚如何用MindSpore2.0來進行模型的遷移,因此更加註重整體的開發流程介紹,針對遷移中程式碼的編寫不會詳細講解,但是會給出樣例以及供查閱的文件連結。最終希望讀者能夠了解遷移模型需要做什麼,每一步應該怎麼做,做完了應該怎麼驗證。話不多說,直接開始:

1、前期準備

本章節介紹開發的前期準備工作,簡要介紹環境配置、MindSpore安裝和尋找遷移參考程式碼的途徑,每一部分的詳細操作大家可以百度搜尋一下,相關部落格非常多,這裡就不贅述。這一章節非常的基礎,如果已經是老手可以直接跳過。

1.1 尋找參照樣例

既然是遷移工作,那麼第一件事肯定是確定自己想要遷移的模型,然後找到該模型的開原始碼,提供以下幾個途徑供大家尋找原始碼,基本上比較知名的模型透過以下幾種方式都是可以找到相應程式碼的:

  • 模型論文原始碼:一般知名的模型和機構釋出的論文和模型都是有開源的,可以直接在github搜這個模型的名字或者論文的題目,找到對應機構的倉庫即可
基於Mindspore2.0的GPT2預訓練模型遷移教程
  • Paper With Code:Paper With Code是一個整合論文和相應開原始碼的網址,其中可以查到絕大多數論文以及多框架版本的實現,如果覺得官方的看不懂的話,可以找一些高引用的其他框架版本作為參考原始碼。
基於Mindspore2.0的GPT2預訓練模型遷移教程
  • Hugging Face:Hugging face 起初是一家總部位於紐約的聊天機器人初創服務商,他們本來打算創業做聊天機器人,然後在github上開源了一個Transformers庫,雖然聊天機器人業務沒搞起來,但是他們的這個庫在機器學習社群迅速大火起來。目前已經共享了超100,000個預訓練模型,10,000個資料集,變成了機器學習界的github。那其實看起來這個網站和PaperWithCode很類似,但是它勝在有模型使用教程,同時有演示視窗,並且呼叫非常方便。
基於Mindspore2.0的GPT2預訓練模型遷移教程

1.2 Git操作

找到了參考程式碼之後,大家會發現這些原始碼基本上都是儲存在GitHub上的,因此為了更方便的查閱我們需要遷移的程式碼,以及跳轉和搜尋可能存在的依賴函式,我建議大家把參考程式碼Clone到本地,然後進行對照開發:

基於Mindspore2.0的GPT2預訓練模型遷移教程

常用的git操作可以參考這篇部落格:Git的下載、安裝與使用

1.3 環境配置

1.3.1 自有硬體

如果大家自己有硬體資源,比如CPU、GPU、伺服器,那麼可以在自己的電腦本地配置神經網路的執行環境,主要包括以下步驟:

 

1.3.2 啟智社群

OpenI 啟智 新一代人工智慧開源開放平臺

啟智AI協作平臺,簡稱啟智社群 ,是一個開源線上Web應用,旨在為人工智慧演算法、模型開發提供線上協同工作環境,它提供了程式碼託管、資料集管理與共享、免費雲端算力資源支援(GPU/NPU)、共享映象等功能。

基於Mindspore2.0的GPT2預訓練模型遷移教程

啟智平臺是可以直接線上建立網路執行環境的平臺,裡面可以白嫖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總體架構如下圖所示:

基於Mindspore2.0的GPT2預訓練模型遷移教程
  • 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的社群並一鍵三連。

基於Mindspore2.0的GPT2預訓練模型遷移教程

1.4.2 MindSpore安裝

MindSpore官網就有非常詳細的安裝教程,大家可以按照官網的步驟進行安裝:

MindSpore安裝指南

基於Mindspore2.0的GPT2預訓練模型遷移教程

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包

基於Mindspore2.0的GPT2預訓練模型遷移教程

之後找到遷移需要用到的configguration_gpt2.py配置檔案和modeling_gpt2.py模型檔案(沒有字尾的一般是pytorch實現,帶tf的是tensorflow實現,我個人對於pytorch更熟悉一些所以選擇pytorch版本進行遷移)

基於Mindspore2.0的GPT2預訓練模型遷移教程

使用pycharm遠端連線,可以直接訪問檔案程式碼:

基於Mindspore2.0的GPT2預訓練模型遷移教程

2.3.1 MindNLP倉庫下載

由於遷移之後的原始碼是需要合入到MindNLP倉庫的,因此大家需要去MindNLP官方GitHub倉庫進行一鍵三連(watch+fork+star)

基於Mindspore2.0的GPT2預訓練模型遷移教程

目前MindNLP中已經有了Bert模型的遷移程式碼,因此我們是可以將這個bert.py與hugging face中的bert程式碼進行對比來學習應該如何遷移的:

基於Mindspore2.0的GPT2預訓練模型遷移教程

下載好之後同樣用pycharm開啟,剛下載的mindnlp開啟圖示的models介面是沒有gpt2這個資料夾的,這個是大家需要根據自己的模型建立的,用於存放之後遷移之後的相關檔案。我的是GPT2所以建立為gpt2,其他模型同理。

基於Mindspore2.0的GPT2預訓練模型遷移教程

之後在該資料夾下新建init___.py、config_gpt2.py、gpt2.py三個檔案,作用分別是:

基於Mindspore2.0的GPT2預訓練模型遷移教程
  • 使得當前資料夾可以被識別為一個模組用於import和呼叫
  • GPT2引數檔案
  • GPT2模型檔案

2.2 API對映

下載好了參考原始碼和MindNLP倉庫之後我們就可以正式開始網路遷移了,經過上面搭積木的例子,大家其實應該知道我們需要做的其實就是把參考原始碼中所使用框架(我參考的是pytorch,之後都以它來講解)的API替換為MindSpore中的API即可。

2.2.1 直接API對映

下面舉一個非常簡單的例子:

GPT2MLP的遷移:

基於Mindspore2.0的GPT2預訓練模型遷移教程

得益於MindSpore中API命令的規範化和統一化,我們可以發現從左邊基於pytorch的實現遷移到右邊基於mindspore的實現基本上可以直接複製貼上,圖中的Conv1D和ACT2FN是左邊hugging face原始碼自己封裝的類別,後面會講解。而這個GPT2MLP中其他的程式碼基本上是直接照搬即可,唯一的差異就是這個nn.Dropout()中的引數有些許不同,這個在2.3API差異中會介紹。

透過這一個例子大家會發現其實遷移還是非常簡單的,只要把程式碼邏輯甚至直接把程式碼搬過來就行了。這得益於目前MindSpore完善的API介面庫,大部分神經網路需要用到的介面都是有的,並且對於輸入輸出等引數的設定也是向大眾的一致標準靠齊的,所以會用其他框架就一定能很快的上手MindSpore(打波小廣告哈哈哈)。

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

基於Mindspore2.0的GPT2預訓練模型遷移教程

大家會發現,這些直接API對映的例子裡面存在一些引數或者名字不對應的情況,這將會在2.3 API差異中為大家講解。

2.2.2 hugging face自封裝類別和函式遷移

還是以GPT2MLP舉例,其中的Conv1D類別是hugging face實現GPT2時自己封裝好的類別:

基於Mindspore2.0的GPT2預訓練模型遷移教程

那我們需要做的其實也很簡單,把這個類別也遷移過來就好了。而關於這個類別該遷移到哪個檔案,這個可以選擇遷移到自己的模型檔案(即gpt2.py),也可以參照hugging face中的檔案路徑在MindNLP中相應路徑新建檔案來儲存。我這裡以遷移到gpt2.py為例:

基於Mindspore2.0的GPT2預訓練模型遷移教程

遷移之後呢,這個hugging face自定義的Conv1D類別我們也可以直接使用啦:

基於Mindspore2.0的GPT2預訓練模型遷移教程

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對映表

這兩張對映表非常重要,是遷移的基礎,一定要收藏、一定要收藏、一定要收藏

基於Mindspore2.0的GPT2預訓練模型遷移教程

比如這張圖中的差異nn.ModuleList是因為Pytorch中網路都繼承了nn.Module類別,而MindSpore網路繼承的是nn.Cell類別,因此命名有些不同:

基於Mindspore2.0的GPT2預訓練模型遷移教程

這個差異是可以在對映表中查到的,並且沒有顯示存在差異,所以我們直接給它替換掉就解決啦:

基於Mindspore2.0的GPT2預訓練模型遷移教程

2.3.2 引數差異

(1)引數值差異

引數值差異是指pytorch/tensorflow與MindSpore中API的名字相同,但是一些引數的名字或者引數的含義不同,導致API在使用時功能會產生差異,比如最經典的nn.Dropout§差異:

基於Mindspore2.0的GPT2預訓練模型遷移教程

PyTorch中預設輸入nn.Dropout(0.2)時代表有每個引數有20%的機率被丟棄,而如果在MindSpore中不指定引數名直接輸入nn.Dropout(0.2)的話代表每個引數有(1 - 0.2)即80%的機率被丟棄。這就是一個非常經典,如果大家有長期使用MindSpore的話肯定知道的差異,當然最新的版本中已經提示這種預設寫法將會刪除,之後就也可以直接使用nn.Dropout(0.2)啦:

基於Mindspore2.0的GPT2預訓練模型遷移教程

(2)引數初始化差異

這一塊主要是有一些網路API的引數在初始化時存在不同,不要小看初始化的差異,有時候網路結構都是對的,但是結果就是對標補上,很有可能就是某些網路的引數初始化不一樣,導致結果大相徑庭。

比如將PyTorch線性層nn.Linear()對映為MindSpore線性層nn.Dense():

從Pytorch的官網來看,他的nn.Linear線性層中的weight和bias應該是用均勻分佈初始化的

基於Mindspore2.0的GPT2預訓練模型遷移教程

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

基於Mindspore2.0的GPT2預訓練模型遷移教程

2.3.3 功能差異

其實大部分的功能差異都是因為2.3.2中引數沒設定好,但是也存在一小部分API確實是功能有差異,這裡舉一個很簡單的例子:

torch中的Tensor.transpose在MindSpore中應該是swapaxes

基於Mindspore2.0的GPT2預訓練模型遷移教程

而MindSpore中的Tensor.transpose實際上對應torch.Tensor.permute

基於Mindspore2.0的GPT2預訓練模型遷移教程

所以如果不仔細檢查,看到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

基於Mindspore2.0的GPT2預訓練模型遷移教程

我們可以用numpy.finfo得到相同的資料之後包裝成mindspore.Tensor

基於Mindspore2.0的GPT2預訓練模型遷移教程
  • 如果該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:

基於Mindspore2.0的GPT2預訓練模型遷移教程

我們需要對它進行測試驗證,那怎麼做呢?實際上網路說複雜了是網路,說簡單點就是一堆函式的拼接,我們測試的一個模組就是一個小的函式,只不過它是一個類別的正向執行函式(forward/construct,只是命名差異,pytorch中叫forward,mindspore中叫construct)罷了。所以想要驗證遷移結果是否正確,我們只需要例項化遷移前和遷移後的兩個類別,然後給他們的正向執行函式輸入相同的數值,再對標兩個函式的輸出結果即可。以下給出簡單的實現:

import numpy as np
import 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 np
import 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 (),點選其中的Files and version,這個介面存放了gpt2不同版本的配置檔案以及模型預訓練引數,我使用的是pytorch版本,因此我下載pytorch_model.bin以及pytorch和tensorflow通用的config.json配置檔案。

基於Mindspore2.0的GPT2預訓練模型遷移教程

將pytorch_model.bin和config.json上傳到伺服器的同一個資料夾內:

基於Mindspore2.0的GPT2預訓練模型遷移教程

接下來進行checkpoint的匯入和轉換

3.3.2 checkpoint匯入與轉換

由於我們手上的是pytorch的預訓練引數,所以我們先參照hugging face中提供的使用樣例將這個pytorch_model.bin匯入

(1)匯入pytorch預訓練引數

import torch
from 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 mindspore
from 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整網引數字典

常見引數命名差異對比

基於Mindspore2.0的GPT2預訓練模型遷移教程

獲取了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表格中的兩列,複製好之後第一件事就是直接看一下兩邊的引數個數是否相同(檢視這兩列的行數是否相同):

基於Mindspore2.0的GPT2預訓練模型遷移教程基於Mindspore2.0的GPT2預訓練模型遷移教程

貼上之後,由於我們知道存在一些命名的差異,因此我們點選mindspore這一列然後ctrl+f之後選擇替換,將gamma換成weight,將beta換成bias,得到:

基於Mindspore2.0的GPT2預訓練模型遷移教程基於Mindspore2.0的GPT2預訓練模型遷移教程

之後我們利用Excel的Exact()函式直接比較pytorch和mindspore每一行的字串是否相同:

基於Mindspore2.0的GPT2預訓練模型遷移教程

可以看到,除了前兩行,後面的引數都是一致的,而前兩行不一致其實也是正常的,因為只有一個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 torch
import mindspore
import numpy as np
from 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,真的比以前的體驗好了很多!

 

點選關注,第一時間瞭解華為雲新鮮技術~

相關文章