激活函数maxout
maxout函数相对于其他的激活函数有很大的区别,可以看做是在神经网络中激活函数的地方加入一个激活函数层。maxout可以看做是一个可学习的分段线性函数,因为可学习所以是需要参数的,而且参数是可以通过反向传播来学习的。因为参数量的增大,势必导致计算量的增大。
传统的神经网络从第i层输入到第i+1层,只需要训练一组权重参数,这些参数决定了上一层输入到到达这一层之后进行的变换。但是maxout做的事情就是第i层到第i+1层之前我们多训练一些参数,对第i层的输出,我们将每个输出连接k个隐藏层,通过k个隐藏层的参数进行计算之后,然后把这k个隐藏层求最大值,作为最终的输出。
这里我们给出maxout的计算公式。
,使用maxout需要人为的设定一个参数k,这个参数k就是每个神经元输出之后接的虚拟隐藏层的个数。而权重参数W的维度是的,d代表的是输入节点的个数,m代表的则是输出层节点的个数。这也是之前神经网络第i+1层的参数设置。但是现在多出来一个维度k,就是为了产生中间的k个输出,然后将这k个输出再取一个最大值。
如上图,原本的神经网络只有输入的两个神经元x,输出的只有一个神经元,普通的神经元则是直接把x和连接,现在相当于在两层之间又增加了个参数,然后输入先经过这个maxout的隐藏层得到输出,然后对输出取最大值,再输入到下一层当中。其中虚拟隐藏层的和就是所需要学习的参数。可以看到,参数量的增大是针对每一个输出神经元增加k个的。
普通的神经元配合激活函数得到一组输出,经过了maxout将会得到k组输出,然后在k组之中取最大值。可见maxout非线性的拟合能力是更强的,因为参数更多,而且可学习。我们能想到,而maxout也是取最大,如果我们能让一个虚拟神经元学习,一个虚拟神经元学习,然后两个神经元求最大就得到了relu激活函数。可见这是k=2的情况,如果我们有更多的k,就可以学习更加负责的激活函数,这就是maxout拟合能力更强的原因。事实上maxout可以拟合任意的凸函数。
相比于ReLU,ReLU有的优点maxout有,是分段线性的,不容易梯度消失。同时ReLU没有的优点,maxout可能也有,比如神经元不会死亡。但是这些优点是通过计算量换来的,maxout的计算量非常大,我们已经解释过。
这个激活函数的实现没什么难的,就是在指定的维度上取最大值即可,但是为了得到能够得到这个要取最大值的维度的变量,往往需要在上一层的神经元上做手脚,需要增加参数量。原本是m维的输出通道数,现在需要变成k×m的数出通道。给出tensorflow代码示例。
x=tf.random_normal([1,3])
m=4 # out_channel
k=3 # maxout_chanenl
d=x.get_shape().as_list()[-1]
W=tf.Variable(tf.random_normal(shape=[d,m,k]))
b=tf.Variable(tf.random_normal(shape=[m,k]))
# 从W和b的维度看出参数量是增大了
dot_z=tf.tensordot(x,W,axes=1)+b
z=tf.reduce_max(dot_z,axis=2) # 这里再把maxout的通道给取最大降维下去
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run([x,dot_z,z]))
事实上这里还有一种方式就是为maxout特殊定义一个方法或者类,然后就可以像其他普通的激活函数一样使用。以下给出tensorflow代码示例。
def maxout(inputs, num_units, axis=None):
'''
inputs: 上一层的输出的结果,这里需要上一层的输出结果的维度是k*m
num_units:激活后的最终的输出结果数m
'''
shape = inputs.get_shape().as_list()
if axis is None:
# Assume that channel is the last dimension
axis = -1
num_channels = shape[axis]
if num_channels % num_units:
raise ValueError('number of features({}) is not a multiple of num_units({})'
.format(num_channels, num_units))
shape[axis] = num_units
shape += [num_channels // num_units]
for i in range(len(shape)):
if shape[i] is None:
shape[i] = -1
outputs = tf.reduce_max(tf.reshape(inputs, shape), -1, keepdims=False)
return outputs
# X.shape = (..., d)
X = tf.layers.conv2d(inputs=X, filters=k * m, kernel_size, strides, padding)
# X.shape = (..., m*k)
X = maxout(inputs=X, num_units=m)
# X.shape = (..., m)
系列文章:
神经网络中的激活函数总述
sigmoid激活函数
tanh激活函数
ReLU系列激活函数
maxout激活函数
Swish激活函数
激活函数发展的新里程——EvoNorms