Keras高级API:实现自定义函数和层

问题分析  

        Keras是一个高度封装的库,它的优点是可以进行快速的建模,缺点是它不处理底层运算,如张量内积等。Keras提供众多常见的已编写好的函数和层对象,例如常见的K.reshape函数、K.sigmoid函数、卷积层、池化层等,可以直接通代码调用。由于新的论文中使用的算法比较新颖、涉及的方法千方百态,Keras不可能涉及地面面俱到。此外,在实现算法过程中,往往会根据具体项目的一些独特性设计一些函数和层。所以,在这个时候,应该在算法中使用Keras自定义一些函数和层。

解决方法

Keras实现自定义函数

        有时候我们会遇到tensorflow或者Keras中没有定义的Op操作,需要自定义。这时候需要借助tf.py_func(func, inp, Tout, stateful=True, name=None),更高版本的tf使用tf.py_function()。tf.py_func()接收的是tensor,然后将其转化为numpy array送入我们自定义的my_func函数,最后再将my_func函数输出的numpy array转化为tensor返回。

        在使用tf.py_func的过程中,主要核心是使用前三个参数。第一个参数func,也是最重要的,是一个用户自定制的函数,输入numpy array,输出也是numpy array,在该函数中,可以自由使用np操作。第二个参数inp,是func函数接收的输入,是一个列表。第三个参数Tout,指定了func函数返回的numpy array转化成tensor后的格式,如果是返回多个值,就是一个列表或元组;如果只有一个返回值,就是一个单独的dtype类型(当然也可以用列表括起来)。

def my_func(a, b):
    return a + b, a-b

x,  y = tf.py_func(my_func,[a, b],[tf.float32, tf.float32])

        注意:第一就是func函数的返回值类型一定要和Tout指定的tensor类型一致。第二就是, tf.py_func中的func是脱离Graph的。在func中不能定义可训练的参数参与网络训练(反传)。

Keras实现自定义层

Keras中的层大致上分为两种类型:

        第一种是带有训练参数的:比如BachNormalization层、Dense层、Conv2D层等等,在网络模型训练的过程中需要更新层的权重和偏置等参数项;

        第二种是不带训练参数的:比如Add层、AveragePooling层、DroupOut层等等,在网络模型中不需要更新它的权重,只需要对输入进行加工处理再输出就行了。

        在项目开发过程中自定义的层无非就是上面两种,一种是带有参数的,一种是不带参数的,不管是哪一种,Keras对自定义层都提供了良好的支持。

不带参数的自定义层

        对于简单、无状态的自定义操作,可以通过 keras.layers.Lambda 层来实现。即使用keras.layers.Lambda()。如果算法自定义层中不包含可训练的权重,而只是对上一层输出做一些函数变换,那么可以直接使用lambda函数:

keras.layers.Lambda(function, output_shape=None, mask=None, arguments=None)

        参数说明: function:要实现的函数,该函数仅接受一个变量,即上一层的输出 。output_shape:函数应该返回的值的shape,可以是一个tuple,也可以是一个根据输入shape计算输出shape的函数。mask: 掩膜。arguments:可选,字典,用来记录向函数中传递的其他关键字参数。

x = Lambda(my_func,output_shape=(4,1),arguments={‘params’:0})(x)

        注意:也不是说对于没有训练参数的层就一定要用Lambda层,也可以使用自定义的层,只不过是没必要那么复杂而已。此外,Lambda 是可以进行参数传递的。

带参数的自定义层

        但是对于那些包含了可训练权重的自定义层,应该自己实现这种层。在这种情况下,需要定义的是一个全新的、拥有可训练权重的层,这个时候就需要使用下面的方法。即通过编写自定义层,从Layer中继承。(下面的内容是使用Keras 2.2.4)

要定制自己的层,需要实现下面三个方法:

  1. build(input_shape):这是定义权重的方法,可训练的权重应该在这里被加入列表self.trainable_weights中。其他的属性还包括self.non_trainabe_weights(列表)和self.updates(需要更新的形如(tensor,new_tensor)的tuple的列表)。这个方法必须设置self.built = True,可通过调用super([layer],self).build()实现。
  2. call(x):这是定义层功能的方法,除非 希望你写的层支持masking,否则你只需要关心call的第一个参数:输入张量。
  3. compute_output_shape(input_shape):如果你的层修改了输入数据的shape,你应该在这里指定shape变化的方法,这个函数使得Keras可以做自动shape推断。

        虽然在自定义类的时候,上面的三个方法,build、call、compute_outout_shape 是必须要有的,但是还有很多其他的方法和属性可以帮助我们更好的实现自定义层。

from keras import backend as K
from keras.layers import Layer, BatchNormalization

class MyLayer(Layer):

    def __init__(self, output_dim, **kwargs):
        self.output_dim = output_dim
        self.bn = BatchNormalization ()
        super(MyLayer, self).__init__(**kwargs)

    def build(self, input_shape):

        self.kernel = self.add_weight(name='kernel',

                                      shape=(input_shape[1], self.output_dim),

                                      initializer='uniform',

                                      trainable=True)
        self.bn.build(input_shape)
        self. trainable_weights += self.bn. trainable_weights
        super(MyLayer, self).build(input_shape)  # Be sure to call this at the end


    def call(self, x):
        x = self.bn(x)
        return K.dot(x, self.kernel)

    def compute_output_shape(self, input_shape):
        return (input_shape[0], self.output_dim)

函数申明

对于新命名的函数,需要重新申明,否则在编译或者模型加载过程中会报错。申明的方法可以是以下两种方式:

方式1:

keras.utils.generic_utils.get_custom_object().update({‘new_layer’:new_layer})
keras.utils.generic_utils.get_custom_object().update({‘new_function’:new_function})

方式2:

keras.models.load_model(‘model.hdf5’,custom_objects={‘new_layer’:new_layer})
keras.models.load_model(‘model.hdf5’,custom_objects={‘new_function’:new_function})

解决过程

  1. 使用tf.py_func函数将cv2或者其它库进行封装。将numpy转成Tensor;
  2. 根据是否需要学习参数,选择Lambda层的封装或者继承Layers层,自定义层;
  3. 将自定义层的名字进行申明更新至网络。
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值