Last active
August 29, 2022 08:44
-
-
Save mutiann/38a7638f75c21479582d7391490df37c to your computer and use it in GitHub Desktop.
Stepwise Monotonic Attention
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
Implementation for https://arxiv.org/abs/1906.00672 | |
Tips: The code could be directly used in place of BadahnauMonotonicAttention in Tensorflow codes. Similar to its | |
base class in the Tensorflow seq2seq codebase, you may use "hard" for hard inference, or "parallel" for training or | |
soft inference. "recurrent" mode in BadahnauMonotonicAttention is not supported. | |
If you have already trained another model using BadahnauMonotonicAttention, the model could be reused, otherwise you | |
possibly have to tune the score_bias_init, which, similar to that in Raffel et al., 2017, is determined a priori to | |
suit the moving speed of the alignments, i.e. speed of speech of your training corpus in TTS cases. So | |
score_bias_init=3.5, is a good one for our data, but not necessarily for yours, and our experiments find that the | |
results are sensitive to this bias: When the parameter is deviated from the best value, by, say, a small amount of | |
0.5, the whole training process may fail. sigmoid_noise=2.0 is enough in our experiments, but if you found that the | |
resultant alignments are far from binary, adding more noise (or annealing the noise) might be useful. Other | |
hyperparameters in our experiments simply follow the original Tacotron2 settings, and they work. | |
''' | |
def monotonic_stepwise_attention(p_choose_i, previous_attention, mode): | |
# p_choose_i, previous_alignments, previous_score: [batch_size, memory_size] | |
# p_choose_i: probability to keep attended to the last attended entry i | |
if mode == "parallel": | |
pad = tf.zeros([tf.shape(p_choose_i)[0], 1], dtype=p_choose_i.dtype) | |
attention = previous_attention * p_choose_i + tf.concat( | |
[pad, previous_attention[:, :-1] * (1.0 - p_choose_i[:, :-1])], axis=1) | |
elif mode == "hard": | |
# Given that previous_alignments is one_hot | |
move_next_mask = tf.concat([tf.zeros_like(previous_attention[:, :1]), previous_attention[:, :-1]], axis=1) | |
stay_prob = tf.reduce_sum(p_choose_i * previous_attention, axis=1) # [B] | |
attention = tf.where(stay_prob > 0.5, previous_attention, move_next_mask) | |
else: | |
raise ValueError("mode must be 'parallel', or 'hard'.") | |
return attention | |
def _stepwise_monotonic_probability_fn(score, previous_alignments, sigmoid_noise, mode, seed=None): | |
if sigmoid_noise > 0: | |
noise = random_ops.random_normal(array_ops.shape(score), dtype=score.dtype, | |
seed=seed) | |
score += sigmoid_noise * noise | |
if mode == "hard": | |
# When mode is hard, use a hard sigmoid | |
p_choose_i = math_ops.cast(score > 0, score.dtype) | |
else: | |
p_choose_i = math_ops.sigmoid(score) | |
alignments = monotonic_stepwise_attention(p_choose_i, previous_alignments, mode) | |
return alignments | |
class BahdanauStepwiseMonotonicAttention(BahdanauMonotonicAttention): | |
def __init__(self, | |
num_units, | |
memory, | |
memory_sequence_length=None, | |
normalize=True, | |
score_mask_value=None, | |
sigmoid_noise=2.0, | |
sigmoid_noise_seed=None, | |
score_bias_init=3.5, | |
mode="parallel", | |
dtype=None, | |
name="BahdanauStepwiseMonotonicAttention"): | |
if dtype is None: | |
dtype = tf.float32 | |
wrapped_probability_fn = functools.partial( | |
_stepwise_monotonic_probability_fn, sigmoid_noise=sigmoid_noise, mode=mode, | |
seed=sigmoid_noise_seed) | |
super(BahdanauMonotonicAttention, self).__init__( | |
query_layer=tf.layers.Dense( | |
num_units, name="query_layer", use_bias=False, dtype=dtype), | |
memory_layer=tf.layers.Dense( | |
num_units, name="memory_layer", use_bias=False, dtype=dtype), | |
memory=memory, | |
probability_fn=wrapped_probability_fn, | |
memory_sequence_length=memory_sequence_length, | |
score_mask_value=score_mask_value, | |
name=name) | |
self._num_units = num_units | |
self._normalize = normalize | |
self._name = name | |
self._score_bias_init = score_bias_init |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment