1 MindSpore 与昇腾开发要回答什么
假设你要给服装图片做分类:输入是 28×28 的灰度图,输出是上衣、鞋、包等 10 个类别。模型在个人电脑上能够训练,损失也在下降。现在准备把训练移到昇腾设备,问题便从“模型会不会学习”扩展为“相同任务是否在指定设备上正确、稳定地运行”。本课用公开的 Fashion-MNIST 数据集贯穿实验,重点是建立可复现的开发方法,不比较硬件品牌,也不把某次速度提升当成普遍结论。
MindSpore 是编写和执行机器学习计算的框架。你通过张量、网络层、损失函数和优化器表达训练逻辑,框架再安排这些计算。昇腾是计算设备平台,运行还依赖驱动、固件、CANN 等软件组件。框架版本、运行时版本与设备型号必须符合对应版本的配套说明。安装命令执行成功,只能说明安装过程完成;设备可见、算子受支持、训练数值正常,都需要后续证据。
可以把开发验收分成 3 个问题。任务层检查模型是否解决问题,例如鞋类是否容易混淆、少数类别召回率是否偏低。框架层检查数据形状、梯度更新和检查点恢复是否正确。设备层检查关键计算是否确实执行在目标设备、稳定 Step 时间是否可接受。这里的 Step 指处理 1 个训练批次并完成参数更新的过程。某个层面通过,不会自动证明其余层面通过。

图像分类的原始特征是像素值,训练前把像素转换为浮点数并缩放到约定范围。如果换成客户流失预测,特征可能是最近 30 天使用次数、账号存续天数、历史投诉数,标签是之后 30 天是否流失。这里“之后”尤其重要:取消服务后的退款记录不能作为预测前的特征,否则产生数据泄漏。短文本分类也一样,同一原文的改写版本跨越训练集与测试集,会让结果显得过于容易。这些问题不会因为换设备而消失。
因此,本课把效果和执行分别记录。效果报告至少说明类别定义、划分方法和指标;执行报告至少说明环境、目标设备和测量窗口。入门阶段先使用小模型、固定输入和单设备训练,能够把错误范围缩小。分布式训练、自定义算子内部实现以及编译器优化机制留到确有需要时再学。
2 图模式、动静结合与调试
写网络时,代码的书写顺序与设备实际执行的组织方式不一定完全相同。在动态图中,运算随着程序运行逐步执行,较容易查看中间张量和定位哪行代码报错。在图模式中,框架先组织计算图并进行编译,再按编译后的执行方式运行。这样有机会减少调度开销并开展图优化,但首次执行可能更慢,部分 Python 写法也受到限制。不能用“第 1 次运行慢”直接判断图模式不适合任务。
建议先用固定的 64 张图检查数据流。输入形状应为 [64, 1, 28, 28],展平后为 [64, 784],分类输出为 [64, 10]。标签形状是 [64],每个元素是 0 到 9 的整数。损失函数接收类别分数和整数标签,返回标量。若模型输出已经过某种概率变换,而损失函数又按原始分数处理,可能重复变换并改变梯度;应按所选损失函数的接口约定连接。
调试时依次查看形状、数据类型、数值范围和损失是否有限。先让固定批次重复训练少量 Step,观察模型能否明显降低该批次损失。这只是计算链路检查,不能当作泛化能力证明。随后恢复真正的训练与验证流程,再切到图模式。若切换后报错,保留同一输入与权重,优先检查不受支持的控制流、动态形状或算子接口,而不是同时修改网络结构和数据处理。

“动静结合”不意味着在任何位置自由切换都没有成本。实际支持方式取决于版本与接口,某些局部编译和动态图能力有专门约束。入门时采用清晰的阶段边界更容易复现:用动态图理解计算,用图模式验证正式执行路径。调试期间的逐 Step 打印、把张量转成主机数组等操作,还可能触发等待或改变执行节奏,性能测量前应减少这些操作。
随机种子可以帮助重复实验,但并不保证跨设备、跨版本逐位相同。并行计算的归约顺序、实现选择以及随机算子的行为都可能影响末位数值。应记录种子,同时用合理误差和最终指标比较结果。真正值得追踪的是损失突然变成非有限值、同一输入出现明显不同预测、或者验证效果持续下降,而不是把所有小数差异都视为错误。
3 算子适配与精度对齐
算子可以理解为框架提供的计算单元,例如矩阵乘法、卷积、求和、类型转换。网络层通常由若干算子构成。某个算子存在于框架接口中,并不代表它在所有设备、数据类型与输入形状上都有相同支持。遇到不支持的情况,应核对当前版本的算子支持信息,并记录算子名、输入形状、数据类型和最小复现代码。只写“昇腾跑不了”无法帮助定位。
适配的起点是保持语义。假如某段预处理在每个 Step 中把张量转换成主机数组,再交给普通 Python 循环逐元素修改,虽然写起来直观,却可能破坏图执行或引入搬运。可以尝试使用框架支持的张量运算表达同一规则。改写后要检查边界:空输入、尾批、类别索引和整数除法都可能改变结果。不能为了让脚本运行就悄悄丢掉异常样本,或者把不支持的分支改成固定输出。
精度对齐先做小范围比较。保存 1 份初始化权重和 1 个固定批次,关闭训练时的随机增强;对于含 Dropout 等层的网络,还要明确比较处于训练状态还是评估状态。先比较参考路径与目标设备的 FP32 输出、损失,必要时再看关键层输出和梯度。比较前应确认预处理、类别顺序、损失的求平均方式完全一致,否则数字不同可能来自输入协议。

数值误差可以按 |目标值-参考值| ≤ atol + rtol × |参考值| 检查。绝对容差照顾接近 0 的数,相对容差照顾数值尺度差异。容差没有适用于所有模型的固定答案,应结合算子、数值范围和下游预测变化制定。除了平均误差,也要查看最大误差、异常值位置以及预测类别是否改变。平均值很小,仍可能掩盖某个样本的严重偏差。
混合精度让部分计算使用较低精度,同时按策略保留某些高精度计算,可能节省设备内存并改善速度。它也可能引入溢出、下溢或训练不稳定。应在 FP32 路径已正确的基础上尝试框架支持的混合精度方式,观察损失、梯度是否有限,并重新验证指标。若启用损失缩放,还要关注更新被跳过等现象。入门阶段无需推导浮点实现细节,但必须知道“更快”需要同时满足效果约束。
4 动手:跑通一次昇腾训练脚本
使用 Fashion-MNIST 官方训练集的 60000 张图片,按类别分层划出训练 54000 张、验证 6000 张,官方测试集 10000 张保持独立。保存划分索引与随机种子,使切换运行环境时仍使用相同样本。不要反复查看测试结果来选择学习率。若标准化使用从数据估计的均值和标准差,只能在训练集上拟合,再应用到验证集和测试集;本例仅除以 255,不需要拟合统计量。

准备环境时,从对应版本文档确认 MindSpore、CANN、驱动及设备的配套关系,采用其中经过支持的安装组合。在终端运行 npu-smi info 可作为设备可见性的起点,但还需确认当前用户或容器能使用指定设备。先运行小矩阵运算检查,再启动训练。不要在设备初始化失败时捕获异常并自动切换到 CPU,这会改变实验的含义。
下面给出训练入口的核心片段。数据下载、IDX 文件解析和分层划分应在独立的数据准备步骤完成;train_x 是形状为 [N, 1, 28, 28]、范围在 0 到 1 的 float32 数组,train_y 是形状为 [N] 的 int32 标签。验证数据按相同协议准备。片段展示明确的设备、模型和更新路径,接口是否可用仍需按选定 MindSpore 版本确认。
import numpy as np
import mindspore as ms
from mindspore import nn, dataset as ds
ms.set_seed(37)
ms.set_context(mode=ms.GRAPH_MODE, device_target="Ascend",
device_id=0)
def make_dataset(x, y, training):
source = ds.NumpySlicesDataset(
{"image": x.astype(np.float32),
"label": y.astype(np.int32)}, shuffle=training)
return source.batch(64, drop_remainder=False)
net = nn.SequentialCell(
nn.Flatten(), nn.Dense(784, 128), nn.ReLU(),
nn.Dense(128, 10))
loss_fn = nn.SoftmaxCrossEntropyWithLogits(
sparse=True, reduction="mean")
optimizer = nn.Adam(net.trainable_params(), learning_rate=0.001)
train_step = nn.TrainOneStepCell(
nn.WithLossCell(net, loss_fn), optimizer)
def run_epoch(train_data):
train_step.set_train(True)
for image, label in train_data.create_tuple_iterator():
loss = train_step(image, label)
return float(loss.asnumpy())
把这个入口接到数据准备程序后,先检查样本数、标签范围和 1 个批次的形状,再创建数据集并运行。这里返回的是最后 1 个批次的损失,不是整轮平均损失;完整日志应按样本数加权汇总各批次损失。尾批大小可能小于 64,不能在统计时给予每个批次相同权重。实际图模式中,尾批形状还可能触发额外编译,应在记录中说明,必要时按支持方式处理。
每轮训练后设置 net.set_train(False),对验证集输出取 argmax 得到类别,并累计 10×10 混淆矩阵。对某个类别,precision 是预测为该类的样本中有多少正确,recall 是真实属于该类的样本中有多少被找回,F1 是这 2 项的调和平均。宏平均 F1 对 10 个类别的 F1 等权平均,能防止大类别掩盖小类别;分母为 0 时需约定处理规则并保持一致。
以最佳验证宏平均 F1 选择检查点,用 ms.save_checkpoint(net, "best.ckpt") 保存网络参数,并另存配置和指标。网络参数检查点不足以保证中断后精确续训;续训还需要优化器状态、轮次等训练状态,保存方式应单独验证。方案冻结后加载选中的网络检查点,运行测试集并报告混淆矩阵和每类指标。AUC 更常用于观察不同阈值的区分能力,例如流失二分类应使用连续分数计算;不能把已经取整的类别标签当作概率分数。
5 开发效率与性能调优取舍
模型训练的总时间包含环境准备、首次编译、数据读取、训练、验证和保存。若每次只运行很短的实验,减少首次编译或缩短调试周期可能更有价值;若同一配置持续训练很久,稳定吞吐量的重要性才会增大。因此需要同时记录首次可用结果的等待时间和稳定训练速度。只展示最短的某个 Step,无法说明开发周转效率。
测量时先定义口径。排除首次编译后,预热若干 Step,再测固定数量的连续 Step,记录中位数和较高分位数,例如 P95。测量窗口需要覆盖对应设备计算完成的时刻;异步提交返回不等于设备已完成。可以采用所选版本支持的同步方式或性能工具确认边界。逐 Step 把损失搬回主机也可能引入同步,因此既要记录实际训练配置,也要说明计时是否包含日志。

批大小是容易理解的取舍。增大批大小可能提高吞吐量,但会占用更多设备内存,并减少每轮参数更新次数,可能需要重新选择学习率与训练轮数。Step 变长不必然代表变慢:如果批大小翻倍,而 Step 时间增加不到 2 倍,每秒处理的图片数仍可能增加。比较收敛效率还应看达到同一验证指标需要多少总时间,不能只比较每秒样本数。
数据管道同样会限制速度。图像解码、增强和批处理如果不能及时提供输入,设备就会等待。可以先用预先准备好的固定张量测试计算路径,再与真实数据管道比较,帮助区分计算瓶颈和输入瓶颈。但最终报告必须使用真实数据流程,固定张量结果只作为诊断证据。缓存、并行预处理和预取都需控制内存用量,也要避免增强随机性失控。
每次只改变 1 个主要因素,例如批大小、数据读取并行度或混合精度策略,并保留对应效果指标。若同时修改模型、数据增强和运行模式,即使速度提升也难以解释原因。小模型在加速设备上未必表现出明显优势,调度和搬运开销可能占很大比例。这是工作负载与执行方式的匹配问题,不能据此推断所有模型的表现。
6 常见坑:CPU 假成功掩盖设备错误
最容易误判的情况是脚本结束、损失下降、检查点也存在,于是认定昇腾训练已经完成。实际上,启动参数可能仍是 CPU,配置文件可能被旧值覆盖,或者异常处理把设备失败转成了 CPU 重试。这里的“假成功”是指成功证据与目标不一致,并不意味着 CPU 上的训练结果必然错误。设备验收必须单独完成。
从启动日志开始,记录框架版本、模式、目标和设备编号;再确认实际进程关联的设备活动。设备编号要结合当前可见设备解释,容器内的编号可能与宿主环境的物理编号不同。仅有 device_target="Ascend" 这行源码不够,因为运行的入口、配置覆盖和部署目录可能不同。应保存本次运行实际生效的配置,而不是开发者记忆中的配置。

即使设备有活动,也应通过性能记录或执行信息检查关键前向、反向和参数更新路径。数据加载、文件读取和部分控制逻辑在 CPU 上执行很正常;需要解释的是预期由设备承担的关键计算为何出现在主机侧,或为何存在频繁往返。算子是否会回退、何时直接报错,与版本和执行路径有关,不能假设所有不支持的算子都会自动改到 CPU。
遇到设备初始化错误,按层排查:先看设备是否可见,再看驱动与运行时是否配套,再看框架安装包是否适用,最后确认进程权限、容器映射和设备占用。不要为了消除报错随意升级某个组件,也不要靠不断增加环境变量碰运气。把完整错误、版本组合和最小脚本保存下来,通常比截图“最后 1 行失败”更有用。
另有 2 类错误经常混在设备问题里。类别不均衡可能让总准确率很高,而少数类别 recall 接近 0,应看每类指标并检查采样或损失设置。分布偏移则可能表现为公开测试集正常,但新采集的手机照片效果明显下降,因为背景、光照与裁剪方式不同。设备迁移不会自动修复这些任务问题;应保留独立的新分布评估集,说明模型的适用范围。
7 案例:未融合算子拖垮 Step 时间
下面是一组教学用的合成测量,用来练习定位方法,不对应具体设备。某图像分类脚本在稳定阶段的 Step 中位数是 42 ms,其中按时间线划分的数据等待为 6 ms,其余主机开销为 8 ms,设备计算与搬运为 28 ms。这种相加只适用于此例按不重叠区间统计的口径;真实流程可能重叠,不能把各工具面板的数值直接相加。
开发者最初想增加数据读取并行度,但时间线显示,设备侧存在很多持续时间很短的算子,之间还有提交间隙。检查模型后发现,每个中间结果都重复做类型转换,还把本可连续表达的偏置、激活和逐元素运算拆进了复杂控制逻辑。此时“未融合”只是候选解释,需要进一步检查实际执行记录,而不能看到算子多就直接下结论。

处理过程先锁定相同权重、批次和精度配置,再把重复转换移到合适边界,用框架支持的常规网络层表达连续计算。这样给编译器留下可优化的结构,但是否实际融合仍取决于版本、形状与支持条件。随后比较固定批次输出和损失,确认语义未变,再重做预热与稳定窗口测量。此例改后 Step 中位数为 31 ms,改善约为 26%,还应同时报告 P95 与多次运行波动。
完整验证还要回到训练任务。若改写顺便改变了计算精度,固定批次误差通过并不能保证长期训练相同;需要用相同划分和训练预算比较宏平均 F1、每类召回率与损失走势。如果速度改善落在运行波动范围内,就不能宣称优化有效。即便效果一致,也应记录额外编译时间和峰值设备内存,防止收益被转移到其他成本上。
这类案例的可迁移结论是“根据证据减少冗余并验证实际执行”。不应把所有网络层强行合并,也不应入门就编写自定义融合算子。如果时间线显示主要等待来自图片解码,那么改写设备算子很难解决主要问题。只有确认瓶颈位置、改写语义和效果约束,优化结论才可复用。
8 把昇腾工具链写进交付方案
交付训练结果时,应把工具链写成可以重建的环境契约。列出操作系统、Python、MindSpore、CANN、驱动和设备型号的实际版本,说明配套依据以及启动方式。把普通运行依赖与调试、性能采集依赖区分清楚,避免接收方为了加载检查点被迫安装全部开发工具。性能采集工具的具体命令以目标版本文档为准,方案应说明采集目的、窗口和产物位置。
数据契约要能回答“输入究竟是什么”。对图像写明通道顺序、尺寸、像素缩放、类别映射;对表格写明缺失值处理、类别编码和特征时间窗口;对短文本写明分词器版本、最大长度及截断规则。训练中估计的预处理参数应随模型保存,不能让接收方在测试数据上重新拟合。数据获取地址、版本或校验信息与划分索引也应随实验记录保存。
验收表应同时含效果、设备和性能门槛。效果门槛由业务风险决定,例如流失任务更关心被识别客户的 precision,还是遗漏客户的 recall,必须先讨论。设备门槛要求目标设备完成关键路径,性能门槛说明批大小、精度、预热方式和计时边界。此处不应凭教材示例设定适用于所有任务的 F1 下限,而应提供可填写且可追溯的验收字段。
最后安排失败处理与复现责任。保存最佳检查点、训练配置和对应提交版本,说明失败时如何保留日志、如何恢复,以及谁确认兼容性问题。若只交网络参数,须明确这是用于加载评估还是用于续训。若还没有验证部署运行时兼容性,就把它列为下一阶段待验证事项,不能用“训练通过”代替部署验收。
9 从MindSpore 与昇腾开发走向模型部署(A38)
训练完成后,模型不再执行反向传播和优化器更新,但输入、输出与数值约定仍要保持。A38 将转向模型部署:如何选择导出产物、准备推理运行环境、验证加载后的输出,以及测量实际请求的延迟和吞吐量。本课先把训练出口整理清楚,避免进入部署阶段才发现类别顺序或预处理规则没有记录。
可准备 1 组来自允许使用的数据的固定样本,保存输入处理说明、参考类别分数和允许误差。部署侧用它检查加载和计算是否符合预期,再用独立评估集检查整体指标。固定样本只能发现有限问题,不能代表所有输入;尤其应覆盖容易混淆的类别、边界尺寸及错误输入的处理约定。
训练出口还应验证一次独立加载:新建相同结构的网络,用 ms.load_checkpoint 读取参数,再用 ms.load_param_into_net 装入网络,检查返回的未加载参数信息,并设置 net.set_train(False)。不能只在训练进程中测试,因为内存中的参数可能掩盖检查点路径错误或文件过期。若修改过层名称或分类头,部分参数可能无法匹配;应逐项解释,不能忽略提示后继续交付。
准备导出时,要区分参数检查点与计算图产物。检查点需要配套网络代码才能重建计算,导出图则还受导出接口、输入签名和目标运行时支持范围约束。例如以 [64, 1, 28, 28] 样例输入调用导出接口,不能据此认定产物接受 [1, 1, 28, 28];应记录批维是否固定,并在目标路径验证所需批大小。若选用 MindIR,还需核对接收端对该格式及算子的支持。加载失败与输出偏差分别保留证据,避免把图签名问题误当成模型效果退化。
训练时的 64 批次吞吐量不能直接转换为在线单请求延迟。部署还涉及请求等待、预处理、数据搬运和结果返回,实际输入批大小也可能变化。设备内存需求通常会与训练不同,但必须实测。交接时把哪些结论已验证、哪些需要在部署路径重测写清楚,就能让后续比较建立在相同任务和相同证据上。
放进方案里
本项目使用 Fashion-MNIST 建立服装图像分类基线,输入为 1 通道、28×28 像素图像,输出为固定顺序的 10 类分数。官方训练数据按类别分层划为 54000 张训练样本与 6000 张验证样本,官方 10000 张测试样本仅在配置冻结后评估。交付保存划分索引、数据版本与像素缩放规则,所有配置选择依据验证集结果完成,禁止用测试集选择轮次或学习率。
训练采用经兼容性确认的 MindSpore、CANN、驱动与昇腾设备组合,实际版本、启动参数及生效配置随运行记录交付。先以固定批次完成 FP32 数值对齐,再决定是否启用混合精度。设备验收同时提供目标配置、进程关联的设备活动与关键计算执行证据;初始化失败直接报告,不自动切换 CPU。效果报告包含宏平均 F1、每类 precision、recall 和混淆矩阵,并说明零分母处理规则。
性能报告分别列出首次编译开销与稳定阶段结果,注明批大小、预热、同步边界、样本数量和采集工具版本,给出 Step 中位数、P95、吞吐量及峰值设备内存。优化须保持相同数据协议,并在改动后复核效果;未通过验证的加速配置不进入正式结果。验收门槛由项目根据试验基线与使用风险确定,示例数字不作为硬件承诺。
交付包包含网络检查点、模型代码、配置、类别映射、环境清单、指标记录和固定输入的参考输出。若要求中断续训,另行验证优化器状态与训练进度的恢复。新分布数据需要单独评估并记录失效情形。下一阶段验证导出模型、推理环境及请求延迟,训练通过只作为该阶段的输入条件,部署负责人据此完成独立验收。
10 小结:下一步模型部署(A38)
MindSpore 表达训练逻辑,昇腾工具链承接目标设备执行。可靠开发从固定数据协议出发,依次确认形状与损失、图模式执行、算子支持、数值对齐和稳定性能,再把证据随检查点交付。下一步进入 A38,把训练产物转成可验证的推理服务或离线推理程序。
自测时试着解释:为什么损失下降不能证明使用了指定设备?为什么首次 Step 不适合代表稳定速度?为什么批大小增大后仍要重测宏平均 F1?如果能分别给出设备、计时和效果证据,你已经具备继续学习模型部署的基础。