MindSpore图模式多级编译易用性升级,让开发者好用易用

01 将全局graph mode的配置优化为@jit使能编译加速

早期版本中,用户需通过mindspore.set_context(mode= GRAPH_MODE)手动切换图模式,这种方式存在一些易用性问题,例如:

  • 入门门槛高,需要确保模型中所有nn.Cell的构图逻辑符合静态图语法约束。
  • 设置与代码逻辑分离,用户可能不清楚模型中哪些部分实际是按静态图执行,哪些部分仍然按动态图执行。
  • 用户需要记忆MindSpore context的特定mode枚举值。

将graph mode设置简化为装饰器@jit,是易用性提升的重要一步。这种设计遵循 “就近原则”,用户无需关注全局 context 配置,仅在需要graph mode加速的函数或网络定义前添加装饰器,即可快速启用编译。例如:

# 优化前:全局模式设置
import mindspore
mindspore.set_context(mode=GRAPH_MODE)
class Net(ms.nn.Cell):
    def construct(self, x):
        return x*2

# 优化后:局部装饰器直达
import mindspore
@mindspore.jit
class Net(ms.nn.Cell):
    def construct(self, x):
        return x*2

用户无需理解“context 全局静态图模式配置”的底层逻辑,通过“装饰器 = graph mode”的直观映射即可上手;同时支持局部graph mode启用,用户可针对性能瓶颈模块精准优化,避免全局graph mode带来的调试复杂度。此外,@jit装饰器兼容原有 context 配置逻辑,保障用户老代码的平滑迁移,实现易用性与兼容性的平衡。

02 jit接口参数优化

早期版本中,jit的接口参数较为复杂,不乏一些用户难以理解的参数,跟业界常用的graph编译使能接口如torch.compile等有较大差异。基于此,我们对比了业界的一些参数配置,重新设计了jit接口的参数和实现了对应的功能。以下简单介绍常用的几个参数。

1、capture_mode参数 ,表示图捕获的方式。提供三种选项,分别为:

  • “ast”: 通过源到源的方式编译Python代码,先把Python源码解析成Python抽象语法树,然后把抽象语法树解析为MindIR。
  • “bytecode”:解析用户代码的Python字节码,通过将Python字节码逐个映射为MindIR的节点,完成图捕获过程。对于无法转换为 MindIR 节点的Python字节码或操作,编译器提供了“裂图”的方式,直接走回Python解释器去执行,通过计算图和裂图结合的方式,支持了更多的Python语法范围。
  • “trace”:在动态图执行过程中,通过记录动态图执行算子的轨迹,过滤出计算图。生成计算图后,后续的step可以不用继续通过动态图方式执行,而是直接执行计算图,以此来提升模型性能。

三种图捕获方式各有优劣,例如ast方式支持捕获动态控制流入图;bytecode模式对用户代码语法约束减少且提供Guard机制保证编译产物可复用;trace模式进一步减少对用户代码的语法约束,但是不支持动态控制流和动态shape。用户可根据实际场景灵活选择图捕获方式。

2、jit_level参数, 控制图优化级别。提供两种选项,分别为:

  • jit_level=O0:只做基本的图切分优化,以及算子选择(硬件相关),优点是可以保证IR图的原始结构,编译速度较快。
  • jit_level=O1:增加图优化和自动算子融合,编译性能有所损失,但模型开始训练后,效率较高。

3、dynamic参数, 控制是否开启自动动态shape功能进行编译。提供两种选项,分别为:

  • 0:不进行动态shape编译
  • 1:使能动态shape编译并自动检测shape的变化

除了自动检测shape变化的功能,我们还额外提供@enable_dynamic装饰器,搭配jit接口使用,让用户可以指定动态shape变化的轴和指定动态rank。

4、fullgraph参数 控制是否把整个函数捕获成图。对于用户代码中无法捕获成图的部分,如果该参数设置为False,则允许回退到host侧用动态图执行,能有效减少部分语法限制,降低静态图使用门槛;如果设置为True,则可以检查用户代码中无法入图的部分,指导用户修改代码,使整个函数可以全部被捕获入图,提升模型性能。

03 自定义能力增强

随着AI模型与硬件生态的日益复杂,开发者在追求极致性能与创新时,常面临一个问题,就是框架的默认优化策略与运行时无法充分释放专用硬件(如新型AI加速卡)的全部潜力。这种“框架通用性”与“业务/硬件特殊性”之间的鸿沟,限制了技术落地的深度与效率。我们重点围绕自定义PASS、自定义后端两个维度,为开发者提供了更灵活、高效的框架扩展机制。

1、自定义PASS

MindSpore开放了框架pass编写及注册接口,用户可通过编写并注册自定义PASS插入自定义图优化逻辑,在编译期对计算图进行变换:

  • 开放PatternToPattern Pass父类及必要的工具方法,用户可以通过继承PatternToPattern Pass,实现源pattern、目标pattern、额外匹配条件等pass逻辑,实现计算图变换优化目标。
class PatternToPatternPass : public NodePass {
public:
    explicit PatternToPatternPass(const std::string &name = "pattern_to_pattern_pass") 
        : NodePass(name) {}
    virtual ~PatternToPatternPass() = default;

protected:
    // 定义源模式 - 需要匹配的图结构
    virtual void DefineSrcPattern(SrcPattern *src_pattern) = 0;
    
    // 定义目标模式 - 替换后的图结构
    virtual void DefineDstPattern(DstPattern *dst_pattern) = 0;
    
    // 检查匹配条件 - 可选的额外验证逻辑
    virtual bool CheckMatchedDAG(const PatternMap &pattern_map,
                             const FuncGraphPtr &func_graph, 
                             const AnfNodePtr &node) const {
        return true;  // 默认总是匹配   
    }
};
  • 提供自定义Pass注册接口register_custom_pass,用户可以根据需要选择不同后端、不同阶段注册相关自定义pass。
import mindspore 
 
# 注册自定义pass
success = mindspore.register_custom_pass(
    pass_name="MyCustomPass",
    plugin_so_path="/path/to/libmy_custom_pass.so",  
    device="ascend"
)
if success:
      print("Custom pass registered successfully")

2、自定义后端

MindSpore开放了后端编译执行扩展接口,允许用户适配第三方后端:

MindSpore框架通过增强自定义能力,显著提升了框架的开放性与可扩展性。开发者能够针对特定算法、硬件或场景,灵活定制计算逻辑、优化策略与运行时支持,从而在保持框架统一性的同时,充分发挥软硬件协同潜力,加速AI应用创新与落地。

04 报错日志信息优化

关于静态图报错日志信息的优化工作,也在持续开展,其中我们重点优化了Tensor重载接口的报错日志信息。例如对于如下的程序:

import mindspore
 
@mindspore.jit
def func(x):
     return x.pow(None)
 
print(func(mindspore.Tensor(1)))

优化前的报错日志信息如下:

该报错日志信息没有明确接口是如何使用错的,只是把重载的函数全部列出来,让用户自己去体会。

而优化后的报错如下:

优化后的报错日志信息会把出错的原因打印出来,而且加上波浪线指示出具体有问题的代码位置。

05 后续演进方向思考

易用性的提升仍然是MindSpore Graph Mode后续演进的重点方向。我们围绕下面几个点来展开:

1、多级编译的分层解耦

当前用户调用被@jit装饰的函数时,MindSpore会对这个函数进行前端图编译(图捕获、硬件无关优化等)、后端图编译(算子选择、硬件相关优化等)和图执行等一系列操作,最终只把计算结果返回出去。但用户可能有以下诉求:

  • 调用被@jit装饰的函数出现报错时,需要知道报错在哪个阶段,这个信息有可能帮助用户进行问题定位。
  • 把前端编译的IR图、后端编译的IR图和最终算子的执行序列打印出来,方便做网络调试。
  • 进一步地,用户可能想通过调用MindSpore提供的Python接口,就能实现修改前端或后端编译生成的IR图或算子执行序,以简单地实现模型定制优化。

这些诉求可以通过将jit装饰的函数调用分解为前端编译、后端编译、图执行等阶段的接口调用来实现,用户可以分别调用各个阶段对应的接口,得到不同阶段的产物,再调用这些产物提供的改图或者调试接口。例如:

import mindspore

def func(x, y):
    out1 = x + y
    out2 = out1 *2
    return out2

input_x = mindspore.tensor(1, mindspore.int32)
input_y = mindspore.tensor(2, mindspore.int32)

# 接口命名以正式上线的为准,当前仅为demo演示
# 前端编译和IR打印
frontend_graph = mindspore.graph.frontend_compile(func, x, y)
print (frontend_graph)

# 后端编译和IR/执行序打印
backend_graph = mindspore.graph.backendend_compile(frontend_graph)
print (backend_graph)
backend_graph.print_exec_order()

# 后端执行
output = mindspore.graph.backendend_run(backend_graph, input_x, input_y)
print(output)

2、自定义能力增强

为了满足用户深度定制模型优化的需求,框架需要提供更强的自定义能力,例如:

  • 扩展自定义Pass功能,支持对整图(包括控制流子图等)的修改,除了算子融合,用户可以以此来实现一些算子执行序的调优,如通算掩盖等。
  • 提供通用图优化、算子融合优化等的配置功能,让用户可以自行选择需要执行的优化Pass,而不是框架默认地把所有的优化都执行。

3、报错日志信息持续优化

报错信息的优化是Graph Mode编译易用性提升的重点和挑战,Graph Mode除了运行时错误,比动态图还多了编译期的错误,如语法不兼容、图结构非法等,这种错误往往较难定位根因。尽管Graph Mode的报错信息已实现大幅优化,但仍面临三大核心挑战:

  1. 信息过载与简洁性的平衡:高级开发者需要详细的底层日志,而新手开发者可能被过多信息困扰,如何根据用户需求动态调整报错信息的详略程度,是一大难题;

  2. 特殊场景覆盖不足:对于那些使用频率低、场景特殊、图结构复杂的 “小众报错场景”,缺乏完善的易懂性引导,仍依赖底层技术日志,导致开发者调试成本高。

  3. 上下文分析的准确性:复杂模型中,报错的根因可能与报错位置不直接相关(如上游张量shape错误导致下游算子报错),当前上下文分析能力仍需提升,避免 “治标不治本”。

后续我们还是围绕上面几大挑战来持续优化报错日志信息,也欢迎广大开发者给Graph Mode报错反馈问题和提供宝贵意见。

2026年我们将重点对API/模型接入做大模型生成技术的探索,若对昇思MindSpore的易用性提升,开发者体验提升感兴趣,可以关注昇思MindSpore开源社区:https://gitee.com/mindspore/docs