背景
私は製造業の研究職です。少し前より深層学習モデルを用いた外観検査システムを運用しており、システムのGPUにはQuadro P6000 (Pascal世代), 深層学習ライブラリにはChainer v1.24, CUDAはv8.0を使っていました。
今回、新たなラインに外観検査システムを導入することとなりましたが、Quadro P6000はディスコンとなっており、代わりにQuadro RTX6000 (Turing世代) を積んだPCをベンダより薦められました。
しかし、
- Turing世代のGPUでは(古いバージョンである)CUDA8.0はそのままでは使用できないため、CUDAのアップデートが必要
- Chainer v1.24がサポートするCUDAは8.0までのため、CUDAアップデートをするとchainerもアップデートが必要
- chainerをアップデートするとソースコードの改修が必要
ことが問題でした。
ソースコードが異なるソフトが存在すると、外観検査システムの今後の運用保守のトラブルになりうるため、Turing世代GPU, CUDA8.0, Chainer v1.24を使う方法を探しました。
Turing世代GPU, CUDA8, Chainer v1.24をそのまま動かすと?
下記のエラーが出てソフトが止まります。
nvcc fatal : Value 'sm_75' is not defined for option 'gpu-architecture'
これはCUDA8のプログラム上ではTuring世代のGPUがサポートされていない(定義されていない)ため、NVCC ( NVIDIA CUDA C++ compiler) がChainerの動作に必要なCUDAプログラムをコンパイルしようとする際にエラーが出ています。
解決法
NVCCにはcubinファイルと呼ばれるGPU世代に固有のファイルを生成するオプションと先方互換性ーーつまり、自分より新しい世代のGPUへの互換性ーーを持たせたptxファイルと呼ばれるファイルを生成するオプションがあります。
これを使うと(古い)Pascal世代までをサポートするCUDA8を使って(新しい)Turing世代で動作が可能になります(参考)。
そこで、chainer内部でcubinファイルを生成する処理を行っている箇所をptxファイルを生成するように変更します。
具体的には、site-packages/cupy/cuda/compiler.pyのnvcc関数でその処理が行われていますので、ここを変更します。
変更1
# cmd = ['nvcc', '--cubin', '-arch', arch] + list(options)
cmd = ['nvcc', '--ptx', '-gencode=arch=compute_61,code=sm_61']
コメントアウトしたオリジナルのコードでは、--cubin
という引数から推測できるようにcubinファイルを生成しています。
ここを--ptx
に変更し、-gencode
引数でPascal世代より先に先方互換性を持たせたptxファイルを生成することを宣言します。
'sm_XX'がどの世代のGPUを指しているのかはこのサイトに分かりやすくまとめてあります。
変更2
with TemporaryDirectory() as root_dir:
path = os.path.join(root_dir, 'kern')
cu_path = '%s.cu' % path
cubin_path = '%s.cubin' % path
ptx_path = '%s.ptx' % path ## modified
with open(cu_path, 'w') as cu_file:
cu_file.write(source)
cmd.append(cu_path)
_run_nvcc(cmd, root_dir)
with open(ptx_path, 'rb') as bin_file: ## modified
return bin_file.read()
オリジナルのコードでは(ptxファイルでなく)cubinファイルを読み込むように作られているので、変更1だけではcubinファイルが見つからない、と怒られます。
なので、ptxファイルをreadするように修正します。
これで(私の環境では)動きました。
注意
コンパイルは時間のかかる処理のため、一度コンパイルしたファイルはhome/.cupy/kernel_cache下にキャッシュされています。
そのため、変更前の動作しないキャッシュファイルが残っていると、chainerはキャッシュファイルを読み込み、「compiler.pyを修正したのに動かない!」となるかもしれません。
副作用:cuDNN初期化処理が遅い
cubinファイルでは、cuDNNの初期化処理は1秒未満で終わっていましたが、ptxファイルを使うと30~60秒ほど時間がかかるようになってしまいました。
この初期化処理はNVIDIA謹製のsoファイル内で行われているようで、私の力で修正するのは諦めました。
私の外観検査システムでは初期化に30~60秒ほどかかるのはNGであるため、cuDNNを使用しないようにすることで解決しました。
その結果、推論速度が0.2s -> 0.4sと倍かかるようになりましたが、これは私のシステムでは許容できます。
chainerでcuDNNをdisableにするには環境変数で設定すればいいです。
ただし、chainerをimportする前に設定しておく必要があります。
os.environ['CHAINER_CUDNN'] = '0'
P.S.
Chainerすごく使いやすく大好きでした。おかげで深層学習の研究開発スピードが非常に上がりました。
Preferred Networksさん、ありがとうございました。
さいごに
- (外観検査システムなど)機械学習システムの運用保守についての工夫・苦労をお聞きしたいです。
「うちではこんな風にエッジ側の環境構築してる/AIモデルをデプロイしてる/モデル精度を監視してる」などなど...。 - cuDNNの初期化が遅くなる問題について、解決法と思しきものをご存知でしたらお聞きしたいです。