wip - running some training with overfitting - https://wandb.ai/snoozie/vasa-overfitting
320
stars
272
commits
Python
primary language
Jan 24, 2026
updated
This repository contains the VASA implementation separated from EMOPortraits, with all components properly configured for standalone training.
| Project | Description | Status |
|---|---|---|
| IMTalker | Built on my recreated Microsoft IMF paper - most promising direction, active development focused here | Active |
| IMF | Training code for Implicit Motion Function (Microsoft paper recreation) | Training |
| OmniTransfer-hack | LTX2 / OmniTransfer implementation (paper) | Experimental |
Training video models requires significant GPU compute. If you find this work useful, please consider donating Vast.ai credits to help continue development.
Send Vast.ai credits to: jp@bellgeorge.com
vastai transfer credit jp@bellgeorge.com <AMOUNT>
| Tier | Suggested Amount | What It Helps With |
|---|---|---|
| Buy Me a Coffee | $5-10 | Quick experiments, bug fixes |
| Mates Rates | $25-50 | A few hours of A100 training |
| Supporter | $100-250 | Full training run (10k steps) |
| Enterprise | $500+ | Multi-stage training, new features |
Every contribution helps push this research forward. Thank you!
Live Training Dashboard: wandb.ai/snoozie/vasa-overfitting
![]()
The training visualization shows four panels demonstrating the expression transfer pipeline:
| Panel | Description |
|---|---|
| Identity (Source) | The source identity image - the person whose appearance we want to preserve |
| Target | The driving video frame - provides the expression/pose we want to transfer |
| EMO Generated | Output from the EMOPortraits volumetric avatar model (baseline) |
| VASA Generated | Output from our VASA diffusion model - learns to predict motion parameters that drive expression transfer while preserving source identity |
The green outline in the VASA output shows facial landmark detection used for loss computation. The goal is for VASA Generated to match the Target's expression while maintaining the Identity's appearance.

This visualization shows the audio-to-expression correlation during training, demonstrating how the model learns to map audio features to facial expressions for lip-sync.

This shows the target expression parameters that the model must learn to predict from audio alone. The expression embedding captures facial dynamics (mouth shape, eye openness, eyebrow position, etc.) frame-by-frame.

When the model successfully predicts the expression parameters from audio, combined with the identity image, it recreates the target expression while preserving the source identity. This demonstrates the full pipeline working end-to-end.
# Add Weights & Biases MCP server for Claude
claude mcp add wandb -- uvx --from git+https://github.com/wandb/wandb-mcp-server wandb_mcp_server && uvx wandb login
# Clone with submodules included
git clone --recurse-submodules https://github.com/johndpope/VASA-1-hack.git
cd VASA-1-hack
# Or if you already cloned without submodules:
git submodule update --init --recursive
# Install system dependencies
sudo apt-get update
sudo apt-get install -y ffmpeg git-lfs
mkdir -p ~/miniconda3
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda.sh
chmod +x ~/miniconda.sh
~/miniconda.sh
# carefully accept - type yes -
# Create conda environment
conda create -n vasa python=3.12
conda activate vasa
# Install PyTorch (adjust for your CUDA version)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu129
# Install required packages
pip install omegaconf wandb opencv-python-headless pillow scipy matplotlib tqdm
pip install transformers diffusers accelerate einops
pip install facenet-pytorch insightface hsemotion-onnx
pip install mediapipe OmegaConf wandb
pip install memory-profiler rich
pip install diffusers h5py scikit-learn seaborn python_speech_features
pip install onnxruntime-gpu lpips pytorch_msssim
# EMOPortaits
cd nemo
chmod +x ./bootstrap.sh
./bootstrap.sh
# Create symlink for repos (required for relative paths)
ln -s nemo/repos repos
# Create symlink for data directory (required for aligned keypoints)
ln -s nemo/data data
# Create symlink for losses directory (required for loss model weights)
ln -s nemo/losses losses
The pre-trained model should be placed in:
nemo/logs/Retrain_with_17_V1_New_rand_MM_SEC_4_drop_02_stm_10_CV_05_1_1/checkpoints/328_model.pth
# Create directories
mkdir -p junk cache checkpoints
# Place your training videos in the junk directory
# Videos should be .mp4 format
cp your_training_videos/*.mp4 junk/
VASA-1-hack/
โโโ nemo/ # Git submodule: nemo repository (base EMOPortraits code)
โ โโโ models/ # Model implementations
โ โโโ networks/ # Network architectures
โ โโโ losses/ # Loss functions
โ โโโ datasets/ # Dataset loaders
โ โโโ repos/ # External repositories (face_par_off, etc.)
โ โโโ logs/ # Pre-trained model checkpoints
โ
โโโ vasa_*.py # VASA-specific implementations
โ โโโ vasa_trainer.py # Main training script
โ โโโ vasa_model.py # VASA model architecture
โ โโโ vasa_dataset.py # VASA dataset handler
โ โโโ vasa_scheduler.py # Diffusion scheduler
โ โโโ vasa_lip_normalizer.py # Lip normalization utilities
โ
โโโ vasa_config.yaml # Main configuration file
โโโ video_tracker.py # Video tracking utilities
โโโ syncnet.py # Sync network implementation
โ
โโโ data/ # Data files
โ โโโ aligned_keypoints_3d.npy
โโโ losses/ # Loss model weights
โ โโโ loss_model_weights/
โโโ junk/ # Training videos directory
โโโ cache/ # Cache for processed data
โโโ checkpoints/ # Model checkpoints
โโโ repos/ # Symlink to nemo/repos
Edit vasa_config.yaml to configure paths and training parameters:
paths:
volumetric_model: "nemo/logs/[...]/328_model.pth" # Pre-trained model
volumetric_config: "nemo/models/stage_1/volumetric_avatar/va.yaml"
data_dir: "data"
video_folder: "junk" # Your training videos directory
cache_dir: "cache"
checkpoint_dir: "checkpoints"
train:
batch_size: 1
num_epochs: 4000
lr: 1e-3
# ... other training parameters
python test_vasa_setup.py
Expected output:
โ Config loaded successfully
โ All paths exist
โ All modules import correctly
โ Setup looks good! You can now run vasa_trainer.py
Test your setup and verify model can train properly:
# Run overfitting test with optimized settings
python train_overfit.py
This uses overfit_config.yaml with:
Use the standard configuration for training on your complete dataset:
# Uses vasa_config.yaml by default
python vasa_trainer.py
# Or explicitly specify the config
python vasa_trainer.py --config vasa_config.yaml
Key parameters in vasa_config.yaml:
window_size: 50 - Full 50-frame windowsn_layers: 8 - Full 8 transformer layersnum_steps: 1000 - Full 1000 diffusion stepsbatch_size: 1 - Adjust based on GPU memorynum_epochs: 4000 - Full training scheduleUse the overfitting configuration via vasa_trainer:
# Use the overfitting configuration with vasa_trainer
python vasa_trainer.py --config overfit_config.yaml
Key differences in overfit_config.yaml:
window_size: 20 - Smaller windows for faster processingn_layers: 2 - Reduced transformer depth (2x-4x faster)num_steps: 100 - Reduced diffusion steps (10x faster)batch_size: 4 - Larger batch for better GPU utilizationnum_epochs: 100 - Shorter training for quick iterationmax_videos: 100 - Limited dataset sizenum_workers: 8 - Multi-threaded data loadingWhen to use overfitting mode:
For faster training, preprocess all windows into a single cache file:
# Preprocess data for overfitting test (small dataset)
python preprocess_single_bucket.py --max_videos 100 --cache_dir cache_overfit
# Preprocess full dataset
python preprocess_single_bucket.py --max_videos 1000 --cache_dir cache_full
Benefits of single-bucket caching:
The cache will be automatically used if:
use_single_bucket: true in your config filecache_dirBoth training modes support WandB logging:
# View training progress
# Visit the URL printed at training start, e.g.:
# wandb: ๐ View run at https://wandb.ai/your-username/vasa/runs/run-id
For overfitting mode, runs are grouped as "overfit-experiments" in WandB for easy comparison.
To use a different dataset (e.g., CelebV-HQ):
# Edit the config file or create a custom one
# Update video_folder path in the config:
# video_folder: "/path/to/your/dataset"
# For example, using CelebV-HQ:
# video_folder: "/media/12TB/Downloads/CelebV-HQ/celebvhq/35666"
The trainer will:
save_freqcheckpoints/ (or checkpoints_overfit/ for overfitting mode)| Parameter | Vanilla Training | Overfitting Mode | Speedup |
|---|---|---|---|
| Window Size | 50 frames | 20 frames | 2.5x |
| Transformer Layers | 8 | 2 | 4x |
| Diffusion Steps | 1000 | 100 | 10x |
| Batch Size | 1 | 4 | 4x |
| Workers | 0 | 8 | Parallel loading |
| Epoch Time (RTX 5090) | ~5 min | ~1.5 min | 3.3x |
The project includes several debugging pipelines for analyzing face swap and identity preservation issues:
# Test with video (uses joint extraction to prevent identity drift)
python nemo/pipeline3.py --target nemo/data/VID_1.mp4 --max-frames 10
# Test with single image
python nemo/pipeline3.py --target nemo/data/IMG_2.png
# Use custom source identity
python nemo/pipeline3.py --source path/to/source.png --target path/to/target.mp4
# Swap identity mode (use driver's identity with source's expression)
python nemo/pipeline3.py --default-video --swap-identity
# This is useful when the model is extracting the wrong identity
Features:
--swap-identity flag to use driver's identity with source's expressiondebug_pipeline3/# The reference pipeline that produces correct results
python nemo/pipeline2.py
This is the baseline implementation that pipeline3.py was designed to match.
Various analysis scripts for specific debugging:
check_identity_confusion.py - Analyze identity preservationdebug_identity_extraction.py - Test identity feature extractiontest_polished_face_swap.py - Test face swap qualityextract_and_apply_warps_properly.py - Analyze warp field applicationThe volumetric avatar system uses two types of warps:
XY Warps (Rigid + Non-rigid 3D warping)
UV Warps (Expression transfer)
Problem: Generated face morphs away from source identity Cause: Solo extraction (processing source alone without driver context) Solution: Joint extraction - process source+first_driver_frame together
Problem: Male faces (e.g., IMG_1.png) appear feminine in results Cause: Identity embeddings not properly calibrated to driver motion space Solution: Joint extraction ensures embeddings are aligned with driver poses
debug_pipeline3/
โโโ trace_YYYYMMDD_HHMMSS.json # Complete execution trace
โโโ step_NNNN_*.png # Intermediate images at each step
โโโ step_NNNN_*.pt # Tensor checkpoints
โโโ frame_NNN_result.png # Final output frames
โโโ video_comparison.png # Grid comparison of all frames
The trace files contain detailed information about each processing step:
Use the trace to identify where identity drift or other issues occur in the pipeline. | Convergence | 1000+ epochs | 10-20 epochs | 50x+ |
The VASA model uses a sophisticated two-stage warping system to separate identity from expression, enabling clean expression transfer between faces.
Source Face (๐) โ [XY Warp] โ Canonical (๐) โ [UV Warp] โ Target Face (๐ฎ)
This separation enables:
The warps are extracted during dataset preprocessing:
# In vasa_dataset.py - extract warps for training
motion_data = {
'xy_warps': xy_warps, # [T, 16, 64, 64, 3] - normalizes to canonical
'rigid_warps': rigid_warps, # [T, 16, 64, 64, 3] - head pose alignment
'uv_warps': uv_warps, # [T, 16, 64, 64, 3] - applies target expression
'source_theta': thetas # [T, 3, 4] - pose matrices
}
To cleanly separate VASA from the volumetric avatar implementation, we've developed a bridge interface that abstracts all EMOPortraits-specific details.
vasa_emo_bridge_interface.py)Abstract interface that any volumetric avatar backend must implement:
class VolumetricAvatarBridgeInterface:
def extract_warps_for_window(frames, identity_frame_idx) -> WindowWarpData
def extract_warps_for_frame(identity_frame, target_frame) -> FrameWarpData
def generate_canonical_view(identity_frame) -> canonical_image
def get_identity_embedding(identity_frame) -> identity_embed
Concrete implementation for EMOPortraits/MegaPortraits models:
from vasa_emo_bridge_interface import create_bridge
# Create bridge (abstracts all EMO details)
bridge = create_bridge("emoportraits", emo_model)
# Extract warps for entire window at once
window_warps = bridge.extract_warps_for_window(
frames=frames, # [T, C, H, W]
identity_frame_idx=0 # Use first frame as identity
)
# Access extracted warps
xy_warps = window_warps.xy_warps # [T, D, H, W, 3]
rigid_warps = window_warps.rigid_warps # [T, D, H, W, 3]
uv_warps = window_warps.uv_warps # [T, D, H, W, 3]
# Generate canonical view
canonical = bridge.generate_canonical_view(identity_frame)
The system can generate canonical (neutral, front-facing) views from any input expression:
A canonical view represents a person in a standardized state:
When given different expressions as input, the canonical generation produces nearly identical neutral views:
The project uses Python's logging module with three configurable levels defined in nemo/logger.py:28-30:
# log_level = logging.WARNING # Minimal output - only warnings and errors
log_level = logging.INFO # Standard output - informational messages (default)
# log_level = logging.DEBUG # Verbose output - detailed debugging information
Logging Levels Explained:
WARNING (logging.WARNING)
INFO (logging.INFO) - Currently Active
DEBUG (logging.DEBUG)
To change the logging level:
nemo/logger.py line 29Additional Features:
project.log file for later reviewModuleNotFoundError: No module named 'logger'
# The logger module is in nemo, paths are already configured
# If still having issues, check that nemo is cloned properly
FileNotFoundError: './repos/face_par_off/res/cp/79999_iter.pth'
# Ensure the symlink exists:
ln -s nemo/repos repos
ValueError: num_samples should be a positive integer value, but got num_samples=0
# No videos found. Add videos to junk/ directory:
cp your_video.mp4 junk/
FileNotFoundError: Config file not found at channel_config.yaml
# Copy from EMOPortraits or create a basic one
CUDA out of memory
batch_size in vasa_config.yamlsequence_length in dataset configFFmpeg warnings
pip install ffmpeg-pythonIf you're missing files, you'll need these from EMOPortraits:
channel_config.yaml - Channel configurationsyncnet.py - Sync network implementationdata/aligned_keypoints_3d.npy - 3D keypoint alignmentslosses/loss_model_weights/*.pth - Pre-trained loss modelsTraining progress is logged to:
checkpoints/Monitor training:
# Watch training logs
tail -f project.log
# Check W&B dashboard
# https://wandb.ai/YOUR_USERNAME/vasa/
vasa_*.py)nemo/ directoryvasa_config.yamljunk/ directorycheckpoints/ directoryUpdate nemo to latest version:
cd nemo
git pull origin main
cd ..
git add nemo
git commit -m "Update nemo submodule to latest"
Lock to specific nemo version:
cd nemo
git checkout <commit-hash>
cd ..
git add nemo
git commit -m "Lock nemo to specific version"
junk/ directoryrepos symlink is required for backward compatibilityThis project is licensed under the MIT License - see the LICENSE file for details.
Note: The nemo submodule and other dependencies may have their own licenses.
272 commits
Python
89.9%
Shell
10.1%
wip - running some training with overfitting - https://wandb.ai/snoozie/vasa-overfitting
320
stars
272
commits
Python
primary language
Jan 24, 2026
updated
This repository contains the VASA implementation separated from EMOPortraits, with all components properly configured for standalone training.
| Project | Description | Status |
|---|---|---|
| IMTalker | Built on my recreated Microsoft IMF paper - most promising direction, active development focused here | Active |
| IMF | Training code for Implicit Motion Function (Microsoft paper recreation) | Training |
| OmniTransfer-hack | LTX2 / OmniTransfer implementation (paper) | Experimental |
Training video models requires significant GPU compute. If you find this work useful, please consider donating Vast.ai credits to help continue development.
Send Vast.ai credits to: jp@bellgeorge.com
vastai transfer credit jp@bellgeorge.com <AMOUNT>
| Tier | Suggested Amount | What It Helps With |
|---|---|---|
| Buy Me a Coffee | $5-10 | Quick experiments, bug fixes |
| Mates Rates | $25-50 | A few hours of A100 training |
| Supporter | $100-250 | Full training run (10k steps) |
| Enterprise | $500+ | Multi-stage training, new features |
Every contribution helps push this research forward. Thank you!
Live Training Dashboard: wandb.ai/snoozie/vasa-overfitting
![]()
The training visualization shows four panels demonstrating the expression transfer pipeline:
| Panel | Description |
|---|---|
| Identity (Source) | The source identity image - the person whose appearance we want to preserve |
| Target | The driving video frame - provides the expression/pose we want to transfer |
| EMO Generated | Output from the EMOPortraits volumetric avatar model (baseline) |
| VASA Generated | Output from our VASA diffusion model - learns to predict motion parameters that drive expression transfer while preserving source identity |
The green outline in the VASA output shows facial landmark detection used for loss computation. The goal is for VASA Generated to match the Target's expression while maintaining the Identity's appearance.

This visualization shows the audio-to-expression correlation during training, demonstrating how the model learns to map audio features to facial expressions for lip-sync.

This shows the target expression parameters that the model must learn to predict from audio alone. The expression embedding captures facial dynamics (mouth shape, eye openness, eyebrow position, etc.) frame-by-frame.

When the model successfully predicts the expression parameters from audio, combined with the identity image, it recreates the target expression while preserving the source identity. This demonstrates the full pipeline working end-to-end.
# Add Weights & Biases MCP server for Claude
claude mcp add wandb -- uvx --from git+https://github.com/wandb/wandb-mcp-server wandb_mcp_server && uvx wandb login
# Clone with submodules included
git clone --recurse-submodules https://github.com/johndpope/VASA-1-hack.git
cd VASA-1-hack
# Or if you already cloned without submodules:
git submodule update --init --recursive
# Install system dependencies
sudo apt-get update
sudo apt-get install -y ffmpeg git-lfs
mkdir -p ~/miniconda3
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda.sh
chmod +x ~/miniconda.sh
~/miniconda.sh
# carefully accept - type yes -
# Create conda environment
conda create -n vasa python=3.12
conda activate vasa
# Install PyTorch (adjust for your CUDA version)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu129
# Install required packages
pip install omegaconf wandb opencv-python-headless pillow scipy matplotlib tqdm
pip install transformers diffusers accelerate einops
pip install facenet-pytorch insightface hsemotion-onnx
pip install mediapipe OmegaConf wandb
pip install memory-profiler rich
pip install diffusers h5py scikit-learn seaborn python_speech_features
pip install onnxruntime-gpu lpips pytorch_msssim
# EMOPortaits
cd nemo
chmod +x ./bootstrap.sh
./bootstrap.sh
# Create symlink for repos (required for relative paths)
ln -s nemo/repos repos
# Create symlink for data directory (required for aligned keypoints)
ln -s nemo/data data
# Create symlink for losses directory (required for loss model weights)
ln -s nemo/losses losses
The pre-trained model should be placed in:
nemo/logs/Retrain_with_17_V1_New_rand_MM_SEC_4_drop_02_stm_10_CV_05_1_1/checkpoints/328_model.pth
# Create directories
mkdir -p junk cache checkpoints
# Place your training videos in the junk directory
# Videos should be .mp4 format
cp your_training_videos/*.mp4 junk/
VASA-1-hack/
โโโ nemo/ # Git submodule: nemo repository (base EMOPortraits code)
โ โโโ models/ # Model implementations
โ โโโ networks/ # Network architectures
โ โโโ losses/ # Loss functions
โ โโโ datasets/ # Dataset loaders
โ โโโ repos/ # External repositories (face_par_off, etc.)
โ โโโ logs/ # Pre-trained model checkpoints
โ
โโโ vasa_*.py # VASA-specific implementations
โ โโโ vasa_trainer.py # Main training script
โ โโโ vasa_model.py # VASA model architecture
โ โโโ vasa_dataset.py # VASA dataset handler
โ โโโ vasa_scheduler.py # Diffusion scheduler
โ โโโ vasa_lip_normalizer.py # Lip normalization utilities
โ
โโโ vasa_config.yaml # Main configuration file
โโโ video_tracker.py # Video tracking utilities
โโโ syncnet.py # Sync network implementation
โ
โโโ data/ # Data files
โ โโโ aligned_keypoints_3d.npy
โโโ losses/ # Loss model weights
โ โโโ loss_model_weights/
โโโ junk/ # Training videos directory
โโโ cache/ # Cache for processed data
โโโ checkpoints/ # Model checkpoints
โโโ repos/ # Symlink to nemo/repos
Edit vasa_config.yaml to configure paths and training parameters:
paths:
volumetric_model: "nemo/logs/[...]/328_model.pth" # Pre-trained model
volumetric_config: "nemo/models/stage_1/volumetric_avatar/va.yaml"
data_dir: "data"
video_folder: "junk" # Your training videos directory
cache_dir: "cache"
checkpoint_dir: "checkpoints"
train:
batch_size: 1
num_epochs: 4000
lr: 1e-3
# ... other training parameters
python test_vasa_setup.py
Expected output:
โ Config loaded successfully
โ All paths exist
โ All modules import correctly
โ Setup looks good! You can now run vasa_trainer.py
Test your setup and verify model can train properly:
# Run overfitting test with optimized settings
python train_overfit.py
This uses overfit_config.yaml with:
Use the standard configuration for training on your complete dataset:
# Uses vasa_config.yaml by default
python vasa_trainer.py
# Or explicitly specify the config
python vasa_trainer.py --config vasa_config.yaml
Key parameters in vasa_config.yaml:
window_size: 50 - Full 50-frame windowsn_layers: 8 - Full 8 transformer layersnum_steps: 1000 - Full 1000 diffusion stepsbatch_size: 1 - Adjust based on GPU memorynum_epochs: 4000 - Full training scheduleUse the overfitting configuration via vasa_trainer:
# Use the overfitting configuration with vasa_trainer
python vasa_trainer.py --config overfit_config.yaml
Key differences in overfit_config.yaml:
window_size: 20 - Smaller windows for faster processingn_layers: 2 - Reduced transformer depth (2x-4x faster)num_steps: 100 - Reduced diffusion steps (10x faster)batch_size: 4 - Larger batch for better GPU utilizationnum_epochs: 100 - Shorter training for quick iterationmax_videos: 100 - Limited dataset sizenum_workers: 8 - Multi-threaded data loadingWhen to use overfitting mode:
For faster training, preprocess all windows into a single cache file:
# Preprocess data for overfitting test (small dataset)
python preprocess_single_bucket.py --max_videos 100 --cache_dir cache_overfit
# Preprocess full dataset
python preprocess_single_bucket.py --max_videos 1000 --cache_dir cache_full
Benefits of single-bucket caching:
The cache will be automatically used if:
use_single_bucket: true in your config filecache_dirBoth training modes support WandB logging:
# View training progress
# Visit the URL printed at training start, e.g.:
# wandb: ๐ View run at https://wandb.ai/your-username/vasa/runs/run-id
For overfitting mode, runs are grouped as "overfit-experiments" in WandB for easy comparison.
To use a different dataset (e.g., CelebV-HQ):
# Edit the config file or create a custom one
# Update video_folder path in the config:
# video_folder: "/path/to/your/dataset"
# For example, using CelebV-HQ:
# video_folder: "/media/12TB/Downloads/CelebV-HQ/celebvhq/35666"
The trainer will:
save_freqcheckpoints/ (or checkpoints_overfit/ for overfitting mode)| Parameter | Vanilla Training | Overfitting Mode | Speedup |
|---|---|---|---|
| Window Size | 50 frames | 20 frames | 2.5x |
| Transformer Layers | 8 | 2 | 4x |
| Diffusion Steps | 1000 | 100 | 10x |
| Batch Size | 1 | 4 | 4x |
| Workers | 0 | 8 | Parallel loading |
| Epoch Time (RTX 5090) | ~5 min | ~1.5 min | 3.3x |
The project includes several debugging pipelines for analyzing face swap and identity preservation issues:
# Test with video (uses joint extraction to prevent identity drift)
python nemo/pipeline3.py --target nemo/data/VID_1.mp4 --max-frames 10
# Test with single image
python nemo/pipeline3.py --target nemo/data/IMG_2.png
# Use custom source identity
python nemo/pipeline3.py --source path/to/source.png --target path/to/target.mp4
# Swap identity mode (use driver's identity with source's expression)
python nemo/pipeline3.py --default-video --swap-identity
# This is useful when the model is extracting the wrong identity
Features:
--swap-identity flag to use driver's identity with source's expressiondebug_pipeline3/# The reference pipeline that produces correct results
python nemo/pipeline2.py
This is the baseline implementation that pipeline3.py was designed to match.
Various analysis scripts for specific debugging:
check_identity_confusion.py - Analyze identity preservationdebug_identity_extraction.py - Test identity feature extractiontest_polished_face_swap.py - Test face swap qualityextract_and_apply_warps_properly.py - Analyze warp field applicationThe volumetric avatar system uses two types of warps:
XY Warps (Rigid + Non-rigid 3D warping)
UV Warps (Expression transfer)
Problem: Generated face morphs away from source identity Cause: Solo extraction (processing source alone without driver context) Solution: Joint extraction - process source+first_driver_frame together
Problem: Male faces (e.g., IMG_1.png) appear feminine in results Cause: Identity embeddings not properly calibrated to driver motion space Solution: Joint extraction ensures embeddings are aligned with driver poses
debug_pipeline3/
โโโ trace_YYYYMMDD_HHMMSS.json # Complete execution trace
โโโ step_NNNN_*.png # Intermediate images at each step
โโโ step_NNNN_*.pt # Tensor checkpoints
โโโ frame_NNN_result.png # Final output frames
โโโ video_comparison.png # Grid comparison of all frames
The trace files contain detailed information about each processing step:
Use the trace to identify where identity drift or other issues occur in the pipeline. | Convergence | 1000+ epochs | 10-20 epochs | 50x+ |
The VASA model uses a sophisticated two-stage warping system to separate identity from expression, enabling clean expression transfer between faces.
Source Face (๐) โ [XY Warp] โ Canonical (๐) โ [UV Warp] โ Target Face (๐ฎ)
This separation enables:
The warps are extracted during dataset preprocessing:
# In vasa_dataset.py - extract warps for training
motion_data = {
'xy_warps': xy_warps, # [T, 16, 64, 64, 3] - normalizes to canonical
'rigid_warps': rigid_warps, # [T, 16, 64, 64, 3] - head pose alignment
'uv_warps': uv_warps, # [T, 16, 64, 64, 3] - applies target expression
'source_theta': thetas # [T, 3, 4] - pose matrices
}
To cleanly separate VASA from the volumetric avatar implementation, we've developed a bridge interface that abstracts all EMOPortraits-specific details.
vasa_emo_bridge_interface.py)Abstract interface that any volumetric avatar backend must implement:
class VolumetricAvatarBridgeInterface:
def extract_warps_for_window(frames, identity_frame_idx) -> WindowWarpData
def extract_warps_for_frame(identity_frame, target_frame) -> FrameWarpData
def generate_canonical_view(identity_frame) -> canonical_image
def get_identity_embedding(identity_frame) -> identity_embed
Concrete implementation for EMOPortraits/MegaPortraits models:
from vasa_emo_bridge_interface import create_bridge
# Create bridge (abstracts all EMO details)
bridge = create_bridge("emoportraits", emo_model)
# Extract warps for entire window at once
window_warps = bridge.extract_warps_for_window(
frames=frames, # [T, C, H, W]
identity_frame_idx=0 # Use first frame as identity
)
# Access extracted warps
xy_warps = window_warps.xy_warps # [T, D, H, W, 3]
rigid_warps = window_warps.rigid_warps # [T, D, H, W, 3]
uv_warps = window_warps.uv_warps # [T, D, H, W, 3]
# Generate canonical view
canonical = bridge.generate_canonical_view(identity_frame)
The system can generate canonical (neutral, front-facing) views from any input expression:
A canonical view represents a person in a standardized state:
When given different expressions as input, the canonical generation produces nearly identical neutral views:
The project uses Python's logging module with three configurable levels defined in nemo/logger.py:28-30:
# log_level = logging.WARNING # Minimal output - only warnings and errors
log_level = logging.INFO # Standard output - informational messages (default)
# log_level = logging.DEBUG # Verbose output - detailed debugging information
Logging Levels Explained:
WARNING (logging.WARNING)
INFO (logging.INFO) - Currently Active
DEBUG (logging.DEBUG)
To change the logging level:
nemo/logger.py line 29Additional Features:
project.log file for later reviewModuleNotFoundError: No module named 'logger'
# The logger module is in nemo, paths are already configured
# If still having issues, check that nemo is cloned properly
FileNotFoundError: './repos/face_par_off/res/cp/79999_iter.pth'
# Ensure the symlink exists:
ln -s nemo/repos repos
ValueError: num_samples should be a positive integer value, but got num_samples=0
# No videos found. Add videos to junk/ directory:
cp your_video.mp4 junk/
FileNotFoundError: Config file not found at channel_config.yaml
# Copy from EMOPortraits or create a basic one
CUDA out of memory
batch_size in vasa_config.yamlsequence_length in dataset configFFmpeg warnings
pip install ffmpeg-pythonIf you're missing files, you'll need these from EMOPortraits:
channel_config.yaml - Channel configurationsyncnet.py - Sync network implementationdata/aligned_keypoints_3d.npy - 3D keypoint alignmentslosses/loss_model_weights/*.pth - Pre-trained loss modelsTraining progress is logged to:
checkpoints/Monitor training:
# Watch training logs
tail -f project.log
# Check W&B dashboard
# https://wandb.ai/YOUR_USERNAME/vasa/
vasa_*.py)nemo/ directoryvasa_config.yamljunk/ directorycheckpoints/ directoryUpdate nemo to latest version:
cd nemo
git pull origin main
cd ..
git add nemo
git commit -m "Update nemo submodule to latest"
Lock to specific nemo version:
cd nemo
git checkout <commit-hash>
cd ..
git add nemo
git commit -m "Lock nemo to specific version"
junk/ directoryrepos symlink is required for backward compatibilityThis project is licensed under the MIT License - see the LICENSE file for details.
Note: The nemo submodule and other dependencies may have their own licenses.
272 commits
Python
89.9%
Shell
10.1%