Model merging in ComfyUI allows you to combine weights from multiple models to create hybrid models with characteristics of both sources. This technique is widely used in the generative AI community to blend artistic styles, improve model capabilities, or create specialized variants.
Models in PyTorch store their learnable parameters in a state dictionary:
import torch# Get model state dictstate_dict = model.model.state_dict()# State dict structure:{ 'layer1.weight': tensor([...]), 'layer1.bias': tensor([...]), 'layer2.weight': tensor([...]), ...}
Each key represents a layer or parameter name, and each value is a tensor containing the weights.
def merge_linear(model1, model2, ratio=0.5): """ Merge two models using linear interpolation Args: model1: First model (ComfyUI model object) model2: Second model (ComfyUI model object) ratio: Blend ratio (0.0 to 1.0) Returns: Merged model """ # Clone the first model merged_model = model1.clone() # Get state dicts state_dict1 = model1.model.state_dict() state_dict2 = model2.model.state_dict() # Merge weights merged_state = {} for key in state_dict1.keys(): if key in state_dict2: merged_state[key] = ( state_dict1[key] * (1 - ratio) + state_dict2[key] * ratio ) else: # Keep original weight if not in model2 merged_state[key] = state_dict1[key] # Load merged weights merged_model.model.load_state_dict(merged_state) return merged_model
Linear interpolation works best when models share the same architecture and were trained on similar data.
def merge_add_difference(base_model, model_a, model_b, multiplier=1.0): """ Merge using: base + (model_a - model_b) * multiplier This technique adds the "difference" between two models to a base. Useful for transferring specific features or styles. """ merged_model = base_model.clone() state_base = base_model.model.state_dict() state_a = model_a.model.state_dict() state_b = model_b.model.state_dict() merged_state = {} for key in state_base.keys(): if key in state_a and key in state_b: difference = state_a[key] - state_b[key] merged_state[key] = state_base[key] + difference * multiplier else: merged_state[key] = state_base[key] merged_model.model.load_state_dict(merged_state) return merged_model
Use Case Example
If you have:
Base: General purpose model
Model A: Model fine-tuned for anime style
Model B: Original base before fine-tuning
Then base + (A - B) transfers the anime style to your base model.
def merge_selective(model1, model2, layer_ratios): """ Merge with different ratios per layer Args: layer_ratios: Dict mapping layer patterns to ratios e.g., {'encoder': 0.3, 'decoder': 0.7, 'default': 0.5} """ merged_model = model1.clone() state_dict1 = model1.model.state_dict() state_dict2 = model2.model.state_dict() merged_state = {} for key in state_dict1.keys(): # Determine ratio for this layer ratio = layer_ratios.get('default', 0.5) for pattern, r in layer_ratios.items(): if pattern in key: ratio = r break if key in state_dict2: merged_state[key] = ( state_dict1[key] * (1 - ratio) + state_dict2[key] * ratio ) else: merged_state[key] = state_dict1[key] merged_model.model.load_state_dict(merged_state) return merged_model
Selective merging is useful when you want to preserve certain model characteristics. For example, keep encoder layers from model1 while using decoder layers from model2.
ComfyUI uses a patching system for efficient model modifications:
# Clone a model (creates a patcher)cloned_model = original_model.clone()# Access the underlying PyTorch modelpytorch_model = cloned_model.model# Get all models in the patch chainfor patch_model in model.model_patches_models(): print(f"Patch: {patch_model}")
Shares weights with the original (memory efficient)
Allows independent modifications
Tracks parent-child relationships
from comfy import model_management# Clone preserves the patch hierarchycloned = model.clone()# Check if models are clonesif model.is_clone(cloned): print("Models share base weights")
import safetensors.torchimport torchdef save_merged_model(model, output_path): """ Save merged model to disk """ # Get state dict state_dict = model.model.state_dict() # Convert to CPU and appropriate dtype cpu_state_dict = {} for key, value in state_dict.items(): # Move to CPU cpu_value = value.cpu() # Convert FP8/quantized to FP16 for compatibility if hasattr(cpu_value, 'dequantize'): cpu_value = cpu_value.dequantize() if cpu_value.dtype in [torch.float8_e4m3fn, torch.float8_e5m2]: cpu_value = cpu_value.to(torch.float16) cpu_state_dict[key] = cpu_value # Save using safetensors (recommended) or torch if output_path.endswith('.safetensors'): safetensors.torch.save_file(cpu_state_dict, output_path) else: torch.save(cpu_state_dict, output_path) print(f"Model saved to {output_path}")
Different ratios for different transformer blocks:
def merge_block_weighted(model1, model2, block_weights): """ Merge with different weights per transformer block Args: block_weights: List of ratios, one per block e.g., [0.1, 0.2, 0.3, ..., 0.9] """ merged_model = model1.clone() state_dict1 = model1.model.state_dict() state_dict2 = model2.model.state_dict() merged_state = {} for key in state_dict1.keys(): # Determine which block this layer belongs to ratio = 0.5 # Default # Extract block number from key (e.g., 'layers.5.attn.weight') import re match = re.search(r'layers\.(\d+)\.', key) if match: block_idx = int(match.group(1)) if block_idx < len(block_weights): ratio = block_weights[block_idx] if key in state_dict2: merged_state[key] = ( state_dict1[key] * (1 - ratio) + state_dict2[key] * ratio ) else: merged_state[key] = state_dict1[key] merged_model.model.load_state_dict(merged_state) return merged_model