一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情。
我们熟悉的 numpy 是运行在 CPU 上用于操作数组的库。而我们都知道 GPU 更适合进行大规模的平行计算,所以适合处理一些矩阵运算。虽然今天不会深入介绍 cuda 编程模型,但是有一点还是需要大家清楚的,就是 GPU 是为了数据并行计算而设计,如果我们运行满足每一个计算单元都是独立的,相互并不依赖而且每一个计算单元是相同,并且这样的计算是大量的,我们就可以考虑将这样计算移动 GPU 上,今天深度学习中关于矩阵变换和计算就满足以上条件,所以 GPU 出现加速了深度学习向前发展
导入 cuPy
import cupy as cp
import numpy as np
创建数组
用 random.randint 来创建是一个 2000 x 2000 大小二维矩阵
arr_cpu = np.random.randint(0,255,size=(2000,2000))
然后我们输出一下这个数组内容
array([[ 60, 222, 168, ..., 155, 195, 116],
[146, 83, 213, ..., 130, 253, 48],
[ 58, 111, 241, ..., 186, 203, 82],
...,
[212, 157, 101, ..., 207, 0, 10],
[ 18, 238, 128, ..., 182, 176, 114],
[ 69, 14, 31, ..., 248, 111, 134]])
我们可以通过下面方式来查看刚刚创建好的 arr_cpu 所占的内存数量
arr_cpu.nbytes/1e6 #32.0
可以通过 numpy 的 nbytes 来查看 numpy 数组所占用的内存数量
x = np.array([100, 12, 32])
x.nbytes #24
然后可以使用 cp.asarray 将 cpu 上数组转移到 gpu 上
%%timeit
arr_gpu = cp.asarray(arr_cpu)
将 cpu 上数组移动到 gpu 上是需要消耗一定成本的
100 loops, best of 5: 7.54 ms per loop
如果将更大的数组从 cpu 上移动到 gpu 上我们需要耗费更多时间,所以这个也是我们在设计程序时候需要考虑到的。
arr_cpu = np.random.randint(0,255,size=(4000,4000))
%%timeit
arr_gpu = cp.asarray(arr_cpu)
%%timeit
arr_gpu = cp.asarray(arr_cpu_2)
100 loops, best of 5: 17.7 ms per loop
傅里叶变换
from scipy import fft
%%timeit
fft.fftn(arr_cpu)
1 loop, best of 5: 430 ms per loop
接下来我们尝试用 scipy 提供傅里叶变换的方法 fft.fftn 来对 arr_gpu 进行操作
fft.fftn(arr_gpu)
这是因为现在 arr_gpu 并不是位于内存上的 Numpy array 而是一个 cupy 数组,scipy 不知道应该如何处理这个数据
TypeError: Implicit conversion to a NumPy array is not allowed. Please use `.get()` to construct a NumPy array explicitly.
from cupyx.scipy import fft as fft_gpu
%%timeit
fft_gpu.fftn(arr_gpu)
The slowest run took 4229.40 times longer than the fastest. This could mean that an intermediate result is being cached. 1 loop, best of 5: 344 µs per loop
这个操作大约耗时 344 微秒,相比上面在 cpu 上运行,这次在 gpu 上运行速度要远远超过在 cpu 上运算速度。The slowest run took 4229.40 times longer than the fastest 这是什么原因,这是因为首先需要将函数加载到 gpu kernel 上,并且需要进行编译到 gpu kernel,所以第一次时间
fft_cpu = fft.fftn(arr_cpu)
fft_from_gpu = cp.asnumpy(fft_gpu.fftn(arr_gpu))
np.allclose(fft_cpu,fft_from_gpu)
True
对于 scipy 的 fftn 方法无法操作 gpu 上数组,不过也有一些方法,例如下面 np.max 可以操作 gpu 上数组,下面我们可以运行一下。
np.max(arr_gpu)
type(np.max(arr_gpu)) #cupy._core.core.ndarray
我们通常需要显式地将数组在 cpu 和 gpu 之间传递,例如上面我们先在 cpu 上创建一个 arr_cpu 然后用 cp.asarray 方法将创建好的 cpu 上数组转移到 gpu 上,其实这不同我们直接用 cp.random.randint 来直接在 gpu 上创建数组。