动态图模块总结
在比赛一开始,我们队长就将每个人的模块和区域进行分划,采取自主选择模式,我选择的是核心代码模块中的前四个模块,分别是数据处理与数据增强模块,分布式并行模块,图优化模块和动态图模块。起初我对C++语言并不是十分熟悉,所以花了一部分时间学习,在这四个模块中,我主攻的是动态图模块和数据处理与数据增强模块中的dataset模块,其他的模块只是有一个简单的了解。
动态图模块
首先MindSpore是支持两种运行模式,一种是Graph模式,一种是PyNative模式,我选择的是动态图模块,也就是PyNative模式,在此之前,先简单的介绍一下Graph和PyNative。(以下为MindSpore官网提供)
什么是Graph模式和PyNative模式
- Graph模式被称为静态图模式或者图模式,将神经网络模型编译成一整张图,然后下发执行。该模式利用图优化等技术提高运行性能,同时有助于规模部署和跨平台运行。
- PyNative模式被称为动态图模式,它是将神经网络中的各个算子逐一下发执行,方便用户编写和调试神经网络模型。
Graph与PyNative互相转换性
-
在一般的情况下,MindSpore处于Graph模式,可以通过
context.set_context(mode=context.PYNATIVE_MODE)
切换为PyNative模式; -
同样地,MindSpore处于PyNative模式时,可以通过
context.set_context(mode=context.GRAPH_MODE)
切换为Graph模式。
Graph和PyNative两种模式的区别
1.使用场景(整体性和分散性)
- Graph模式需要一开始就构建好网络结构,然后框架做整图优化和执行,比较适合网络固定没有变化,且需要高性能的场景。
- PyNative模式逐行执行算子,支持单独求梯度。
2.网络执行
- Graph模式和PyNative模式在执行相同的网络和算子时,精度效果是一致的
- 由于Graph模式运用了图优化、计算图整图下沉等技术,所以Graph模式执行网络的性能和效率更高。
3.代码调试
- 在脚本开发和网络流程调试中,推荐使用PyNative模式进行调试。
- 在PyNative模式下,可以方便地设置断点,获取网络执行的中间结果,也可以通过pdb的方式对网络进行调试
- 但是Graph模式无法设置断点,只能先指定算子进行打印,然后在网络执行完成后查看输出结果。
PyNative模块的具体使用
-
MindSpore提供了Staging功能,该功能可以在PyNative模式下将Python函数或者Python类的方法编译成计算图,通过图优化等技术提高运行速度,是一种混合运行机制。
-
Staging功能的使用通过
ms_function
装饰器达成,该装饰器会将模块编译成计算图,在给定输入之后,以图的形式下发执行。 -
PyNative模式中支持单独的梯度求取操作,具体操作可通过
GradOperation
求该函数或者网络所有的输入梯度。需要注意的是:输入类型仅支持Tensor。
部分优秀代码解析
std::string GetSingleOpGraphInfo(const OpExecInfoPtr &op_exec_info, const std::vector<tensor::TensorPtr> &input_tensors,
const std::vector<int64_t> &tensors_mask) {
MS_EXCEPTION_IF_NULL(op_exec_info);
// tensors的大小应该等于tensors_mask的大小
if (input_tensors.size() != tensors_mask.size()) {
MS_LOG(EXCEPTION) << "Input tensors size " << input_tensors.size() << " should be equal to tensors mask size "
<< tensors_mask.size();
}
std::string graph_info;
// 获取输入的tensors的信息
for (size_t index = 0; index < input_tensors.size(); ++index) {
MS_EXCEPTION_IF_NULL(input_tensors[index]);
// auto可以在声明变量时根据变量初始值的类型自动为此变量选择匹配的类型
auto tensor_shape = input_tensors[index]->shape();
// 将graph_info的信息用_分隔开,方便阅读与查看
(void)std::for_each(tensor_shape.begin(), tensor_shape.end(), [&](#) {
(void)graph_info.append(std::to_string(dim));
graph_info += "_";
});
(void)graph_info.append(std::to_string(input_tensors[index]->data_type()));
graph_info += "_";
(void)graph_info.append(input_tensors[index]->padding_type());
graph_info += "_";
auto tensor_addr = input_tensors[index]->device_address();
if (tensor_addr != nullptr) { // 如果tensor_addr为空,添加_在graph_info中
(void)graph_info.append(std::to_string(std::dynamic_pointer_cast<device::DeviceAddress>(tensor_addr)->type_id()));
graph_info += "_";
(void)graph_info.append(std::dynamic_pointer_cast<device::DeviceAddress>(tensor_addr)->format());
graph_info