chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
import importlib
|
||||
import os
|
||||
|
||||
|
||||
for file in os.listdir(os.path.dirname(__file__)):
|
||||
if file.endswith(".py") and not file.startswith("_"):
|
||||
model_name = file[: file.find(".py")]
|
||||
importlib.import_module("examples.speech_recognition.models." + model_name)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import math
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq.models import (
|
||||
FairseqEncoder,
|
||||
FairseqEncoderModel,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.modules.fairseq_dropout import FairseqDropout
|
||||
|
||||
|
||||
default_conv_enc_config = """[
|
||||
(400, 13, 170, 0.2),
|
||||
(440, 14, 0, 0.214),
|
||||
(484, 15, 0, 0.22898),
|
||||
(532, 16, 0, 0.2450086),
|
||||
(584, 17, 0, 0.262159202),
|
||||
(642, 18, 0, 0.28051034614),
|
||||
(706, 19, 0, 0.30014607037),
|
||||
(776, 20, 0, 0.321156295296),
|
||||
(852, 21, 0, 0.343637235966),
|
||||
(936, 22, 0, 0.367691842484),
|
||||
(1028, 23, 0, 0.393430271458),
|
||||
(1130, 24, 0, 0.42097039046),
|
||||
(1242, 25, 0, 0.450438317792),
|
||||
(1366, 26, 0, 0.481969000038),
|
||||
(1502, 27, 0, 0.51570683004),
|
||||
(1652, 28, 0, 0.551806308143),
|
||||
(1816, 29, 0, 0.590432749713),
|
||||
]"""
|
||||
|
||||
|
||||
@register_model("asr_w2l_conv_glu_encoder")
|
||||
class W2lConvGluEncoderModel(FairseqEncoderModel):
|
||||
def __init__(self, encoder):
|
||||
super().__init__(encoder)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
parser.add_argument(
|
||||
"--input-feat-per-channel",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="encoder input dimension per input channel",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--in-channels",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="number of encoder input channels",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--conv-enc-config",
|
||||
type=str,
|
||||
metavar="EXPR",
|
||||
help="""
|
||||
an array of tuples each containing the configuration of one conv layer
|
||||
[(out_channels, kernel_size, padding, dropout), ...]
|
||||
""",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
conv_enc_config = getattr(args, "conv_enc_config", default_conv_enc_config)
|
||||
encoder = W2lConvGluEncoder(
|
||||
vocab_size=len(task.target_dictionary),
|
||||
input_feat_per_channel=args.input_feat_per_channel,
|
||||
in_channels=args.in_channels,
|
||||
conv_enc_config=eval(conv_enc_config),
|
||||
)
|
||||
return cls(encoder)
|
||||
|
||||
def get_normalized_probs(self, net_output, log_probs, sample=None):
|
||||
lprobs = super().get_normalized_probs(net_output, log_probs, sample)
|
||||
lprobs.batch_first = False
|
||||
return lprobs
|
||||
|
||||
|
||||
class W2lConvGluEncoder(FairseqEncoder):
|
||||
def __init__(
|
||||
self, vocab_size, input_feat_per_channel, in_channels, conv_enc_config
|
||||
):
|
||||
super().__init__(None)
|
||||
|
||||
self.input_dim = input_feat_per_channel
|
||||
if in_channels != 1:
|
||||
raise ValueError("only 1 input channel is currently supported")
|
||||
|
||||
self.conv_layers = nn.ModuleList()
|
||||
self.linear_layers = nn.ModuleList()
|
||||
self.dropouts = []
|
||||
cur_channels = input_feat_per_channel
|
||||
|
||||
for out_channels, kernel_size, padding, dropout in conv_enc_config:
|
||||
layer = nn.Conv1d(cur_channels, out_channels, kernel_size, padding=padding)
|
||||
layer.weight.data.mul_(math.sqrt(3)) # match wav2letter init
|
||||
self.conv_layers.append(nn.utils.weight_norm(layer))
|
||||
self.dropouts.append(
|
||||
FairseqDropout(dropout, module_name=self.__class__.__name__)
|
||||
)
|
||||
if out_channels % 2 != 0:
|
||||
raise ValueError("odd # of out_channels is incompatible with GLU")
|
||||
cur_channels = out_channels // 2 # halved by GLU
|
||||
|
||||
for out_channels in [2 * cur_channels, vocab_size]:
|
||||
layer = nn.Linear(cur_channels, out_channels)
|
||||
layer.weight.data.mul_(math.sqrt(3))
|
||||
self.linear_layers.append(nn.utils.weight_norm(layer))
|
||||
cur_channels = out_channels // 2
|
||||
|
||||
def forward(self, src_tokens, src_lengths, **kwargs):
|
||||
|
||||
"""
|
||||
src_tokens: padded tensor (B, T, C * feat)
|
||||
src_lengths: tensor of original lengths of input utterances (B,)
|
||||
"""
|
||||
B, T, _ = src_tokens.size()
|
||||
x = src_tokens.transpose(1, 2).contiguous() # (B, feat, T) assuming C == 1
|
||||
|
||||
for layer_idx in range(len(self.conv_layers)):
|
||||
x = self.conv_layers[layer_idx](x)
|
||||
x = F.glu(x, dim=1)
|
||||
x = self.dropouts[layer_idx](x)
|
||||
|
||||
x = x.transpose(1, 2).contiguous() # (B, T, 908)
|
||||
x = self.linear_layers[0](x)
|
||||
x = F.glu(x, dim=2)
|
||||
x = self.dropouts[-1](x)
|
||||
x = self.linear_layers[1](x)
|
||||
|
||||
assert x.size(0) == B
|
||||
assert x.size(1) == T
|
||||
|
||||
encoder_out = x.transpose(0, 1) # (T, B, vocab_size)
|
||||
|
||||
# need to debug this -- find a simpler/elegant way in pytorch APIs
|
||||
encoder_padding_mask = (
|
||||
torch.arange(T).view(1, T).expand(B, -1).to(x.device)
|
||||
>= src_lengths.view(B, 1).expand(-1, T)
|
||||
).t() # (B x T) -> (T x B)
|
||||
|
||||
return {
|
||||
"encoder_out": encoder_out, # (T, B, vocab_size)
|
||||
"encoder_padding_mask": encoder_padding_mask, # (T, B)
|
||||
}
|
||||
|
||||
def reorder_encoder_out(self, encoder_out, new_order):
|
||||
encoder_out["encoder_out"] = encoder_out["encoder_out"].index_select(
|
||||
1, new_order
|
||||
)
|
||||
encoder_out["encoder_padding_mask"] = encoder_out[
|
||||
"encoder_padding_mask"
|
||||
].index_select(1, new_order)
|
||||
return encoder_out
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum input length supported by the encoder."""
|
||||
return (1e6, 1e6) # an arbitrary large number
|
||||
|
||||
|
||||
@register_model_architecture("asr_w2l_conv_glu_encoder", "w2l_conv_glu_enc")
|
||||
def w2l_conv_glu_enc(args):
|
||||
args.input_feat_per_channel = getattr(args, "input_feat_per_channel", 80)
|
||||
args.in_channels = getattr(args, "in_channels", 1)
|
||||
args.conv_enc_config = getattr(args, "conv_enc_config", default_conv_enc_config)
|
||||
Reference in New Issue
Block a user