Acumos C++ Client User Guide

Target Users

The target users of this guide are modelers with sufficient C++ to write and build C++ applications.

Overview

This guide will describe the steps needed to onboard a c++ model using a tutorial where the provided example iris-classifier is prepared to be onboarded. Basically the following steps are needed: 1. Train the model 2. Create the serialized model 3. Define the gRPC service 4. Create service executable 5. Create the bundles for onboarding

Architecture

In Acumos a model is packed as a dockerized microservice exposing which is specified using Google protobuf. In order to achieve that, the modeler must write a short C++ program that attaches the trained model with the generated gRPC stub in order to build an executable that contains the gRPC webserver as well as the trained model. This executable will then be started in the docker container.

The minimum C++ standard level is C++11 and the recommended compiler is gcc 7.4

Tutorial

Onboarding the Iris Kmeans Classifier

Overview

In the examples directory contains the complete steps to onboard the well known Iris Classifier using a KMeans implementation

Prerequisites

The examples was developed in the following environment: * Ubuntu 18.04 * g++ 7.4 (default version on Ubuntu 18.04) * gRPC 1.20, should be installed from source (https://github.com/grpc/grpc) into /usr/local including all plugins * python 3.6 * cmake

In the text all we assume that you are in the directory examples/iris-kmeans.

Step 1: Train model

The file src/iris-kmeans.cpp trains the iris classifier model by finding a centroid for each of the three iris species. The classify method then finds the closest centroid to the given data point and returns it as the most probable species. Thus in this case, the three centroids make up the trained model.

Step 2: Serialize trained model

The targeted microservice needs to load the serialized trained model at startup. It is completely up to the developer how this is done. The example uses protobuf, because it fits in the technology lineup of the whole example. To save and load the trained model, the example uses a protobuf definition the can be found in step2_serialize_model/centroids.proto:

Then, generate the respective c++ code using the protobuf compiler:

protoc --cpp_out=. centroids.proto

An use a small code snippet to save the data to a file:

string model_file="data/iris-model.bin";
fstream output(model_file, ios::out | ios::binary);
centroids.SerializePartialToOstream(&output);

The two examples to load and save the iris model must be run from the iris-kmeans directory to get all file paths right: they expect the data directory in the cwd and will write the model to data/iris-model.bin

Step 3: Create Microservice

The microservice must be implemented and at first read the serialized model from step2. The example implementation can be found in the file run-microservice.cpp.

Then, the service interface of the microservice must be specified using protobuf. In our example, it is the classify method with its input and output parameters must be defined in a file that should be named model.proro:

syntax = "proto3";
package cppservice;

service Model {
  rpc classify (IrisDataFrame) returns (ClassifyOut);
}

message IrisDataFrame {
  repeated double sepal_length = 1;
  repeated double sepal_width = 2;
  repeated double petal_length = 3;
  repeated double petal_width = 4;
}

message ClassifyOut {
  repeated int64 value = 1;
}

From this file, the necessary code fragments and gRPC stubs can the be generated like this:

protoc --cpp_out=. model.proto
protoc --grpc_out=. --plugin=protoc-gen-grpc=/usr/local/bin/grpc_cpp_plugin model.proto

After that, the gRPC service method has to be implemented:

Status classify(ServerContext *context, const IrisDataFrame *input, ClassifyOut *response) override {
    cout << "enter classify service" << endl;
    std::array<float, 4> query;
    query[0]=input->sepal_length(0);
    query[1]=input->sepal_width(0);
    query[2]=input->petal_length(0);
    query[3]=input->petal_width(0);
    auto cluster_index = dkm::predict<float, 4>(means, query);
    cout << "data point classified as cluster " << cluster_index << endl;
    response->add_value(cluster_index);

    return Status::OK;
}

And finally, the gRPC server has to be started:

string server_address("0.0.0.0:"+port);
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&iris_model);
unique_ptr<Server> server(builder.BuildAndStart());
cout << endl << "Server listening on " << server_address << endl;
server->Wait();

To prepare for packaging, to specific folders will be expected: 1. the data folder, where all files of the serialized model are stored 2. the lib folder that should contain the shared libraries that are not part of the g++ base installation

Step 4: Create Onboarding Bundle

In the last step, the onboarding bundle for web-onboarding will be created using the cpp-client.py script. It should be called from the model’s base directory, in this case iris-kmeans. The script asks several questions and please note that for files and paths, normal tab-completion is possible. The script generates all artefacts into the onboarding directory and specifically the file ending with -bundle.zip is the one that is ready for web onboarding.