52.1_CuPy的安装和使用方法
52.1 CuPy的安装和使用方法
CuPy是用于在GPU上进行并行计算的库,可通过pip安装,安装命令如下所示。
$ pip install cupy
下面开始使用CuPy。它的优点是拥有与NumPy相同的API,所以我们掌握的NumPy知识可以直接用于CuPy。例如,我们可以使用CuPy编写下面这样的代码。
import copy as cp
x = cp.arange(6).reshape(2, 3)
print(x)
y = x.sum(axis=1)
print(y)运行结果
[[012][345]][312]上面的代码导入了CuPy并进行求和计算。正如我们看到的那样,这段代码几乎与NumPy完全相同,其实我们所做的不过是将np替换为cp,让cp进行必要的计算而已,而这个计算在幕后使用的是GPU。
因此,NumPy的代码很容易转换为GPU版本的代码。只要将NumPy代码中的np(numpy)替换为cp(cupy)即可。

虽然 与NumPy的许多API是相同的,但二者并不完全兼容。
下面让DeZero支持GPU。我们要做的是将DeZero中使用NumPy的代码切换为使用CuPy的代码。准确来说,我们要创建能够切换二者的机制。要做到这一点,我们需要了解关于CuPy的两件事。第一件事是在NumPy和CuPy之间转换多维数组的方法。代码示例如下。
import numpy as np
import cupy as cp
# numpy -> cupy
n = np.array([1,2,3])
c = cp.asarray(n)assert type(c) == cp.ndearray #copy -> numpy c = cp.array([1, 2, 3]) n = cp.asnumpy(c) assert type(n) == np.ndearray如代码所示,从NumPy转换为CuPy需要使用cp.asarray函数,从CuPy转换为NumPy需要使用cp.asnumpy函数。

使用cp.asarray函数和cp.asnumpy函数时,数据会从PC的内存传输到GPU的显存上(或者反方向传输)。这个传输处理往往会成为深度学习计算的瓶颈。因此,理想的做法是尽可能减少数据传输。
第二件事与函数cp.get_array_module有关。该函数根据数据返回相应的模块,具体的用法如下所示。
$\mathbf{x} = \mathbf{np}$ .array([1,2,3])
xp $=$ cp.get_array_module(x)
assert xp $= =$ np
$\mathbf{x} = \mathbf{cp}$ .array([1,2,3])
xp $=$ cp.get_array_module(x)
assert xp $= =$ cp如上面的代码所示,如果 是一个NumPy或CuPy的多维数组,使用 就可以返回该数组的模块。有了这个函数,即使不知道 是NumPy的数据还是CuPy的数据,也能获得相应的模块。利用它可以编写同时支持CuPy和NumPy的代码,例如,通过 和 ,就可以达到相应的效果。
关于CuPy的知识,了解这些就足够了。下面在DeZero中创建在CuPy和NumPy之间切换的机制。
52.2uda模块
在DeZero中,我们把CuPy相关的函数放到dezero/cuda.py模块(文件)中。顺便说一下,CUDA是NVIDIA提供的面向GPU的开发环境。首先,dezero/cuda.py的导入部分如下所示。
dezero/cuda.py
import numpy as np
gpu_enable $=$ True
try: import copy as cp copy $= \mathrm{cp}$
except ImportError: gpu_enable $=$ False
fromdezeroimportVariable上面的代码导入了NumPy和CuPy。由于CuPy是可选库,所以我们还需要考虑它没有被安装的情况。因此,上面使用了try语句执行导入操作,如果发生ImportError,我们就设置gpu_enable = False。这样即使环境中没有安装CuPy也不会出现任何错误。接下来在dezero/cuda.py中添加以下3个函数。
dezero/cuda.py
def get_array_module(x):
if isinstance(x, Variable):
x = x.data
if not gpu_enable:
return np
xp = cp.get_array_module(x)
return xp
def as_numpy(x):
if isinstance(x, Variable):
x = x.data
if np.iscalar(x):return np.array(x)
elif isinstance(x, np.ndarray):
return x
return cp.asnumpy(x)
def as_cupy(x):
if isinstance(x, Variable):
x = x.data
if not gpu_enable:
raise Exception('CuPy cannot be loaded. Install CuPy!')
return cp.asarray(x)第一个函数get_array_module(x)返回参数x对应的模块,其中x是Variable或ndarray(numpy.ndearray或cupy.ndearray)。它主要是对cp.get_array_module函数进行封装,但也执行了copy没有被导入时的处理。具体来说,如果gpu_enable为False,它将总是返回np(numpy)。
剩下的两个函数是用于将参数转换为NumPy/CuPy多维数组的函数。将参数转换为NumPy的ndarray的函数是as_numpy,将参数转换为CuPy的ndarray的函数是as_cupy。以上就是dezero/cuda.py中的所有代码。这个模块(文件)中的3个函数现在可在DeZero的其他类中使用。