Maxout 论文笔记

Maxout:Maxout networks

Maxout模型实际上是一种新型的激活函数。

maxout的思想其实和dropout有点像,都是多个不同训练模型的集成技术。

1.Dropout
dropout由Hinton提出的一个网络正则技术。dropout通过对共享参数的fc层的一些神经元固定概率的随机置零,来集成多个模型,并且对多个模型的预测进行了平均。dropout是一个通用技术,可以用于任何网络,但是dropout的模型平均能力并没有被证明。使用了dropout的训练过程和一般的SGD方法完全不同。dropout在更新时使用更大的步长最有效,因为这样可以在不同的训练子集上对不同的模型有明显的影响来使得目标函数有持续的波动性,理想情况下整个训练过程就类似于使用bagging来训练集成的模型(带有参数共享的约束)。而一般的SGD更新时会使用更小的步长,来使得目标函数平滑的下降。对于深度网络模型,dropout只能作为模型平均的一种近似,显式的设计模型来最小化这种近似误差也可以提高dropout的性能[参考文献]。

dropout训练的集成模型中,所有模型都只包括部分输入和部分隐层参数。对每一个训练样本,我们都会训练一个包括不同隐层参数的子模型。dropout与bagging的相同点是不同的模型使用不同数据子集,不同点是dropout的每个模型都只训练一次且所有模型共享参数。这一段有争议

对于预测时如何平均所有子模型的问题,bagging一般使用的是算数平均,而对dropout产生的指数多个子模型则并非显而易见。但是如果模型只有一层,则最终的预测分布就是简单的指数多个子模型的算数平均。这个结果只能用在单层模型中,如果是深层模型,则dropout产生的指数个模型的集成更复杂,将由单层的算数平均转为几何平均。这一段有争议

2.Maxout
使用maxout的默认先验:样本集是凸集可分的
Maxout其实是一种新的激活函数。在前馈式神经网络中,Maxout的输出取该层的多个fc的同一位置的神经元的最大值;卷积中,Maxout的输出取多个feature map同一位置的最大值。这句话需要好好理解

我们知道,多层感知器(MLP)是一种通用的函数拟合器,也就是说它可以拟合任意复杂的函数,只不过有些函数使用MLP来训练比较麻烦而已。

而Maxout模型恰好是一种基于MLP的激活函数,因为每个Maxout模型内部可以包含任意的仿射变换,这就导致了Maxout模型也是一个通用的函数拟合器。

例如:下图是一个包含4个神经元的Maxout网络。
Maxout 论文笔记_第1张图片
从输入层到Maxout的输出,是经过了三个仿射变换,我们可以认为这三个仿射变换就是三个特征提取器,此变换中没有任何非线性激活函数,完全是一个线性变换,最终由这三个变换取最大值得到Maxout的4个输出神经元。

根据上图,我们可以看到,假如输入变量的维度是2,那么Maxout中的权重变量就是4维,因为多余的两个维度分别是Maxout自身神经元的个数以及特征提取器的个数。

我们再回到前面所说的Maxout的通用拟合的功能,下图是原始论文中出现的一张图,其目的是告诉我们无论是ReLU函数,还是V型函数还是二次型函数,我们都可以通过构造多个仿射变换来对其进行逼近,并且在每个区间内取其最大值即可。这不就是Maxout的功能吗?虽然这里只给出了二维函数的逼近示意图,但是实际上Maxout可以拟合任意的凸函数。
Maxout 论文笔记_第2张图片
maxout激活函数的数学描述: h i ( x ) = max ⁡ j ∈ [ 1 , k ] z i j h_{i}(x)=\max\limits_{j\in{[1,k]}}{z_{ij}} hi(x)=j[1,k]maxzij
在MLP中 z i j = x T W . . . i j + b i j z_{ij}=x^{T}W_{...ij}+b_{ij} zij=xTW...ij+bij其中 W ∈ R d × m × k W\in{\mathbb{R}^{d\times{m}\times{k}}} WRd×m×k b ∈ R m × k b\in{\mathbb{R}^{m\times{k}}} bRm×k

在卷积网络中 z i j z_{ij} zij为各个feature map

maxout网络可以拟合任意连续函数,只要maxout网络中有足够多的 z i j z_{ij} zij

maxout网络不仅可以学习到k个 z i j z_{ij} zij之间的关系,还可以学习到maxout每个输出单元的激活函数。

maxout放弃了传统激活函数的设计,它产生的表示不再是稀疏的,但是它的梯度是稀疏的,且dropout可以将它稀疏化。

maxout没有上下界,所以让它在某一端饱和是零概率事件。

如果训练时使用dropout,则dropout操作在矩阵相乘之前,而并不对max操作的输入执行dropout。

3.Dropout和Maxout结合
Dropout和Maxout结合起来效果会更好。
结合时,应该在矩阵相乘之前使用dropout,而并不对max操作的输入执行dropout

4.Maxout的各种实现:
1.Tensorflow内部的Maxout激活函数:

# 代码来自于tensorflow.contrib.layers.maxout
import tensorflow as tf
def maxout(inputs, num_units, axis=-1, scope='Maxout'):
  '''
  inputs: Tensor input
  num_units: The num of unit keeped after amxout
  axis: The dimension max op performed
  scope: Optional scope for variable_scope
  注意:这是略加修改的版本。替换了一些不常用的API函数
  '''
  with tf.variable_scope(scope):
    shape = inputs.get_shape().as_list()
    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] = -1
    shape += [num_channels // num_units]

    # Dealing with batches with arbitrary sizes
    for i in range(len(shape)): # 这里用来处理shape中包含None的情况
      if shape[i] is None:
        shape[i] = tf.shape(inputs)[i]
    outputs = tf.reduce_max( tf.reshape(inputs, shape), -1, keepdims=False)
    return outputs

上面是绝对正确的TF实现,其对应文章中的 h i ( x ) = max ⁡ j ∈ [ 1 , k ] h_{i}(x)=\max\limits_{j\in{[1,k]}} hi(x)=j[1,k]max,inputs对应 z i j z_{ij} zij,并不包含 z i j z_{ij} zij的计算。

2.Keras里MLP版Maxout层的实现(该实现现在已经被去除掉了)

# maxout 网络层类的定义
# 这里的MaxoutDense只是Maxout在MLP中的实施
class MaxoutDense(Layer):  
    # 网络输入数据矩阵大小为(nb_samples, input_dim)  
    # 网络输出数据矩阵大小为(nb_samples, output_dim)  
    input_ndim = 2  
   #nb_feature就是我们前面说的k的个数了,这个是maxout层特有的参数  
    def __init__(self, output_dim, nb_feature=4,  
                 init='glorot_uniform', weights=None,  
                 W_regularizer=None, b_regularizer=None, activity_regularizer=None,  
                 W_constraint=None, b_constraint=None, input_dim=None, **kwargs):  
        self.output_dim = output_dim  
        self.nb_feature = nb_feature  
        self.init = initializations.get(init)  
  
        self.W_regularizer = regularizers.get(W_regularizer)  
        self.b_regularizer = regularizers.get(b_regularizer)  
        self.activity_regularizer = regularizers.get(activity_regularizer)  
  
        self.W_constraint = constraints.get(W_constraint)  
        self.b_constraint = constraints.get(b_constraint)  
        self.constraints = [self.W_constraint, self.b_constraint]  
  
        self.initial_weights = weights  
        self.input_dim = input_dim  
        if self.input_dim:  
            kwargs['input_shape'] = (self.input_dim,)  
        self.input = K.placeholder(ndim=2)  
        super(MaxoutDense, self).__init__(**kwargs)  
    #参数初始化部分  
    def build(self):  
        input_dim = self.input_shape[1]  
  
        self.W = self.init((self.nb_feature, input_dim, self.output_dim))#nb_feature是我们上面说的k。  
        self.b = K.zeros((self.nb_feature, self.output_dim))  
  
        self.params = [self.W, self.b]  
        self.regularizers = []  
  
        if self.W_regularizer:  
            self.W_regularizer.set_param(self.W)  
            self.regularizers.append(self.W_regularizer)  
  
        if self.b_regularizer:  
            self.b_regularizer.set_param(self.b)  
            self.regularizers.append(self.b_regularizer)  
  
        if self.activity_regularizer:  
            self.activity_regularizer.set_layer(self)  
            self.regularizers.append(self.activity_regularizer)  
  
        if self.initial_weights is not None:  
            self.set_weights(self.initial_weights)  
            del self.initial_weights  
  
    def get_output(self, train=False):  
        X = self.get_input(train)#需要切记这个x的大小是(nsamples,input_num)   
        # -- don't need activation since it's just linear.  
        output = K.max(K.dot(X, self.W) + self.b, axis=1)#maxout激活函数  
        return output

上面代码其实最主要的一句是:

output = K.max(K.dot(X, self.W) + self.b, axis=1)

注意Keras和TF里Maxout实现的不同,TF实现了一个通用的maxout激活函数,keras里将论文中的 z i j z_{ij} zij的计算也包含到了代码实现中。

最后附上一些网上的实现,但是都没有TF里实现的好,TF里的maxout更加通用。

# 实现1
def maxout(x, k, m):
    # 这个实现只适用于MLP
    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]))
    z = tf.tensordot(x, W, axes=1) + b
    z = tf.reduce_max(z, axis=2)
    return z

# 实现2
def maxout_cnn_layer(inputs, filter, bind_num,strides, padding, name=None):
  # 这个实现只适用于卷积层
  conv = tf.nn.conv2d(input=inputs, filter=filter, strides=strides, padding=padding, name=name)
  return maxout(conv, bind_num)

def maxout_weights(filter_shape,bind_num):
  filter_shape[- 1]=filter_shape[- 1]*bind_num
  return tf.Variable(tf.random_normal(filter_shape, stddev= 0.01))

def maxout(inputs, bind_num):
  shape = inputs.get_shape().as_list()
  in_chan = shape[- 1]
  shape[- 1] = in_chan//bind_num
  shape += [bind_num]
  shape[ 0]=- 1
  return tf.reduce_max(tf.reshape(inputs,shape),- 1,keep_dims= False)

maxout网络的tensorflow实现(数据集:MNIST)

import tensorflow as tf
maxout = tf.contrib.layers.maxout
'''
未来如果maxout激活函数被TF去除,读者将上面的maxout激活函数在TF的实现搬下来即可。
network configuration comes from goodfellow's github
'''

def inference(inputs,
              num_classes=10,
              is_training=True,
              dropout_keep_prob=0.5,
              scope='inference'):
  
  x = inputs
  with tf.variable_scope('maxout1'):
    x = tf.layers.Conv2D(48, [8,8], padding='SAME')(x)
    x = maxout(x, 4) # 2
    x = tf.layers.MaxPooling2D([4,4], [2,2], padding='SAME')(x)
  with tf.variable_scope('maxout2'):
    x = tf.layers.Conv2D(48, [8,8], padding='SAME')(x)
    x = maxout(x, 4) # 2
    x = tf.layers.MaxPooling2D([4,4], [2,2], padding='SAME')(x)
  with tf.variable_scope('maxout3'):
    x = tf.layers.Conv2D(24, [5,5], padding='SAME')(x)
    x = maxout(x, 8) # 4
    x = tf.layers.MaxPooling2D([2,2], [2,2], padding='SAME')(x)
  with tf.variable_scope('fc'):
    _ksize = x.get_shape().as_list()[1]
    logits = tf.layers.Conv2D(num_classes, [_ksize, _ksize], activation=tf.nn.softmax)(x)
    logits = tf.layers.flatten(logits)
  return logits

if __name__ == '__main__':
  batch_size = 64
  x = tf.placeholder(tf.float32, [batch_size, 784])
  images = tf.reshape(x,[-1,28,28,1])
  labels = tf.placeholder(tf.float32, [batch_size, 10])
  
  logits = inference(inputs=images, num_classes=10)
  print('inference is ok!')

上面的代码只给出了inference部分。网络的参数来自于于作者Goodfellow的github,作者使用的是pylearn2框架。这里对作者的参数做了一点点修改,修改的地方注释了原始参数。

注意:使用本博客的代码,请添加引用

参考文献:
Maxout网络学习

你可能感兴趣的:(论文笔记,图像分类)