4、MindSpore Lite推理实战:创建推理上下文、创建会话、输入数据填充、执行推理、获取输出
好,咱们今天直接进入正题。MindSpore Lite的推理流程,说白了就是五步走:建上下文、建会话、塞数据、跑推理、拿结果。这五个步骤环环相扣,少一步都不行。
我在项目里见过不少新手,上来就急着跑模型,结果上下文没配好,推理直接崩了。嗯,咱们一步一步来,稳扎稳打。
4.1 创建推理上下文(Context)
推理上下文,你可以把它理解成「推理环境的配置单」。它告诉框架:你要在哪个设备上跑?用几个线程?要不要支持float16加速?
我个人习惯,在创建Context之前,先想清楚目标设备。如果是手机端,一般用CPU就够了;如果是带NPU的旗舰机,可以试试NPU加速。
核心配置项:
- 设备类型:CPU、GPU、NPU(Ascend)
- 线程数:建议2-4个,太多反而会因线程切换降低性能
- 精度模式:fp32(默认)、fp16(加速)、int8(量化)
- 是否绑定CPU核心:大核优先还是小核优先
来看代码,这是我在一个图像分类项目里用过的配置:
// 创建上下文
OH_AI_ContextHandle context = OH_AI_ContextCreate();
if (context == nullptr) {
// 处理创建失败的情况
return;
}
// 设置线程数,我一般设2个,平衡性能和功耗
OH_AI_ContextSetThreadNum(context, 2);
// 设置设备类型为CPU
OH_AI_DeviceInfoHandle cpu_device = OH_AI_DeviceInfoCreate(OH_AI_DEVICETYPE_CPU);
OH_AI_ContextAddDeviceInfo(context, cpu_device);
小提示:如果你用的是华为麒麟芯片,可以试试开启fp16。我实测过,推理速度能提升30%左右,精度损失几乎可以忽略。
4.2 创建会话(Session)
会话,就是推理的「工作车间」。你把模型加载进来,它负责管理模型的生命周期和资源分配。
创建会话时,需要把刚才配好的Context传进去。这里有个坑——Context一旦传给Session,就别再改了。我曾经犯过这个错,改完Context再跑推理,结果模型输出全是乱码。
// 创建会话
OH_AI_ModelHandle model = OH_AI_ModelCreate();
if (model == nullptr) {
OH_AI_ContextDestroy(&context);
return;
}
// 加载模型文件
// 注意:模型路径要写绝对路径,或者确保应用有文件访问权限
const char *model_path = "/data/local/tmp/model.mindir";
OH_AI_Status ret = OH_AI_ModelBuild(model, context, model_path);
if (ret != OH_AI_STATUS_SUCCESS) {
// 模型加载失败,检查路径和模型文件是否完整
OH_AI_ModelDestroy(&model);
OH_AI_ContextDestroy(&context);
return;
}
注意:模型文件路径不要写相对路径!Android系统下,相对路径经常找不到文件。我建议用getFilesDir()或getExternalFilesDir()获取绝对路径。
4.3 输入数据填充
模型建好了,接下来就是往里面塞数据。这一步看似简单,但最容易出错。
你想想看,模型训练时输入是归一化后的数据,推理时也得做同样的预处理。我见过有人直接拿原始像素值往里塞,结果模型输出概率全是0.5——嗯,那肯定不对。
填充数据的核心步骤:
- 获取模型输入张量(Tensor)
- 把预处理后的数据拷贝到张量里
- 设置张量的形状和数据类型
// 获取输入张量
OH_AI_TensorHandleArray inputs = OH_AI_ModelGetInputs(model);
if (inputs.handle_num == 0) {
// 没有输入张量,检查模型
return;
}
// 假设模型输入是1x3x224x224的float32数据
OH_AI_TensorHandle input_tensor = inputs.handle_list[0];
OH_AI_TensorSetData(input_tensor, your_image_data);
OH_AI_TensorSetShape(input_tensor, shape_data); // shape_data = {1, 3, 224, 224}
关键点:输入数据的排列顺序要和模型训练时一致。比如训练时用的是CHW(通道-高-宽),推理时也得是CHW。别搞反了,否则模型会「看不懂」你的数据。
4.4 执行推理
数据准备好了,点一下「开始」按钮就行。这一步其实是最简单的,框架帮你处理了所有计算。
但我要提醒你——推理不是瞬间完成的。尤其是大模型,一次推理可能要几百毫秒。如果你在主线程里跑推理,UI会卡死。我建议开个子线程去做。
// 执行推理
OH_AI_Status ret = OH_AI_ModelPredict(model, inputs, &outputs, nullptr, nullptr);
if (ret != OH_AI_STATUS_SUCCESS) {
// 推理失败,检查输入数据是否正确
return;
}
性能优化:如果你需要连续推理多张图片,可以复用同一个Session。我做过测试,复用Session比每次重新创建快5倍以上。
4.5 获取输出
推理完了,该拿结果了。输出张量里存的就是模型的预测结果。
对于分类模型,输出通常是一个概率分布。你需要找到概率最大的那个索引,然后映射到具体的类别标签。
// 获取输出张量
OH_AI_TensorHandleArray outputs = OH_AI_ModelGetOutputs(model);
if (outputs.handle_num == 0) {
// 没有输出,检查模型
return;
}
// 假设输出是1x1000的float32数据
OH_AI_TensorHandle output_tensor = outputs.handle_list[0];
float *output_data = (float *)OH_AI_TensorGetData(output_tensor);
// 找到最大概率的索引
int max_index = 0;
float max_value = output_data[0];
for (int i = 1; i < 1000; i++) {
if (output_data[i] > max_value) {
max_value = output_data[i];
max_index = i;
}
}
// 输出结果
printf("预测类别索引: %d, 置信度: %f\n", max_index, max_value);
注意:输出数据是float32类型,别用int去读。我曾经犯过这个错,读出来的全是0。嗯,调试了半天才发现是类型不匹配。
4.6 完整流程示意图
下面这张图,是我自己画的推理流程。你可以把它当成「作弊小抄」,写代码时对照着看。
4.7 避坑指南
最后,我把自己踩过的坑总结一下,希望能帮你少走弯路:
- 内存泄漏:每次推理完,记得释放Tensor和Session。我刚开始做时忘了释放,跑了100次推理后内存爆了。
- 线程安全:多个线程同时调用同一个Session,可能会出问题。建议每个线程创建独立的Session。
- 模型版本:MindSpore Lite和MindSpore的版本要匹配。我用过1.7的Lite加载1.8的模型,直接报错。
- 输入形状:模型的输入形状是固定的,别传不同尺寸的数据。如果模型支持动态形状,记得在创建Session时配置好。
总结一下:MindSpore Lite的推理流程,核心就是这五步。你只要把Context配好、Session建好、数据填对、推理跑通、结果拿对,一个完整的推理应用就搞定了。