Keras是一个高度封装的库,它的优点是可以进行快速的建模,缺点是它不处理底层运算,如张量内积等。为了弥补这个问题,Keras提供“后端引擎”来实现底层运算操作。目前Keras支持的后端引擎有tensorflow,CNTK,Theano。默认的是使用tensorflow,你可以在.keras/keras.json文件中更改backend。我们可以使用keras提供的后端来实现任意你想实现的layer。
1.首先来看下keras官方给的示例:
只有一个张量输入输出:
from keras import backend as K
from keras.layers import Layer
class MyLayer(Layer):
def __init__(self, output_dim, **kwargs):
self.output_dim = output_dim
super(MyLayer, self).__init__(**kwargs)
def build(self, input_shape):
# Create a trainable weight variable for this layer.
self.kernel = self.add_weight(name='kernel',
shape=(input_shape[1], self.output_dim),
initializer='uniform',
trainable=True)
super(MyLayer, self).build(input_shape) # Be sure to call this at the end
def call(self, x):
return K.dot(x, self.kernel)
def compute_output_shape(self, input_shape):
return (input_shape[0], self.output_dim)
有多个张量输入输出时:
from keras import backend as K
from keras.layers import Layer
class MyLayer(Layer):
def __init__(self, output_dim, **kwargs):
self.output_dim = output_dim
super(MyLayer, self).__init__(**kwargs)
def build(self, input_shape):
assert isinstance(input_shape, list)
# Create a trainable weight variable for this layer.
self.kernel = self.add_weight(name='kernel',
shape=(input_shape[0][1], self.output_dim),
initializer='uniform',
trainable=True)
super(MyLayer, self).build(input_shape) # Be sure to call this at the end
def call(self, x):
assert isinstance(x, list)
a, b = x
return [K.dot(a, self.kernel) + b, K.mean(b, axis=-1)]
def compute_output_shape(self, input_shape):
assert isinstance(input_shape, list)
shape_a, shape_b = input_shape
return [(shape_a[0], self.output_dim), shape_b[:-1]]
1
2. 如何自定义一个层
基本的需要实现以下四个方法:
1. __init __ (self, output_dim, **kwargs) :这个方法是用来初始化并自定义自定义层所需的属性,比如output_dim,以及一个必需要执行的super().__init __(**kwargs),这行代码是去执行Layer类中的初始化函数,当它执行了你就没有必要去管input_shape,weights,trainable等关键字参数了因为父类(Layer)的初始化函数实现了它们与layer实例的绑定。
2. build(self, input_shape): 创建层权重的地方。你可以通过Layer类的add_weight方法来自定义并添加一个权重矩阵。这个方法一定有input_shape参数。这个方法必须设self.built = True,目的是为了保证这个层的权重定义函数build被执行过了,这个self.built其实是个标记而已,当然也可以通过调用super([MyLayer], self).build(input_shape)来完成。build这个方法是用来创建权重的,在这个函数中我们需要说明这个权重各方面的属性比如shape,初始化方式以及可训练性等信息,这也是为什么keras设计单独的一个方法来定义权重。
3. call(self, x): 这里是编写层的功能逻辑的地方。你只需要关注传入call的第一个参数:输入张量x,而且它只能是一种形式不能是具体的变量也就是它说它不能被定义。如果你希望你的层能支持masking,我们建议直接使用官方给的Masking层即可。这个call函数就是该层的计算逻辑,或计算图了,当创建好了这个层实例后,这个实例可以使用像函数调用那样的语法来执行call函数(不懂的可以了解一下python中的__call__魔法方法)。显然,这个层的核心应该是一段符号式的输入张量到输出张量的计算过程。再次强调因为输入只是个形式,所以输入变量不能被事先定义。这个跟python中的匿名函数类似,在python中没有被赋过值的变量就是未定义的。
4. compute_output_shape(self, input_shape):为了能让Keras内部shape的匹配检查通过,这里需要重写compute_output_shape方法去覆盖父类中的同名方法,来保证输出shape是正确的。父类Layer中的compute_output_shape方法直接返回的是input_shape这明显是不对的,所以需要我们重写这个方法。所以这个方法也是4个要实现的基本方法之一。
当然你还可以根据需要自定义实现一些其他的方法。
相关参考:
https://keras.io/layers/writing-your-own-keras-layers/
https://blog.csdn.net/buchidanhuang/article/details/99210547