from keras import backend as K from keras.engine.topology import Layer 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) # 可能seq_len是64,output_dim是128 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("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 / (64 ** 0.5) QK = K.softmax(QK) print("QK.shape", QK.shape) # distribution函数: (QxKt)/dk 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) 解释这段代码,举例怎样使用
时间: 2024-03-26 17:39:26 浏览: 113
这段代码实现了一个自注意力层(Self-Attention Layer),用于文本分类等任务中。下面是对代码的解释:
```python
from keras import backend as K
from keras.engine.topology import Layer
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)
# 可能seq_len是64,output_dim是128
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 / (64 ** 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)
```
这个自注意力层的输入是一个形状为`(batch_size, time_steps, seq_len)`的张量,其中`seq_len`表示序列的长度,例如一个句子中的单词数。输出是形状为`(batch_size, time_steps, output_dim)`的张量,其中`output_dim`表示自注意力层的输出维度,例如一个句子中的每个单词都会被映射成一个长度为128的向量。
在`__init__`方法中,我们定义了输出维度`output_dim`,并调用了父类的`__init__`方法。
在`build`方法中,我们定义了一个可训练的权重`kernel`,它是一个形状为`(3, seq_len, output_dim)`的张量,其中`3`表示了我们需要计算出`Q`、`K`、`V`三个向量,`seq_len`为输入序列的长度,`output_dim`为自注意力层的输出维度。我们使用了`add_weight`方法来创建这个权重,并指定了它的名称、形状和初始化方法,将它设置为可训练的。最后,我们调用了父类的`build`方法。
在`call`方法中,我们首先根据`kernel`权重计算出`Q`、`K`、`V`三个向量,分别对输入`x`进行线性变换得到。然后,我们通过`batch_dot`方法计算出`Q`和`K`之间的点积,并使用`softmax`函数将其归一化,得到注意力分布`QK`。最后,我们将注意力分布`QK`和`V`进行加权求和,得到自注意力层的输出`V`。
在`compute_output_shape`方法中,我们返回了自注意力层的输出形状`(batch_size, time_steps, output_dim)`。
使用这个自注意力层的方法如下:
```python
from keras.layers import Input, Dense, Masking, LSTM, Bidirectional
from keras.models import Model
import numpy as np
# 定义输入数据形状和类别数
max_len = 64
num_classes = 5
# 构建模型
inputs = Input(shape=(max_len,))
x = Masking(mask_value=0)(inputs) # 对输入进行 Masking,将填充部分忽略
x = Bidirectional(LSTM(64, return_sequences=True))(x) # 双向 LSTM
x = Self_Attention(output_dim=128)(x) # 自注意力层
x = Dense(64, activation='relu')(x)
outputs = Dense(num_classes, activation='softmax')(x)
model = Model(inputs=inputs, outputs=outputs)
# 编译模型并训练
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
x_train = np.random.randint(5, size=(64, 64)) # 随机生成训练数据
y_train = np.random.randint(num_classes, size=(64,)) # 随机生成训练标签
y_train = np.eye(num_classes)[y_train] # 将标签转换为 one-hot 编码
model.fit(x_train, y_train, epochs=10, batch_size=8)
```
在这个示例中,我们首先定义了输入数据的形状和类别数,然后构建了一个包含自注意力层的模型。这个模型首先对输入进行 Masking,然后使用双向 LSTM 进行编码,接着使用自注意力层进行加权求和,最后通过全连接层进行分类。我们使用了随机生成的数据进行训练。需要注意的是,在实际使用中,我们还需要根据具体的任务场景和数据情况进行模型的调参和优化。
阅读全文