クーの自由研究

かえるのクーはただいま冬眠中です。無色の緑色の考えが猛烈に眠ります。...ZZZ...    

cupyとnumpyを簡単に切り替える方法

昔作ったプログラムをcupyで動かしてみます

こんにちわ、こんばんわ。かえるのクーです。

CUDAに入門してGPGPUとはどんなものなのかすこし分かってきました。

今日は昔つくったプログラムでnumpyからcupyに変更して、実行速度がどうなるか調べてみます。

f:id:np2LKoo:20181208190820p:plain

MNISTを「AutoEncode」するプログラムでやってみます

import numpy as np

import cupy as np

に変えるだけなのですが、何本もプログラムがあると切り替えが面倒です。

 そこで1か所の設定だけで切り替えられるようにしてみました。

もっとクールな方法があるのでしょうが、ボク的にはお手軽なこれでOKです。

cupyとnumpyを簡単に切り替えるプログラム

f:id:np2LKoo:20181208195147p:plain

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です。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です。 

*1:人類のみなさんの定義ではカエルの場合は越冬というらしいですが、カエル界ではふつうに冬眠といっています。