实习01-MindSpore 做了什么
昇思 MindSpore 是华为推出的全场景 AI 深度学习框架,旨在弥合算法研究与生产部署之间的鸿沟。MindSpore = PyTorch 的开发体验 + TensorFlow 的部署能力 + 昇腾硬件的深度优化以下从使用范围层面技术实现层面。
1 相关工作
Torch 常见 API(复习):
https://fairy-study.blog.csdn.net/article/details/157873104
2 介绍
昇思 MindSpore 是华为推出的全场景 AI 深度学习框架,旨在弥合算法研究与生产部署之间的鸿沟。
MindSpore = PyTorch 的开发体验 + TensorFlow 的部署能力 + 昇腾硬件的深度优化
以下从 使用范围层面 、技术实现层面 介绍 MindSpore 到底做了什么:
2.1 使用范围层面:
PyTorch 相比于 MindSpore 来说,更加通用,市面上几乎所有的 GPU 都支持(包含CPU),但是昇思只支持华为生产的昇腾系列 GPU,较为局限。
2.2 技术实现层面:
修改底层算子(优化),使其适应在昇腾 GPU,但功能与 PyTorch 类似。如以下表格所示。
| 概念 | PyTorch | MindSpore | 说明 |
|---|---|---|---|
| 核心数据结构 | torch.Tensor |
mindspore.Tensor |
都是多维数组,支持 GPU/NPU 加速 |
| 神经网络层 | torch.nn.Conv2d |
mindspore.nn.Conv2d |
定义模型结构的方式几乎一样 |
| 自动求导 | loss.backward() |
grad_fn / value_and_grad |
都能自动计算梯度 |
| 优化器 | torch.optim.SGD |
mindspore.nn.SGD |
更新参数的逻辑一致 |
| 数据加载 | torch.utils.data |
mindspore.dataset |
都有 Dataset 和 DataLoader |
2.2.1 MindSpore 和 PyTorch 底层执行引擎不同:
PyTorch 默认是 动态图(执行一行编译一行) ,灵活但部署时需要额外转换(如 ONNX/TorchScript)。MindSpore 主打动静统一, 调试时用 动态图(PyNative 模式), 但在部署时用 静态图(Graph 模式,加个 @jit 就能编译优化),性能更强,尤其是对华为昇腾(Ascend)硬件。
- 动态图(Dynamic Graph): 边运行边定义,类似走一步看一步,优点在于方便调试,如果出现 bug,断点停在那一行,你就能看到那一刻的所有变量数值。缺点在于“走一步看一步”,底层引擎没法提前知道你下一步要做什么(就像贪心算法一样),所以很难做全局的性能优化(比如把几步操作合并成一步)。
- 静态图 (Static Graph): 先编译成蓝图再执行,类似盖房子,你必须先画出一套完整的施工图纸(Graph),确定哪里放梁、哪里走线。图纸画好并审核通过后,工人们(底层硬件)才开始统一施工。优点在于运行快,因为有了全景图,编译器可以进行深度优化(比如:发现这两步操作可以合并、那个变量以后不用了可以提前释放内存)。缺点在于不灵活,图一旦建好,运行中很难修改。
为了让你看清底层发生了什么,我们直接对比普通 Python 代码和 MindSpore 静态图代码在运行时的“角色分工”。假设我们要算一个简单的公式: d = a × b + c d = a \times b + c d=a×b+c。
2.2.1.1 普通 Python 代码(不用 MindSpore)
如果你写的是纯 Python,角色只有 Python 解释器。
运行过程:
- Python 解释器读到第一行:它像个翻译官,把 a * b 翻译成底层机器指令,告诉 CPU 去算。
- CPU 算出结果 2,把它交给 Python 解释器。
- Python 解释器把这个 2 存进内存(起名叫 res)。
- Python 解释器读到第二行:再去内存里把 res 找出来,再翻译 + c,再交给 CPU 算。
- CPU 算出 5,再还给 Python。
特点: 解释器像个“传声筒”,每一行都要翻译、传达、等待结果、存结果。Python 解释器很忙,CPU 却总是在等翻译官说话。
2.2.1.2 使用 MindSpore 静态图(加了 @jit)
当你用了 MindSpore 并在函数上加了 @jit 装饰器,MindSpore 编译器就进场了。
运行过程(分为两步):
第一步:编译期(MindSpore 编译器领头)
当你 第一次 调用这个函数时,Python 解释器准备去翻译第一行,结果被 MindSpore 编译器 拦住了:
- 编译器说: “别一行行翻译了,太慢!把你的函数代码给我,我整体看一遍。”
- 编译器操作: 它把 a * b + c 这种逻辑画成一张“图”(Graph)。它发现 res 只是个中间变量,不需要存回内存。
- 编译器优化: 它把这串逻辑精简成一个高效的任务包(二进制指令),直接传给硬件(比如昇腾 NPU)。
第二步:执行期(Python 解释器靠边站)
当函数真正运行数据时: - Python 解释器: 此时基本在“睡觉”或者“看戏”。数据都交由 MindSpore 编译器执行。
- 硬件芯片(NPU/GPU): 拿着 MindSpore 编译器给的“任务包”,一口气把 a*b+c 算完,中间不产生任何 Python 变量。
- 结果: 算完了直接把最终结果返还给 Python 解释器。
2.2.1.3 MindSpore 动态图模式(你的任务主要模式)
场景: 普通的 MindSpore 代码,Notebook 默认模式,PyNative_MODE。此时 Python 解释器非常忙。
执行流程:
- Python 解释器:读到 z = x + y。
- Python 解释器:发现 x 是 Tensor,于是调用 MindSpore 的 add 函数。
- MindSpore 运行时:接收指令,立刻调用 NPU 算一下 x+y。
- NPU:算完,返回结果。
- Python 解释器:拿到结果,赋值给 z。
- Python 解释器:继续读下一行代码。
特点:
- 每一行计算代码,Python 解释器都要参与调度。
- 它没有“睡觉”,它在不断地发号施令:“算这个”、“算那个”、“打印这个”。
结论:在你的 D2L 任务中,因为是 Notebook 教学代码,大部分是动态图。Python 解释器依然在逐行翻译和执行控制流,只是具体的矩阵计算交给了 NPU。
2.2.2 例子:动态图和静态图在代码中的体现
1. 动态图(PyTorch):像“即时通讯”
在动态图中,Python 运行到哪,计算就真的发生在哪。
# 动态图伪代码
a = 10
b = 20
c = a + b # 执行到这一行时,CPU/GPU 真的算出了 30
print(c) # 此时 c 已经是 30 了
- Python 说: “喂,底层算子,把这两个数加一下。”
- 底层算子: “好嘞,算完了,结果是 30,给你。”
- Python 说: “收到,那我继续下一行代码了。”
特点: 每一步都在你的眼皮子底下发生。
2. 静态图(MindSpore Graph 模式):像“写脚本录像”
在静态图中,你写的代码并不会立即计算,而是在“录制”逻辑。
# 静态图模式(MindSpore)
@jit # 加上这个,下面这段就变成了“录像模式”
def add_func(a, b):
return a + b
# 当你调用 add_func(10, 20) 时:
# 1. 第一次调用:MindSpore 并不算数,而是看了一眼代码:
# “哦,有两个输入,要把它们加起来,返回结果。”
# 于是它生成了一张【逻辑图】保存在内存里。
# 2. 第二步:它把这张图丢给华为昇腾芯片。
# 3. 第三步:芯片拿着图,灌入 10 和 20,飞速算出结果。
Python 说: “我要定义一个加法函数。”
MindSpore 编译器: “别急着算!先别动!让我看看你这函数里都有啥……噢,就是一个加法。行了,我把它优化成一段芯片能直接读懂的二进制指令了。”
执行时: 数据直接走芯片,绕过了 Python 解释器。
3. 假设我们要算 y = x + x + x y = x + x + x y=x+x+x :
动态图:
1. 从内存中取 x x x (第一次搬运,读)
2. 计算 t e m p = x + x temp = x + x temp=x+x
3. 把 t e m p temp temp 存回内存。(第二次搬运,写)
4. 从内存读取回 t e m p temp temp,计算 y = t e m p + x y = temp + x y=temp+x(第三次搬运,读)
5. 一共做了 2 次加法,3 次内存搬运。
静态图:
1. 第 1 次搬运(读): 取出 x x x。
2. 计算: 芯片内部直接算 3 × x 3 \times x 3×x。
3. 一共做了 2 次加法,1 次内存搬运。
结论: 极大优化了计算速度,另外搬运数据的时间往往比算数的时间长得多。
3. 如何理解 MindSpore 实现的 “动静统一”
所谓 “动静统一”,意思就是:同一套代码,可以通过一个 “开关” ,选择在“动态模式”跑,还是在“静态模式”跑。 你不需要为了部署而重写代码。
1️⃣ 核心:那个“开关”是什么?
在 MindSpore 里,这个开关通常是 @jit 装饰器 或者 context 库设置。
我们写一个最简单的计算函数:
from mindspore import mint, jit, context
# 定义一个计算函数(代码逻辑完全一样)
def compute(x, y):
z = x + y # 刚才说的加法
w = z * 2 # 再乘个 2
return w
🟢 场景一:调试时(动态图模式)
目的: 我要看中间变量 z 是多少,我要慢慢查错。
操作: 不加 @jit,或者设置 PYNATIVE_MODE 。
# 1. 设置动态模式(其实默认就是)
context.set_context(mode=context.PYNATIVE_MODE)
x = mint.Tensor([1])
y = mint.Tensor([2])
# 2. 直接运行
result = compute(x, y)
print(result) # ✅ 可以随便 print,能看到每一步
执行过程(回顾之前的理解):
Python 解释器读到compute函数。- 进入函数,读到
z = x + y。 Python调用 MindSpore 算一下x+y,拿到z。Python读到w = z * 2。Python调用 MindSpore 算一下z*2,拿到w。Python返回结果。
特点: Python 解释器全程参与,每一步都能停下来检查。适合你现在的 D2L 作业。
🔴 场景二:部署时(静态图模式)
目的: 代码调试好了,要放到服务器上大量运行,要求速度最快。
操作: 加上 @jit。
# 1. 设置静态模式(或者直接用 @jit 覆盖)
context.set_context(mode=context.GRAPH_MODE)
# 或者更常用的方式:只在关键函数加 @jit
@jit # ← 这就是那个“开关”
def compute(x, y):
z = x + y
w = z * 2
return w
x = mint.Tensor([1])
y = mint.Tensor([2])
# 2. 运行
result = compute(x, y)
执行过程(回顾之前的理解):
1.第一次运行:
- Python 解释器 读到
@jit,发现这是个“特殊任务”。 - MindSpore 编译器 介入:把
compute 函数里的 z=x+y 和 w=z*2 一次性读走。 - 编译器 画蓝图:生成一个
Input -> Add -> Mul -> Output的优化图。 - 编译器 把蓝图编译成机器指令。
2.后续运行: - Python 解释器:调用函数后,直接休息。
- 硬件 (NPU):拿着蓝图,一口气算完 x+y 和 *2,中间不告诉 Python。
- 结果:直接返回最终结果。
特点: Python 解释器介入少,速度快。适合正式产品。
结论
你现在处于 “开发/教学” 阶段,相当于 “调试时”。
1. 保持动态图:
你的 .ipynb 文件需要显示每一步的输出(比如 Loss 下降曲线、中间打印的信息)。
动态图(不加 @jit 或 PYNATIVE_MODE)最适合这个,因为 Python 解释器能随时响应 print。
2. 不用管部署优化:
你不需要为了 “性能” 强行加 @jit。
只要把 ops 改成 mint,代码能在 Atlas 服务器上跑通,有结果,任务就完成了。
3. 理解未来场景:
如果以后要把这个 D2L 模型真正用到产品里(比如放到手机里识别图片),那时候才需要把训练好的模型转换成静态图模式去加速推理。
总结
- 动静统一 = 代码写法不变,只改运行开关。
- 动态图 = Python 解释器全程指挥(适合你现在的作业,方便看结果)。
- 静态图 = MindSpore 编译器画蓝图后独立运行(适合以后产品上线,速度快)。
更多推荐



所有评论(0)