| import mlx.core as mx |
| import mlx.nn as nn |
|
|
| from attention import RelPositionMultiHeadAttention |
|
|
| class Swish(nn.Module): |
| def __call__(self, x): |
| return x * mx.sigmoid(x) |
|
|
| class ConformerFeedForward(nn.Module): |
| def __init__(self, d_model: int, d_ff: int, dropout: float = 0.0): |
| super().__init__() |
| self.d_model = d_model |
| self.d_ff = d_ff |
| |
| self.linear1 = nn.Linear(d_model, d_ff) |
| self.activation = Swish() |
| self.dropout = nn.Dropout(p=dropout) |
| self.linear2 = nn.Linear(d_ff, d_model) |
|
|
| def __call__(self, x): |
| x = self.linear1(x) |
| x = self.activation(x) |
| x = self.dropout(x) |
| x = self.linear2(x) |
| return x |
|
|
| class ConformerConvolution(nn.Module): |
| def __init__(self, d_model: int, kernel_size: int): |
| super().__init__() |
| self.d_model = d_model |
| self.kernel_size = kernel_size |
| |
| |
| |
| self.pointwise_conv1 = nn.Linear(d_model, d_model * 2) |
| |
| |
| |
| self.depthwise_conv = nn.Conv1d( |
| in_channels=d_model, |
| out_channels=d_model, |
| kernel_size=kernel_size, |
| stride=1, |
| padding=(kernel_size - 1) // 2, |
| groups=d_model |
| ) |
| |
| self.batch_norm = nn.BatchNorm(d_model) |
| self.activation = Swish() |
| self.pointwise_conv2 = nn.Linear(d_model, d_model) |
|
|
| def __call__(self, x): |
| |
| x = self.pointwise_conv1(x) |
| |
| |
| |
| x1, x2 = mx.split(x, 2, axis=-1) |
| x = x1 * mx.sigmoid(x2) |
| |
| x = self.depthwise_conv(x) |
| x = self.batch_norm(x) |
| x = self.activation(x) |
| x = self.pointwise_conv2(x) |
| |
| return x |
|
|
| class ConformerLayer(nn.Module): |
| def __init__(self, d_model: int, d_ff: int, n_heads: int, conv_kernel_size: int, dropout: float = 0.1): |
| super().__init__() |
| self.fc_factor = 0.5 |
| |
| |
| self.norm_feed_forward1 = nn.LayerNorm(d_model) |
| self.feed_forward1 = ConformerFeedForward(d_model, d_ff, dropout) |
| |
| |
| self.norm_self_att = nn.LayerNorm(d_model) |
| self.self_attn = RelPositionMultiHeadAttention(n_head=n_heads, n_feat=d_model, dropout_rate=dropout) |
| |
| |
| |
| self.norm_conv = nn.LayerNorm(d_model) |
| self.conv = ConformerConvolution(d_model, conv_kernel_size) |
| |
| |
| self.norm_feed_forward2 = nn.LayerNorm(d_model) |
| self.feed_forward2 = ConformerFeedForward(d_model, d_ff, dropout) |
| |
| self.dropout = nn.Dropout(dropout) |
| self.norm_out = nn.LayerNorm(d_model) |
|
|
| def __call__(self, x, pos_emb, mask=None): |
| |
| residual = x |
| x = self.norm_feed_forward1(x) |
| x = self.feed_forward1(x) |
| x = residual + self.dropout(x) * self.fc_factor |
| |
| |
| residual = x |
| x = self.norm_self_att(x) |
| x = self.self_attn(query=x, key=x, value=x, mask=mask, pos_emb=pos_emb) |
| x = residual + self.dropout(x) |
| |
| |
| residual = x |
| x = self.norm_conv(x) |
| x = self.conv(x) |
| x = residual + self.dropout(x) |
| |
| |
| residual = x |
| x = self.norm_feed_forward2(x) |
| x = self.feed_forward2(x) |
| x = residual + self.dropout(x) * self.fc_factor |
| |
| x = self.norm_out(x) |
| return x |
|
|