Skip to content

Commit 91eb3f7

Browse files
yuanliu939changlan
authored andcommitted
[Audio] Allow either transformer or conformer in SpeechContextNetwork
GitOrigin-RevId: f667695
1 parent 32d781b commit 91eb3f7

2 files changed

Lines changed: 99 additions & 3 deletions

File tree

axlearn/audio/encoder_asr.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from axlearn.audio.frontend import LogMelFrontend
1717
from axlearn.audio.spectrum_augmenter import SpectrumAugmenter
1818
from axlearn.audio.subsamplers import ConvSubSampler
19-
from axlearn.common.attention import SinusoidalPositionalEmbedding
19+
from axlearn.common.attention import RepeatedTransformerLayer, SinusoidalPositionalEmbedding
2020
from axlearn.common.base_layer import BaseLayer
2121
from axlearn.common.config import REQUIRED, Required, config_class
2222
from axlearn.common.conformer import RepeatedConformerLayer
@@ -165,11 +165,23 @@ def forward(self, inputs: Tensor, *, paddings: Tensor) -> dict[str, Tensor]:
165165
- outputs: A Tensor of shape [batch_size, seq_len, output_dim].
166166
- output_paddings: A 0/1 Tensor of shape [batch_size, seq_len].
167167
"""
168+
cfg = self.config
168169
# [batch, seq_len, input_dim].
169170
x = self.input_linear(inputs)
170171
x = self.dropout(x)
171-
x = x + self.pos_emb(jnp.arange(x.shape[1]))
172-
x = self.context(inputs=x, paddings=paddings)
172+
173+
if isinstance(cfg.context, RepeatedConformerLayer.Config):
174+
x = x + self.pos_emb(jnp.arange(x.shape[1]))
175+
x = self.context(inputs=x, paddings=paddings)
176+
elif isinstance(cfg.context, RepeatedTransformerLayer.Config):
177+
# We don't need to do add pos_emb for transformer block
178+
x = self.context(data=x)
179+
x = x.data
180+
else:
181+
raise ValueError(
182+
f"The type of `self.context` ({cfg.context.klass}) "
183+
"is not supported by SpeechContextNetwork."
184+
)
173185
self._add_activation_summary(
174186
name="speech_context",
175187
activations=x,

axlearn/audio/encoder_asr_test.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from axlearn.audio import frontend_utils
1111
from axlearn.audio.encoder_asr import ASREncoder, SpeechContextNetwork, SpeechFeatureLayer
1212
from axlearn.audio.test_utils import fake_audio
13+
from axlearn.common.attention import RepeatedTransformerLayer
14+
from axlearn.common.kv_cache.sliding_window_kv_cache import enable_sliding_window_attention
1315
from axlearn.common.module import functional as F
1416
from axlearn.common.test_utils import TestCase
1517
from axlearn.common.utils import Tensor, shapes
@@ -173,6 +175,88 @@ def test_speech_context_network(self, is_training: bool):
173175
output_collections.summaries["activations/speech_context_norm"].weight, weights
174176
)
175177

178+
@parameterized.parameters([True, False])
179+
def test_transformer(self, is_training: bool) -> None:
180+
"""Test the code branch with RepeatedTransformerLayer as context layer.
181+
182+
Args:
183+
is_training: Whether the is_training code path is tested.
184+
"""
185+
input_dim, output_dim, dropout_rate, num_layers = 32, 16, 0.2, 2
186+
num_heads = 8
187+
hidden_dim = 4 * input_dim
188+
189+
cfg = SpeechContextNetwork.default_config().set(
190+
input_dim=input_dim, output_dim=output_dim, dtype=jnp.float64
191+
)
192+
cfg.dropout.rate = dropout_rate
193+
cfg.context = RepeatedTransformerLayer.default_config().set(num_layers=num_layers)
194+
attention = cfg.context.layer.self_attention.attention
195+
attention.num_heads = num_heads
196+
attention = enable_sliding_window_attention(attention, sliding_window_size=3)
197+
cfg.context.layer.self_attention.attention = attention
198+
# Dropout in transformer
199+
cfg.context.layer.self_attention.dropout.rate = dropout_rate
200+
cfg.context.layer.feed_forward.set(
201+
hidden_dim=hidden_dim,
202+
)
203+
204+
# Initialize layer parameters.
205+
prng_key = jax.random.PRNGKey(123)
206+
prng_key, init_key, input_key, length_key = jax.random.split(prng_key, num=4)
207+
layer = cfg.set(name="test").instantiate(parent=None)
208+
layer_params = layer.initialize_parameters_recursively(init_key)
209+
210+
# Generate inputs.
211+
batch_size, seq_len = 4, 10
212+
inputs = jnp.tile(
213+
jax.random.normal(input_key, [batch_size // 2, seq_len, input_dim]), [2, 1, 1]
214+
)
215+
lengths = jnp.tile(
216+
jax.random.randint(length_key, shape=[batch_size // 2, 1], minval=0, maxval=seq_len),
217+
[2, 1],
218+
)
219+
paddings = jnp.arange(seq_len)[None, :] >= lengths
220+
padding_data = jax.random.normal(jax.random.PRNGKey(135), inputs.shape)
221+
inputs = jnp.where(paddings[..., None], padding_data, inputs)
222+
223+
# Compute outputs.
224+
output_batch, output_collections = F(
225+
layer,
226+
inputs=dict(inputs=inputs, paddings=paddings),
227+
is_training=is_training,
228+
prng_key=prng_key,
229+
state=layer_params,
230+
)
231+
outputs, output_paddings = output_batch["outputs"], output_batch["paddings"]
232+
self.assertSequenceEqual(outputs.shape, (batch_size, seq_len, output_dim))
233+
self.assertTrue(jnp.all(output_paddings == paddings))
234+
235+
# If is_training, outputs should always be different due to augmentation.
236+
# Otherwise, outputs should be the same despite differences in padding.
237+
self.assertEqual(not is_training, bool(jnp.allclose(outputs[:2], outputs[2:])))
238+
239+
outputs = outputs * (1 - output_paddings[:, :, None])
240+
weights = jnp.sum(1 - output_paddings)
241+
output_norms = jnp.sqrt(jnp.sum(outputs**2, axis=2)) / jnp.sqrt(output_dim)
242+
expected_outputs_mean = jnp.sum(outputs) / weights / output_dim
243+
expected_outputs_norm = jnp.sum(output_norms) / weights
244+
245+
self.assertNestedAllClose(
246+
output_collections.summaries["activations/speech_context_mean"].mean,
247+
expected_outputs_mean,
248+
)
249+
self.assertNestedAllClose(
250+
output_collections.summaries["activations/speech_context_norm"].mean,
251+
expected_outputs_norm,
252+
)
253+
self.assertNestedAllClose(
254+
output_collections.summaries["activations/speech_context_mean"].weight, weights
255+
)
256+
self.assertNestedAllClose(
257+
output_collections.summaries["activations/speech_context_norm"].weight, weights
258+
)
259+
176260

177261
class ASREncoderTest(TestCase):
178262
"""Tests ASREncoder."""

0 commit comments

Comments
 (0)