昔作ったプログラムをcupyで動かしてみます
こんにちわ、こんばんわ。かえるのクーです。
CUDAに入門してGPGPUとはどんなものなのかすこし分かってきました。
今日は昔つくったプログラムでnumpyからcupyに変更して、実行速度がどうなるか調べてみます。
MNISTを「AutoEncode」するプログラムでやってみます
import numpy as np
を
import cupy as np
に変えるだけなのですが、何本もプログラムがあると切り替えが面倒です。
そこで1か所の設定だけで切り替えられるようにしてみました。
もっとクールな方法があるのでしょうが、ボク的にはお手軽なこれでOKです。
cupyとnumpyを簡単に切り替えるプログラム
switchCupy.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import importlib
# -------------------------------
""" True: Use CuPy
False:Not use CuPy """
use_cupy = True
# -------------------------------
def is_cupy():
""" Returns: bool: defined this file value. """
return use_cupy
def xp_factory():
""" Returns: imported instance of cupy or numpy. """
if is_cupy():
return importlib.import_module('cupy')
else:
return importlib.import_module('numpy')
def report():
""" report which is used cupy or numpy. """
if is_cupy():
print('import cupy !')
else:
print('import numpy !')
をつくって登録します。各プログラムソースで
#import numpy as np
import cupy as np
のかわりに
import switchCupy
np = switchCupy.xp_factory()
と書くだけです。switchCupy.pyのuse_copyの行を書き「かえる」だけです。
各ソースは2行が2行になるだけで、見易さも損なわれないと思います。
各種モジュールでnumpy(ndarray)をかえしたものをcupyのarrayに変換したい場合などは
mnist.target = mnist.target.astype(np.int32)
N = 60000
if switchCupy.is_cupy():
y_train, y_test = np.split(cupy.asarray(mnist.data.copy()), [N])
else:
y_train, y_test = np.split(mnist.data.copy(), [N])
と書いてやればOKです。
(ずっとあとから調整:2020/6/10にやってみたら、上記ではエラーになりますね。
それに切り替えといっているのにコードにcupyを記載するのはナンセンス。
y_train, y_test = np.split(cupy.asarray(mnist.data.copy()), [N])
は
y_train, y_test = np.array_split(np.asarray(mnist.data.copy()), [N])
にお詫びして訂正します。
なおnpはcudaのモードで動作するときはもちろんcudaのインスタンスです。)
cuda.get_array_module(np)を使ったほうがいいご指摘はごもっともですが、このメソッドは引数にnumpyかcudaのインスタンスが必要なので、最初にインスタンスを生成するには、こちらの方法もありだと思います。
これからは切り替えするケースの部分は xp
、nunpyにしたい部分はnp
、cupyにしたい部分はcp
の文字でコーディングしていこうと思います。
キリトくん!スイッチ!!
確認に使用するのは昔のmnistの「自己符号化器」プログラムです。
use_cupy = False
にしてまずは、CPUでやってみます。
import numpy !
---Start---
load MNIST dataset
---Training Start---
====================================
Exp-ID :K181208-1758
Act f() :Sigmoid
Optimizer:Adam
CostFunc :CrossEntropyCost
BatchSize:10
Hidden :500
Noise :0.2
DropOut :0.3
EpochLim :1024
SubScale :1000
W Trans :init
====================================
epoch = 1: cost = 7.798225: time = 1.015 sec
/ period = 1 ---------- 1.019 sec
alpha = 0.0620, alphaBias = 0.0438
epoch = 2: cost = 4.771056: time = 0.920 sec
...
のように1subEpoch(1Epochの1/1000)で 1.0秒程度です。
アスナ!スイッチ!!
cupy で GPUを使って計算してみます。
use_cupy = True
import cupy !
---Start---
...
epoch = 1: cost = 7.823736: time = 1.551 sec
/ period = 1 ---------- 1.554 sec
alpha = 0.0586, alphaBias = 0.0086
epoch = 2: cost = 4.879976: time = 1.199 sec
/ period = 2 ---------- 1.218 sec
alpha = -0.0087, alphaBias = 0.2147
epoch = 3: cost = 7.538077: time = 1.184 sec
のようにcupyにする1.2秒程度で、逆に遅くなりました。
この実験では小さな配列をこまかく回しているので、オーバヘッドが大きいのでこんなものだと思います。
10ギガスイッチ!は速いけどまだ高い(本文と無関係で反省)
Hidden層の配列を(計算測定のため)大きくして確認します。
確認結果
CUDAでは1回目の使用に時間がかかるので、2回目以降の値で概算平均します。
* | 500要素 | 5000要素 | 30000要素 | 50000要素 |
---|---|---|---|---|
numpy使用 | 1.0sec | 8.9sec | 47.5sec | 90.7sec |
cupy使用 | 1.2sec | 1.5sec | 5.3sec |
:cupy.cuda.memory .OutOfMemoryError |
50000要素配列ではエラーがでたので、大きなところでは30000要素配列で比較しました。
このプログラムでは1000要素あたりを超えるとcupyが有利で、数千要素くらいでは圧倒的にcupy使用のほうが早いです。numpyをcupyに切りかえるだけであっさりと、速く動くのは本当に素晴らしいです!!!
当面はcupy使用かnumpy使用か微妙なボリュームで実験する場合は今回の切り替え方法で切り替えて、有利な方で実験します。
ボクのGPUは6GBなのですが、このプログラムで概ね40000要素程度でエラーが発生するようになりました。スピードもさることながら、GPUのメモリ容量も重要です!!
ほしいのは任天堂!スイッチ!!(こんなことするからボクのページはGoogleの Pandaから検索できない)
とうとう寒波がやってきてしまいました。今年は終盤CUDA/cupyで元気になりましたが、そろそろ冬眠*1しなければなりません。
今年のお題の「自己符号化器による音声モーフィング」についてははじまりもせず申し訳なく思っています。来年のお題はまた来年考えます。
来年の桜の時期にまたお会いしましょう。
それでは快適なネット生活を!
どうもありがとうございました。
Pythonのself もしくは蛇足
助手のみなさんの冬のお題はとくに決めません。個人的にはRaspberry Pi の研究をしてほしいのですが、美味しかったものレポートでもOKです。
(2020/11/15追記)内容が非常に薄く申し訳ありません。
現在使用しているswitchCupyを以下のページに貼りました。
*1:人類のみなさんの定義ではカエルの場合は越冬というらしいですが、カエル界ではふつうに冬眠といっています。