MergeKit Tutorial: Merging LLM Models to Create Custom AI Models
Open-source large language models (LLMs) continue to evolve rapidly. With numerous fine-tuned models available on Hugging Face, an interesting technique has emerged that allows us to combine multiple models into a single new model without any retraining. This technique is called model merging, and MergeKit is the most popular toolkit for doing it.
MergeKit, developed by Arcee AI, enables us to merge two or more transformer models using various methods such as SLERP, TIES, DARE, and linear merge. The result is a new model that combines capabilities from the source models without requiring GPU resources for training.
In this tutorial, we will learn how to use MergeKit from installation to creating a merged model ready for deployment.
Why Model Merging?
Before diving into the techniques, it is important to understand why model merging has become popular:
Installing MergeKit
Prerequisites
- Python 3.10 or newer
- Minimum 16 GB RAM (depends on model size)
- Sufficient disk space to store models (each 7B model requires approximately 14 GB in fp16)
Install from PyPI
pip install mergekit
Install from Source (Recommended)
To get the latest features:
git clone https://github.com/arcee-ai/mergekit.git
cd mergekit
pip install -e .
If you want to use the evolution feature (evolutionary merge):
pip install -e ".[evolve]"
Verify Installation
mergekit-yaml --help
If this command displays help options, the installation was successful.
Core Concepts of Model Merging
Supported Architectures
MergeKit supports transformer-based models, including:
- LLaMA and derivatives (Mistral, Qwen, etc.)
- GPT-NeoX
- Phi
- Gemma
- StableLM
- And other compatible architectures
Available Merge Methods
MergeKit provides several merge methods:
| Method | Description | When to Use |
|--------|-------------|-------------|
| Linear | Simple weighted average of model weights | Baseline, fast, easy to understand |
| SLERP | Spherical Linear Interpolation | Merging 2 models with smooth transition |
| TIES | TrIm, Elect, and merGe | Reducing inter-task interference |
| DARE | Drop And REscale | Similar to TIES with a different approach |
| Passthrough | Concatenating layers sequentially | Creating larger "frankenmerge" models |
| Model Stock | Geometric merge in weight space | More robust merging |
Creating Your First Merge Configuration
MergeKit uses YAML files to define merge configurations. Let us start with simple examples.
Linear Merge (Weighted Average)
Create a file linear-merge.yml:
mergemethod: linear
dtype: float16
models:
- model: NousResearch/Hermes-2-Pro-Mistral-7B
parameters:
weight: 0.6
- model: teknium/OpenHermes-2.5-Mistral-7B
parameters:
weight: 0.4
Run the merge:
mergekit-yaml linear-merge.yml ./output-linear-merge --cuda --lazy-unpickle
Parameters:
--cuda: Use GPU if available (optional, speeds up the process)--lazy-unpickle: Save memory with lazy loading
SLERP Merge
SLERP is a very popular method for merging two models. Create a file slerp-merge.yml:
mergemethod: slerp
dtype: float16
basemodel: NousResearch/Hermes-2-Pro-Mistral-7B
parameters:
t: 0.5
models:
- model: NousResearch/Hermes-2-Pro-Mistral-7B
- model: teknium/OpenHermes-2.5-Mistral-7B
The t parameter controls interpolation:
t: 0.0= 100% first modelt: 0.5= balanced mixturet: 1.0= 100% second model
Run:
mergekit-yaml slerp-merge.yml ./output-slerp-merge --cuda
TIES Merge
TIES (TrIm, Elect Sign, and merGe) is effective at reducing inter-task interference. Create a file ties-merge.yml:
mergemethod: ties
dtype: float16
basemodel: mistralai/Mistral-7B-v0.1
parameters:
density: 0.5
normalize: true
models:
- model: NousResearch/Hermes-2-Pro-Mistral-7B
parameters:
weight: 1.0
density: 0.5
- model: teknium/OpenHermes-2.5-Mistral-7B
parameters:
weight: 0.5
density: 0.5
Key TIES parameters:
density: Proportion of parameters to keep (0.0-1.0). Lower values mean more parameters are trimmed.normalize: Normalize weights after merging.basemodel: Reference model for computing delta weights.
mergekit-yaml ties-merge.yml ./output-ties-merge --cuda
DARE Merge
DARE (Drop And REscale) is similar to TIES but uses random dropping. Create a file dare-merge.yml:
mergemethod: dareties
dtype: float16
basemodel: mistralai/Mistral-7B-v0.1
parameters:
normalize: true
models:
- model: NousResearch/Hermes-2-Pro-Mistral-7B
parameters:
weight: 1.0
density: 0.6
- model: teknium/OpenHermes-2.5-Mistral-7B
parameters:
weight: 0.5
density: 0.6
mergekit-yaml dare-merge.yml ./output-dare-merge --cuda
Advanced Usage
Frankenmerge (Passthrough)
Frankenmerge allows us to combine layers from different models sequentially, creating a larger model. Create a file franken-merge.yml:
mergemethod: passthrough
dtype: float16
slices:
- sources:
- model: NousResearch/Hermes-2-Pro-Mistral-7B
layerrange: [0, 16]
- sources:
- model: teknium/OpenHermes-2.5-Mistral-7B
layerrange: [8, 24]
- sources:
- model: NousResearch/Hermes-2-Pro-Mistral-7B
layerrange: [16, 32]
The configuration above takes layers 0-15 from the first model, layers 8-23 from the second model, and layers 16-31 from the first model again, resulting in a model with 48 layers (larger than either source).
mergekit-yaml franken-merge.yml ./output-franken-merge --cuda
Per-Layer Configuration with Gradients
We can set different weights per layer using gradients:
mergemethod: slerp
dtype: float16
basemodel: NousResearch/Hermes-2-Pro-Mistral-7B
parameters:
t:
- filter: selfattn
value: [0.0, 0.3, 0.5, 0.7, 1.0]
- filter: mlp
value: [1.0, 0.7, 0.5, 0.3, 0.0]
- value: 0.5
models:
- model: NousResearch/Hermes-2-Pro-Mistral-7B
- model: teknium/OpenHermes-2.5-Mistral-7B
This configuration uses different gradients for attention layers and MLP layers, providing granular control over the merge process.
Merge with Custom Tokenizer
If the models being merged have different tokenizers:
mergemethod: linear
dtype: float16
tokenizer
source: union
models:
- model: modelA
parameters:
weight: 0.5
- model: modelB
parameters:
weight: 0.5
tokenizersource options:
union: Combine vocabulary from all modelsbase: Use the base model's tokenizermodel:: Use a specific model's tokenizer
Using Local Models
You do not have to download from Hugging Face. Local models can also be used:
mergemethod: slerp
dtype: float16
basemodel: /path/to/local/model-a
parameters:
t: 0.5
models:
- model: /path/to/local/model-a
- model: /path/to/local/model-b
Evaluating Merged Models
After merging, it is important to evaluate the results. Here are several approaches:
Quick Test with Transformers
from transformers import AutoModelForCausalLM, AutoTokenizer
modelpath = "./output-slerp-merge"
tokenizer = AutoTokenizer.frompretrained(modelpath)
model = AutoModelForCausalLM.frompretrained(
modelpath,
torchdtype="auto",
devicemap="auto"
)
prompt = "Explain machine learning in 3 sentences:"
inputs = tokenizer(prompt, returntensors="pt").to(model.device)
outputs = model.generate(inputs, maxnewtokens=200, temperature=0.7)
print(tokenizer.decode(outputs[0], skipspecialtokens=True))
Benchmarking with lm-eval
pip install lm-eval
lmeval --model hf \
--modelargs pretrained=./output-slerp-merge \
--tasks hellaswag,arceasy,mmlu \
--batchsize 8
Output Comparison
A simple script to compare outputs from the original and merged models:
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
def generateresponse(modelpath, prompt, maxtokens=200):
tokenizer = AutoTokenizer.frompretrained(modelpath)
model = AutoModelForCausalLM.frompretrained(
modelpath, torchdtype=torch.float16, devicemap="auto"
)
inputs = tokenizer(prompt, returntensors="pt").to(model.device)
outputs = model.generate(inputs, maxnewtokens=maxtokens, temperature=0.7)
return tokenizer.decode(outputs[0], skipspecialtokens=True)
testprompts = [
"Write a Python function to calculate fibonacci numbers:",
"Explain quantum computing to a 5-year-old:",
"What are the best practices for database indexing?",
]
models = {
"Model A": "NousResearch/Hermes-2-Pro-Mistral-7B",
"Merged": "./output-slerp-merge",
}
for prompt in testprompts:
print(f"\nPrompt: {prompt}")
print("=" * 60)
for name, path in models.items():
response = generateresponse(path, prompt)
print(f"\n[{name}]:")
print(response)
Uploading to Hugging Face Hub
Once you are satisfied with the merge result, upload it to Hugging Face:
from huggingfacehub import HfApi
api = HfApi()
api.createrepo(
repoid="username/my-merged-model",
repotype="model",
private=False
)
api.uploadfolder(
folderpath="./output-slerp-merge",
repoid="username/my-merged-model",
repotype="model",
)
Or using the CLI:
huggingface-cli upload username/my-merged-model ./output-slerp-merge
Remember to create a Model Card (README.md) that explains:
- Source models used
- Merge method and parameters
- Evaluation results
- Applicable licenses
Converting to GGUF Format
To run the merged model in llama.cpp or Ollama, convert it to GGUF:
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp
python converthftogguf.py ../output-slerp-merge --outfile merged-model.gguf --outtype f16
For quantization (reducing file size):
./llama-quantize merged-model.gguf merged-model-Q4KM.gguf Q4KM
Popular quantization levels:
Q4KM: Balance between size and quality (recommended)Q5KM: Higher quality, larger fileQ80: Quality nearly equivalent to fp16, large file
Best Practices
1. Choose the Right Source Models
- Use models with the same architecture (e.g., all Mistral 7B-based)
- Ensure models use the same or compatible tokenizers
- Select models fine-tuned for different tasks so they complement each other
2. Experiment with Parameters
- Start with SLERP
t: 0.5as a baseline - For TIES/DARE, try various
densityvalues (0.3, 0.5, 0.7) - Use per-layer gradients for finer control
- Document every experiment along with its results
3. Resource Management
- Monitor RAM usage during the merge process
- Use
--lazy-unpickleto reduce memory consumption - Use
--low-cpu-memoryif RAM is limited - Store models in safetensors format for safety and speed
4. Thorough Evaluation
- Do not rely on a single benchmark
- Test with prompts relevant to your use case
- Compare with source models to ensure no regression
- Check coherence and consistency of outputs
5. Documentation
- Create well-documented YAML configs
- Save all experiment configurations
- Record source models, methods, parameters, and evaluation results
Troubleshooting
Error: Out of Memory
RuntimeError: CUDA out of memory
Solutions:
- Run on CPU (remove the
--cudaflag) - Use
--lazy-unpickle - Use
--low-cpu-memory - Merge in a smaller dtype (bfloat16 or float16)
Error: Incompatible Architecture
RuntimeError: Incompatible architectures
Solutions:
- Ensure all models use the same architecture
- Verify that layer counts and hidden sizes match (except for passthrough merge)
Error: Tokenizer Mismatch
If models have different vocabularies, use tokenizersource: union or select a tokenizer from one of the models.
Conclusion
MergeKit is an incredibly useful toolkit for creating custom LLM models without any training. By understanding the various merge methods and available parameters, you can experiment and find the optimal combination for your specific needs.
Key takeaways:
- Start with simple methods (linear or SLERP) before trying more complex ones
- Always evaluate merged models thoroughly
- Document every experiment for reproducibility
- The best merged models typically combine models that excel in different areas
With model merging techniques and MergeKit, you can create AI models tailored to your needs without expensive training costs. Happy experimenting!