0

This is python code to generate a simple multi-ports MLP model-two inputs,two outputs.Using HybridBlock and erport functions make it possible to use in C++.

Net Graph:

enter image description here enter image description here

from mxnet import nd
from mxnet.gluon import nn
import mxnet as mx

class HybridNet(nn.HybridBlock):
    def __init__(self, **kwargs):
        super(HybridNet, self).__init__(**kwargs)
        with self.name_scope():
            self.dense0 = nn.Dense(3)
            self.dense1 = nn.Dense(3)
            self.dense2 = nn.Dense(6)

    def hybrid_forward(self, F,x,y):
        result1 = F.relu(self.dense0(x))+F.relu(self.dense1(y))
        result2 = F.relu(self.dense2(result1))
        return [result1,result2]

net = HybridNet()
net.initialize()
net.hybridize()
x = nd.random.normal(shape=(4,3))
y = nd.random.normal(shape=(4,5))
res=net(x,y)
print "output1:",res[0]
print "output2:",res[1]
net.export('model')

We can re-import the model into mxnet to check weather the exporting is correct. You can see the two results(from gluon and mxnet) are the same.

from collections import namedtuple
sym = mx.symbol.load('model-symbol.json') 
mod=mx.mod.Module(symbol=sym,data_names=['data0','data1'])
mod.bind(data_shapes=[('data0',(1,3)),('data1',(1,5))])
mod.load_params('model-0000.params')
Batch=namedtuple('Batch',['data'])
mod.forward(Batch(data=[x,y]), is_train=False)
print mod.get_outputs()

enter image description here

See what outputs look like

sym.list_outputs()

['hybridnet0__plus0_output', 'hybridnet0_relu2_output']

Here is the first part of the C++ code and it raise error.I make sure the num_input_nodes and num_output_nodes both are two.And using MXPredCreatePartialOut to custom my multi-tasks output.

enter image description here

#include <mxnet/c_predict_api.h>

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <assert.h>

// Read file to buffer
class BufferFile {
public:
    std::string file_path_;
    int length_;
    char* buffer_;

    explicit BufferFile(std::string file_path)
        :file_path_(file_path) {

        std::ifstream ifs(file_path.c_str(), std::ios::in | std::ios::binary);
        if (!ifs) {
            std::cerr << "Can't open the file. Please check " << file_path << ". \n";
            length_ = 0;
            buffer_ = NULL;
            return;
        }

        ifs.seekg(0, std::ios::end);
        length_ = ifs.tellg();
        ifs.seekg(0, std::ios::beg);
        std::cout << file_path.c_str() << " ... " << length_ << " bytes\n";

        buffer_ = new char[sizeof(char) * length_];
        ifs.read(buffer_, length_);
        ifs.close();
    }

    int GetLength() {
        return length_;
    }
    char* GetBuffer() {
        return buffer_;
    }

    ~BufferFile() {
        if (buffer_) {
            delete[] buffer_;
            buffer_ = NULL;
        }
    }
};

int main(int argc, char* argv[]) {

    // Models path for your model, you have to modify it
    std::string json_file = "./model-symbol.json";
    std::string param_file = "./model-0000.params";

    BufferFile json_data(json_file);
    BufferFile param_data(param_file);

    // Parameters
    int dev_type = 1;  // 1: cpu, 2: gpu
    int dev_id = 1;  // arbitrary.
    mx_uint num_input_nodes = 2;
    mx_uint num_output_nodes = 2;

    const char* input_key[2] = { "data0" , "data1" };
    const char** input_keys = input_key;
    const char* output_key[2] = { "hybridnet0__plus0" , "hybridnet0_relu2" };
    const char** output_keys = output_key;

    // input-dims
    int data0_len = 3;
    int data1_len = 5;
    const mx_uint input_shape_indptr[4] = { 0,2,2,4 };
    const mx_uint input_shape_data[4] = {1,static_cast<mx_uint>(data0_len),1,static_cast<mx_uint>(data1_len) };
    PredictorHandle pred_hnd = 0;

    if (json_data.GetLength() == 0 || param_data.GetLength() == 0)
        return -1;

    // Create Predictor
    assert(0 == MXPredCreatePartialOut(
        (const char*)json_data.GetBuffer(),
        (const char*)param_data.GetBuffer(),
        static_cast<size_t>(param_data.GetLength()),
        dev_type,
        dev_id,
        num_input_nodes,
        input_keys,
        input_shape_indptr,
        input_shape_data,
        num_output_nodes,
        output_keys,
        &pred_hnd));   //ERROR HERE
    assert(pred_hnd);

    return 0;
}
partida
  • 501
  • 4
  • 20

1 Answers1

1

Looks like this line is wrong. const mx_uint input_shape_indptr[4] = { 0,2,2,4 };

Change it to const mx_uint input_shape_indptr[3] = { 0,2,4 };

reminisce
  • 141
  • 3
  • Yeah,Thank you!.There is no error.But Why delete `2` in the `input_shape_indptr`? – partida Oct 20 '17 at 07:14
  • Take a look at this [code](https://github.com/apache/incubator-mxnet/blob/master/src/c_api/c_predict_api.cc#L175). `indptr` contains 0 as the first element, and accumulated shape data lengths. – reminisce Oct 20 '17 at 07:19
  • `input_shape_indptr` indicates where the data start and end,so length of it is even.Isnt it right? – partida Oct 20 '17 at 07:22
  • Thank you very much.It's my first time to see original code of MXNet – partida Oct 20 '17 at 07:24
  • For example, your input shape is `(3, 5)`, `(2, 3, 6)`. The shape data passed to c-api is `shape_data = [3, 5, 2, 3, 6]`. `indptr` tells the c-api how to divide the shape data array into two such that `[shape_data[indptr[0]], shape_data[indptr[1]])` is the first input shape, and `[shape_data[indptr[1]], shape_data[indptr[2]])` is the second input shape. That's why `indptr=[0, 2, 5]` in this case. – reminisce Oct 20 '17 at 07:28
  • The explanation is simple and clear.This problem let me puzzle for a whole day,Thanks for pointing this mistake. – partida Oct 20 '17 at 07:39
  • WoW. The complete code can run correctly! Thannnnk you! – partida Oct 20 '17 at 07:46