在Graph模式下,Python代碼并不是由Python解釋器去執行,而是將代碼編譯成靜態計算圖,然后執行靜態計算圖。
在靜態圖模式下,MindSpore通過源碼轉換的方式,將Python的源碼轉換成中間表達IR(Intermediate Representation),并在此基礎上對IR圖進行優化,最終在硬件設備上執行優化后的圖。MindSpore使用基于圖表示的函數式IR,稱為MindIR,詳情可參考中間表示MindIR。
MindSpore的靜態圖執行過程實際包含兩步,對應靜態圖的Define和Run階段,但在實際使用中,在實例化的Cell對象被調用時用戶并不會分別感知到這兩階段,MindSpore將兩階段均封裝在Cell的__call__方法中,因此實際調用過程為:
model(inputs) = model.compile(inputs) + model.construct(inputs),其中model為實例化Cell對象。
使用Graph模式有兩種方式:一是調用@jit裝飾器修飾函數或者類的成員方法,所修飾的函數或方法將會被編譯成靜態計算圖。jit使用規則詳見jit API文檔。二是設置ms.set_context(mode=ms.GRAPH_MODE),使用Cell類并且在construct函數中編寫執行代碼,此時construct函數的代碼將會被編譯成靜態計算圖。Cell定義詳見Cell API文檔。
由于語法解析的限制,當前在編譯構圖時,支持的數據類型、語法以及相關操作并沒有完全與Python語法保持一致,部分使用受限。借鑒傳統JIT編譯的思路,從圖模式的角度考慮動靜圖的統一,擴展圖模式的語法能力,使得靜態圖提供接近動態圖的語法使用體驗,從而實現動靜統一。為了便于用戶選擇是否擴展靜態圖語法,提供了JIT語法支持級別選項jit_syntax_level,其值必須在[STRICT,LAX]范圍內,選擇STRICT則認為使用基礎語法,不擴展靜態圖語法。默認值為LAX,更多請參考本文的擴展語法(LAX級別)章節。全部級別都支持所有后端。
-
STRICT: 僅支持基礎語法,且執行性能最佳。可用于MindIR導入導出。
-
LAX: 支持更多復雜語法,最大程度地兼容Python所有語法。由于存在可能無法導出的語法,不能用于MindIR導入導出。
本文主要介紹,在編譯靜態圖時,支持的數據類型、語法以及相關操作,這些規則僅適用于Graph模式。
使用靜態圖加速
背景介紹
AI編譯框架分為兩種運行模式,分別是動態圖模式以及靜態圖模式。MindSpore默認情況下是以動態圖模式運行,但也支持手工切換為靜態圖模式。兩種運行模式的詳細介紹如下:
動態圖模式
動態圖的特點是計算圖的構建和計算同時發生(Define by run),其符合Python的解釋執行方式,在計算圖中定義一個Tensor時,其值就已經被計算且確定,因此在調試模型時較為方便,能夠實時得到中間結果的值,但由于所有節點都需要被保存,導致難以對整個計算圖進行優化。
在MindSpore中,動態圖模式又被稱為PyNative模式。由于動態圖的解釋執行特性,在腳本開發和網絡流程調試過程中,推薦使用動態圖模式進行調試。 如需要手動控制框架采用PyNative模式,可以通過以下代碼進行網絡構建:
%%capture captured_output
# 實驗環境已經預裝了mindspore==2.2.14,如需更換mindspore版本,可更改下面mindspore的版本號
!pip uninstall mindspore -y
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore==2.2.14
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor
ms.set_context(mode=ms.PYNATIVE_MODE) # 使用set_context進行動態圖模式的配置class Network(nn.Cell):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.dense_relu_sequential = nn.SequentialCell(nn.Dense(28*28, 512),nn.ReLU(),nn.Dense(512, 512),nn.ReLU(),nn.Dense(512, 10))
?def construct(self, x):x = self.flatten(x)logits = self.dense_relu_sequential(x)return logits
?
model = Network()
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
output = model(input)
print(output)
[[-0.00134926 -0.13563682 -0.02863023 -0.05452826 0.03290743 -0.12423715
-0.0582641 -0.10854103 -0.08558805 0.06099342]
[-0.00134926 -0.13563682 -0.02863023 -0.05452826 0.03290743 -0.12423715
-0.0582641 -0.10854103 -0.08558805 0.06099342]
[-0.00134926 -0.13563682 -0.02863023 -0.05452826 0.03290743 -0.12423715
-0.0582641 -0.10854103 -0.08558805 0.06099342]
[-0.00134926 -0.13563682 -0.02863023 -0.05452826 0.03290743 -0.12423715
-0.0582641 -0.10854103 -0.08558805 0.06099342]
[-0.00134926 -0.13563682 -0.02863023 -0.05452826 0.03290743 -0.12423715
-0.0582641 -0.10854103 -0.08558805 0.06099342]
…
[-0.00134926 -0.13563682 -0.02863023 -0.05452826 0.03290743 -0.12423715
-0.0582641 -0.10854103 -0.08558805 0.06099342]
[-0.00134926 -0.13563682 -0.02863023 -0.05452826 0.03290743 -0.12423715
-0.0582641 -0.10854103 -0.08558805 0.06099342]
[-0.00134926 -0.13563682 -0.02863023 -0.05452826 0.03290743 -0.12423715
-0.0582641 -0.10854103 -0.08558805 0.06099342]
[-0.00134926 -0.13563682 -0.02863023 -0.05452826 0.03290743 -0.12423715
-0.0582641 -0.10854103 -0.08558805 0.06099342]]
靜態圖模式
相較于動態圖而言,靜態圖的特點是將計算圖的構建和實際計算分開(Define and run)。有關靜態圖模式的運行原理,可以參考靜態圖語法支持。
在MindSpore中,靜態圖模式又被稱為Graph模式,在Graph模式下,基于圖優化、計算圖整圖下沉等技術,編譯器可以針對圖進行全局的優化,獲得較好的性能,因此比較適合網絡固定且需要高性能的場景。
如需要手動控制框架采用靜態圖模式,可以通過以下代碼進行網絡構建:
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor
ms.set_context(mode=ms.GRAPH_MODE) # 使用set_context進行運行靜態圖模式的配置
?
class Network(nn.Cell):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.dense_relu_sequential = nn.SequentialCell(nn.Dense(28*28, 512),nn.ReLU(),nn.Dense(512, 512),nn.ReLU(),nn.Dense(512, 10))
?def construct(self, x):x = self.flatten(x)logits = self.dense_relu_sequential(x)return logits
?
model = Network()
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
output = model(input)
print(output)
[[ 0.05363735 0.05117104 -0.03343301 0.06347139 0.07546629 0.03263091
0.02790363 0.06269836 0.01838502 0.04387159]
[ 0.05363735 0.05117104 -0.03343301 0.06347139 0.07546629 0.03263091
0.02790363 0.06269836 0.01838502 0.04387159]
[ 0.05363735 0.05117104 -0.03343301 0.06347139 0.07546629 0.03263091
0.02790363 0.06269836 0.01838502 0.04387159]
[ 0.05363735 0.05117104 -0.03343301 0.06347139 0.07546629 0.03263091
0.02790363 0.06269836 0.01838502 0.04387159]
…
[ 0.05363735 0.05117104 -0.03343301 0.06347139 0.07546629 0.03263091
0.02790363 0.06269836 0.01838502 0.04387159]
[ 0.05363735 0.05117104 -0.03343301 0.06347139 0.07546629 0.03263091
0.02790363 0.06269836 0.01838502 0.04387159]
[ 0.05363735 0.05117104 -0.03343301 0.06347139 0.07546629 0.03263091
0.02790363 0.06269836 0.01838502 0.04387159]
[ 0.05363735 0.05117104 -0.03343301 0.06347139 0.07546629 0.03263091
0.02790363 0.06269836 0.01838502 0.04387159]]
靜態圖模式的使用場景
MindSpore編譯器重點面向Tensor數據的計算以及其微分處理。因此使用MindSpore API以及基于Tensor對象的操作更適合使用靜態圖編譯優化。其他操作雖然可以部分入圖編譯,但實際優化作用有限。另外,靜態圖模式先編譯后執行的模式導致其存在編譯耗時。因此,如果函數無需反復執行,那么使用靜態圖加速也可能沒有價值。
有關使用靜態圖來進行網絡編譯的示例,請參考網絡構建。
靜態圖模式開啟方式
通常情況下,由于動態圖的靈活性,我們會選擇使用PyNative模式來進行自由的神經網絡構建,以實現模型的創新和優化。但是當需要進行性能加速時,我們需要對神經網絡部分或整體進行加速。MindSpore提供了兩種切換為圖模式的方式,分別是基于裝飾器的開啟方式以及基于全局context的開啟方式。
基于裝飾器的開啟方式
MindSpore提供了jit裝飾器,可以通過修飾Python函數或者Python類的成員函數使其被編譯成計算圖,通過圖優化等技術提高運行速度。此時我們可以簡單的對想要進行性能優化的模塊進行圖編譯加速,而模型其他部分,仍舊使用解釋執行方式,不丟失動態圖的靈活性。無論全局context是設置成靜態圖模式還是動態圖模式,被jit修飾的部分始終會以靜態圖模式進行運行。
在需要對Tensor的某些運算進行編譯加速時,可以在其定義的函數上使用jit修飾器,在調用該函數時,該模塊自動被編譯為靜態圖。需要注意的是,jit裝飾器只能用來修飾函數,無法對類進行修飾。jit的使用示例如下:
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor
?
class Network(nn.Cell):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.dense_relu_sequential = nn.SequentialCell(nn.Dense(28*28, 512),nn.ReLU(),nn.Dense(512, 512),nn.ReLU(),nn.Dense(512, 10))
?def construct(self, x):x = self.flatten(x)logits = self.dense_relu_sequential(x)return logits
?
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
?
@ms.jit # 使用ms.jit裝飾器,使被裝飾的函數以靜態圖模式運行
def run(x):model = Network()return model(x)
?
output = run(input)
print(output)
[[-0.12126954 0.06986676 -0.2230821 -0.07087803 -0.01003947 0.01063392
0.10143848 -0.0200909 -0.09724037 0.0114444 ]
[-0.12126954 0.06986676 -0.2230821 -0.07087803 -0.01003947 0.01063392
0.10143848 -0.0200909 -0.09724037 0.0114444 ]
[-0.12126954 0.06986676 -0.2230821 -0.07087803 -0.01003947 0.01063392
0.10143848 -0.0200909 -0.09724037 0.0114444 ]
[-0.12126954 0.06986676 -0.2230821 -0.07087803 -0.01003947 0.01063392
0.10143848 -0.0200909 -0.09724037 0.0114444 ]
…
[-0.12126954 0.06986676 -0.2230821 -0.07087803 -0.01003947 0.01063392
0.10143848 -0.0200909 -0.09724037 0.0114444 ]
[-0.12126954 0.06986676 -0.2230821 -0.07087803 -0.01003947 0.01063392
0.10143848 -0.0200909 -0.09724037 0.0114444 ]
[-0.12126954 0.06986676 -0.2230821 -0.07087803 -0.01003947 0.01063392
0.10143848 -0.0200909 -0.09724037 0.0114444 ]
[-0.12126954 0.06986676 -0.2230821 -0.07087803 -0.01003947 0.01063392
0.10143848 -0.0200909 -0.09724037 0.0114444 ]]
除使用修飾器外,也可使用函數變換方式調用jit方法,示例如下:
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor
?
class Network(nn.Cell):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.dense_relu_sequential = nn.SequentialCell(nn.Dense(28*28, 512),nn.ReLU(),nn.Dense(512, 512),nn.ReLU(),nn.Dense(512, 10))
?def construct(self, x):x = self.flatten(x)logits = self.dense_relu_sequential(x)return logits
?
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
?
def run(x):model = Network()return model(x)
?
run_with_jit = ms.jit(run) # 通過調用jit將函數轉換為以靜態圖方式執行
output = run(input)
print(output)
[[ 0.11027216 -0.09628229 0.0457969 0.05396656 -0.06958974 0.0428197
-0.1572069 -0.14151613 -0.04531277 0.07521383]
[ 0.11027216 -0.09628229 0.0457969 0.05396656 -0.06958974 0.0428197
-0.1572069 -0.14151613 -0.04531277 0.07521383]
[ 0.11027216 -0.09628229 0.0457969 0.05396656 -0.06958974 0.0428197
-0.1572069 -0.14151613 -0.04531277 0.07521383]
[ 0.11027216 -0.09628229 0.0457969 0.05396656 -0.06958974 0.0428197
-0.1572069 -0.14151613 -0.04531277 0.07521383]
…
[ 0.11027216 -0.09628229 0.0457969 0.05396656 -0.06958974 0.0428197
-0.1572069 -0.14151613 -0.04531277 0.07521383]
[ 0.11027216 -0.09628229 0.0457969 0.05396656 -0.06958974 0.0428197
-0.1572069 -0.14151613 -0.04531277 0.07521383]
[ 0.11027216 -0.09628229 0.0457969 0.05396656 -0.06958974 0.0428197
-0.1572069 -0.14151613 -0.04531277 0.07521383]
[ 0.11027216 -0.09628229 0.0457969 0.05396656 -0.06958974 0.0428197
-0.1572069 -0.14151613 -0.04531277 0.07521383]]
當我們需要對神經網絡的某部分進行加速時,可以直接在construct方法上使用jit修飾器,在調用實例化對象時,該模塊自動被編譯為靜態圖。示例如下:
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor
?
class Network(nn.Cell):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.dense_relu_sequential = nn.SequentialCell(nn.Dense(28*28, 512),nn.ReLU(),nn.Dense(512, 512),nn.ReLU(),nn.Dense(512, 10))
?@ms.jit # 使用ms.jit裝飾器,使被裝飾的函數以靜態圖模式運行def construct(self, x):x = self.flatten(x)logits = self.dense_relu_sequential(x)return logits
?
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
model = Network()
output = model(input)
print(output)
[[ 0.10522258 0.06597593 -0.09440921 -0.04883489 0.07194916 0.1343117
-0.06813788 0.01986085 0.0216996 -0.05345828]
[ 0.10522258 0.06597593 -0.09440921 -0.04883489 0.07194916 0.1343117
-0.06813788 0.01986085 0.0216996 -0.05345828]
[ 0.10522258 0.06597593 -0.09440921 -0.04883489 0.07194916 0.1343117
-0.06813788 0.01986085 0.0216996 -0.05345828]
[ 0.10522258 0.06597593 -0.09440921 -0.04883489 0.07194916 0.1343117
-0.06813788 0.01986085 0.0216996 -0.05345828]
…
[ 0.10522258 0.06597593 -0.09440921 -0.04883489 0.07194916 0.1343117
-0.06813788 0.01986085 0.0216996 -0.05345828]
[ 0.10522258 0.06597593 -0.09440921 -0.04883489 0.07194916 0.1343117
-0.06813788 0.01986085 0.0216996 -0.05345828]
[ 0.10522258 0.06597593 -0.09440921 -0.04883489 0.07194916 0.1343117
-0.06813788 0.01986085 0.0216996 -0.05345828]
[ 0.10522258 0.06597593 -0.09440921 -0.04883489 0.07194916 0.1343117
-0.06813788 0.01986085 0.0216996 -0.05345828]]
基于context的開啟方式
context模式是一種全局的設置模式。代碼示例如下:
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor
ms.set_context(mode=ms.GRAPH_MODE) # 使用set_context進行運行靜態圖模式的配置
?
class Network(nn.Cell):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.dense_relu_sequential = nn.SequentialCell(nn.Dense(28*28, 512),nn.ReLU(),nn.Dense(512, 512),nn.ReLU(),nn.Dense(512, 10))
?def construct(self, x):x = self.flatten(x)logits = self.dense_relu_sequential(x)return logits
?
model = Network()
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
output = model(input)
print(output)
[[ 0.08501796 -0.04404321 -0.05165704 0.00357929 0.00051521 0.00946456
0.02748473 -0.19415936 -0.00278988 0.04024826]
[ 0.08501796 -0.04404321 -0.05165704 0.00357929 0.00051521 0.00946456
0.02748473 -0.19415936 -0.00278988 0.04024826]
[ 0.08501796 -0.04404321 -0.05165704 0.00357929 0.00051521 0.00946456
0.02748473 -0.19415936 -0.00278988 0.04024826]
[ 0.08501796 -0.04404321 -0.05165704 0.00357929 0.00051521 0.00946456
0.02748473 -0.19415936 -0.00278988 0.04024826]
…
[ 0.08501796 -0.04404321 -0.05165704 0.00357929 0.00051521 0.00946456
0.02748473 -0.19415936 -0.00278988 0.04024826]
[ 0.08501796 -0.04404321 -0.05165704 0.00357929 0.00051521 0.00946456
0.02748473 -0.19415936 -0.00278988 0.04024826]
[ 0.08501796 -0.04404321 -0.05165704 0.00357929 0.00051521 0.00946456
0.02748473 -0.19415936 -0.00278988 0.04024826]
[ 0.08501796 -0.04404321 -0.05165704 0.00357929 0.00051521 0.00946456
0.02748473 -0.19415936 -0.00278988 0.04024826]]
靜態圖的語法約束
在Graph模式下,Python代碼并不是由Python解釋器去執行,而是將代碼編譯成靜態計算圖,然后執行靜態計算圖。因此,編譯器無法支持全量的Python語法。MindSpore的靜態圖編譯器維護了Python常用語法子集,以支持神經網絡的構建及訓練。詳情可參考靜態圖語法支持。
JitConfig配置選項
在圖模式下,可以通過使用JitConfig配置選項來一定程度的自定義編譯流程,目前JitConfig支持的配置參數如下:
- jit_level: 用于控制優化等級。
- exec_mode: 用于控制模型執行方式。
- jit_syntax_level: 設置靜態圖語法支持級別,詳細介紹請見靜態圖語法支持。
靜態圖高級編程技巧
使用靜態圖高級編程技巧可以有效地提高編譯效率以及執行效率,并可以使程序運行的更加穩定。