2021SC@SDUSC
类名
class TransformerAttentionLayer(base_layer.BaseLayer)
方法
def _FinishExtendStep(self, theta, query_vec, unnormalized_query_vec, extended_packed_src, t=None)
参数列表:
- theta:
.NestedMap
对象,该对象包含此层及其子层的权重值。 - query_vec: [target_batch, dim]
- unnormalized_query_vec: [target_batch, dim]
- extended_packed_src: A
.NestedMap
包含source_vecs
、source_contexts
、source_padding
和source_segment_ids
的对象 - t: 一个标量,当前时间步长,以0为基础。
函数作用
通过多余一个时间步完成前缀扩展。将这个函数从ExtendStep中分离出来,可以将自我注意推广到对其他输入的因果注意。
源码
def _FinishExtendStep(self,
theta,
query_vec,
unnormalized_query_vec,
extended_packed_src,
t=None):
p = self.params
计算per_step_source_padding
。填充已补全,要屏蔽的时间索引接收填充权重为1.0。
query_batch_size = py_utils.GetShape(query_vec)[0]
source_seq_len = py_utils.GetShape(extended_packed_src.source_vecs)[0]
zero_padding = tf.fill([source_seq_len],
tf.constant(0.0, dtype=query_vec.dtype))
ones_padding = tf.ones_like(zero_padding, dtype=query_vec.dtype)
if t is not None:
per_step_source_padding = tf.where(
tf.less(tf.range(source_seq_len), tf.fill([source_seq_len], t + 1)),
zero_padding, ones_padding)
per_step_source_padding = tf.tile(
tf.expand_dims(per_step_source_padding, axis=0),
[query_batch_size, 1])
可以用 N-gram进行maks屏蔽
预期加速应该伴随 ‘per_step_source_padding =None
’ , 每次更新prefix_states, 不仅扩展了一个时间步长, 也只保留prefix_states
从开始到最近N步的前缀状态。
elif p.is_masked and p.mask_type == 'ngram':
assert p.mask_ngram_order
idx = tf.maximum(0, source_seq_len - p.mask_ngram_order)
per_step_source_padding = tf.where(
tf.less(tf.range(source_seq_len), tf.fill([source_seq_len], idx)),
ones_padding, zero_padding)
per_step_source_padding = tf.tile(
tf.expand_dims(per_step_source_padding, axis=0),
[query_batch_size, 1])
else:
per_step_source_padding = None
ctx_vec, atten_prob, _ = self.atten.ComputeContextVectorWithCachedSource(
theta.atten,
extended_packed_src,
query_vec,
per_step_source_padding=per_step_source_padding)
ctx_vec = self.residual_dropout.FProp(theta.residual_dropout, ctx_vec)
input_to_add = (
unnormalized_query_vec if p.add_unnormalized_input else query_vec)
input_after_sublayer = tf.reshape(ctx_vec, py_utils.GetShape(query_vec))
if p.residual_function is None:
h = input_to_add + input_after_sublayer
else:
h = self.residual_function.FProp(theta.residual_function, input_to_add,
input_after_sublayer)
if not p.pre_layer_norm:
h = self.layer_norm.FProp(theta.layer_norm, h)
new_states = py_utils.NestedMap(
key=extended_packed_src.source_vecs,
value=extended_packed_src.source_contexts)
return h, atten_prob, new_states
def ExtendStep(self, theta, query_vec, prefix_state, t=None)
参数列表:
- theta:
.NestedMap
对象,该对象包含此层及其子层的权重值。 - query_vec: [target_batch, dim]
- prefix_state: dict, 包含先前注意的结果的张量,用于快速解码。
- t: 一个标量,当前时间步长,以0为基础。
def ExtendStep(self, theta, query_vec, prefix_state, t=None):
p = self.params
!! 必须是随机注意 !!
assert p.is_masked
unnormalized_query_vec = query_vec
if p.pre_layer_norm:
query_vec = self.layer_norm.FProp(theta.layer_norm, query_vec)
cached_packed_src = py_utils.NestedMap(
source_vecs=prefix_state.key,
source_contexts=prefix_state.value,
source_padding=None,
source_segment_id=None)
extended_packed_src = self.atten.ExtendSourcePacked(theta.atten, query_vec,
query_vec, None, None,
cached_packed_src, t)
return self._FinishExtendStep(theta, query_vec, unnormalized_query_vec,
extended_packed_src, t)
类
class TransformerMultiSourceAttentionLayer(TransformerAttentionLayer)
Multi-source multi-headed attention 多源多头注意力机制
由编码器和解码器组成,在编码器的一个网络块中,由一个多头attention子层和一个前馈神经网络子层组成,整个编码器栈式搭建了N个块。类似于编码器,只是解码器的一个网络块中多了一个多头attention层。为了更好的优化深度网络,整个网络使用了残差连接和对层进行了规范化(Add&Norm)。
Query,Key,Value首先进过一个线性变换,然后输入到放缩点积attention,注意这里要做h次,其实也就是所谓的多头,每一次算一个头。而且每次Q,K,V进行线性变换的参数W是不一样的。然后将h次的放缩点积attention结果进行拼接,再进行一次线性变换得到的值作为多头attention的结果。可以看到,google提出来的多头attention的不同之处在于进行了h次计算而不仅仅算一次,论文中说到这样的好处是可以允许模型在不同的表示子空间里学习到相关的信息,后面还会根据attention可视化来验证。
首先在编码器到解码器的地方使用了多头attention进行连接,K,V,Q分别是编码器的层输出(这里K=V)和解码器中多头attention的输入。其实就和主流的机器翻译模型中的attention一样,利用解码器和编码器attention来进行翻译对齐。然后在编码器和解码器中都使用了多头自注意力self-attention来学习文本的表示。Self-attention即K=V=Q,例如输入一个句子,那么里面的每个词都要和该句子中的所有词进行attention计算。目的是学习句子内部的词依赖关系,捕获句子的内部结构。
参考论文: attention is all you need
类**class TransformerFeedForwardLayer(base_layer.BaseLayer)**
前馈层
该类实现了转换器层的第二个子层。首先,输入通过带有一个隐藏层的前馈神经网络,然后投影回原输入维数应用残差。
输出层,然后进行规范化。
源码:
def __init__(self, params):
super().__init__(params)
p = self.params
assert p.name
assert p.input_dim
assert symbolic.ToStatic(p.hidden_dim) > 0
初始化前馈层
params = p.fflayer_tpl.Copy()
params.name = 'fflayer'
params.input_dim = p.input_dim
params.activation = [p.activation, 'NONE']
if p.output_dim == 0:
params.hidden_layer_dims = [p.hidden_dim, p.input_dim]
else:
params.hidden_layer_dims = [p.hidden_dim, p.output_dim]
if p.output_dim != p.input_dim:
pj = p.res_proj_tpl.Copy()
pj.name = 'res_proj'
pj.input_dim = p.input_dim
pj.output_dim = p.output_dim
pj.activation = 'NONE'
self.CreateChild('res_proj_layer', pj)
params.dropout = [
params.dropout.cls.Params().Set(keep_prob=1.0 - p.relu_dropout_prob),
params.dropout.cls.Params().Set(keep_prob=1.0)
]
self.CreateChild('fflayer', params)
初始化前馈层准则函数
params = p.ln_tpl.Copy()
params.name = 'fflayer_ln'
params.input_dim = p.input_dim
self.CreateChild('layer_norm', params)
dropout_tpl = p.residual_dropout_tpl.Copy()
dropout_tpl.keep_prob = (1.0 - p.residual_dropout_prob)
self.CreateChild('residual_dropout', dropout_tpl)
if p.residual_droppath_prob > 0:
assert p.add_skip_connection
droppath_p = StochasticResidualLayer.Params().Set(
name='residual_droppath',
survival_prob=1.0 - p.residual_droppath_prob)
self.CreateChild('residual_droppath', droppath_p)
在前馈神经网络中,各神经元分别属于不同的层。每一层的神经元可以接收前一层神经元的信号,并产生信号输出到下一层。第 0 层叫输入层,最后一层叫输出层,其它中间层叫做隐藏层,相邻两层的神经元之间为全连接关系,也称为全连接神经网络(F N N)。