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)即可。

虽然 CuPy\mathrm{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

如上面的代码所示,如果 xx 是一个NumPy或CuPy的多维数组,使用 xp=cp.getarraymodule(x)xp = cp.get_array_module(x) 就可以返回该数组的模块。有了这个函数,即使不知道 xx 是NumPy的数据还是CuPy的数据,也能获得相应的模块。利用它可以编写同时支持CuPy和NumPy的代码,例如,通过 xp=cp.getarraymodule(x)xp = cp.get_array_module(x)y=xp.sin(x)y = xp.sin(x) ,就可以达到相应的效果。

关于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的其他类中使用。