3. Create Model#

An integral part of improving your ML system is iterating on your models. You can define multiple different models (by creating them with ef model create), as well as versions of the same model (by pushing a new version with ef model push), such that you can track key performance indicators and see the direction of development.

Models definition#

We need to define several parts in order to integrate the model with the Efemarai system.

Name and files#

We should extend the previous project file with the following data.

models:
  - name: Our First Model
    files:
      - name: params
        url: weights/model.pt

This specifies that we would like to call our model Our First Model and has a single file associated with it that we can further internally refer to as params. It points to the file in the filesystem under the url - weights/model.pt.

To upload multiple files, or ones in remote repositories one can extend it as:

    files:
      - name: performance-graph
        url: weights/loss.png
      - name: performance-log
        url: weights/loss-log.json
      - name: remote-params
        url: https://some-remote-bucket.example.com
        upload: False

If you upload images, json, or any other configuration files, you’ll have the opportunity to use them when loading the model, inspect them and see exactly what is associated with this particular model in the UI.

Sometimes weights can be quite large and stored in a separate repository/bucket. To accommodate this workflow, you can specify a direct path to them and you can avoid uploading the weights at the current time with upload: False.

Runtime#

An important part of connecting your model to the Efemarai platform is specifying two python functions under the runtime definitions - load - for loading the model; and predict - running a forward pass on it.

    runtime:
      image: pytorch/pytorch:1.9.1-cuda11.1-cudnn8-runtime
      load:
        entrypoint: inference:get_model
        inputs:
          - name: params_url
            value: ${model.files.params.url}
        output:
          name: model
      predict:
        entrypoint: inference:predict
        inputs:
          - name: model
            value: ${model.runtime.load.output.model}
          - name: datapoints
            value: ${datapoints}
        output:
          name: predictions
          keys:
            - boxes
            - classes
            - scores

Image#

We rely on Docker containerization to create the right working environment for your model. You can specify a base image under the image directive with any publicly accessible image or Dockefile.

After the image has been pulled, we install any dependencies in requirements.txt, as well as some additional artifacts to interface the container with our system.

load function#

The function definition logic is shared between the load, predict functions and has the following structure:

      function-name:
        entrypoint: module.entry-function
        inputs:
          - name: param_name
            value: "absolute" <or refered value> ${model.files.params.url}
        output:
          name: function-output

For the load call we can decompose

      load:
        entrypoint: inference:get_model
        inputs:
          - name: params_url
            value: ${model.files.params.url}
        output:
          name: model

To load the model, we would find inference.py in the root directory of the project, and call the get_model function with a single argument named params_url that will have the value, which we defined in the above step (weights/model.pt).

The output of the load function is a single object, named model.

predict function#

      predict:
        entrypoint: inference:predict
        inputs:
          - name: model
            value: ${model.runtime.load.output.model}
          - name: datapoints
            value: ${datapoints}
        output:
          name: predictions

A key difference to note here is the use of ${model.runtime.load.output.model}, which is exactly what the load function returns.

A second element is the ${datapoints} variable, which is the list of datapoints passed to the function. Each datapoint can have different images, video, text associated with it and your model can prepare the input in any standard in order to consume and make a forward pass.

The method returns a single output predictions that contains the list of predictions for each datapoint (a list of lists).

Full example#

Here is a full example defining a local model.

models:
  - name: YOLOR-P6-Local
    files:
      - name: params
        url: results/yolor_p6.pt
        upload: True
    runtime:
      image: pytorch/pytorch:1.9.1-cuda11.1-cudnn8-runtime
      load:
        entrypoint: inference:get_model
        inputs:
          - name: params_url
            value: ${model.files.params.url}
        output:
          name: model
      predict:
        entrypoint: inference:predict
        inputs:
          - name: model
            value: ${model.runtime.load.output.model}
          - name: datapoints
            value: ${datapoints}
        output:
          name: predictions
          keys:
            - boxes
            - classes
            - scores

Up until now, your efemarai.yaml should contain a project name name and the two datasets. Now add the model to your file. After you have added the above code snippet to your efemarai.yaml file, you should be able to add a model via our CLI. Note that the following commands should be executed in the directory of your local code such that it is correctly uploaded.

efemarai model create efemarai.yaml

or if you’re using the standard name as above, you can simply type

efemarai model create .

When ready, you will be able to see the loaded model in the Models page and in the CLI

ef model list

Loaded model

Next, let’s create an Operational Domain to test this model.