NVIDIA DALIを使ってみた(DALI単体編)
NVIDIAからjpegなどの画像をGPU上でデコードするライブラリがリリースされました。
'18/7/8時点はver.0.1.1なので正式リリースではないのですが、せっかくなので触ってみた感触をレポートします。
DALIとTensorFlowを組み合わせて使う方法はこちらを参照してください。
目次
NVIDIA DALIを使って得する人
Deep Learningで画像認識を解いている人。特に画像のデコードとかdata augmentationがボトルネックとなってGPUを活かしきれていない人はDALIを使うと幸せになる(かも)しれません。
ちなみに対応しているDeep Learningフレームワークは
- MXNet
- pyTorch
- TensorFlow
となっています。
環境
インストール方法
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist nvidia-dali
使い方
NVIDIA DALIのコード内のサンプル画像を使うため、GitHubからcloneしておきます。
git clone https://github.com/NVIDIA/DALI.git mkdir dali-example cd dali-example
まずはPipelineクラスを定義しましょう。
※注意点:ops.FileReader()
にfile_rootで与える第一引数は画像の2階層上のディレクトリ(images/dog/dog_n.jpgの場合はimages)のパスを渡します。
image_dir = "../dali/examples/images/" batch_size = 8 class nvJPEGPipeline(Pipeline): def __init__(self, batch_size, num_threads, device_id): super(nvJPEGPipeline, self).__init__(batch_size, num_threads, device_id, seed=12) self.input = ops.FileReader(file_root=image_dir, random_shuffle=True, initial_fill=21) self.decode = ops.nvJPEGDecoder(device="mixed", output_type=types.RGB) def define_graph(self): jpegs, labels = self.input() images = self.decode(jpegs) # images are on the GPU return images, labels
つぎにPipelineをインスタンス化→build()
→run()
という順で呼び出します。するとrun()
の返り値としてimagesとlabelsが得られます。
pipe = nvJPEGPipeline(batch_size, 1, 0) pipe.build() pipe_out = pipe.run() images, labels = pipe_out
imagesの内容は以下の関数で画面に表示できます。
import matplotlib.gridspec as gridspec import matplotlib.pyplot as plt def show_images(image_batch): columns = 4 rows = (batch_size + 1) // (columns) fig = plt.figure(figsize=(32, (32 // columns) * rows)) gs = gridspec.GridSpec(rows, columns) for j in range(rows*columns): plt.subplot(gs[j]) plt.axis("off") plt.imshow(image_batch.at(j)) plt.show() show_images(images.asCPU())
かわいい
CPU/GPUデコードの速度比較
GPUでデコードする場合、CPUでデコードする場合の比較です。比較用に作成したスクリプトはこちらにあります。
上がCPUデコード、下がGPUデコードです。
バッチサイズ64の場合
read 21 files from 2 directories Speed: 2807.4778410571894 imgs/s read 21 files from 2 directories Speed: 4956.1491537075135 imgs/s
バッチサイズ8の場合
read 21 files from 2 directories Speed: 2202.594687067823 imgs/s read 21 files from 2 directories Speed: 3669.342854389418 imgs/s
バッチサイズ1の場合
read 21 files from 2 directories Speed: 462.7281609674059 imgs/s read 21 files from 2 directories Speed: 561.744339581996 imgs/s
バッチサイズが大きいほどGPU側が有利なようですね。バッチサイズ64の時は1.76倍になっています。
NVIDIA DALIのデータを扱うときの注意点
NVIDIA DALIを扱う時は以下の2点を意識しましょう。
例えば上のサンプルコードではimages, labelsはそれぞれTensorListGPU, TensorListCPUという型です。下のコードを実行すると
print("Images type is: " + str(type(images))) print("Labels type is: " + str(type(labels)))
Images type is: <class 'nvidia.dali.backend_impl.TensorListGPU'> Labels type is: <class 'nvidia.dali.backend_impl.TensorListCPU'>
こんなメッセージが出てきます。読んだ通り、画像はGPUでラベルはCPUで保持してる型だとわかります。
print("Images is_dense_tensor: " + str(images.is_dense_tensor())) print("Labels is_dense_tensor: " + str(labels.is_dense_tensor()))
Images is_dense_tensor: False Labels is_dense_tensor: True
またis_dense_tensor()
の返り値がTrueの場合のみ、TensorListを numpy.ndarray に変換できます。
つまり np.array(labels.as_tensor())
はOKだけどnp.array(images.as_tensor())
はNGです。無理して変換しようとするとこんなエラーが出てしまいます。
[/opt/dali/dali/pipeline/data/tensor.h:188] Assert on "tl->IsDenseTensor()" failed: All tensors in the input TensorList must have the same shape and be densely packed.
images
をnumpy.ndarrayとして扱いたい場合は、あらかじめCPU側に移して画像を1枚ずつ取り出す必要があります。
おそらく複数の画像が別のサイズの場合に対処するための仕様だと思われます。
# GPUからCPUに実体を移動 images_on_cpu = images.asCPU() # 0枚目の画像を取り出し images_tensor = images_on_cpu.at(0) # numpy.ndarrayに変換 images_ndarray = nd.array(images_tensor) print(images_ndarray)
[[[231 234 239] [231 234 239] ...(省略)
まとめ
GPUデコードにすると1.76倍にスピードアップ。なかなかいい数字ではないでしょうか。
DALIとTensorFlowと組み合わせて使う方法はこちらでレポートしています。
参考URL
- Announcing NVIDIA DALI and NVIDIA nvJPEG - NVIDIA Developer News CenterNVIDIA Developer News Center
- DALI Developer Guide :: Deep Learning SDK Documentation
- NVIDIA/DALI: A library containing both highly optimized building blocks and an execution engine for data pre-processing in deep learning applications