Search code examples
objective-ccoreml

getting coreml running in objective-C


I'm having trouble creating a very simple example of using Apple's CoreML in Objective-C. I have already created a model file using python which works rather simply now:

coreml_model_svm = coremltools.models.MLModel("svm.mlmodel")
test_x = [1.0 for x in range(160)]
predictions_coreml_svm = coreml_model_svm.predict({"input":test_x})

I would like to reproduce the above three lines in Objective-C. I know that my data has to be an MLMultiArray and the model needs to be loaded into MLModel. I've been trying to find some info on the syntax, but it seems I don't understand how the documentation works, and all the examples are in Swift. Here's my code so far. Commenting the MLMultiArray leads to an uncaught exception for initializing the MLModel. When not commenting the MLMultiArray I get no known class method for selector 'initWithShape:dataType:error'.

#import <Foundation/Foundation.h>
#import <CoreML/CoreML.h>

//clang -framework Foundation coremltest.m -o coremltest
int main (int argc, const char * argv[])
{
        NSLog(@"start");

        NSArray * shape = [[NSArray alloc]  init];
        MLMultiArrayDataType dataType = MLMultiArrayDataTypeDouble;
        NSError * error = nil;

        MLMultiArray * input =  [MLMultiArray initWithShape:(NSArray*) shape
                             dataType:(MLMultiArrayDataType ) dataType
                        error:(NSError **) error];

        MLModel * mymodel = [[MLModel init] initWithContentsOfFile:@"svm.mlmodel"];

        return 0;
}

I would appreciate any help with this.


Solution

  • First of all you need to import the your model class, in your case svm (would be better Svm starting with upper case letter):

    #import "svm.h" 
    

    This class definition contains all the informations you need for the inputs and outputs and the method definitions. You could find this specification in the automatically generated class description by clicking on the small arrow just next the class name when you choose the mlmodel file on the left. In this class description

    enter image description here

    In your case the input is a MLMultiArray with 160 Double elements as a vector. So first define the dimensions with the shape array

    NSArray *shape = @[@1, @160];
    

    Then define the MLMultiArray which will be your svmModelInput for the prediction process (defined by XCode automatically again):

        MLMultiArrayDataType dataType = MLMultiArrayDataTypeDouble;
        NSError *error = nil;
    
        MLMultiArray *theMultiArray =  [[MLMultiArray alloc] initWithShape:(NSArray*)shape
                                              dataType:(MLMultiArrayDataType)dataType
                                                 error:&error] ;
    
        for (int i = 0; i < 160; i++) {
             [theMultiArray setObject:[NSNumber numberWithDouble:1.0] atIndexedSubscript:(NSInteger)i];
        }
    

    The used

    initWithShape

    is an MLMultiArray method by Apple. I filled the array with "1" just for test, but you have to replace it later with your real input of course.

    No need to get your model, just instantiate your svm then run the

    predictionFromInput:

    method which is from the class built by the XCode again:

            svm *mySvm = [[svm alloc] init];
    
            svmOutput * svmModelOutput = [(svm *)mySvm predictionFromInput:theMultiArray error:&error];
            NSLog(@"SVM Model output = %lld -- %@", svmModelOutput.classLabel, svmModelOutput.classProbability );
    
               if (!error)
               {
                   NSLog(@"svm finished without error");
               }
               else
               {
                   NSLog(@"Error: %@", error.localizedDescription);
               }
    

    As you print out the svmModelOutput (created by XCode for you) you can check the predicted classLabel as an Integer and the probabilities for all the labels, like this:

    2017-12-04 07:32:45.765015+0100 CoreML_test[2634:877638] SVM Model output = 2 -- {
        11 = "0.002656571278812773";
        3 = "0.2121030282896462";
        10 = "0.004570897664662783";
        2 = "0.5825387375626612";
        9 = "0.02911120023388797";
        4 = "0.1690195649703292";
    }