The key innovation: all 4 tokens in an audio frame share the same position ID.
# Text tokens: positions 0, 1, 2, ..., N
# Audio tokens: positions grouped by frame
# Frame 1: [N+1, N+1, N+1, N+1] # All 4 tokens share position N+1
# Frame 2: [N+2, N+2, N+2, N+2] # All 4 tokens share position N+2
This reduces the RoPE distance between tokens across frames, improving coherence in long-form generation.
make setup
Or manually:
./setup.sh
source venv/bin/activate
Edit configuration files in configs/:
configs/model_config.yaml - Model settings:
model:
model_id: "LiquidAI/LFM2-350M"
first_train: true # Set false when resuming from checkpoint
attn_implementation: "flash_attention_2"
configs/training_config.yaml - Training hyperparameters:
training:
num_train_epochs: 3
per_device_train_batch_size: 8
gradient_accumulation_steps: 4
learning_rate: 9.0e-4
save_steps: 5000
configs/dataset_config.yaml - Dataset sources:
max_duration_sec: 20
hf_datasets:
- reponame: "username/dataset-name"
split: "train"
# ... see file for full options
make dataset
To create your own dataset, use this REPO
make train
KaniTTS2-Pretrain/
├── configs/ # Configuration files
│ ├── model_config.yaml # Model configuration
│ ├── training_config.yaml # Training hyperparameters
│ ├── dataset_config.yaml # Dataset sources
│ └── accelerate_config.yaml # FSDP multi-GPU config
├── utils/ # Core utilities (OOP)
│ ├── __init__.py
│ ├── config.py # Configuration classes
│ ├── model.py # Flash Attention LFM2 model
│ ├── data.py # Data collator and loading
│ └── trainer.py # Trainer class
├── train.py # Main training script
├── prepare_dataset.py # Dataset preparation script
├── upload_to_hf.py # HuggingFace upload utility
├── dataset_processor.py # Dataset processing logic
├── Makefile # Convenient commands
└── README.md # This file
configs/model_config.yaml)model:
model_id: "LiquidAI/LFM2-350M" # Base model
text_vocab_size: 64400 # Original vocab size
audio_codebook_size: 4032 # Tokens per layer
tokens_per_frame: 4 # Tokens in one frame
attn_implementation: "flash_attention_2" # Attention type
dtype: "bfloat16" # Model precision
first_train: true # Resize embeddings?
CRITICAL: Set first_train: false when resuming from checkpoint!
configs/training_config.yaml)training:
# Checkpointing
output_dir: "./checkpoints"
save_steps: 5000
save_total_limit: 10
# Training
num_train_epochs: 3
per_device_train_batch_size: 8
gradient_accumulation_steps: 4
# Optimizer
learning_rate: 9.0e-4
lr_scheduler_type: "cosine"
warmup_steps: 2000
# Data
dataset_path: "./train_dataset"
wandb:
project: "LFM2-nano-codec-custom-attention"
name: "train_flash_attention_v1"
# Attention Metrics
metrics:
enable_metrics: true # Enable attention analysis
log_steps: 100 # Compute metrics every N steps
influence_steps: 1000 # Compute token influence (expensive)
print_interpretation: true # Print results to console
The system includes comprehensive metrics to verify that frame-level position encoding achieves layer isolation without explicit attention masking.
Measures prediction quality separately for each audio layer.
Good layer isolation means:
Thresholds:
Measures attention focus by tracking GQA layer output statistics.
Good layer isolation means:
Thresholds:
Analyzes which tokens influence predictions using gradients.
Good layer isolation means:
Note: This metric is expensive and runs less frequently (every 1000 steps by default).
Shows if the model confuses different audio layers.
Good layer isolation means:
Enable/disable metrics in configs/training_config.yaml:
metrics:
enable_metrics: true # Set false to disable
log_steps: 100 # How often to compute metrics
influence_steps: 1000 # Token influence interval (expensive)
print_interpretation: true # Print human-readable reports
During training, you'll see reports like:
=== Layer-Specific Perplexity Analysis ===
Layer 0: 45.23 ✓ Excellent
Layer 1: 48.91 ✓ Excellent
Layer 2: 52.34 ✓ Good
Layer 3: 49.12 ✓ Excellent
Variance: 0.0234 ✓ Good layer isolation
=== Output Variance Analysis ===
Average variance: 2.134 ✓ Excellent focus
Range: [1.823, 2.445]
=== Cross-Layer Confusion Matrix ===
L0 L1 L2 L3
L0 0.87 0.05 0.04 0.04
L1 0.03 0.89 0.05 0.03
L2 0.02 0.04 0.91 0.03
L3 0.03 0.02 0.04 0.91
Average diagonal: 0.890 ✓ Excellent layer separation
Success Indicators:
These metrics validate that frame-level position encoding successfully achieves layer isolation without explicit masking.
All metrics are automatically logged to Wandb under the metrics/ namespace:
metrics/layer_{0,1,2,3}_pplmetrics/avg_audio_pplmetrics/ppl_variancemetrics/avg_output_variancemetrics/confusion_{i}_{j}metrics/layer_{i}_same_layer_influencemetrics/layer_{i}_cross_layer_influencemake help # Show all commands
make setup # Setup environment
make dataset # Prepare dataset
make train # Train model
make resume CHECKPOINT=./checkpoints/checkpoint-5000
make upload REPO=username/model CHECKPOINT=./checkpoints/checkpoint-5000
make clean # Clean generated files
make test # Test Flash Attention
make check-env # Check environment
python train.py \
--model-config my_model.yaml \
--training-config my_training.yaml \
--dataset-config my_dataset.yaml
make resume CHECKPOINT=./checkpoints/checkpoint-5000
Or directly:
accelerate launch train.py \
--config-file configs/accelerate_config.yaml \
--resume-from ./checkpoints/checkpoint-5000
from utils import load_configs, KaniTTS2Trainer
# Load configurations
model_config, training_config, _ = load_configs()
# Create trainer
trainer = KaniTTS2Trainer(model_config, training_config)
# Setup and train
trainer.setup()
trainer.train()
# Save model
trainer.save_model("./final_model")
# Or push to Hub
trainer.push_to_hub("username/model-name", private=True)
Edit configs/accelerate_config.yaml:
num_processes: 2 # Number of GPUs
fsdp_sharding_strategy: FULL_SHARD
mixed_precision: bf16
Training automatically uses FSDP with accelerate launch.
On 2x H100 80GB GPUs:
See requirements.txt for complete list.
Error: RuntimeError: index out of bounds
Fix: Set first_train: false in configs/model_config.yaml when resuming from checkpoint.
Error: RuntimeError: FlashAttention only support fp16 and bf16
Fix: Ensure dtype: "bfloat16" in model config.
Fix: Reduce per_device_train_batch_size or increase gradient_accumulation_steps in training config.
Error: Dataset not found
Fix: Run make dataset first to prepare the training dataset.
make upload REPO=username/model-name CHECKPOINT=./checkpoints/checkpoint-5000
python upload_to_hf.py \
--repo username/model-name \
--checkpoint ./checkpoints/checkpoint-5000 \
--private
trainer.push_to_hub("username/model-name", private=True)
If you use this code in your research, please cite:
@software{kani_tts_2,
author = {Nineninesix},
title = {KaniTTS2: Text-to-Speech Model with Frame-level Position Encoding},
year = {2026},
publisher = {Hugging Face},
howpublished = {\url{https://huggingface.co/nineninesix/kani-tts-2-pt}},
note = {Open-source TTS model}
}
This project builds on LFM2 by Liquid AI. We thank the Liquid AI team for developing and open-sourcing the LFM2 hybrid architecture under the LFM Open License v1.0.
Apache 2 - see LICENSE file for details
6 commits
3 commits
Python
93.5%
Shell
3.6%
Makefile
2.9%
The key innovation: all 4 tokens in an audio frame share the same position ID.
# Text tokens: positions 0, 1, 2, ..., N
# Audio tokens: positions grouped by frame
# Frame 1: [N+1, N+1, N+1, N+1] # All 4 tokens share position N+1
# Frame 2: [N+2, N+2, N+2, N+2] # All 4 tokens share position N+2
This reduces the RoPE distance between tokens across frames, improving coherence in long-form generation.
make setup
Or manually:
./setup.sh
source venv/bin/activate
Edit configuration files in configs/:
configs/model_config.yaml - Model settings:
model:
model_id: "LiquidAI/LFM2-350M"
first_train: true # Set false when resuming from checkpoint
attn_implementation: "flash_attention_2"
configs/training_config.yaml - Training hyperparameters:
training:
num_train_epochs: 3
per_device_train_batch_size: 8
gradient_accumulation_steps: 4
learning_rate: 9.0e-4
save_steps: 5000
configs/dataset_config.yaml - Dataset sources:
max_duration_sec: 20
hf_datasets:
- reponame: "username/dataset-name"
split: "train"
# ... see file for full options
make dataset
To create your own dataset, use this REPO
make train
KaniTTS2-Pretrain/
├── configs/ # Configuration files
│ ├── model_config.yaml # Model configuration
│ ├── training_config.yaml # Training hyperparameters
│ ├── dataset_config.yaml # Dataset sources
│ └── accelerate_config.yaml # FSDP multi-GPU config
├── utils/ # Core utilities (OOP)
│ ├── __init__.py
│ ├── config.py # Configuration classes
│ ├── model.py # Flash Attention LFM2 model
│ ├── data.py # Data collator and loading
│ └── trainer.py # Trainer class
├── train.py # Main training script
├── prepare_dataset.py # Dataset preparation script
├── upload_to_hf.py # HuggingFace upload utility
├── dataset_processor.py # Dataset processing logic
├── Makefile # Convenient commands
└── README.md # This file
configs/model_config.yaml)model:
model_id: "LiquidAI/LFM2-350M" # Base model
text_vocab_size: 64400 # Original vocab size
audio_codebook_size: 4032 # Tokens per layer
tokens_per_frame: 4 # Tokens in one frame
attn_implementation: "flash_attention_2" # Attention type
dtype: "bfloat16" # Model precision
first_train: true # Resize embeddings?
CRITICAL: Set first_train: false when resuming from checkpoint!
configs/training_config.yaml)training:
# Checkpointing
output_dir: "./checkpoints"
save_steps: 5000
save_total_limit: 10
# Training
num_train_epochs: 3
per_device_train_batch_size: 8
gradient_accumulation_steps: 4
# Optimizer
learning_rate: 9.0e-4
lr_scheduler_type: "cosine"
warmup_steps: 2000
# Data
dataset_path: "./train_dataset"
wandb:
project: "LFM2-nano-codec-custom-attention"
name: "train_flash_attention_v1"
# Attention Metrics
metrics:
enable_metrics: true # Enable attention analysis
log_steps: 100 # Compute metrics every N steps
influence_steps: 1000 # Compute token influence (expensive)
print_interpretation: true # Print results to console
The system includes comprehensive metrics to verify that frame-level position encoding achieves layer isolation without explicit attention masking.
Measures prediction quality separately for each audio layer.
Good layer isolation means:
Thresholds:
Measures attention focus by tracking GQA layer output statistics.
Good layer isolation means:
Thresholds:
Analyzes which tokens influence predictions using gradients.
Good layer isolation means:
Note: This metric is expensive and runs less frequently (every 1000 steps by default).
Shows if the model confuses different audio layers.
Good layer isolation means:
Enable/disable metrics in configs/training_config.yaml:
metrics:
enable_metrics: true # Set false to disable
log_steps: 100 # How often to compute metrics
influence_steps: 1000 # Token influence interval (expensive)
print_interpretation: true # Print human-readable reports
During training, you'll see reports like:
=== Layer-Specific Perplexity Analysis ===
Layer 0: 45.23 ✓ Excellent
Layer 1: 48.91 ✓ Excellent
Layer 2: 52.34 ✓ Good
Layer 3: 49.12 ✓ Excellent
Variance: 0.0234 ✓ Good layer isolation
=== Output Variance Analysis ===
Average variance: 2.134 ✓ Excellent focus
Range: [1.823, 2.445]
=== Cross-Layer Confusion Matrix ===
L0 L1 L2 L3
L0 0.87 0.05 0.04 0.04
L1 0.03 0.89 0.05 0.03
L2 0.02 0.04 0.91 0.03
L3 0.03 0.02 0.04 0.91
Average diagonal: 0.890 ✓ Excellent layer separation
Success Indicators:
These metrics validate that frame-level position encoding successfully achieves layer isolation without explicit masking.
All metrics are automatically logged to Wandb under the metrics/ namespace:
metrics/layer_{0,1,2,3}_pplmetrics/avg_audio_pplmetrics/ppl_variancemetrics/avg_output_variancemetrics/confusion_{i}_{j}metrics/layer_{i}_same_layer_influencemetrics/layer_{i}_cross_layer_influencemake help # Show all commands
make setup # Setup environment
make dataset # Prepare dataset
make train # Train model
make resume CHECKPOINT=./checkpoints/checkpoint-5000
make upload REPO=username/model CHECKPOINT=./checkpoints/checkpoint-5000
make clean # Clean generated files
make test # Test Flash Attention
make check-env # Check environment
python train.py \
--model-config my_model.yaml \
--training-config my_training.yaml \
--dataset-config my_dataset.yaml
make resume CHECKPOINT=./checkpoints/checkpoint-5000
Or directly:
accelerate launch train.py \
--config-file configs/accelerate_config.yaml \
--resume-from ./checkpoints/checkpoint-5000
from utils import load_configs, KaniTTS2Trainer
# Load configurations
model_config, training_config, _ = load_configs()
# Create trainer
trainer = KaniTTS2Trainer(model_config, training_config)
# Setup and train
trainer.setup()
trainer.train()
# Save model
trainer.save_model("./final_model")
# Or push to Hub
trainer.push_to_hub("username/model-name", private=True)
Edit configs/accelerate_config.yaml:
num_processes: 2 # Number of GPUs
fsdp_sharding_strategy: FULL_SHARD
mixed_precision: bf16
Training automatically uses FSDP with accelerate launch.
On 2x H100 80GB GPUs:
See requirements.txt for complete list.
Error: RuntimeError: index out of bounds
Fix: Set first_train: false in configs/model_config.yaml when resuming from checkpoint.
Error: RuntimeError: FlashAttention only support fp16 and bf16
Fix: Ensure dtype: "bfloat16" in model config.
Fix: Reduce per_device_train_batch_size or increase gradient_accumulation_steps in training config.
Error: Dataset not found
Fix: Run make dataset first to prepare the training dataset.
make upload REPO=username/model-name CHECKPOINT=./checkpoints/checkpoint-5000
python upload_to_hf.py \
--repo username/model-name \
--checkpoint ./checkpoints/checkpoint-5000 \
--private
trainer.push_to_hub("username/model-name", private=True)
If you use this code in your research, please cite:
@software{kani_tts_2,
author = {Nineninesix},
title = {KaniTTS2: Text-to-Speech Model with Frame-level Position Encoding},
year = {2026},
publisher = {Hugging Face},
howpublished = {\url{https://huggingface.co/nineninesix/kani-tts-2-pt}},
note = {Open-source TTS model}
}
This project builds on LFM2 by Liquid AI. We thank the Liquid AI team for developing and open-sourcing the LFM2 hybrid architecture under the LFM Open License v1.0.
Apache 2 - see LICENSE file for details
6 commits
3 commits
Python
93.5%
Shell
3.6%
Makefile
2.9%