• Layer层自定义


    keras允许自定义Layer层, 大大方便了一些复杂操作的实现. 也方便了一些novel结构的复用, 提高搭建模型的效率.

    实现方法

    通过继承keras.engine.Layer类, 重写其中的部分方法, 实现层的自定义. 主要需要实现的方法及其意义有:

    • _ init _(self, **kwargs)

      作为类的初始化方法, 一般将需要传入的自定义参数存为对象的属性. 需要注意的有以下几点:

      • 由于继承Layer类, 所以在处理完自定义的参数之后, 仍可能还有参数需要父类处理, 所以需要调用父类的初始化方法, 将kwargs参数传入:

        class DecayingDropout(Layer):
            def __init__(self, initial_keep_rate=1., decay_interval=10000, decay_rate=0.977,
                         noise_shape=None, seed=None, **kwargs):
                super(DecayingDropout, self).__init__(**kwargs)
        
      • 对象的self.supports_masking方法的作用是本层中是否涉及到使用mask或对mask矩阵进行计算. mask的作用是屏蔽传入Tensor的部分值, 常常在NLP问题中, 对句子padding之后, 不想让填补的0值对应的位置参与运算而使用. 这个参数默认为False, 如果有使用到, 需要将其值为True:

        self.supports_masking = True
        
    • build(self, input_shape, **kwargs)

      这里是定义权重的地方, 需要注意的有以下几点:

      • 通过self.add_weight方法定义权重, 且需要将权重存为类的属性, 例如:

        self.iterations = self.add_weight(name='iterations', shape=(1,), dtype=K.floatx(),
                                          initializer='zeros', trainable=False)
        

        其中self.iterations需要在初始化时设置为None, 符合类编程的习惯. self.add_weight方法有若干参数, 常用的即为上面几个.

      • 由于要求build方法必须设置self.built = True , 而这个方法在父类中实现, 因此, 在方法的最后需要调用:

        super(DecayingDropout, self).build(input_shape)
        
    • call(self, inputs, **kwargs)

      这里是编写层的功能逻辑的地方, 传入的第一个参数即输入张量, 即调用_ call _方法传入的张量. 除此之外, 需要注意的点有:

      • 如果需要在计算的过程中使用mask, 则需要传入mask参数:

        def call(self, x, mask=None):
                if mask is not None:
                    mask = K.repeat(mask, x.shape[-1])
                    mask = tf.transpose(mask, [0,2,1])
                    mask = K.cast(mask, K.floatx())
                    x = x * mask
                    return K.sum(x, axis=self.axis) / K.sum(mask, axis=self.axis)
                else:
                    return K.mean(x, axis=self.axis)
        
      • 如果该层在训练和预测时的行为不一样(如Dropout)函数, 需要传入指定参数training, 即使用布尔值指定调用的环境. 例如在Dropout层的源码中, call方法是这样实现的:

        def call(self, inputs, training=None):
            if 0. < self.rate < 1.:
                noise_shape = self._get_noise_shape(inputs)
        
                def dropped_inputs():
                    return K.dropout(inputs, self.rate, noise_shape,
                                     seed=self.seed)
                return K.in_train_phase(dropped_inputs, inputs,
                                        training=training)
        

        K.in_train_phase() 方法就是用来区别在不同环境调用时, 返回的不同值的. 这个函数通过training参数区别调用环境, 如果是训练环境, 则返回第一个参数对应的结果, 预测环境则返回第二个参数对应的结果. 可以传入函数, 返回这个函数对应的返回结果.

      • 除了计算之外, 这个函数也是更新层内参数的地方, 即build方法中增加的参数. 通过self.add_update方法进行更新, 例如:

        def call(sekf, x):
            self.add_update([K.moving_average_update(self.moving_mean, mean,self.momentum),
                             K.moving_average_update(self.moving_variance,variance,self.momentum)],
                            inputs)
        

        或者:

        def call(self, inputs, training=None):
            self.add_update([K.update_add(self.iterations, [1])], inputs)
        

        可以看到, self.add_update方法传入一个列表, 包含一些列更新的动作. 这些更新的动作需要借助K的一些函数实现, 如K.moving_average_update, K.update_add等等.

        另外还可以传入inputs函数, 作为更新的前提条件.


    除此之外, 还有一些常常需要重新定义的方法:

    • get_config(self):

      返回层的一些参数. 对于自定义的参数, 需要在此指定返回:

      def get_config(self):
          config = {'initial_keep_rate':  self.initial_keep_rate,
                    'decay_interval':     self.decay_interval,
                    'decay_rate':         self.decay_rate,
                    'noise_shape':        self.noise_shape,
                    'seed':               self.seed}
          base_config = super(DecayingDropout, self).get_config()
          return dict(list(base_config.items()) + list(config.items()))
      
    • compute_output_shape(input_shape) :

      计算输出shape. input_shape是输入数据的shape.

    • compute_mask(self, input, input_mask=None):

      计算输出的mask, 其中input_mask为输入的mask. 需要注意的有:

      • 如果input_mask为None, 说明上一层没有mask. 可以在本层创建一个新的mask矩阵.

      • 如果以后的层不需要使用mask, 返回None即可, 之后就不存在mask矩阵了

        def compute_mask(self, input, input_mask=None):
                # need not to pass the mask to next layers
                return None
        
      • 如果经过本层, mask矩阵没有变化, 不用实现该函数, 只需要在初始化时, 指定self.supports_masking = True即可.

    参考资料

    编写你自己的Keras层

    Keras编写自定义层--以GroupNormalization为例

    Keras自定义实现带masking的meanpooling层

    Keras实现支持masking的Flatten层

  • 相关阅读:
    Spring IoC容器实现
    Spring IoC简介及使用
    tomcat使用及原理
    tomcat的连接数与线程池
    tomcat配置文件server.xml
    java源码之Comparable和Comparator
    java源码之TreeSet
    25 二叉搜索树与双向链表
    24 复杂链表的复制
    条款04:确定对象被使用前已被初始化
  • 原文地址:https://www.cnblogs.com/databingo/p/9352303.html
Copyright © 2020-2023  润新知