Skip to content

Instantly share code, notes, and snippets.

@mutiann
Last active August 29, 2022 08:44
Show Gist options
  • Save mutiann/38a7638f75c21479582d7391490df37c to your computer and use it in GitHub Desktop.
Save mutiann/38a7638f75c21479582d7391490df37c to your computer and use it in GitHub Desktop.
Stepwise Monotonic Attention
'''
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