1.5 计算的高速化
神经网络的学习和推理需要大量的计算。因此,如何高速地计算神经网络是一个重要课题。本节将简单介绍一下可以有效加速神经网络的计算的位精度和GPU的相关内容。
相比计算的高速化,本书更加重视实现的易理解性。但是,从计算的高速化的角度出发,之后进行的实现将考虑数据的位精度。另外,在需要花费大量时间进行计算的地方,会将代码设计为可在GPU上执行。
1.5.1 位精度
NumPy的浮点数默认使用64位的数据类型。不过,是否为64位还依赖于具体的环境,包括操作系统、Python和NumPy的版本等。我们可以使用下面的代码来验证是否使用了64位浮点数。
>>> import numpy as np >>> a = np.random.randn(3) >>> a.dtype dtype('float64')
通过NumPy数组的实例变量dtype,可以查看数据类型。上面的结果是float64,表示64位的浮点数。
NumPy中默认使用64位浮点数。但是,我们已经知道使用32位浮点数也可以无损地(识别精度几乎不下降)进行神经网络的推理和学习。从内存的角度来看,因为32位只有64位的一半,所以通常首选32位。另外,在神经网络的计算中,数据传输的总线带宽有时会成为瓶颈。在这种情况下,毫无疑问数据类型也是越小越好。再者,就计算速度而言,32位浮点数也能更高速地进行计算(浮点数的计算速度依赖于CPU或GPU的架构)。
因此,本书优先使用32位浮点数。要在NumPy中使用32位浮点数,可以像下面这样将数据类型指定为np.float32或者’f'。
>>> b = np.random.randn(3).astype(np.float32) >>> b.dtype dtype('float32') >>> c = np.random.randn(3).astype('f') >>> c.dtype dtype('float32')
另外,我们已经知道,如果只是神经网络的推理,则即使使用16位浮点数进行计算,精度也基本上不会下降[6]。不过,虽然NumPy中准备有16位浮点数,但是普通CPU或GPU中的运算是用32位执行的。因此,即便变换为16位浮点数,因为计算本身还是用32位浮点数执行的,所以处理速度方面并不能获得什么好处。
但是,如果是要(在外部文件中)保存学习好的权重,则16位浮点数是有用的。具体地说,将权重数据用16位精度保存时,只需要32位时的一半容量。因此,本书仅在保存学习好的权重时,将其变换为16位浮点数。
随着深度学习备受瞩目,最近的GPU已经开始支持16位半精度浮点数的存储与计算。另外,谷歌公司设计了一款名为TPU的专用芯片,可以支持8位计算[7]。
1.5.2 GPU(CuPy)
深度学习的计算由大量的乘法累加运算组成。这些乘法累加运算的绝大部分可以并行计算,这是GPU比CPU擅长的地方。因此,一般的深度学习框架都被设计为既可以在CPU上运行,也可以在GPU上运行。
本书中可以选用Python库CuPy[3]。CuPy是基于GPU进行并行计算的库。要使用CuPy,需要使用安装有NVIDIA的GPU的机器,并且需要安装CUDA这个面向GPU的通用并行计算平台。详细的安装方法请参考CuPy的官方安装文档[4]。
使用Cupy,可以轻松地使用NVIDIA的GPU进行并行计算。更重要的是,CuPy和NumPy拥有共同的API。下面我们来看一个简单的使用示例。
>>> import cupy as cp >>> x = cp.arange(6).reshape(2 , 3).astype('f') >>> x array([[ 0., 1., 2.], [ 3., 4., 5.]], dtype=float32) >>> x.sum(axis=1) array([ 3., 12.], dtype=float32)
如上所示,CuPy的使用方法与NumPy基本相同。另外,它的内部使用GPU进行计算。这意味着使用NumPy写的代码可以轻松地改成“GPU版”,因为我们要做的(基本上)只是把numpy替换为cupy而已。
截至2018年6月,CuPy并没有完全覆盖NumPy的方法。虽然CuPy和NumPy并不完全兼容,但是它们有许多共同的API。
重申一下,本书为了使代码实现易于理解,基本上都基于CPU进行实现。对于计算上要耗费大量时间的代码,则提供使用了CuPy的实现(可选)。不过,即便在使用CuPy的情况下,也会尽量做到不让读者感到是在使用CuPy。
本书中可以在GPU上运行的代码最先出现在第4章(ch04/train.py)。这个ch04/train.py从以下的import语句开始。
import sys
sys.path.append('..')
import numpy as np
from common import config
# 在用GPU运行时,请打开下面的注释(需要cupy)
# ===============================================
# config.GPU = True
# ===============================================
...
用CPU执行上述代码需要花费几个小时,但是如果使用GPU,则只需要几十分钟。并且,只要修改上述源代码中的一行,本书提供的代码就可以在GPU模式下运行。具体而言,只需打开注释# config.GPU = True,并用CuPy代替NumPy即可。如此,代码就可以在GPU上运行,并高速地进行学习。请有GPU的读者多多尝试一下。
将NumPy切换为CuPy的机制非常简单,感兴趣的读者请参考common/config.py 、common/np.py和common/layers.py的import语句。