複数の推論リクエストをグループ化してバッチ処理する事で、高いスループットを実現する事が可能です。グループ化された各推論リクエストに対して必要となるパラーメータは外部メモリへのアクセス無しで再利用されます。
Neuronコア Batchingによる最適化を行うためにはコンパイル時にバッチサイズを指定する必要があります。
Batchingにはレイテンシのコストを払うことになるため、最適なバッチサイズ(最適なレイテンシとスループットの組み合わせ)はユーザによるトライアルエラーにより決定されます。
以下の内容でcompile_resnet50_batch.py
というファイル名の Python スクリプトを作成します。
import shutil
import tensorflow.neuron as tfn
model_dir = 'resnet50'
for batch_size in [1, 2, 4, 8, 16]:
# Prepare export directory (old one removed)
compiled_model_dir = 'resnet50_neuron_batch' + str(batch_size)
shutil.rmtree(compiled_model_dir, ignore_errors=True)
# Compile using Neuron
tfn.saved_model.compile(model_dir, compiled_model_dir, batch_size=batch_size, dynamic_batch_size=True)
コンパイルスクリプトを実行します。ここでは batch サイズ、1, 2, 4, 8, 16のモデルを用意します。inf1.2xlarge では計~8分程度かかります。
python compile_resnet50_batch.py
以下の内容でinfer_resnet50_batch.py
というファイル名の推論実行 Python スクリプトを作成します。章4.1、章4.2で使用したCPU向けモデル、本章Step 2で用意した各Batchサイズ毎のコンパイル済みモデルを利用します。
import os
import time
import numpy as np
import tensorflow as tf
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.resnet50 import ResNet50, preprocess_input, decode_predictions
from concurrent import futures
# added for utilizing 4 neuron cores
os.environ['NEURON_RT_VISIBLE_CORES'] = '0-3'
# Load model
model_dir = 'resnet50'
predictor_cpu = tf.contrib.predictor.from_saved_model(model_dir)
# measure the performance per batch size
for batch_size in [1, 2, 4, 8, 16]:
USER_BATCH_SIZE = batch_size
print("batch_size: {}, USER_BATCH_SIZE: {}". format(batch_size, USER_BATCH_SIZE))
# Load model
compiled_model_dir = 'resnet50_neuron_batch' + str(batch_size)
predictor_inferentia = tf.contrib.predictor.from_saved_model(compiled_model_dir)
# Create input from image
img_sgl = image.load_img('kitten_small.jpg', target_size=(224, 224))
img_arr = image.img_to_array(img_sgl)
img_arr2 = np.expand_dims(img_arr, axis=0)
img_arr3 = preprocess_input(np.repeat(img_arr2, USER_BATCH_SIZE, axis=0))
model_feed_dict={'input': img_arr3}
# warmup
infa_rslts = predictor_cpu(model_feed_dict)
infa_rslts = predictor_inferentia(model_feed_dict)
num_loops = 100
num_inferences = num_loops * USER_BATCH_SIZE
# Run inference on CPUs, Display results
start = time.time()
with futures.ThreadPoolExecutor(8) as exe:
fut_list = []
for i in range (1, num_loops + 1):
fut = exe.submit(predictor_cpu, model_feed_dict)
fut_list.append(fut)
for fut in fut_list:
infa_rslts = fut.result()
elapsed_time = time.time() - start
print('By CPU - num_inferences:{:>6}[images], elapsed_time:{:6.2f}[sec], Throughput:{:8.2f}[images/sec]'.format(num_inferences, elapsed_time, num_inferences / elapsed_time))
# Run inference on Neuron Cores, Display results
start = time.time()
with futures.ThreadPoolExecutor(8) as exe:
fut_list = []
for i in range (1, num_loops + 1):
fut = exe.submit(predictor_inferentia, model_feed_dict)
fut_list.append(fut)
for fut in fut_list:
infa_rslts = fut.result()
elapsed_time = time.time() - start
print('By Neuron Core - num_inferences:{:>6}[images], elapsed_time:{:6.2f}[sec], Throughput:{:8.2f}[images/sec]'.format(num_inferences, elapsed_time, num_inferences / elapsed_time))
推論実行スクリプトinfer_resnet50_batch.py
を実行します。
python infer_resnet50_batch.py
出力結果から、Batchサイズを大きくした場合、CPU上での推論スループットは一定であるのに対し、Neuronコア上での推論スループットは大きく向上している事が確認できます。
batch_size: 1, USER_BATCH_SIZE: 1
By CPU - num_inferences: 100[images], elapsed_time: 3.50[sec], Throughput: 28.55[images/sec]
By Neuron Core - num_inferences: 100[images], elapsed_time: 0.13[sec], Throughput: 753.22[images/sec]
batch_size: 2, USER_BATCH_SIZE: 2
By CPU - num_inferences: 200[images], elapsed_time: 6.45[sec], Throughput: 31.02[images/sec]
By Neuron Core - num_inferences: 200[images], elapsed_time: 0.17[sec], Throughput: 1176.86[images/sec]
batch_size: 4, USER_BATCH_SIZE: 4
By CPU - num_inferences: 400[images], elapsed_time: 12.12[sec], Throughput: 33.00[images/sec]
By Neuron Core - num_inferences: 400[images], elapsed_time: 0.29[sec], Throughput: 1388.48[images/sec]
batch_size: 8, USER_BATCH_SIZE: 8
By CPU - num_inferences: 800[images], elapsed_time: 23.82[sec], Throughput: 33.58[images/sec]
By Neuron Core - num_inferences: 800[images], elapsed_time: 0.70[sec], Throughput: 1141.48[images/sec]
batch_size: 16, USER_BATCH_SIZE: 16
By CPU - num_inferences: 1600[images], elapsed_time: 48.71[sec], Throughput: 32.85[images/sec]
By Neuron Core - num_inferences: 1600[images], elapsed_time: 1.28[sec], Throughput: 1253.75[images/sec]
スクリプトinfer_resnet50_batch.py
中、batch_size
はコンパイル時に指定したバッチサイズ、USER_BATCH_SIZE
はランタイム時のバッチサイズを示しています。
スループットを最大化するため、ランタイム時のバッチサイズは、コンパイル時に指定したバッチサイズの倍数を指定して下さい。
バッチサイズについての詳細はこちらをご確認下さい。