- keras没有提供attention机制的实现,这里参考kaggle上一个kernel中的attention机制的实现,也学习一下keras中如何自定义层。
- 也是想熟悉一下attention机制的代码实现。本文中的attention layer用于文本分类,和encoder-decoder的attention有些不同。
1 Keras源码参考
- keras官网 写的非常简洁,给了一个类的框架,然后直接说参考源码中其他Layer的写法。这里直接参考一个最简单的Dense吧,Keras中的Dense Layer也就是全连接层。说起来每个框架里全连接层起名都不一样…caffe叫InnerProduct(内积),IP层, pytorch叫linear,tf叫matmul,现在也改叫dense了,毕竟和keras一家的。
- 我下载的源码版本是2.2.4。Dense类在
keras/layers/core.py
中。 - Dense类中的
call()
函数如下,call函数中写具体逻辑,可以看到逻辑非常简单,就是做一个点乘,有bias加bias,有activation做activation。self.kernel
的初始化在函数build()
里。
def call(self, inputs):
output = K.dot(inputs, self.kernel)
if self.use_bias:
output = K.bias_add(output, self.bias, data_format='channels_last')
if self.activation is not None:
output = self.activation(output)
return output
- Dense类中的
build()
函数如下。其中对self.kernel
做了初始化,其shape为input_shape[-1], self_units
,self_units
就是创建Dense对象时传进来的参数。自己从numpy写神经网络,或者使用框架时都非常需要ndarray要有一个维度是表示batch,这是我容易忽略的。
这里为参数分配空间时,显然是不需要考虑batch的,因为每batch显然都是使用同样的参数,可以看到其shape = (input_dim, self.units)
。
def build(self, input_shape):
assert len(input_shape) >= 2
input_dim = input_shape[-1]
self.kernel = self.add_weight(shape=(input_dim, self.units),
initializer=self.kernel_initializer,
name='kernel',
regularizer=self.kernel_regularizer,
constraint=self.kernel_constraint)
if self.use_bias:
self.bias = self.add_weight(shape=(self.units,),
initializer=self.bias_initializer,
name='bias',
regularizer=self.bias_regularizer,
constraint=self.bias_constraint)
else:
self.bias = None
self.input_spec = InputSpec(min_ndim=2, axes={
-1: input_dim})
self.built = True
可以注意到除了最后两行,其他代码都很清楚,self.built = True
是必须要写的,大概是表示分配空间,调用super(MyLayer, self).build(input_shape)
也是可以的,Layer类中的build()
函数里就一句self.built = True
。
InputSpec()
类可以看一下其源码,在engine/base_layer.py
中,它有一个__repr__
函数,是python类自带的一个方法,是用来显示的,print该类的对象时就会调用__repr__
函数,也就是我们可以这样,print(model.layer[2].input_spec)
查看当前层的dtype,shape等信息。但是print(model.summary)
更好用,能看到model结构更详细的信息,以及每层之间怎么连接的。
- Dense类中的
compute_output_shape()
如下。可以看到这里是计算输出的shape, keras中间结果为向量时,shape为(None, dim)
,类的说明写了,shape[0]
表示batch维度(即batch_size),这里写None
表示与任何数值兼容。
# now: model.output_shape == (None, 32)
# note: `None` is the batch dimension
显然全连接层的输出shape为(None, self.units)
def compute_output_shape(self, input_shape):
assert input_shape and len(input_shape) >= 2
assert input_shape[-1]
output_shape = list(input_shape