use crate::model_config;
use super::traits::*;
use anyhow::Result;
use serde::{Serialize, Deserialize};
model_config!(EncodecConfig {
vocab_size: usize = 1024, hidden_size: usize = 128,
num_hidden_layers: usize = 4, audio_channels: usize = 1,
sample_rate: usize = 24000,
num_filters: usize = 32,
num_residual_layers: usize = 1,
upsampling_ratios: Vec<usize> = vec![8, 5, 4, 2],
norm_type: String = "weight_norm".to_string(),
codebook_size: usize = 1024,
codebook_dim: usize = 128,
num_codebooks: usize = 32,
use_causal_conv: bool = true,
pad_mode: String = "reflect".to_string(),
bandwidth: f32 = 6.0,
layer_norm_eps: f32 = 1e-5,
pad_token_id: i64 = 0,
bos_token_id: i64 = 1,
eos_token_id: i64 = 2,
});
impl EncodecConfig {
pub fn from_gguf_config(gguf: &crate::weight_loader_core::GGUFModelConfig) -> Self {
Self {
hidden_size: gguf.hidden_size,
..Default::default()
}
}
}
pub struct EncodecModelV2 {
config: EncodecConfig,
device: Device,
encoder: EncodecEncoder,
decoder: EncodecDecoder,
quantizer: ResidualVectorQuantizer,
}
pub struct EncodecEncoder {
conv_in: EncodecConv1d,
down_blocks: Vec<EncodecDownsampleBlock>,
conv_out: EncodecConv1d,
lstm: Option<EncodecLSTM>,
}
pub struct EncodecDecoder {
conv_in: EncodecConv1d,
up_blocks: Vec<EncodecUpsampleBlock>,
conv_out: EncodecConv1d,
lstm: Option<EncodecLSTM>,
}
pub struct EncodecDownsampleBlock {
conv_layers: Vec<EncodecConv1d>,
downsample: EncodecConv1d,
residual_layers: Vec<EncodecResidualUnit>,
}
pub struct EncodecUpsampleBlock {
upsample: EncodecConvTranspose1d,
conv_layers: Vec<EncodecConv1d>,
residual_layers: Vec<EncodecResidualUnit>,
}
pub struct EncodecResidualUnit {
conv1: EncodecConv1d,
conv2: EncodecConv1d,
}
pub struct EncodecConv1d {
weight: Tensor,
bias: Option<Tensor>,
in_channels: usize,
out_channels: usize,
kernel_size: usize,
stride: usize,
padding: usize,
}
pub struct EncodecConvTranspose1d {
weight: Tensor,
bias: Option<Tensor>,
in_channels: usize,
out_channels: usize,
kernel_size: usize,
stride: usize,
}
pub struct EncodecLSTM {
weight_ih: Tensor,
weight_hh: Tensor,
bias_ih: Tensor,
bias_hh: Tensor,
hidden_size: usize,
num_layers: usize,
}
pub struct ResidualVectorQuantizer {
codebooks: Vec<Tensor>, codebook_size: usize,
codebook_dim: usize,
num_codebooks: usize,
}
impl Model for EncodecModelV2 {
type Config = EncodecConfig;
fn new(config: EncodecConfig) -> Result<Self> {
let device = Device::CPU;
let encoder = EncodecEncoder::new(&config, &device)?;
let decoder = EncodecDecoder::new(&config, &device)?;
let quantizer = ResidualVectorQuantizer::new(&config, &device)?;
Ok(Self { config, device, encoder, decoder, quantizer })
}
fn from_weights(config: EncodecConfig, weights: ModelWeights) -> Result<Self> {
let model = Self::new(config)?;
Ok(model)
}
fn forward(&self, inputs: &ModelInputs) -> Result<ModelOutputs> {
match inputs {
ModelInputs::Audio { input_features, .. } => {
let encoded = self.encoder.forward(input_features)?;
let (quantized, codes) = self.quantizer.forward(&encoded)?;
let decoded = self.decoder.forward(&quantized)?;
Ok(ModelOutputs::Logits {
logits: decoded,
hidden_states: Some(encoded)
})
}
_ => Err(anyhow::anyhow!("Encodec requires audio input")),
}
}
fn generate(&self, _prompt: &str, _config: &GenerationConfig) -> Result<String> {
Err(anyhow::anyhow!("Encodec is a codec model, use encode/decode methods"))
}
fn config(&self) -> &Self::Config { &self.config }
fn memory_requirements(&self) -> MemoryRequirements {
let p = self.config.hidden_size * self.config.num_filters * 1000 * 4;
MemoryRequirements { gpu_memory: p, cpu_memory: p / 4, kv_cache_memory: 0, peak_memory: p * 2 }
}
fn to_device(&mut self, device: &Device) -> Result<()> {
self.device = device.clone();
Ok(())
}
}
impl EncodecEncoder {
fn new(config: &EncodecConfig, device: &Device) -> Result<Self> {
let mut channels = config.num_filters;
let conv_in = EncodecConv1d::new(config.audio_channels, channels, 7, 1, 3, device)?;
let mut down_blocks = Vec::new();
for &ratio in &config.upsampling_ratios {
let out_channels = channels * 2;
down_blocks.push(EncodecDownsampleBlock::new(
channels, out_channels, ratio, config.num_residual_layers, device
)?);
channels = out_channels;
}
let conv_out = EncodecConv1d::new(channels, config.hidden_size, 7, 1, 3, device)?;
let lstm = Some(EncodecLSTM::new(config.hidden_size, 2, device)?);
Ok(Self { conv_in, down_blocks, conv_out, lstm })
}
fn forward(&self, input: &Tensor) -> Result<Tensor> {
let mut x = self.conv_in.forward(input)?;
x = elu(&x)?;
for block in &self.down_blocks {
x = block.forward(&x)?;
}
x = self.conv_out.forward(&x)?;
if let Some(ref lstm) = self.lstm {
x = lstm.forward(&x)?;
}
Ok(x)
}
}
fn elu(input: &Tensor) -> Result<Tensor> {
let x = input.to_candle()?;
let result = x.elu(1.0)?;
Ok(Tensor::from_candle(result))
}
impl EncodecDecoder {
fn new(config: &EncodecConfig, device: &Device) -> Result<Self> {
let mut channels = config.hidden_size;
let conv_in = EncodecConv1d::new(config.hidden_size, channels, 7, 1, 3, device)?;
let lstm = Some(EncodecLSTM::new(channels, 2, device)?);
let mut up_blocks = Vec::new();
let ratios: Vec<_> = config.upsampling_ratios.iter().rev().copied().collect();
for (i, &ratio) in ratios.iter().enumerate() {
let out_channels = if i == ratios.len() - 1 {
config.num_filters
} else {
channels / 2
};
up_blocks.push(EncodecUpsampleBlock::new(
channels, out_channels, ratio, config.num_residual_layers, device
)?);
channels = out_channels;
}
let conv_out = EncodecConv1d::new(config.num_filters, config.audio_channels, 7, 1, 3, device)?;
Ok(Self { conv_in, up_blocks, conv_out, lstm })
}
fn forward(&self, input: &Tensor) -> Result<Tensor> {
let mut x = self.conv_in.forward(input)?;
if let Some(ref lstm) = self.lstm {
x = lstm.forward(&x)?;
}
x = elu(&x)?;
for block in &self.up_blocks {
x = block.forward(&x)?;
}
self.conv_out.forward(&x)
}
}
impl EncodecDownsampleBlock {
fn new(in_channels: usize, out_channels: usize, ratio: usize, num_residual: usize, device: &Device) -> Result<Self> {
let mut conv_layers = Vec::new();
let mut residual_layers = Vec::new();
for _ in 0..num_residual {
residual_layers.push(EncodecResidualUnit::new(in_channels, device)?);
}
conv_layers.push(EncodecConv1d::new(in_channels, in_channels, 3, 1, 1, device)?);
let downsample = EncodecConv1d::new(in_channels, out_channels, ratio * 2, ratio, ratio / 2, device)?;
Ok(Self { conv_layers, downsample, residual_layers })
}
fn forward(&self, input: &Tensor) -> Result<Tensor> {
let mut x = input.clone();
for layer in &self.residual_layers {
x = layer.forward(&x)?;
}
for conv in &self.conv_layers {
x = elu(&conv.forward(&x)?)?;
}
self.downsample.forward(&x)
}
}
impl EncodecUpsampleBlock {
fn new(in_channels: usize, out_channels: usize, ratio: usize, num_residual: usize, device: &Device) -> Result<Self> {
let upsample = EncodecConvTranspose1d::new(in_channels, out_channels, ratio * 2, ratio, device)?;
let mut conv_layers = Vec::new();
let mut residual_layers = Vec::new();
conv_layers.push(EncodecConv1d::new(out_channels, out_channels, 3, 1, 1, device)?);
for _ in 0..num_residual {
residual_layers.push(EncodecResidualUnit::new(out_channels, device)?);
}
Ok(Self { upsample, conv_layers, residual_layers })
}
fn forward(&self, input: &Tensor) -> Result<Tensor> {
let mut x = self.upsample.forward(input)?;
for conv in &self.conv_layers {
x = elu(&conv.forward(&x)?)?;
}
for layer in &self.residual_layers {
x = layer.forward(&x)?;
}
Ok(x)
}
}
impl EncodecResidualUnit {
fn new(channels: usize, device: &Device) -> Result<Self> {
let conv1 = EncodecConv1d::new(channels, channels, 3, 1, 1, device)?;
let conv2 = EncodecConv1d::new(channels, channels, 1, 1, 0, device)?;
Ok(Self { conv1, conv2 })
}
fn forward(&self, input: &Tensor) -> Result<Tensor> {
let x = elu(&self.conv1.forward(input)?)?;
let x = self.conv2.forward(&x)?;
ops_fn::add(input, &x)
}
}
impl EncodecConv1d {
fn new(in_channels: usize, out_channels: usize, kernel_size: usize, stride: usize, padding: usize, device: &Device) -> Result<Self> {
let weight = ops_fn::zeros(&[out_channels, in_channels, kernel_size], DataType::Float32, device)?;
let bias = Some(ops_fn::zeros(&[out_channels], DataType::Float32, device)?);
Ok(Self { weight, bias, in_channels, out_channels, kernel_size, stride, padding })
}
fn forward(&self, input: &Tensor) -> Result<Tensor> {
let out = ops_fn::conv1d(input, &self.weight, None, self.stride, self.padding)?;
if let Some(ref bias) = self.bias {
let bias_candle = bias.to_candle()?;
let out_candle = out.to_candle()?;
let bias_expanded = bias_candle.reshape(&[1, self.out_channels, 1])?;
Ok(Tensor::from_candle(out_candle.broadcast_add(&bias_expanded)?))
} else {
Ok(out)
}
}
}
impl EncodecConvTranspose1d {
fn new(in_channels: usize, out_channels: usize, kernel_size: usize, stride: usize, device: &Device) -> Result<Self> {
let weight = ops_fn::zeros(&[in_channels, out_channels, kernel_size], DataType::Float32, device)?;
let bias = Some(ops_fn::zeros(&[out_channels], DataType::Float32, device)?);
Ok(Self { weight, bias, in_channels, out_channels, kernel_size, stride })
}
fn forward(&self, input: &Tensor) -> Result<Tensor> {
let input_candle = input.to_candle()?;
let weight_candle = self.weight.to_candle()?;
let shape = input_candle.shape().dims();
let (batch, _, seq_len) = (shape[0], shape[1], shape[2]);
let out_len = seq_len * self.stride;
let device = input_candle.device();
let output = candle_core::Tensor::zeros(&[batch, self.out_channels, out_len], candle_core::DType::F32, device)?;
if let Some(ref bias) = self.bias {
let bias_candle = bias.to_candle()?;
let bias_expanded = bias_candle.reshape(&[1, self.out_channels, 1])?;
Ok(Tensor::from_candle(output.broadcast_add(&bias_expanded)?))
} else {
Ok(Tensor::from_candle(output))
}
}
}
impl EncodecLSTM {
fn new(hidden_size: usize, num_layers: usize, device: &Device) -> Result<Self> {
let weight_ih = ops_fn::zeros(&[4 * hidden_size, hidden_size], DataType::Float32, device)?;
let weight_hh = ops_fn::zeros(&[4 * hidden_size, hidden_size], DataType::Float32, device)?;
let bias_ih = ops_fn::zeros(&[4 * hidden_size], DataType::Float32, device)?;
let bias_hh = ops_fn::zeros(&[4 * hidden_size], DataType::Float32, device)?;
Ok(Self { weight_ih, weight_hh, bias_ih, bias_hh, hidden_size, num_layers })
}
fn forward(&self, input: &Tensor) -> Result<Tensor> {
Ok(input.clone())
}
}
impl ResidualVectorQuantizer {
fn new(config: &EncodecConfig, device: &Device) -> Result<Self> {
let mut codebooks = Vec::with_capacity(config.num_codebooks);
for _ in 0..config.num_codebooks {
codebooks.push(ops_fn::zeros(
&[config.codebook_size, config.codebook_dim],
DataType::Float32,
device
)?);
}
Ok(Self {
codebooks,
codebook_size: config.codebook_size,
codebook_dim: config.codebook_dim,
num_codebooks: config.num_codebooks,
})
}
fn forward(&self, input: &Tensor) -> Result<(Tensor, Vec<Tensor>)> {
let mut residual = input.clone();
let input_candle = input.to_candle()?;
let mut quantized = Tensor::from_candle(input_candle.zeros_like()?);
let mut codes = Vec::with_capacity(self.num_codebooks);
for codebook in &self.codebooks {
let (code_indices, quantized_part) = self.quantize_residual(&residual, codebook)?;
quantized = ops_fn::add(&quantized, &quantized_part)?;
residual = ops_fn::sub(&residual, &quantized_part)?;
codes.push(code_indices);
}
Ok((quantized, codes))
}
fn quantize_residual(&self, input: &Tensor, codebook: &Tensor) -> Result<(Tensor, Tensor)> {
let input_candle = input.to_candle()?;
let codebook_candle = codebook.to_candle()?;
let shape = input_candle.shape().dims();
let (batch, channels, seq_len) = (shape[0], shape[1], shape[2]);
let input_flat = input_candle.transpose(1, 2)?.reshape(&[batch * seq_len, channels])?;
let input_sq = input_flat.sqr()?.sum(1)?;
let codebook_sq = codebook_candle.sqr()?.sum(1)?;
let inner = input_flat.matmul(&codebook_candle.t()?)?;
let input_sq_expanded = input_sq.unsqueeze(1)?;
let codebook_sq_expanded = codebook_sq.unsqueeze(0)?;
let distances = (input_sq_expanded.broadcast_add(&codebook_sq_expanded)? - (inner * 2.0)?)?;
let indices = distances.argmin(1)?;
let quantized_flat = codebook_candle.index_select(&indices, 0)?;
let quantized = quantized_flat.reshape(&[batch, seq_len, channels])?.transpose(1, 2)?;
let indices_reshaped = indices.reshape(&[batch, seq_len])?;
Ok((Tensor::from_candle(indices_reshaped), Tensor::from_candle(quantized)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encodec_config() {
let config = EncodecConfig::default();
assert_eq!(config.hidden_size, 128);
assert_eq!(config.num_codebooks, 32);
assert_eq!(config.codebook_size, 1024);
}
#[test]
fn test_encodec_model_creation() {
let config = EncodecConfig {
hidden_size: 32,
num_filters: 8,
num_residual_layers: 1,
upsampling_ratios: vec![2, 2],
num_codebooks: 4,
codebook_size: 64,
codebook_dim: 32,
..Default::default()
};
let model = EncodecModelV2::new(config).unwrap();
assert_eq!(model.config().hidden_size(), 32);
}
}