不管是一般的编程还是机器学习,凡是计算机中的数据都是以一定的数据格式表示的。但有些情况下数据格式会带来明显的限制,造成计算结果的异常。
不过在 OpenAI 的研究人员们看来,这种“计算结果的异常”可以成为给神经网络带来新的可能性的落脚点。在这篇博文中,他们就介绍了一种异常的来源以及如何运用,还取得了不错的效果。雷锋网 AI 科技评论把文章编译如下。
之前已经有证据表明,深度线性网络如果用浮点数值运算实现的话,结果是不完全线性的,会展现出一些非线性的计算特点。在这项研究中,OpenAI的研究人员们使用了进化计算的策略,在线性网络中找到带来非线性特点的参数,希望能够帮助一些疑难问题。
神经网络是由许多线性层和其后的非线性层堆叠起来形成的,常见的非线性函数有 tanh 或者 ReLU。如果没有这些非线性的部分,连续的几个线性层在数学理论上应当等效于单独一个线性层。所以浮点数的计算具有足够的非线性程度,以至于这样也能形成一个可训练的深度神经网络,这就有点出乎意料。
背景
计算机中使用的数字并不是理想的数学化的对象,而是用有限位的0和1近似地表示的。计算机中常用的一类数学对象表示方法就是浮点数。每个浮点格式的数据都可以分为小数和指数两个部分。在 IEEE 的32位浮点数标准中,其中的23位用来表示小数部分,8位用来表示指数部分,还有一位用来表示符号。
根据浮点数的定义和其中使用的2进制位的表示范围限制,它所能表示的最小非零数字就是(二进制)1.000...0 x 2^-126,这里把这个值称为 min。然而,可表示的数字里面第二小的是 1.000...01 x 2^-126,这个数字可以用 min + 0.000...01 x 2^-126 表示。可以明显看到, min 和第二小的数之间的差距,只有 min 和0之间差距的 2^-20 那么大,还不到一百万分之一倍。在32位浮点数中,如果要需要表示的数字比 min 还要接近0,那么这个数就会直接被表示为0。由于这种“下溢”(underflow)的影响,所有在0附近进行的浮点数运算都会变成非线性的。
“非规格化数”(denormal numbers)就不受这些限制的影响,不过某些计算硬件上是不支持这种数据格式的。虽然 GPU 和 cuBLAS 默认都是可以使用非规格化数的,TensorFlow 在构建模型的时候却是默认把非规格化数选项关闭的(对于 ftz=true 标志集)。这就意味着,在TensorFlow中编写的所有非矩阵乘法操作都带有隐含的非线性(假设参与计算的数量级在 e^-38 附近)。
所以,虽然总体来说任何一个数字的真实值和用浮点格式表示以后的值之间的区别都很小,但是当这个数字非常接近0的时候,这一部分近似误差就会变得非常显著。
这件事的后果就是我们熟悉的数学规律不再适用,接下来就会有很多奇怪的事情发生。比如 (a + b) x c 就不再和 a x c + b x c 相等。
举例说明,令 a = 0.4 x min ,b = 0.5 x min ,c = 1/ min
那么, (a+b) x c = (0.4 x min + 0.5 x min ) x 1 / min = (0 + 0) x 1 / min = 0
然而 (a x c) + (b x c) = 0.4 x min / min + 0.5 x min x 1 / min = 0.9
再举一个例子,这次令 a = 2.5 x min, b = -1.6 x min , c = 1 x min
那么 (a+b) + c = (0) + 1 x min = min
然而 (b+c) + a = (0 x min) + 2.5 x min = 2.5 x min
在这样的数量级上,连最基本的结合律都变得非线性了!
借助进化策略利用非线性
OpenAI 的研究人员们很想知道这种固有的非线性可否作为计算中的非线性得到利用,如果可行的话就可以让深度线性网络实现非线性的计算操作。其中的挑战是,用到的现代微分库通常都对如此小尺度下的非线性视而不见。这样以来,就非常困难、甚至没有可能通过反向传播的方法训练一个神经网络来利用这些非线性。
OpenAI 的研究人员们决定使用进化策略(evolution strategies)来估计梯度,不再依靠微分一类的方法。通过进化策略,他们确实能够把32位浮点数在零值附近的非线性行为加以利用,发展为计算性的非线性。以 MNIST 数据集的训练为例,用反向传播算法训练的深度线性网络可以达到94%的训练准确率和92%的测试准确率;相比之下,同一个线性神经网络可以达到超过99%的训练准确率和96.7%的测试准确只,就是用进化策略训练得到的,同时保证激活权重都足够小,在32位浮点数的非线性行为能够产生影响的范围内即可。这种训练表现的提升就是因为进化策略发现并充分利用了32位浮点数表示中的非线性。这些强有力的非线性行为可以让任意一个层根据低层特征的非线性组合产生全新的特征。这个网络的结构如下:
x = tf.placeholder(dtype=tf.float32, shape=[batch_size,784])
y = tf.placeholder(dtype=tf.float32, shape=[batch_size,10])
w1 = tf.Variable(np.random.normal(scale=np.sqrt(2./784),size=[784,512]).astype(np.float32))
b1 = tf.Variable(np.zeros(512,dtype=np.float32))
w2 = tf.Variable(np.random.normal(scale=np.sqrt(2./512),size=[512,512]).astype(np.float32))
b2 = tf.Variable(np.zeros(512,dtype=np.float32))
w3 = tf.Variable(np.random.normal(scale=np.sqrt(2./512),size=[512,10]).astype(np.float32))
b3 = tf.Variable(np.zeros(10,dtype=np.float32))
params = [w1,b1,w2,b2,w3,b3]
nr_params = sum([np.prod(p.get_shape().as_list()) for p in params])
scaling = 2**125
def get_logits(par):
h1 = tf.nn.bias_add(tf.matmul(x , par[0]), par[1]) / scaling
h2 = tf.nn.bias_add(tf.matmul(h1, par[2]) , par[3] / scaling)
o = tf.nn.bias_add(tf.matmul(h2, par[4]), par[5]/ scaling)*scaling
return o
除了 MNIST 之外,OpenAI 的研究人员们相信未来还会有更多有意思的实验把这种思想拓展到循环神经网络,或者把非线性计算运用在提升复杂语言建模和翻译之类的机器学习任务表现中。他们非常期待可以和广大研究人员们一起探索其中的可能性。