andreamena commited on
Commit
4b144a6
·
verified ·
1 Parent(s): 6907fd3

Upload layers.py

Browse files
Files changed (1) hide show
  1. layers.py +541 -0
layers.py ADDED
@@ -0,0 +1,541 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created on Fri Sep 27 13:11:00 2019
5
+
6
+ Script for Multi-Head Scaled Dot-Product Attention and Context Attention layers
7
+
8
+ @author: Chenkai Li
9
+ """
10
+
11
+ from tf_keras import backend as K
12
+ from tf_keras import initializers, regularizers, constraints, activations
13
+ from tf_keras.layers import Layer
14
+
15
+
16
+ class Attention(Layer):
17
+ """
18
+ Adapted from https://github.com/lzfelix/keras_attention
19
+
20
+ Implementation based on the work of Yang et al. "Hierarchical
21
+ Attention Networks for Document Classification".
22
+ [https://www.aclweb.org/anthology/N16-1174]
23
+
24
+ The mathematical formulation of the model is as follows:
25
+ ```
26
+ u = f(W * h + b),
27
+ a_i = softmax(u_i^T * u_s),
28
+ v_i = \sigma_i a_i * h_i.
29
+ ```
30
+
31
+ Where h are the input tensors with shape (batch, n_timesteps, hidden_size), for
32
+ instance, all hidden vectors produced by a recurrent layer, such as a LSTM and the
33
+ output has shape `(batch, hidden_size)`. This layer also works with inputs with more
34
+ than 3 dimensions as well, such as sentences in a document, where each input has
35
+ size (batch, n_docs, n_sentences, embedding_size), outputing
36
+ (batch, n_docs, embedding_size)`.
37
+
38
+
39
+ # Arguments
40
+ activation: The activation function f used by the layer (see
41
+ [activations](../activations.md)). By default tanh is used, another common
42
+ option is "linear".
43
+ use_bias: Boolean, whether the layer uses a bias vector.
44
+ initializer: Initializer for the `kernel` and `context` matrices
45
+ (see [initializers](../initializers.md)).
46
+ return_attention: If True, instead of returning the sequence descriptor, this
47
+ layer will return the computed attention coefficients for each of the
48
+ sequence timesteps. See Output section for details.
49
+ W_regularizer: Regularizer function applied to the `kernel` weights matrix
50
+ (see [regularizer](../regularizers.md)).
51
+ u_regularizer: Regularizer function applied to the `context` weights matrix
52
+ (see [regularizer](../regularizers.md)).
53
+ b_regularizer: Regularizer function applied to the bias vector
54
+ (see [regularizer](../regularizers.md)).
55
+ W_constraint: Constraint function applied to the `kernel` weights matrix
56
+ (see [constraints](../constraints.md)).
57
+ u_constraint: Constraint function applied to the `contextl` weights matrix
58
+ (see [constraints](../constraints.md)).
59
+ b_constraint: Constraint function applied to the bias vector
60
+ (see [constraints](../constraints.md)).
61
+
62
+ # Input shape
63
+ nD tensor with shape: `(batch_size, ..., timesteps, input_dim)`.
64
+ The most common situation would be a 3D input with shape
65
+ `(batch_size, timesteps, input_dim)`.
66
+
67
+ # Outuput shape
68
+ The sequence descriptor with shape `(batch_size, ..., timestamps)`. If
69
+ `return_attention` is True, this layer will return the `alpha_i` weights
70
+ for each timestep, and consequently its output shape will be different, namely:
71
+ `(batch_size, ..., timesteps)`.
72
+ """
73
+
74
+ def __init__(self,
75
+ activation='tanh',
76
+ initializer='glorot_uniform',
77
+ return_attention=False,
78
+ W_regularizer=None,
79
+ u_regularizer=None,
80
+ b_regularizer=None,
81
+ W_constraint=None,
82
+ u_constraint=None,
83
+ b_constraint=None,
84
+ bias=True,
85
+ **kwargs):
86
+
87
+ self.activation = activations.get(activation)
88
+ self.initializer = initializers.get(initializer)
89
+
90
+ self.W_regularizer = regularizers.get(W_regularizer)
91
+ self.u_regularizer = regularizers.get(u_regularizer)
92
+ self.b_regularizer = regularizers.get(b_regularizer)
93
+
94
+ self.W_constraint = constraints.get(W_constraint)
95
+ self.u_constraint = constraints.get(u_constraint)
96
+ self.b_constraint = constraints.get(b_constraint)
97
+
98
+ self.bias = bias
99
+ self.supports_masking = True
100
+ self.return_attention = return_attention
101
+
102
+ super().__init__(**kwargs)
103
+
104
+ def build(self, input_shape):
105
+ # input_shape: (batch, time, amount_features)
106
+
107
+ # the attention size matches the feature dimension
108
+ amount_features = input_shape[-1]
109
+ attention_size = input_shape[-1]
110
+
111
+ self.W = self.add_weight(shape=(amount_features, attention_size),
112
+ initializer=self.initializer,
113
+ regularizer=self.W_regularizer,
114
+ constraint=self.W_constraint,
115
+ name='attention_W')
116
+ self.b = None
117
+ if self.bias:
118
+ self.b = self.add_weight(shape=(attention_size,),
119
+ initializer='zero',
120
+ regularizer=self.b_regularizer,
121
+ constraint=self.b_constraint,
122
+ name='attention_b')
123
+
124
+ self.context = self.add_weight(shape=(attention_size,),
125
+ initializer=self.initializer,
126
+ regularizer=self.u_regularizer,
127
+ constraint=self.u_constraint,
128
+ name='attention_us')
129
+
130
+ super().build(input_shape)
131
+
132
+ def call(self, x, mask=None):
133
+ # U = tanh(H*W + b) (eq. 8)
134
+ ui = K.dot(x, self.W) # (b, t, a)
135
+ if self.b is not None:
136
+ ui += self.b
137
+ ui = self.activation(ui) # (b, t, a)
138
+
139
+ # Z = U * us (eq. 9)
140
+ us = K.expand_dims(self.context) # (a, 1)
141
+ ui_us = K.dot(ui, us) # (b, t, a) * (a, 1) = (b, t, 1)
142
+ ui_us = K.squeeze(ui_us, axis=-1) # (b, t, 1) -> (b, t)
143
+
144
+ # alpha = softmax(Z) (eq. 9)
145
+ alpha = self._masked_softmax(ui_us, mask) # (b, t)
146
+ alpha = K.expand_dims(alpha, axis=-1) # (b, t, 1)
147
+
148
+ if self.return_attention:
149
+ return alpha
150
+ else:
151
+ # v = alpha_i * x_i (eq. 10)
152
+ return K.sum(x * alpha, axis=1)
153
+
154
+ def _masked_softmax(self, logits, mask):
155
+ """Keras's default implementation of softmax doesn't allow masking, while
156
+ this method does if `mask` is not `None`."""
157
+
158
+ # softmax(x):
159
+ # b = max(x)
160
+ # s_i = exp(xi - b) / exp(xj - b)
161
+ # return s
162
+
163
+ b = K.max(logits, axis=-1, keepdims=True)
164
+ logits = logits - b
165
+
166
+ exped = K.exp(logits)
167
+
168
+ # ignoring masked inputs
169
+ if mask is not None:
170
+ mask = K.cast(mask, K.floatx())
171
+ exped *= mask
172
+
173
+ partition = K.sum(exped, axis=-1, keepdims=True)
174
+
175
+ # if all timesteps are masked, the partition will be zero. To avoid this
176
+ # issue we use the following trick:
177
+ partition = K.maximum(partition, K.epsilon())
178
+
179
+ return exped / partition
180
+
181
+ def compute_output_shape(self, input_shape):
182
+ """The attention mechanism computes a weighted average between
183
+ all hidden vectors generated by the previous sequential layer,
184
+ hence the input is expected to be
185
+ `(batch_size, seq_len, amount_features)` if `return_attention` is
186
+ `False`, otherwise the output should be (batch_size, seq_len)."""
187
+ if self.return_attention:
188
+ return input_shape[:-1]
189
+ else:
190
+ return input_shape[:-2] + input_shape[-1:]
191
+
192
+ def compute_mask(self, x, input_mask=None):
193
+ """This layer produces a single attended vector from a list
194
+ of hidden vectors, hence it can't be masked as this means
195
+ masking a single vector."""
196
+ return None
197
+
198
+ def get_config(self):
199
+ config = {
200
+ 'activation': self.activation,
201
+ 'initializer': self.initializer,
202
+ 'return_attention': self.return_attention,
203
+
204
+ 'W_regularizer': initializers.serialize(self.W_regularizer),
205
+ 'u_regularizer': initializers.serialize(self.u_regularizer),
206
+ 'b_regularizer': initializers.serialize(self.b_regularizer),
207
+
208
+ 'W_constraint': constraints.serialize(self.W_constraint),
209
+ 'u_constraint': constraints.serialize(self.u_constraint),
210
+ 'b_constraint': constraints.serialize(self.b_constraint),
211
+
212
+ 'bias': self.bias
213
+ }
214
+
215
+ base_config = super().get_config()
216
+ return dict(list(base_config.items()) + list(config.items()))
217
+
218
+
219
+ class ScaledDotProductAttention(Layer):
220
+ """
221
+ Adapted from https://github.com/CyberZHG/keras-self-attention
222
+
223
+ Implementation based on the work of Vaswani et al. "Attention Is All You
224
+ Need".
225
+ [https://arxiv.org/pdf/1706.03762.pdf]
226
+
227
+ The attention layer that takes three inputs representing queries, keys and values.
228
+ \text{Attention}(Q, K, V) = \text{softmax}(\frac{Q K^T}{\sqrt{d_k}}) V
229
+ """
230
+
231
+ def __init__(self,
232
+ return_attention=False,
233
+ history_only=False,
234
+ **kwargs):
235
+ """Initialize the layer.
236
+ :param return_attention: Whether to return attention weights.
237
+ :param history_only: Whether to only use history data.
238
+ :param kwargs: Arguments for parent class.
239
+ """
240
+ super(ScaledDotProductAttention, self).__init__(**kwargs)
241
+ self.supports_masking = True
242
+ self.return_attention = return_attention
243
+ self.history_only = history_only
244
+
245
+ def get_config(self):
246
+ config = {
247
+ 'return_attention': self.return_attention,
248
+ 'history_only': self.history_only,
249
+ }
250
+ base_config = super(ScaledDotProductAttention, self).get_config()
251
+ return dict(list(base_config.items()) + list(config.items()))
252
+
253
+ def compute_output_shape(self, input_shape):
254
+ if isinstance(input_shape, list):
255
+ query_shape, key_shape, value_shape = input_shape
256
+ else:
257
+ query_shape = key_shape = value_shape = input_shape
258
+ output_shape = query_shape[:-1] + value_shape[-1:]
259
+ if self.return_attention:
260
+ attention_shape = query_shape[:2] + (key_shape[1],)
261
+ return [output_shape, attention_shape]
262
+ return output_shape
263
+
264
+ def compute_mask(self, inputs, mask=None):
265
+ if isinstance(mask, list):
266
+ mask = mask[0]
267
+ if self.return_attention:
268
+ return [mask, None]
269
+ return mask
270
+
271
+ def call(self, inputs, mask=None, **kwargs):
272
+ if isinstance(inputs, list):
273
+ query, key, value = inputs
274
+ else:
275
+ query = key = value = inputs
276
+ if isinstance(mask, list):
277
+ mask = mask[1]
278
+ feature_dim = K.shape(query)[-1]
279
+ e = K.batch_dot(query, key, axes=2) / K.sqrt(K.cast(feature_dim, dtype=K.floatx()))
280
+ e = K.exp(e - K.max(e, axis=-1, keepdims=True))
281
+ if self.history_only:
282
+ query_len, key_len = K.shape(query)[1], K.shape(key)[1]
283
+ indices = K.expand_dims(K.arange(0, key_len), axis=0)
284
+ upper = K.expand_dims(K.arange(0, query_len), axis=-1)
285
+ e *= K.expand_dims(K.cast(indices <= upper, K.floatx()), axis=0)
286
+ if mask is not None:
287
+ e *= K.cast(K.expand_dims(mask, axis=-2), K.floatx())
288
+ a = e / (K.sum(e, axis=-1, keepdims=True) + K.epsilon())
289
+ v = K.batch_dot(a, value)
290
+ if self.return_attention:
291
+ return [v, a]
292
+ return v
293
+
294
+
295
+ class MultiHeadAttention(Layer):
296
+ """
297
+ Adapted from https://github.com/CyberZHG/keras-multi-head
298
+
299
+ Implementation based on the work of Vaswani et al. "Attention Is All You
300
+ Need".
301
+ [https://arxiv.org/pdf/1706.03762.pdf]
302
+
303
+ Multi-head scaled dot-product attention layer
304
+ """
305
+
306
+ def __init__(self,
307
+ head_num,
308
+ activation='relu',
309
+ use_bias=True,
310
+ kernel_initializer='glorot_normal',
311
+ bias_initializer='zeros',
312
+ kernel_regularizer=None,
313
+ bias_regularizer=None,
314
+ kernel_constraint=None,
315
+ bias_constraint=None,
316
+ history_only=False,
317
+ return_multi_attention=False,
318
+ **kwargs):
319
+ """Initialize the layer.
320
+
321
+ :param head_num: Number of heads.
322
+ :param activation: Activations for linear mappings.
323
+ :param use_bias: Whether to use bias term.
324
+ :param kernel_initializer: Initializer for linear mappings.
325
+ :param bias_initializer: Initializer for linear mappings.
326
+ :param kernel_regularizer: Regularizer for linear mappings.
327
+ :param bias_regularizer: Regularizer for linear mappings.
328
+ :param kernel_constraint: Constraints for linear mappings.
329
+ :param bias_constraint: Constraints for linear mappings.
330
+ :param history_only: Whether to only use history in attention layer.
331
+ :param return_multi_attention: Whether to return the attention matrix.
332
+ """
333
+ self.supports_masking = True
334
+ self.head_num = head_num
335
+ self.activation = activations.get(activation)
336
+ self.use_bias = use_bias
337
+ self.kernel_initializer = initializers.get(kernel_initializer)
338
+ self.bias_initializer = initializers.get(bias_initializer)
339
+ self.kernel_regularizer = regularizers.get(kernel_regularizer)
340
+ self.bias_regularizer = regularizers.get(bias_regularizer)
341
+ self.kernel_constraint = constraints.get(kernel_constraint)
342
+ self.bias_constraint = constraints.get(bias_constraint)
343
+ self.history_only = history_only
344
+ self.return_multi_attention = return_multi_attention
345
+
346
+ self.Wq, self.Wk, self.Wv, self.Wo = None, None, None, None
347
+ self.bq, self.bk, self.bv, self.bo = None, None, None, None
348
+ super(MultiHeadAttention, self).__init__(**kwargs)
349
+
350
+ def get_config(self):
351
+ config = {
352
+ 'head_num': self.head_num,
353
+ 'activation': activations.serialize(self.activation),
354
+ 'use_bias': self.use_bias,
355
+ 'kernel_initializer': initializers.serialize(self.kernel_initializer),
356
+ 'bias_initializer': initializers.serialize(self.bias_initializer),
357
+ 'kernel_regularizer': regularizers.serialize(self.kernel_regularizer),
358
+ 'bias_regularizer': regularizers.serialize(self.bias_regularizer),
359
+ 'kernel_constraint': constraints.serialize(self.kernel_constraint),
360
+ 'bias_constraint': constraints.serialize(self.bias_constraint),
361
+ 'history_only': self.history_only,
362
+ 'return_multi_attention': self.return_multi_attention
363
+ }
364
+ base_config = super(MultiHeadAttention, self).get_config()
365
+ return dict(list(base_config.items()) + list(config.items()))
366
+
367
+ def compute_output_shape(self, input_shape):
368
+ if self.return_multi_attention:
369
+ if isinstance(input_shape, list):
370
+ q, k, v = input_shape
371
+ return (q[0], self.head_num, q[1], k[1])
372
+ return (input_shape[0], self.head_num, input_shape[1], input_shape[1])
373
+ else:
374
+ if isinstance(input_shape, list):
375
+ q, k, v = input_shape
376
+ return q[:-1] + (v[-1],)
377
+ return input_shape
378
+
379
+ def compute_mask(self, inputs, input_mask=None):
380
+ if isinstance(input_mask, list):
381
+ return input_mask[0]
382
+ return input_mask
383
+
384
+ def build(self, input_shape):
385
+ if isinstance(input_shape, list):
386
+ q, k, v = input_shape
387
+ else:
388
+ q = k = v = input_shape
389
+ feature_dim = int(v[-1])
390
+ if feature_dim % self.head_num != 0:
391
+ raise IndexError('Invalid head number %d with the given input dim %d' % (self.head_num, feature_dim))
392
+ self.Wq = self.add_weight(
393
+ shape=(int(q[-1]), feature_dim),
394
+ initializer=self.kernel_initializer,
395
+ regularizer=self.kernel_regularizer,
396
+ constraint=self.kernel_constraint,
397
+ name='%s_Wq' % self.name,
398
+ )
399
+ if self.use_bias:
400
+ self.bq = self.add_weight(
401
+ shape=(feature_dim,),
402
+ initializer=self.bias_initializer,
403
+ regularizer=self.bias_regularizer,
404
+ constraint=self.bias_constraint,
405
+ name='%s_bq' % self.name,
406
+ )
407
+ self.Wk = self.add_weight(
408
+ shape=(int(k[-1]), feature_dim),
409
+ initializer=self.kernel_initializer,
410
+ regularizer=self.kernel_regularizer,
411
+ constraint=self.kernel_constraint,
412
+ name='%s_Wk' % self.name,
413
+ )
414
+ if self.use_bias:
415
+ self.bk = self.add_weight(
416
+ shape=(feature_dim,),
417
+ initializer=self.bias_initializer,
418
+ regularizer=self.bias_regularizer,
419
+ constraint=self.bias_constraint,
420
+ name='%s_bk' % self.name,
421
+ )
422
+ self.Wv = self.add_weight(
423
+ shape=(int(v[-1]), feature_dim),
424
+ initializer=self.kernel_initializer,
425
+ regularizer=self.kernel_regularizer,
426
+ constraint=self.kernel_constraint,
427
+ name='%s_Wv' % self.name,
428
+ )
429
+ if self.use_bias:
430
+ self.bv = self.add_weight(
431
+ shape=(feature_dim,),
432
+ initializer=self.bias_initializer,
433
+ regularizer=self.bias_regularizer,
434
+ constraint=self.bias_constraint,
435
+ name='%s_bv' % self.name,
436
+ )
437
+ self.Wo = self.add_weight(
438
+ shape=(feature_dim, feature_dim),
439
+ initializer=self.kernel_initializer,
440
+ regularizer=self.kernel_regularizer,
441
+ constraint=self.kernel_constraint,
442
+ name='%s_Wo' % self.name,
443
+ )
444
+ if self.use_bias:
445
+ self.bo = self.add_weight(
446
+ shape=(feature_dim,),
447
+ initializer=self.bias_initializer,
448
+ regularizer=self.bias_regularizer,
449
+ constraint=self.bias_constraint,
450
+ name='%s_bo' % self.name,
451
+ )
452
+ super(MultiHeadAttention, self).build(input_shape)
453
+
454
+ @staticmethod
455
+ def _reshape_to_batches(x, head_num):
456
+ input_shape = K.shape(x)
457
+ batch_size, seq_len, feature_dim = input_shape[0], input_shape[1], input_shape[2]
458
+ head_dim = feature_dim // head_num
459
+ x = K.reshape(x, (batch_size, seq_len, head_num, head_dim))
460
+ x = K.permute_dimensions(x, [0, 2, 1, 3])
461
+ return K.reshape(x, (batch_size * head_num, seq_len, head_dim))
462
+
463
+ @staticmethod
464
+ def _reshape_from_batches(x, head_num):
465
+ input_shape = K.shape(x)
466
+ batch_size, seq_len, feature_dim = input_shape[0], input_shape[1], input_shape[2]
467
+ x = K.reshape(x, (batch_size // head_num, head_num, seq_len, feature_dim))
468
+ x = K.permute_dimensions(x, [0, 2, 1, 3])
469
+ return K.reshape(x, (batch_size // head_num, seq_len, feature_dim * head_num))
470
+
471
+ @staticmethod
472
+ def _reshape_attention_from_batches(x, head_num):
473
+ input_shape = K.shape(x)
474
+ batch_size, seq_len = input_shape[0], input_shape[1]
475
+ return K.reshape(x, (batch_size // head_num, head_num, seq_len, seq_len))
476
+
477
+ @staticmethod
478
+ def _reshape_mask(mask, head_num):
479
+ if mask is None:
480
+ return mask
481
+ seq_len = K.shape(mask)[1]
482
+ mask = K.expand_dims(mask, axis=1)
483
+ mask = K.tile(mask, [1, head_num, 1])
484
+ return K.reshape(mask, (-1, seq_len))
485
+
486
+ def call(self, inputs, mask=None):
487
+ if isinstance(inputs, list):
488
+ q, k, v = inputs
489
+ else:
490
+ q = k = v = inputs
491
+ if isinstance(mask, list):
492
+ q_mask, k_mask, v_mask = mask
493
+ else:
494
+ q_mask = k_mask = v_mask = mask
495
+ q = K.dot(q, self.Wq)
496
+ k = K.dot(k, self.Wk)
497
+ v = K.dot(v, self.Wv)
498
+ if self.use_bias:
499
+ q += self.bq
500
+ k += self.bk
501
+ v += self.bv
502
+ if self.activation is not None:
503
+ q = self.activation(q)
504
+ k = self.activation(k)
505
+ v = self.activation(v)
506
+ y,a = ScaledDotProductAttention(
507
+ return_attention=True,
508
+ history_only=self.history_only,
509
+ name='%s-Attention' % self.name,
510
+ )(
511
+ inputs=[
512
+ self._reshape_to_batches(q, self.head_num),
513
+ self._reshape_to_batches(k, self.head_num),
514
+ self._reshape_to_batches(v, self.head_num),
515
+ ],
516
+ mask=[
517
+ self._reshape_mask(q_mask, self.head_num),
518
+ self._reshape_mask(k_mask, self.head_num),
519
+ self._reshape_mask(v_mask, self.head_num),
520
+ ],
521
+ )
522
+
523
+ y = self._reshape_from_batches(y, self.head_num)
524
+ a = self._reshape_attention_from_batches(a, self.head_num)
525
+ y = K.dot(y, self.Wo)
526
+ if self.use_bias:
527
+ y += self.bo
528
+ if self.activation is not None:
529
+ y = self.activation(y)
530
+ '''
531
+ if TF_KERAS:
532
+ # Add shape information to tensor when using `tf.keras`
533
+ input_shape = [K.int_shape(q), K.int_shape(k), K.int_shape(v)]
534
+ output_shape = self.compute_output_shape(input_shape)
535
+ if output_shape[1] is not None:
536
+ output_shape = (-1,) + output_shape[1:]
537
+ y = K.reshape(y, output_shape)
538
+ '''
539
+ if self.return_multi_attention:
540
+ return a
541
+ return y