canary-speechlm-mlx / modules.py
Santosh-bold's picture
Upload modules.py with huggingface_hub
07e27b4 verified
Raw
History Blame Contribute Delete
3.77 kB
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
# PyTorch uses (N, C, L) for Conv1d, MLX uses (N, L, C)
# So pointwise convolutions are just linear layers in MLX
self.pointwise_conv1 = nn.Linear(d_model, d_model * 2)
# Depthwise Conv1d in MLX
# groups = channels
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 is (B, T, C)
x = self.pointwise_conv1(x)
# GLU activation
# Split along channels (last dim)
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
# Feed forward 1
self.norm_feed_forward1 = nn.LayerNorm(d_model)
self.feed_forward1 = ConformerFeedForward(d_model, d_ff, dropout)
# Self Attention
self.norm_self_att = nn.LayerNorm(d_model)
self.self_attn = RelPositionMultiHeadAttention(n_head=n_heads, n_feat=d_model, dropout_rate=dropout)
# Convolution
self.norm_conv = nn.LayerNorm(d_model)
self.conv = ConformerConvolution(d_model, conv_kernel_size)
# Feed forward 2
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):
# 1. FFN 1
residual = x
x = self.norm_feed_forward1(x)
x = self.feed_forward1(x)
x = residual + self.dropout(x) * self.fc_factor
# 2. Attention
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)
# 3. Convolution
residual = x
x = self.norm_conv(x)
x = self.conv(x)
x = residual + self.dropout(x)
# 4. FFN 2
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