#!/usr/bin/env python
# coding: utf-8
# In[1]:
# Copyright 2019 NVIDIA Corporation. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
#
#
# # Torch-TensorRT Getting Started - CitriNet
# ## Overview
#
# [Citrinet](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/main/asr/models.html#citrinet) is an acoustic model used for the speech to text recognition task. It is a version of [QuartzNet](https://arxiv.org/pdf/1910.10261.pdf) that extends [ContextNet](https://arxiv.org/pdf/2005.03191.pdf), utilizing subword encoding (via Word Piece tokenization) and Squeeze-and-Excitation(SE) mechanism and are therefore smaller than QuartzNet models.
#
# CitriNet models take in audio segments and transcribe them to letter, byte pair, or word piece sequences.
#
#
#
# ### Learning objectives
#
# This notebook demonstrates the steps for optimizing a pretrained CitriNet model with Torch-TensorRT, and running it to test the speedup obtained.
#
# ## Content
# 1. [Requirements](#1)
# 1. [Download Citrinet model](#2)
# 1. [Create Torch-TensorRT modules](#3)
# 1. [Benchmark Torch-TensorRT models](#4)
# 1. [Conclusion](#5)
#
# ## 1. Requirements
#
# Follow the steps in [README](README.md) to prepare a Docker container, within which you can run this notebook.
# This notebook assumes that you are within a Jupyter environment in a docker container with Torch-TensorRT installed, such as an NGC monthly release of `nvcr.io/nvidia/pytorch:-py3` (where `yy` indicates the last two numbers of a calendar year, and `mm` indicates the month in two-digit numerical form)
#
# Now that you are in the docker, the next step is to install the required dependencies.
# In[2]:
# Install dependencies
get_ipython().system('pip install wget')
get_ipython().system('apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y libsndfile1 ffmpeg')
get_ipython().system('pip install Cython')
## Install NeMo
get_ipython().system('pip install nemo_toolkit[all]==1.5.1')
#
# ## 2. Download Citrinet model
#
# Next, we download a pretrained Nemo Citrinet model and convert it to a Torchscript module:
# In[3]:
import nemo
import torch
import nemo.collections.asr as nemo_asr
from nemo.core import typecheck
typecheck.set_typecheck_enabled(False)
# In[4]:
variant = 'stt_en_citrinet_256'
print(f"Downloading and saving {variant}...")
asr_model = nemo_asr.models.EncDecCTCModelBPE.from_pretrained(model_name=variant)
asr_model.export(f"{variant}.ts")
# ### Benchmark utility
#
# Let us define a helper benchmarking function, then benchmark the original Pytorch model.
# In[5]:
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import argparse
import timeit
import numpy as np
import torch
import torch_tensorrt as trtorch
import torch.backends.cudnn as cudnn
def benchmark(model, input_tensor, num_loops, model_name, batch_size):
def timeGraph(model, input_tensor, num_loops):
print("Warm up ...")
with torch.no_grad():
for _ in range(20):
features = model(input_tensor)
torch.cuda.synchronize()
print("Start timing ...")
timings = []
with torch.no_grad():
for i in range(num_loops):
start_time = timeit.default_timer()
features = model(input_tensor)
torch.cuda.synchronize()
end_time = timeit.default_timer()
timings.append(end_time - start_time)
# print("Iteration {}: {:.6f} s".format(i, end_time - start_time))
return timings
def printStats(graphName, timings, batch_size):
times = np.array(timings)
steps = len(times)
speeds = batch_size / times
time_mean = np.mean(times)
time_med = np.median(times)
time_99th = np.percentile(times, 99)
time_std = np.std(times, ddof=0)
speed_mean = np.mean(speeds)
speed_med = np.median(speeds)
msg = ("\n%s =================================\n"
"batch size=%d, num iterations=%d\n"
" Median samples/s: %.1f, mean: %.1f\n"
" Median latency (s): %.6f, mean: %.6f, 99th_p: %.6f, std_dev: %.6f\n"
) % (graphName,
batch_size, steps,
speed_med, speed_mean,
time_med, time_mean, time_99th, time_std)
print(msg)
timings = timeGraph(model, input_tensor, num_loops)
printStats(model_name, timings, batch_size)
precisions_str = 'fp32' # Precision (default=fp32, fp16)
variant = 'stt_en_citrinet_256' # Nemo Citrinet variant
batch_sizes = [1, 8, 32, 128] # Batch sizes (default=1,8,32,128)
trt = False # If True, infer with Torch-TensorRT engine. Else, infer with Pytorch model.
precision = torch.float32 if precisions_str =='fp32' else torch.float16
for batch_size in batch_sizes:
if trt:
model_name = f"{variant}_bs{batch_size}_{precision}.torch-tensorrt"
else:
model_name = f"{variant}.ts"
print(f"Loading model: {model_name}")
# Load traced model to CPU first
model = torch.jit.load(model_name).cuda()
cudnn.benchmark = True
# Create random input tensor of certain size
torch.manual_seed(12345)
input_shape=(batch_size, 80, 1488)
input_tensor = torch.randn(input_shape).cuda()
# Timing graph inference
benchmark(model, input_tensor, 50, model_name, batch_size)
# Confirming the GPU we are using here:
# In[6]:
get_ipython().system('nvidia-smi')
#
# ## 3. Create Torch-TensorRT modules
#
# In this step, we optimize the Citrinet Torchscript module with Torch-TensorRT with various precisions and batch sizes.
# In[10]:
import torch
import torch.nn as nn
import torch_tensorrt as torchtrt
import argparse
variant = "stt_en_citrinet_256"
precisions = [torch.float, torch.half]
batch_sizes = [1,8,32,128]
model = torch.jit.load(f"{variant}.ts")
for precision in precisions:
for batch_size in batch_sizes:
compile_settings = {
"inputs": [torchtrt.Input(shape=[batch_size, 80, 1488])],
"enabled_precisions": {precision},
"workspace_size": 2000000000,
"truncate_long_and_double": True,
}
print(f"Generating Torchscript-TensorRT module for batchsize {batch_size} precision {precision}")
trt_ts_module = torchtrt.compile(model, **compile_settings)
torch.jit.save(trt_ts_module, f"{variant}_bs{batch_size}_{precision}.torch-tensorrt")
#
# ## 4. Benchmark Torch-TensorRT models
#
# Finally, we are ready to benchmark the Torch-TensorRT optimized Citrinet models.
# ### FP32 (single precision)
# In[13]:
precisions_str = 'fp32' # Precision (default=fp32, fp16)
batch_sizes = [1, 8, 32, 128] # Batch sizes (default=1,8,32,128)
precision = torch.float32 if precisions_str =='fp32' else torch.float16
trt = True
for batch_size in batch_sizes:
if trt:
model_name = f"{variant}_bs{batch_size}_{precision}.torch-tensorrt"
else:
model_name = f"{variant}.ts"
print(f"Loading model: {model_name}")
# Load traced model to CPU first
model = torch.jit.load(model_name).cuda()
cudnn.benchmark = True
# Create random input tensor of certain size
torch.manual_seed(12345)
input_shape=(batch_size, 80, 1488)
input_tensor = torch.randn(input_shape).cuda()
# Timing graph inference
benchmark(model, input_tensor, 50, model_name, batch_size)
# ### FP16 (half precision)
# In[14]:
precisions_str = 'fp16' # Precision (default=fp32, fp16)
batch_sizes = [1, 8, 32, 128] # Batch sizes (default=1,8,32,128)
precision = torch.float32 if precisions_str =='fp32' else torch.float16
for batch_size in batch_sizes:
if trt:
model_name = f"{variant}_bs{batch_size}_{precision}.torch-tensorrt"
else:
model_name = f"{variant}.ts"
print(f"Loading model: {model_name}")
# Load traced model to CPU first
model = torch.jit.load(model_name).cuda()
cudnn.benchmark = True
# Create random input tensor of certain size
torch.manual_seed(12345)
input_shape=(batch_size, 80, 1488)
input_tensor = torch.randn(input_shape).cuda()
# Timing graph inference
benchmark(model, input_tensor, 50, model_name, batch_size)
#
# ## 5. Conclusion
#
# In this notebook, we have walked through the complete process of optimizing the Citrinet model with Torch-TensorRT. On an A100 GPU, with Torch-TensorRT, we observe a speedup of ~**2.4X** with FP32, and ~**2.9X** with FP16 at batchsize of 128.
#
# ### What's next
# Now it's time to try Torch-TensorRT on your own model. Fill out issues at https://github.com/NVIDIA/Torch-TensorRT. Your involvement will help future development of Torch-TensorRT.
#
# In[ ]: