Android Deploy with new .so format

Hi, I’m working with the android_deploy sample app that uses the three artifact version of a compiled model (.so library, .json graph, .params weights) and the code to load this is available here. Attaching a minimal snippet below:

// load json graph
String modelGraph = null;
String graphFilename = 'modelGraph.json';
modelGraph = new String(getBytesFromFile(assetManager, graphFilename));

// upload tvm compiled function on application cache folder
String libCacheFilePath = null;
String libFilename = 'modelLib.so';
libCacheFilePath = getTempLibFilePath(libFilename);
byte[] modelLibByte = getBytesFromFile(assetManager, libFilename);
FileOutputStream fos = new FileOutputStream(libCacheFilePath);
fos.write(modelLibByte);
fos.close();

// load parameters
byte[] modelParams = null;
String paramFilename = 'modelParams.params';
modelParams = getBytesFromFile(assetManager, paramFilename);

// create java tvm device
Device tvmDev = Device.cpu();

// tvm module for compiled functions
Module modelLib = Module.load(libCacheFilePath);

// get global function module for graph executor
Function runtimeCreFun = Function.getFunction("tvm.graph_executor.create");
TVMValue runtimeCreFunRes = runtimeCreFun.pushArg(modelGraph)
        .pushArg(modelLib)
        .pushArg(tvmDev.deviceType)
        .pushArg(tvmDev.deviceId)
        .invoke();
graphExecutorModule = runtimeCreFunRes.asModule();

// get the function from the module(load parameters)
Function loadParamFunc = graphExecutorModule.getFunction("load_params");
loadParamFunc.pushArg(modelParams).invoke();

// release tvm local variables
modelLib.release();
loadParamFunc.release();
runtimeCreFun.release();

TVM is now deprecating this output structure in favor of a single .so file which contains the graph and weights structures that can be loaded in python as follows:

lib = tvm.runtime.load_module(os.path.join(base, "modelLibrary.so"))
module = tvm.contrib.graph_executor.GraphModule(lib['default'](tvm.cpu()))

Is there an example available to load this kind of combined binary in Android?

you can use this code snipped:

    // depending on  target device wich was used for compiled model
    DLDevice ctx;
    if (device == "CPU") {
        ctx = {kDLCPU, 0};
    } else if (device == "Metal") {
        ctx = {kDLMetal, 0};
    } else if (device == "OpenCL") {
        ctx = {kDLOpenCL, 0};
    } else {    // etc for other target devices
        ...        
    }

    tvm::runtime::Module mod_factory = tvm::runtime::Module::LoadFromFile(input_model);
    // create the graph runtime module
    tvm::runtime::Module gmod = mod_factory.GetFunction("default")(ctx);
    tvm::runtime::PackedFunc set_input = gmod.GetFunction("set_input");
    tvm::runtime::PackedFunc get_output = gmod.GetFunction("get_output");
    tvm::runtime::PackedFunc run = gmod.GetFunction("run");

    // in its turn, the context for data should be CPU only so far:
     DLDevice ctxCPU{kDLCPU, 0};
    tvm::runtime::NDArray x = tvm::runtime::NDArray::Empty({1, 3, 224, 224}, DLDataType{kDLFloat, 32, 1}, ctxCPU);
    // filing of the input to x->data
    // loading data to model
    set_input(0, x);

    // run inference
    run();

    // get ouptut
    tvm::runtime::NDArray y = tvm::runtime::NDArray::Empty({1, 1000}, DLDataType{kDLFloat, 32, 1}, ctxCPU);
    tvm::runtime::NDArray y2 = get_output(0);
    y.CopyFrom(y2);

Thanks for sharing this.

I updated my code to the following:

String libCacheFilePath = null;
String libFilename = MODEL_CPU_LIB_FILE;
try {
    libCacheFilePath = getTempLibFilePath(libFilename);
    byte[] modelLibByte = getBytesFromFile(assetManager, libFilename);
    FileOutputStream fos = new FileOutputStream(libCacheFilePath);
    fos.write(modelLibByte);
    fos.close();
} catch (IOException e) {
    LOGGER.e("Problem uploading compiled function!" + e);
}

// tvm module for compiled functions
Module modelLib = Module.load(libCacheFilePath);
// get global function module for graph executor
graphExecutorModule = modelLib.getFunction("default")
                              .pushArg(Device.cpu().toString())
                              .invoke().asModule();

I get the following error:

error: no suitable method found for pushArg(org.apache.tvm.Device)
        graphExecutorModule = modelLib.getFunction("default").pushArg(tvmDev).invoke().asModule();
                                                             ^
    method org.apache.tvm.Function.pushArg(int) is not applicable
      (argument mismatch; org.apache.tvm.Device cannot be converted to int)
    method org.apache.tvm.Function.pushArg(long) is not applicable
      (argument mismatch; org.apache.tvm.Device cannot be converted to long)
    method org.apache.tvm.Function.pushArg(float) is not applicable
      (argument mismatch; org.apache.tvm.Device cannot be converted to float)
    method org.apache.tvm.Function.pushArg(double) is not applicable
      (argument mismatch; org.apache.tvm.Device cannot be converted to double)
    method org.apache.tvm.Function.pushArg(java.lang.String) is not applicable
      (argument mismatch; org.apache.tvm.Device cannot be converted to java.lang.String)
    method org.apache.tvm.Function.pushArg(org.apache.tvm.NDArrayBase) is not applicable
      (argument mismatch; org.apache.tvm.Device cannot be converted to org.apache.tvm.NDArrayBase)
    method org.apache.tvm.Function.pushArg(org.apache.tvm.Module) is not applicable
      (argument mismatch; org.apache.tvm.Device cannot be converted to org.apache.tvm.Module)
    method org.apache.tvm.Function.pushArg(org.apache.tvm.Function) is not applicable
      (argument mismatch; org.apache.tvm.Device cannot be converted to org.apache.tvm.Function)
    method org.apache.tvm.Function.pushArg(byte[]) is not applicable
      (argument mismatch; org.apache.tvm.Device cannot be converted to byte[])

Should I pass the kDLCPU value 1 to the argument?

I also tried passing an integer to the function (1, which is the kDLCPU value), and it throws: Check failed: type_code_ == kDLDevice (0 vs. 6) : expected DLDevice but got int

Ups, I thought about c/c++ flow. I don’t know if there are java bindings for my above code. There might be a suggestion to create your own c library, call TVM from it like I proposed above and call your functions through JNI, but it might not be convenient.

Hi, I am trying to use the same demo and I put these three files of mobilenet in asset folder. But it shows that I could not load the .so file. Could you please tell me why this happens. Here is the code that generates these three files.

model = models.MobileNetV2(width_mult=1)
        model.eval()
        input_name = "input_1"
        shape_list = [(input_name,(1,3,224,224))]
        img = torch.rand([1,3,224,224])
        model = torch.jit.trace(model,img)
        shape_list = [(input_name,(1,3,224,224))]
        mod, params = relay.frontend.from_pytorch(model, shape_list)
        return (mod, params)

and

print("dumping lib...")
    lib.export_library(output_path_str + "/" + "deploy_lib_cpu.so", ndk.create_shared)
    print("dumping graph...")
    with open(output_path_str + "/" + "deploy_graph.json", "w") as f:
        f.write(graph)
    print("dumping params...")
    with open(output_path_str + "/" + "deploy_param.params", "wb") as f:
        f.write(tvm.runtime.save_param_dict(params))