How to Deploy a Custom Acoustic Model (Conformer-CTC) Trained with TAO Toolkit on Riva#

This tutorial walks you through the deployment of a custom acoustic model (Conformer-CTC) trained with NVIDIA TAO Toolkit on NVIDIA Riva.

NVIDIA Riva Overview#

NVIDIA Riva is a GPU-accelerated SDK for building speech AI applications that are customized for your use case and deliver real-time performance.
Riva offers a rich set of speech and natural language understanding services such as:

  • Automated speech recognition (ASR).

  • Text-to-Speech synthesis (TTS).

  • A collection of natural language processing (NLP) services, such as named entity recognition (NER), punctuation, and intent classification.

In this tutorial, we will deploy a custom acoustic model (Conformer-CTC) trained with TAO Toolkit on Riva.
To understand the basics of Riva ASR APIs, refer to Getting started with Riva ASR in Python.

For more information about Riva, refer to the Riva developer documentation.

Train, Adapt, and Optimize TAO Toolkit#

Train Adapt Optimize (TAO) Toolkit provides the capability to export your model in a format that can be deployed using NVIDIA Riva, a highly performant application framework for multi-modal conversational AI services using GPUs.

This tutorial explores taking a .riva model, the result of the tao speech_to_text_conformer train command (refer to the fine-tuning tutorial) and leveraging the Riva ServiceMaker framework to aggregate all the necessary artifacts for Riva deployment to a target environment. After the model is deployed in Riva, you can issue inference requests to the server. We will demonstrate how quick and straightforward this whole process is. In this tutorial, you will learn how to:

  • Use Riva ServiceMaker to take a TAO exported .riva file and convert it to .rmir.

  • Deploy the model locally on the Riva server.

  • Send inference requests from a demo client using Riva API bindings.


Before we get started, ensure you have:

Riva ServiceMaker#

Riva ServiceMaker is a set of tools that aggregates all the necessary artifacts (models, files, configurations, and user settings) for Riva deployment to a target environment. It has two main components:


This step helps build a Riva-ready version of the model. Its only output is an intermediate format (called an RMIR) of an end-to-end pipeline for the supported services within Riva. Let’s consider a Conformer-CTC ASR model.

riva-build is responsible for the combination of one or more exported models (.riva files) into a single file containing an intermediate format called Riva Model Intermediate Representation (.rmir). This file contains a deployment-agnostic specification of the whole end-to-end pipeline along with all the assets required for the final deployment and inference. For more information, refer to the documentation.


# ServiceMaker Docker
RIVA_SM_CONTAINER = "<add container name>"

# Directory where the .riva model is stored $MODEL_LOC/*.riva
MODEL_LOC = "<add path to model location>"

# Name of the .riva file
MODEL_NAME = "<add model name>"

# Key that model is encrypted with, while exporting with TAO
KEY = "<add encryption key used for trained model>"
# Get the ServiceMaker Docker container
! docker pull $RIVA_SM_CONTAINER

If it doesn’t already exist, create a sub-directory inside MODEL_LOC to store your .rmir files.

! mkdir -p $MODEL_LOC/rmir

Build the .rmir file.#


  1. If you obtained your .riva-formatted acoustic model file from tao <task> export, you may need to replace --nn.fp16_needs_obey_precision_pass with --nn.use_trt_fp32 when invoking riva-build.

  2. Refer to the Riva ASR Pipeline Configuration documentation page if you wish to build an ASR pipeline for a supported language other than US English. To obtain the proper riva-build parameters for your particular application, select the acoustic model, language, and pipeline type (offline for the purposes of this tutorial) from the interactive web menu at the bottom of the first section of the page.

# Syntax: riva-build <task-name> output-dir-for-rmir/model.rmir:key dir-for-riva/model.riva:key
! docker run --rm --gpus 0 -v $MODEL_LOC:/data $RIVA_SM_CONTAINER -- \
    riva-build speech_recognition \
        /data/rmir/asr_offline_conformer_ctc.rmir:$KEY \
        /data/$MODEL_NAME:$KEY \
        --offline \
        --name=asr_offline_conformer_ctc_pipeline \
        --decoder_type=greedy \
        --ms_per_timestep=40 \
        --chunk_size=4.8 \
        --left_padding_size=1.6 \
        --right_padding_size=1.6 \
        --max_batch_size=16 \
        --nn.fp16_needs_obey_precision_pass \
        --featurizer.use_utterance_norm_params=False \
        --featurizer.precalc_norm_time_steps=0 \
        --featurizer.precalc_norm_params=False \
        --featurizer.max_batch_size=512 \
        --featurizer.max_execution_batch_size=512 \


The deployment tool takes as input one or more RMIR files and a target model repository directory. It creates an ensemble configuration specifying the pipeline for the execution and finally writes all those assets to the output model repository directory.

# Syntax: riva-deploy -f dir-for-rmir/model.rmir:key output-dir-for-repository
! docker run --rm --gpus 0 -v $MODEL_LOC:/data $RIVA_SM_CONTAINER -- \
    riva-deploy -f  \
        /data/rmir/asr_offline_conformer_ctc.rmir:$KEY \

Start the Riva Server#

After the model repository is generated, we are ready to start the Riva server. First, download the Riva Quick Start resource from NGC. Set the path to the directory here:

# Set the Riva Quick Start directory
RIVA_DIR = "<Path to the uncompressed folder downloaded from quickstart(include the folder name)>"

Next, we modify the file to enable the relevant Riva services (ASR for the Conformer-CTC model), provide the encryption key, and path to the model repository (riva_model_loc) generated in the previous step among other configurations.

For example, if above the model repository is generated at $MODEL_LOC/models, then you can specify riva_model_loc as the same directory as MODEL_LOC.

Pretrained versions of models specified in models_asr/nlp/tts are fetched from NGC. Since we are using our custom model, we can comment it in models_asr (and any others that are not relevant to your use case). snippet#

# Enable or Disable Riva Services 
service_enabled_asr=true                                                      ## MAKE CHANGES HERE
service_enabled_nlp=false                                                      ## MAKE CHANGES HERE
service_enabled_tts=false                                                     ## MAKE CHANGES HERE

# Specify one or more GPUs to use
# specifying more than one GPU is currently an experimental feature, and may result in undefined behaviours.

# Specify the encryption key to use to deploy models
MODEL_DEPLOY_KEY="tlt_encode"                                                  ## MAKE CHANGES HERE

# Locations to use for storing models artifacts
# If an absolute path is specified, the data will be written to that location
# Otherwise, a docker volume will be used (default).
# will create a `rmir` and `models` directory in the volume or
# path specified. 
# RMIR ($riva_model_loc/rmir)
# Riva uses an intermediate representation (RMIR) for models
# that are ready to deploy but not yet fully optimized for deployment. Pretrained
# versions can be obtained from NGC (by specifying NGC models below) and will be
# downloaded to $riva_model_loc/rmir by ``
# Custom models produced by NeMo or TAO and prepared using riva-build
# may also be copied manually to this location $(riva_model_loc/rmir).
# Models ($riva_model_loc/models)
# During the riva_init process, the RMIR files in $riva_model_loc/rmir
# are inspected and optimized for deployment. The optimized versions are
# stored in $riva_model_loc/models. The riva server exclusively uses these
# optimized versions.
riva_model_loc="<add path>"                              ## MAKE CHANGES HERE (Replace with MODEL_LOC)                      
# Ensure you have permission to execute these scripts
! cd $RIVA_DIR && chmod +x ./ && chmod +x ./
# Run Riva Init. This will fetch the containers/models
! cd $RIVA_DIR && ./
# Run Riva Start. This will deploy your model(s).
! cd $RIVA_DIR && ./

Run Inference#

After the Riva server is up and running with your models, you can send inference requests querying the server.

To send gRPC requests, install the Riva Python API bindings for the client. This is available as a pip .whl file with the Quick Start.

# Install the Client API Bindings
! pip install nvidia-riva-client

Connect to the Riva Server and Run Inference#

Now we can actually query the Riva server. The following cell queries the Riva server (using gRPC) to yield a result.

import argparse
import grpc
import time
import riva.client
import wave

audio_file = "<add path to .wav file>"
server = "localhost:50051"

with open(audio_file, 'rb') as fh:
    data =

auth = riva.client.Auth(uri=server)
client = riva.client.ASRService(auth)
config = riva.client.RecognitionConfig(
riva.client.add_audio_file_specs_to_config(config, audio_file)

response = client.offline_recognize(data, config)

You can stop all Docker containers before shutting down the Jupyter kernel. Caution: The following command will stop all running containers.

! docker stop $(docker ps -a -q)