问题描述
尝试使用 AdditiveAttention
中的 Keras
层。关于 tensorflow 教程中层的手动实现 https://www.tensorflow.org/tutorials/text/nmt_with_attention
import tensorflow as tf
class BahdanauAttention(tf.keras.layers.Layer):
def __init__(self,units):
super(BahdanauAttention,self).__init__()
self.W1 = tf.keras.layers.Dense(units)
self.W2 = tf.keras.layers.Dense(units)
self.V = tf.keras.layers.Dense(1)
def call(self,query,values):
query_with_time_axis = tf.expand_dims(query,1)
score = self.V(tf.nn.tanh(
self.W1(query_with_time_axis) + self.W2(values)))
attention_weights = tf.nn.softmax(score,axis=1)
# context_vector shape after sum == (batch_size,hidden_size)
context_vector = attention_weights * values
context_vector = tf.reduce_sum(context_vector,axis=1)
return context_vector,attention_weights
context_vector
的形状是 (batch_size,units)
而使用与 AdditiveAttention
相同的 keras built-in
层
from tensorflow.keras.layers import AdditiveAttention
shape
的 context_vector
= [batch_size,Tq,dim]
任何有关造成这种 OP shape
差异的原因的建议都会有所帮助。
解决方法
除了一些变化外,两种实现方式都非常相似。该教程中 BahdanauAttention
的实现是一种简化和改编的版本,并使用了一些线性变换。您想知道的 context_vector
的返回形状只不过是输入数据形状的问题。下面是一些演示,让我们看看教程实现:
class BahdanauAttention(tf.keras.layers.Layer):
def __init__(self,units):
super(BahdanauAttention,self).__init__()
self.W1 = tf.keras.layers.Dense(units)
self.W2 = tf.keras.layers.Dense(units)
self.V = tf.keras.layers.Dense(1)
def call(self,query,values):
query_with_time_axis = tf.expand_dims(query,1)
score = self.V(tf.nn.tanh(self.W1(query_with_time_axis) + self.W2(values)))
attention_weights = tf.nn.softmax(score,axis=1)
context_vector = attention_weights * values
context_vector = tf.reduce_sum(context_vector,axis=1)
return context_vector,attention_weights
现在,我们向它传递一些输入,3D
和 2D
。
attention_layer = BahdanauAttention(10)
y = tf.random.uniform((2,60,512))
out,attn = attention_layer(y,y)
out.shape,attn.shape
(TensorShape([2,512]),TensorShape([2,2,1]))
y = tf.random.uniform((2,1]))
现在,将相同的输入传递给内置的 AdditiveAttention
,看看我们会得到什么
buit_attn = tf.keras.layers.AdditiveAttention()
y = tf.random.uniform((2,attn = buit_attn([y,y],return_attention_scores=True)
out.shape,60]))
y = tf.random.uniform((2,2]))
因此,context_vector
的形状在此处具有可比性,但与 attention_weights
的形状不同。原因是,正如我们所提到的,我相信该教程的实现有点修改和采用。如果我们查看 BahdanauAttention
或 AdditiveAttention
的计算,我们会得到:
- 将
query
和value
分别重塑为形状[batch_size,Tq,1,dim]
和[batch_size,Tv,dim]
。 - 将形状为
[batch_size,Tv]
的分数计算为非线性和:scores = tf.reduce_sum(tf.tanh(query + value),axis=-1)
- 使用分数计算形状为
[batch_size,Tv]: distribution = tf.nn.softmax(scores)
的分布。 - 使用分布创建形状为
batch_size,dim]: return tf.matmul(distribution,value)
的值的线性组合。
而且我认为该教程中的实现与计算注意力权重特征有点不同。如果我们遵循上述方法(1 到 4),我们也将获得与 attention_weights
相同的输出形状。这是方法,(但不是这里只是一个演示目的,不是一般性的。)
class BahdanauAttention(tf.keras.layers.Layer):
def __init__(self,self).__init__()
self.W1 = tf.keras.layers.Dense(units)
self.W2 = tf.keras.layers.Dense(units)
self.V = tf.keras.layers.Dense(1)
def call(self,2) # [batch_size,dim]
value_with_time_axis = tf.expand_dims(values,1) # [batch_size,dim]
scores = tf.reduce_sum(tf.tanh(query_with_time_axis +
value_with_time_axis),axis=-1)
distribution = tf.nn.softmax(scores)
return tf.matmul(distribution,values),distribution
现在,如果我们传递相同的输入,我们将从两个实现中获得相同的输出形状。但是,一般来说,用例应该选择内置实现。
attention_layer = BahdanauAttention(10)
y = tf.random.uniform((2,60]))
buit_attn = tf.keras.layers.AdditiveAttention()
y = tf.random.uniform((2,60]))