日期
心得
昇思MindSpore 基础入门学习 使用静态图加速 (AI 代码解析)
使用静态图加速
背景介绍
AI编译框架分为两种运行模式,分别是动态图模式以及静态图模式。MindSpore默认情况下是以动态图模式运行,但也支持手工切换为静态图模式。两种运行模式的详细介绍如下:
动态图模式
动态图的特点是计算图的构建和计算同时发生(Define by run),其符合Python的解释执行方式,在计算图中定义一个Tensor时,其值就已经被计算且确定,因此在调试模型时较为方便,能够实时得到中间结果的值,但由于所有节点都需要被保存,导致难以对整个计算图进行优化。
在MindSpore中,动态图模式又被称为PyNative模式。由于动态图的解释执行特性,在脚本开发和网络流程调试过程中,推荐使用动态图模式进行调试。 如需要手动控制框架采用PyNative模式,可以通过以下代码进行网络构建:
#!/usr/bin/env python
# coding: utf-8
# In[1]:
import numpy as np # 导入NumPy库,用于数值计算
import mindspore # 导入MindSpore框架
from mindspore import nn # 从MindSpore导入神经网络模块
from mindspore import Tensor # 从MindSpore导入Tensor类
# In[2]:
def network():
# 定义一个简单的神经网络模型
model = nn.SequentialCell(
nn.Flatten(), # 将输入张量展平
nn.Dense(28*28, 512), # 全连接层,将展平后的输入映射到512维
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 512), # 另一个全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 10)) # 最后一层全连接层,将输出映射到10维
return model # 返回定义的模型
# ## 保存和加载模型权重
# In[3]:
model = network() # 创建神经网络模型
mindspore.save_checkpoint(model, "model.ckpt") # 保存模型的权重到文件"model.ckpt"
# ## 加载模型权重
# In[4]:
model = network() # 再次创建相同的神经网络模型
param_dict = mindspore.load_checkpoint("model.ckpt") # 从文件"model.ckpt"加载模型权重
param_not_load, _ = mindspore.load_param_into_net(model, param_dict) # 将加载的权重应用到模型中
print(param_not_load) # 打印未被加载的参数列表
# ## 保存和加载MindIR
# In[5]:
model = network() # 创建神经网络模型
inputs = Tensor(np.ones([1, 1, 28, 28]).astype(np.float32)) # 创建输入张量,形状为(1, 1, 28, 28),数据类型为float32
mindspore.export(model, inputs, file_name="model", file_format="MINDIR") # 导出模型为MindIR格式文件
# In[6]:
mindspore.set_context(mode=mindspore.GRAPH_MODE) # 设置MindSpore的执行模式为图模式
graph = mindspore.load("model.mindir") # 加载MindIR格式的模型文件
model = nn.GraphCell(graph) # 使用加载的模型图创建GraphCell实例
outputs = model(inputs) # 传入输入张量进行推理,获取输出
print(outputs.shape) # 打印输出张量的形状
-
导入必要的库:
-
numpy
用于数值计算。 -
mindspore
框架及其相关模块用于神经网络的创建、训练和保存。
-
-
定义神经网络模型:
- 使用
nn.SequentialCell
创建一个简单的前馈神经网络,包含展平层、全连接层和ReLU激活函数。
- 使用
-
保存和加载模型权重:
-
mindspore.save_checkpoint
:将模型的参数保存到一个Checkpoint文件中。 -
mindspore.load_checkpoint
:从Checkpoint文件中加载模型的参数。 -
mindspore.load_param_into_net
:将加载的参数应用到模型中,并返回未成功加载的参数列表。
-
-
保存和加载MindIR:
-
mindspore.export
:将模型及其结构导出为MindIR格式文件。这种格式同时保存模型结构和参数,以便在推理或部署时使用。 -
mindspore.load
:加载MindIR格式的模型文件。 -
nn.GraphCell
:创建一个GraphCell实例用于封装执行图,使其可以像普通的神经网络模型一样被调用进行推理。
-
-
推理执行:
- 设置MindSpore的执行模式为图模式(GRAPH_MODE),加载MindIR模型,并进行推理,最终打印输出张量的形状以验证模型的输出。
静态图模式
相较于动态图而言,静态图的特点是将计算图的构建和实际计算分开(Define and run)。有关静态图模式的运行原理,可以参考静态图语法支持。
在MindSpore中,静态图模式又被称为Graph模式,在Graph模式下,基于图优化、计算图整图下沉等技术,编译器可以针对图进行全局的优化,获得较好的性能,因此比较适合网络固定且需要高性能的场景。
如需要手动控制框架采用静态图模式,可以通过以下代码进行网络构建:
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor
# 配置MindSpore上下文为PYNATIVE_MODE(动态图模式)
ms.set_context(mode=ms.PYNATIVE_MODE)
# 定义一个神经网络类,继承自nn.Cell
class Network(nn.Cell):
def __init__(self):
super().__init__()
# 定义一个flatten层,用于将输入展平
self.flatten = nn.Flatten()
# 定义一个包含Dense和ReLU层的顺序容器
self.dense_relu_sequential = nn.SequentialCell(
nn.Dense(28*28, 512), # 输入为28*28个单元,输出为512个单元的全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 512), # 输入为512个单元,输出为512个单元的全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 10) # 输入为512个单元,输出为10个单元的全连接层
)
# 构建前向传播过程
def construct(self, x):
x = self.flatten(x) # 将输入数据展平
logits = self.dense_relu_sequential(x) # 通过SequentialCell进行前向传播
return logits # 返回输出
# 实例化网络模型
model = Network()
# 创建一个形状为[64, 1, 28, 28]的全1输入张量,并将其转换为float32类型
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
# 通过模型进行前向传播,获取输出
output = model(input)
# 打印输出
print(output)
-
import numpy as np
- 导入NumPy库,用于数组和矩阵操作。
-
import mindspore as ms
- 导入MindSpore库,用于深度学习模型的构建和训练。
-
from mindspore import nn, Tensor
- 从MindSpore库中导入神经网络模块(nn)和张量(Tensor)类。
-
ms.set_context(mode=ms.PYNATIVE_MODE)
- 配置MindSpore的运行模式为PYNATIVE_MODE,即动态图模式。动态图模式允许逐步执行代码,便于调试和开发。
-
class Network(nn.Cell):
- 定义一个名为
Network
的类,继承自nn.Cell
,用于构建神经网络模型。
- 定义一个名为
-
def __init__(self):
- 在类的初始化方法中,定义网络的各个层。包括一个
Flatten
层和一个顺序容器SequentialCell
,其中包含三个全连接层和两个ReLU激活函数。
- 在类的初始化方法中,定义网络的各个层。包括一个
-
def construct(self, x):
- 定义前向传播方法,将输入数据展平后经过顺序容器
dense_relu_sequential
,最后返回输出结果。
- 定义前向传播方法,将输入数据展平后经过顺序容器
-
model = Network()
- 实例化
Network
类,创建模型对象。
- 实例化
-
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
- 创建一个形状为
[64, 1, 28, 28]
的全1输入张量,并将其转换为float32
类型。
- 创建一个形状为
output = model(input)
- 将输入张量传入模型,进行前向传播,并获取输出结果。
print(output)
- 打印输出结果。
静态图模式的使用场景
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
# 定义一个神经网络类,继承自nn.Cell
class Network(nn.Cell):
def __init__(self):
super().__init__()
# 定义一个flatten层,用于将输入展平
self.flatten = nn.Flatten()
# 定义一个包含Dense和ReLU层的顺序容器
self.dense_relu_sequential = nn.SequentialCell(
nn.Dense(28*28, 512), # 输入为28*28个单元,输出为512个单元的全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 512), # 输入为512个单元,输出为512个单元的全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 10) # 输入为512个单元,输出为10个单元的全连接层
)
# 构建前向传播过程
def construct(self, x):
x = self.flatten(x) # 将输入数据展平
logits = self.dense_relu_sequential(x) # 通过SequentialCell进行前向传播
return logits # 返回输出
# 创建一个形状为[64, 1, 28, 28]的全1输入张量,并将其转换为float32类型
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)
-
import numpy as np
- 导入NumPy库,用于数组和矩阵操作。
-
import mindspore as ms
- 导入MindSpore库,用于深度学习模型的构建和训练。
-
from mindspore import nn, Tensor
- 从MindSpore库中导入神经网络模块(nn)和张量(Tensor)类。
-
class Network(nn.Cell):
- 定义一个名为
Network
的类,继承自nn.Cell
,用于构建神经网络模型。
- 定义一个名为
-
def __init__(self):
- 在类的初始化方法中,定义网络的各个层。包括一个
Flatten
层和一个顺序容器SequentialCell
,其中包含三个全连接层和两个ReLU激活函数。
- 在类的初始化方法中,定义网络的各个层。包括一个
-
def construct(self, x):
- 定义前向传播方法,将输入数据展平后经过顺序容器
dense_relu_sequential
,最后返回输出结果。
- 定义前向传播方法,将输入数据展平后经过顺序容器
-
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
- 创建一个形状为
[64, 1, 28, 28]
的全1输入张量,并将其转换为float32
类型。
- 创建一个形状为
-
@ms.jit
- 使用MindSpore的
jit
装饰器,使被装饰的函数以静态图模式运行。静态图模式可以提升运行效率,但需要提前编译计算图。
- 使用MindSpore的
-
def run(x):
- 定义一个名为
run
的函数,用于实例化模型并进行前向传播。
- 定义一个名为
model = Network()
- 在
run
函数中实例化Network
类,创建模型对象。
return model(x)
- 调用模型的前向传播方法,将输入数据传入并返回输出结果。
output = run(input)
- 调用被
ms.jit
装饰的run
函数,传入输入张量并获取输出结果。
print(output)
- 打印输出结果。
除使用修饰器外,也可使用函数变换方式调用jit方法,示例如下:
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor
# 定义一个神经网络类,继承自nn.Cell
class Network(nn.Cell):
def __init__(self):
super().__init__()
# 定义一个flatten层,用于将输入展平
self.flatten = nn.Flatten()
# 定义一个包含Dense和ReLU层的顺序容器
self.dense_relu_sequential = nn.SequentialCell(
nn.Dense(28*28, 512), # 输入为28*28个单元,输出为512个单元的全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 512), # 输入为512个单元,输出为512个单元的全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 10) # 输入为512个单元,输出为10个单元的全连接层
)
# 构建前向传播过程
def construct(self, x):
x = self.flatten(x) # 将输入数据展平
logits = self.dense_relu_sequential(x) # 通过SequentialCell进行前向传播
return logits # 返回输出
# 创建一个形状为[64, 1, 28, 28]的全1输入张量,并将其转换为float32类型
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
# 定义一个函数用于模型的前向传播
def run(x):
model = Network() # 实例化网络模型
return model(x) # 通过模型进行前向传播,获取输出
# 通过调用jit将函数转换为以静态图方式执行
run_with_jit = ms.jit(run)
# 运行转换后的静态图模式函数,获取输出
output = run_with_jit(input)
# 打印输出
print(output)
-
import numpy as np
- 导入NumPy库,用于数组和矩阵操作。
-
import mindspore as ms
- 导入MindSpore库,用于深度学习模型的构建和训练。
-
from mindspore import nn, Tensor
- 从MindSpore库中导入神经网络模块(nn)和张量(Tensor)类。
-
class Network(nn.Cell):
- 定义一个名为
Network
的类,继承自nn.Cell
,用于构建神经网络模型。
- 定义一个名为
-
def __init__(self):
- 在类的初始化方法中,定义网络的各个层。包括一个
Flatten
层和一个顺序容器SequentialCell
,其中包含三个全连接层和两个ReLU激活函数。
- 在类的初始化方法中,定义网络的各个层。包括一个
-
def construct(self, x):
- 定义前向传播方法,将输入数据展平后经过顺序容器
dense_relu_sequential
,最后返回输出结果。
- 定义前向传播方法,将输入数据展平后经过顺序容器
-
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
- 创建一个形状为
[64, 1, 28, 28]
的全1输入张量,并将其转换为float32
类型。
- 创建一个形状为
-
def run(x):
- 定义一个名为
run
的函数,用于实例化模型并进行前向传播。
- 定义一个名为
-
model = Network()
- 在
run
函数中实例化Network
类,创建模型对象。
- 在
return model(x)
- 调用模型的前向传播方法,将输入数据传入并返回输出结果。
run_with_jit = ms.jit(run)
- 通过调用MindSpore的
jit
函数,将run
函数转换为以静态图方式执行。静态图模式可以提升运行效率,但需要提前编译计算图。
output = run_with_jit(input)
- 调用转换后的静态图模式函数,将输入张量传入并获取输出结果。
print(output)
- 打印输出结果。
当我们需要对神经网络的某部分进行加速时,可以直接在construct方法上使用jit修饰器,在调用实例化对象时,该模块自动被编译为静态图。示例如下:
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor
# 定义一个神经网络类,继承自nn.Cell
class Network(nn.Cell):
def __init__(self):
super().__init__()
# 定义一个flatten层,用于将输入展平
self.flatten = nn.Flatten()
# 定义一个包含Dense和ReLU层的顺序容器
self.dense_relu_sequential = nn.SequentialCell(
nn.Dense(28*28, 512), # 输入为28*28个单元,输出为512个单元的全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 512), # 输入为512个单元,输出为512个单元的全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 10) # 输入为512个单元,输出为10个单元的全连接层
)
# 使用ms.jit装饰器,使被装饰的函数以静态图模式运行
@ms.jit
def construct(self, x):
x = self.flatten(x) # 将输入数据展平
logits = self.dense_relu_sequential(x) # 通过SequentialCell进行前向传播
return logits # 返回输出
# 创建一个形状为[64, 1, 28, 28]的全1输入张量,并将其转换为float32类型
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
# 实例化网络模型
model = Network()
# 通过模型进行前向传播,获取输出
output = model(input)
# 打印输出
print(output)
-
import numpy as np
- 导入NumPy库,用于数组和矩阵操作。
-
import mindspore as ms
- 导入MindSpore库,用于深度学习模型的构建和训练。
-
from mindspore import nn, Tensor
- 从MindSpore库中导入神经网络模块(nn)和张量(Tensor)类。
-
class Network(nn.Cell):
- 定义一个名为
Network
的类,继承自nn.Cell
,用于构建神经网络模型。
- 定义一个名为
-
def __init__(self):
- 在类的初始化方法中,定义网络的各个层。包括一个
Flatten
层和一个顺序容器SequentialCell
,其中包含三个全连接层和两个ReLU激活函数。
- 在类的初始化方法中,定义网络的各个层。包括一个
-
@ms.jit
- 使用MindSpore的
jit
装饰器,使被装饰的函数以静态图模式运行。静态图模式可以提升运行效率,但需要提前编译计算图。
- 使用MindSpore的
-
def construct(self, x):
- 定义前向传播方法,将输入数据展平后经过顺序容器
dense_relu_sequential
,最后返回输出结果。
- 定义前向传播方法,将输入数据展平后经过顺序容器
-
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
- 创建一个形状为
[64, 1, 28, 28]
的全1输入张量,并将其转换为float32
类型。
- 创建一个形状为
-
model = Network()
- 实例化
Network
类,创建模型对象。
- 实例化
output = model(input)
- 将输入张量传入模型,进行前向传播,并获取输出结果。
print(output)
- 打印输出结果。
基于context的开启方式
context模式是一种全局的设置模式。代码示例如下:
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor
# 使用set_context进行运行静态图模式的配置
ms.set_context(mode=ms.GRAPH_MODE)
# 定义一个神经网络类,继承自nn.Cell
class Network(nn.Cell):
def __init__(self):
super().__init__()
# 定义一个flatten层,用于将输入展平
self.flatten = nn.Flatten()
# 定义一个包含Dense和ReLU层的顺序容器
self.dense_relu_sequential = nn.SequentialCell(
nn.Dense(28*28, 512), # 输入为28*28个单元,输出为512个单元的全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 512), # 输入为512个单元,输出为512个单元的全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 10) # 输入为512个单元,输出为10个单元的全连接层
)
# 构建前向传播过程
def construct(self, x):
x = self.flatten(x) # 将输入数据展平
logits = self.dense_relu_sequential(x) # 通过SequentialCell进行前向传播
return logits # 返回输出
# 实例化网络模型
model = Network()
# 创建一个形状为[64, 1, 28, 28]的全1输入张量,并将其转换为float32类型
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
# 通过模型进行前向传播,获取输出
output = model(input)
# 打印输出
print(output)
-
import numpy as np
- 导入NumPy库,用于数组和矩阵操作。
-
import mindspore as ms
- 导入MindSpore库,用于深度学习模型的构建和训练。
-
from mindspore import nn, Tensor
- 从MindSpore库中导入神经网络模块(nn)和张量(Tensor)类。
-
ms.set_context(mode=ms.GRAPH_MODE)
- 使用
set_context
函数配置MindSpore的运行模式为静态图模式(GRAPH_MODE)。静态图模式的特点是计算图在运行前会被编译优化,适合大规模深度学习任务。
- 使用
-
class Network(nn.Cell):
- 定义一个名为
Network
的类,继承自nn.Cell
,用于构建神经网络模型。
- 定义一个名为
-
def __init__(self):
- 在类的初始化方法中,定义网络的各个层。包括一个
Flatten
层和一个顺序容器SequentialCell
,其中包含三个全连接层和两个ReLU激活函数。
- 在类的初始化方法中,定义网络的各个层。包括一个
-
def construct(self, x):
- 定义前向传播方法,将输入数据展平后经过顺序容器
dense_relu_sequential
,最后返回输出结果。
- 定义前向传播方法,将输入数据展平后经过顺序容器
-
model = Network()
- 实例化
Network
类,创建模型对象。
- 实例化
-
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
- 创建一个形状为
[64, 1, 28, 28]
的全1输入张量,并将其转换为float32
类型。
- 创建一个形状为
output = model(input)
- 将输入张量传入模型,进行前向传播,并获取输出结果。
print(output)
- 打印输出结果。
静态图的语法约束
在Graph模式下,Python代码并不是由Python解释器去执行,而是将代码编译成静态计算图,然后执行静态计算图。因此,编译器无法支持全量的Python语法。MindSpore的静态图编译器维护了Python常用语法子集,以支持神经网络的构建及训练。详情可参考静态图语法支持。
JitConfig配置选项
在图模式下,可以通过使用JitConfig配置选项来一定程度的自定义编译流程,目前JitConfig支持的配置参数如下:
- jit_level: 用于控制优化等级。
- exec_mode: 用于控制模型执行方式。
- jit_syntax_level: 设置静态图语法支持级别,详细介绍请见静态图语法支持。
静态图高级编程技巧
使用静态图高级编程技巧可以有效地提高编译效率以及执行效率,并可以使程序运行的更加稳定。详情可参考静态图高级编程技巧。
整体代码
上述内容详细介绍了MindSpore中动态图模式和静态图模式的区别、使用场景,以及如何在不同模式下进行网络构建。以下是一些关键代码示例和解析,以帮助理解如何切换和使用这两种模式。
动态图模式示例
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor
# 配置MindSpore为动态图模式
ms.set_context(mode=ms.PYNATIVE_MODE)
# 定义神经网络类
class Network(nn.Cell):
def __init__(self):
super().__init__()
self.flatten = nn.Flatten()
self.dense_relu_sequential = nn.SequentialCell(
nn.Dense(28*28, 512), # 输入为28*28个单元,输出为512个单元的全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 512), # 输入为512个单元,输出为512个单元的全连接层
nn.ReLU(), # ReLU激活函数
nn.Dense(512, 10) # 输入为512个单元,输出为10个单元的全连接层
)
def construct(self, x):
x = self.flatten(x) # 将输入数据展平
logits = self.dense_relu_sequential(x) # 通过SequentialCell进行前向传播
return logits # 返回输出
# 实例化网络模型
model = Network()
# 创建一个形状为[64, 1, 28, 28]的全1输入张量,并将其转换为float32类型
input = Tensor(np.ones([64, 1, 28, 28]).astype(np.float32))
# 通过模型进行前向传播,获取输出
output = model(input)
# 打印输出
print(output)
静态图模式示例
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor