使用Runtime执行推理(Java)

Android 推理应用 模型加载 数据准备 中级 高级

概述

通过MindSpore Lite模型转换后,需在Runtime中完成模型的推理执行流程。本教程介绍如何使用Java接口编写推理代码。

更多Java API说明,请参考 API文档

Android项目引用AAR包

首先将mindspore-lite-{version}.aar文件移动到目标module的libs目录,然后在目标module的build.gradlerepositories中添加本地引用目录,最后在dependencies中添加aar的依赖,具体如下所示。

repositories {
    flatDir {
        dirs 'libs'
    }
}

dependencies {
    implementation(name:'mindspore-lite-{version}', ext:'aar')
}

注意mindspore-lite-{version}是aar的文件名,需要将{version}替换成对应版本信息。

运行MindSpore Lite推理框架

Android项目中使用MindSpore Lite,可以选择采用C++ APIs或者Java APIs运行推理框架。Java APIs与C++ APIs相比较而言,Java APIs可以直接在Java Class中调用,无需实现JNI层的相关代码,具有更好的便捷性。运行Mindspore Lite推理框架主要包括以下步骤:

  1. 加载模型:从文件系统中读取MindSpore Lite模型,并进行模型解析。

  2. 创建配置上下文:创建配置上下文MSConfig,保存会话所需的一些基本配置参数,用于指导图编译和图执行。主要包括deviceType:设备类型、threadNum:线程数、cpuBindMode:CPU绑定模式、enable_float16:是否优先使用float16算子。

  3. 创建会话:创建LiteSession,并调用init方法将上一步得到MSConfig配置到会话中。

  4. 图编译:在图执行前,需要调用LiteSessioncompileGraph接口进行图编译,主要进行子图切分、算子选型调度。这部分会耗费较多时间,所以建议LiteSession创建一次,编译一次,多次执行。

  5. 输入数据:图执行之前需要向输入Tensor中填充数据。

  6. 图执行:使用LiteSessionrunGraph进行模型推理。

  7. 获得输出:图执行结束之后,可以通过输出Tensor得到推理结果。

  8. 释放内存:无需使用MindSpore Lite推理框架的时候,需要将创建的LiteSessionmodel进行释放。

加载模型

MindSpore Lite进行模型推理时,需要先从文件系统中加载模型转换工具转换后的.ms模型,并进行模型解析。Java的model类提供了loadModel,使其可以从Assets或其他文件路径中加载模型。

// Load the .ms model.
model = new Model();
if (!model.loadModel(context, "model.ms")) {
    Log.e("MS_LITE", "Load Model failed");
    return false;
}

创建配置上下文

创建配置上下文MSConfig,保存会话所需的一些基本配置参数,用于指导图编译和图执行。

MindSpore Lite支持异构推理,推理时的主选后端由MSConfigdeviceType指定,目前支持CPU和GPU。在进行图编译时,会根据主选后端进行算子选型调度。

MindSpore Lite内置一个进程共享的线程池,推理时通过threadNum指定线程池的最大线程数,默认为2线程,推荐最多不超过4个线程,否则可能会影响性能。

MindSpore Lite支持float16算子的模式进行推理。enable_float16设置为true后,将会优先使用float16算子。

// Create and init config.
MSConfig msConfig = new MSConfig();
if (!msConfig.init(DeviceType.DT_CPU, 2, CpuBindMode.MID_CPU, true)) {
    Log.e("MS_LITE", "Init context failed");
    return false;
}

创建会话

LiteSession是推理的主入口,通过LiteSession可以进行图编译、图执行。创建LiteSession,并调用init方法将上一步得到MSConfig配置到会话中。LiteSession初始化之后,MSConfig将可以进行释放操作。

// Create the MindSpore lite session.
session = new LiteSession();
if (!session.init(msConfig)) {
    Log.e("MS_LITE", "Create session failed");
    msConfig.free();
    return false;
}
msConfig.free();

图编译

在图执行前,需要调用LiteSessioncompileGraph接口进行图编译,主要进行子图切分、算子选型调度。这部分会耗费较多时间,所以建议LiteSession创建一次,编译一次,多次执行。图编译结束之后,可以调用ModelfreeBuffer函数,释放MindSpore Lite Model中的MetaGraph,用于减小运行时的内存,但释放后该Model就不能再次图编译。

// Compile graph.
if (!session.compileGraph(model)) {
    Log.e("MS_LITE", "Compile graph failed");
    model.freeBuffer();
    return false;
}

// Note: when use model.freeBuffer(), the model can not be compiled.
model.freeBuffer();

输入数据

Java目前支持byte[]或者ByteBuffer两种类型的数据,设置输入Tensor的数据。

// Set input tensor values.
List<MSTensor> inputs = session.getInputs();
MSTensor inTensor = inputs.get(0);
byte[] inData = readFileFromAssets(context, "model_inputs.bin");
inTensor.setData(inData);

图执行

通过LiteSessionrunGraph执行模型推理。

// Run graph to infer results.
if (!session.runGraph()) {
    Log.e("MS_LITE", "Run graph failed");
    return;
}

获得输出

推理结束之后,可以通过输出Tensor得到推理结果。目前输出tensor支持的数据类型包括floatintlongbyte

// Get output tensor values.
List<String> tensorNames = session.getOutputTensorNames();
Map<String, MSTensor> outputs = session.getOutputMapByTensor();
Set<Map.Entry<String, MSTensor>> entries = outputs.entrySet();
for (String tensorName : tensorNames) {
    MSTensor output = outputs.get(tensorName);
    if (output == null) {
        Log.e("MS_LITE", "Can not find output " + tensorName);
        return;
    }
    float[] results = output.getFloatData();

    // Apply infer results.
    ……
}

释放内存

无需使用MindSpore Lite推理框架的时候,需要将创建的sessionmodel进行释放。

private void free() {
    session.free();
    model.free();
}

Android项目使用MindSpore Lite推理框架示例

采用MindSpore Lite Java API推理主要包括加载模型创建配置上下文创建会话图编译输入数据图执行获得输出释放内存等步骤。

private boolean init(Context context) {
    // Load the .ms model.
    model = new Model();
    if (!model.loadModel(context, "model.ms")) {
        Log.e("MS_LITE", "Load Model failed");
        return false;
    }

    // Create and init config.
    MSConfig msConfig = new MSConfig();
    if (!msConfig.init(DeviceType.DT_CPU, 2, CpuBindMode.MID_CPU)) {
        Log.e("MS_LITE", "Init context failed");
        return false;
    }

    // Create the MindSpore lite session.
    session = new LiteSession();
    if (!session.init(msConfig)) {
        Log.e("MS_LITE", "Create session failed");
        msConfig.free();
        return false;
    }
    msConfig.free();

    // Compile graph.
    if (!session.compileGraph(model)) {
        Log.e("MS_LITE", "Compile graph failed");
        model.freeBuffer();
        return false;
    }

    // Note: when use model.freeBuffer(), the model can not be compiled.
    model.freeBuffer();

    return true;
}

private void DoInference(Context context) {
    // Set input tensor values.
    List<MSTensor> inputs = session.getInputs();
    MSTensor inTensor = inputs.get(0);
    byte[] inData = readFileFromAssets(context, "model_inputs.bin");
    inTensor.setData(inData);

    // Run graph to infer results.
    if (!session.runGraph()) {
        Log.e("MS_LITE", "Run graph failed");
        return;
    }

    // Get output tensor values.
    List<String> tensorNames = session.getOutputTensorNames();
    Map<String, MSTensor> outputs = session.getOutputMapByTensor();
    Set<Map.Entry<String, MSTensor>> entries = outputs.entrySet();
    for (String tensorName : tensorNames) {
        MSTensor output = outputs.get(tensorName);
        if (output == null) {
            Log.e("MS_LITE", "Can not find output " + tensorName);
            return;
        }
        float[] results = output.getFloatData();

        // Apply infer results.
        ……
    }
}

// Note: we must release the memory at the end, otherwise it will cause the memory leak.
private void free() {
    session.free();
    model.free();
}