代码演示
废话不多说,直接上代码
class Self_Attention(Layer):
def __init__(self, output_dim, **kwargs):
self.output_dim = output_dim
super(Self_Attention, self).__init__(**kwargs)
def build(self, input_shape):
# 为该层创建一个可训练的权重
# inputs.shape = (batch_size, time_steps, seq_len)
self.kernel = self.add_weight(name='kernel',
shape=(3, input_shape[2], self.output_dim),
initializer='uniform',
trainable=True)
super(Self_Attention, self).build(input_shape) # 一定要在最后调用它
def call(self, x):
WQ = K.dot(x, self.kernel[0])
WK = K.dot(x, self.kernel[1])
WV = K.dot(x, self.kernel[2])
print("WQ.shape", WQ.shape)
print("K.permute_dimensions(WK, [0, 2, 1]).shape", K.permute_dimensions(WK, [0, 2, 1]).shape)
QK = K.batch_dot(WQ, K.permute_dimensions(WK, [0, 2, 1]))
QK = QK / (self.output_dim ** 0.5)
QK = K.softmax(QK)
print("QK.shape", QK.shape)
V = K.batch_dot(QK, WV)
return V
def compute_output_shape(self, input_shape):
return (input_shape[0], input_shape[1], self.output_dim)
调用的时候直接
intention_token_embedding = Self_Attention(768)(all_token_embedding)
调试运行
从 intention_token_embedding = Self_Attention(768)(all_token_embedding)处直接step into my code
跳入构造函数当中,执行完构造函数__init__,就跳入到Layer对象里面的__call__内 ,分析左下角,执行__call__内的函数
执行
在执行build的时候,build的形参input_shape可能是在__call__函数前425行代码自动计算出all_token_embedding的形状的。最后面的super(Self_Attention, self).build(input_shape) ,跳入进去有
这儿的self.built = True是把Layer对象的__init__方法下的带装饰器的built方法赋值(应该是赋值)
使得Layer对象的实例属性变为true
这儿简单解释一下,我的Self_Attention对象里的build方法对父类对象Layer对象的build方法进行了重写,在执行Layer对象的__call__方法的时候,先执行重写后build方法,然后执行父类对象(重写前)的build方法。
接着运行__call__后面的代码,执行到__call__函数内的call方法,这儿执行重写后的方法,没错,Self_Attention的call方法重写的是Layer里的call方法。
以上即是创建self-attention层的全部内容。
有时候会遇到一些报错,最典型的报错就是AttributeError: ‘NoneType’ object has no attribute ‘_inbound_nodes’
参考link.