Research Code Repository: This repository contains the implementation code for a paper currently under review. The paper introduces Latent Merging, a novel paradigm for composing large language models in representation space rather than parameter space.
This repository provides minimal, reproducible code for experimenting with four merging techniques between Qwen2.5-7B-Instruct (base) and OpenThinker3-7B (fine-tuned). Large artifacts (pkl files, safetensors, checkpoints) are excluded from the repository; only code and result summaries are included.
Key Innovation: Instead of merging model weights (static, irreversible), we merge hidden representations (dynamic, reversible, controllable) during inference, enabling layer-wise control without modifying parameters.
Latent_Merging/
├── src/
│ ├── latent_merging.py # Core: LERP/SLERP/RegMean/Task Vector classes
│ └── metrics.py # Evaluation: CKA, midness, geodesic metrics
├── scripts/
│ └── judgebench_eval.py # JudgeBench A/B evaluation (LLM-as-judge)
├── results/ # Experimental summary CSVs
│ ├── LERP.csv / LERP1.csv
│ ├── SLERP.csv / SLERP!.csv
│ ├── RegMean.csv / RegMean1.csv
│ └── system_costs.csv
├── artifacts/ # [NOT IN GIT] Experimental outputs
│ ├── SLERP/ # Scale×step raw (*.pkl) + summary
│ ├── Lerp/
│ ├── RegMean/
│ ├── TaskVector/ # latent/weight merged TaskVector pkl
│ └── root_pkls/ # Baselines, merged weights, responses
├── requirements.txt
├── README.md # This file
└── CLAUDE.md # Detailed AI assistant guide
Note: The artifacts/ directory containing large pkl files, model checkpoints, and merged weights is stored separately and not tracked in Git. See Large File Deployment for details.
# Clone repository
git clone https://github.com/thisiskorea/Latent_Merging.git
cd Latent_Merging
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
Required packages: torch, transformers, datasets, tqdm, pandas, openai (v1+), scikit-learn
from src.latent_merging import get_model, get_tokenizer, latent_mix_generate
# Load models
tokenizer = get_tokenizer("Qwen/Qwen2.5-7B-Instruct")
base_model = get_model("Qwen/Qwen2.5-7B-Instruct")
ft_model = get_model("open-thoughts/OpenThinker3-7B")
# Prepare messages
messages = [{"role": "user", "content": "Explain gradient descent simply."}]
# Generate with SLERP at layer 20, beta=0.5
text = latent_mix_generate(
base_model, ft_model, tokenizer,
messages=messages,
mix_layer=20,
beta=0.5,
mode="slerp", # or "lerp"
max_new_tokens=512
)
print(text)
from src.latent_merging import latent_regmean_generate, delta_generate, ActivationSteerer
# RegMean merging
text = latent_regmean_generate(
base_model, ft_model, tokenizer,
messages=messages,
mix_layers=[20, 21, 22, 23],
alpha=0.5
)
# Task Vector (delta steering)
deltas = {20: delta_vector_layer20, 21: delta_vector_layer21}
steerer = ActivationSteerer(base_model, deltas, alpha=0.1)
# Generate with active steering
output = base_model.generate(...)
steerer.remove() # Always clean up hooks
Compare two response sets using GPT-4o-mini as judge:
OPENAI_API_KEY=sk-... python scripts/judgebench_eval.py \
--path-a artifacts/SLERP/latent_0.5_layer20.pkl \
--path-b artifacts/root_pkls/OpenThinker_baseline.pkl \
--judge-model gpt-4o-mini \
--out results/comparison.jsonl
Output: Win rates, ties, statistical summary
h' = (1 - α) * h_A + α * h_B
# Geodesic interpolation on unit sphere with norm restoration
θ = arccos(<u, v>) # u, v are normalized
h' = norm_interp * ((sin((1-α)θ)/sin(θ)) * u + (sin(αθ)/sin(θ)) * v)
h' = mean([h_A, h_B]) - λ * R(h_i)
h' = h + α * (h_FT - h_base)
| Method | Knowledge | Reasoning | Math | Coding | Overall |
|---|---|---|---|---|---|
| SLERP | 59.42 / 40.59 | 100.00 / 0.00 | 86.37 / 13.64 | 98.34 / 1.67 | 74.76 / 25.25 |
| LERP | 98.03 / 1.97 | 91.31 / 8.69 | 98.53 / 1.47 | 100.00 / 0.00 | 97.15 / 2.85 |
| RegMean | 74.06 / 25.94 | 60.00 / 40.00 | 58.82 / 41.18 | 74.19 / 25.81 | 69.82 / 30.18 |
Format: Latent / Weight (win rates %)
Key Finding: Latent merging consistently dominates weight merging across all operators and task categories, with particularly dramatic improvements in reasoning (100% vs 0%) and coding (100% vs 0%).
| Metric | Latent | Weight | Δ |
|---|---|---|---|
| CKA ↑ | 0.83-0.89 | 0.32-0.35 | +0.51 |
| Midness ↑ | 0.77-0.80 | 0.61-0.64 | +0.15 |
| Arc Deviation ↓ | 0.02-0.12 | 0.16-0.32 | -0.16 |
Key Finding: Latent merging preserves representational geometry significantly better, with ~50% improvement in CKA similarity.
Best Configurations:
Large artifacts are not included in this repository. To reproduce experiments:
Model Checkpoints: Download models via Hugging Face or place local checkpoints in desired paths. Specify paths via script arguments.
Response/Merge Results:
artifacts/SLERP/, Lerp/, RegMean/: Scale×step pkl files (*.pkl, *_summary.pkl)artifacts/TaskVector/: latent_merged-TaskVector.pkl, weight_merged-TaskVector.pklartifacts/root_pkls/: Baselines (Qwen_baseline.pkl, OpenThinker_baseline.pkl), merged weights, evaluation resultsMerged Weights: weight_merged-*.pkl, latent_merged-*.pkl stored separately. Reference paths in scripts.
Recommendation: Store large files in external storage (Google Drive, S3, institutional storage) and download as needed.
We use JudgeBench for evaluation because:
This project is licensed under the MIT License - see the LICENSE file for details.
Research Code Repository: This repository contains the implementation code for a paper currently under review. The paper introduces Latent Merging, a novel paradigm for composing large language models in representation space rather than parameter space.
This repository provides minimal, reproducible code for experimenting with four merging techniques between Qwen2.5-7B-Instruct (base) and OpenThinker3-7B (fine-tuned). Large artifacts (pkl files, safetensors, checkpoints) are excluded from the repository; only code and result summaries are included.
Key Innovation: Instead of merging model weights (static, irreversible), we merge hidden representations (dynamic, reversible, controllable) during inference, enabling layer-wise control without modifying parameters.
Latent_Merging/
├── src/
│ ├── latent_merging.py # Core: LERP/SLERP/RegMean/Task Vector classes
│ └── metrics.py # Evaluation: CKA, midness, geodesic metrics
├── scripts/
│ └── judgebench_eval.py # JudgeBench A/B evaluation (LLM-as-judge)
├── results/ # Experimental summary CSVs
│ ├── LERP.csv / LERP1.csv
│ ├── SLERP.csv / SLERP!.csv
│ ├── RegMean.csv / RegMean1.csv
│ └── system_costs.csv
├── artifacts/ # [NOT IN GIT] Experimental outputs
│ ├── SLERP/ # Scale×step raw (*.pkl) + summary
│ ├── Lerp/
│ ├── RegMean/
│ ├── TaskVector/ # latent/weight merged TaskVector pkl
│ └── root_pkls/ # Baselines, merged weights, responses
├── requirements.txt
├── README.md # This file
└── CLAUDE.md # Detailed AI assistant guide
Note: The artifacts/ directory containing large pkl files, model checkpoints, and merged weights is stored separately and not tracked in Git. See Large File Deployment for details.
# Clone repository
git clone https://github.com/thisiskorea/Latent_Merging.git
cd Latent_Merging
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
Required packages: torch, transformers, datasets, tqdm, pandas, openai (v1+), scikit-learn
from src.latent_merging import get_model, get_tokenizer, latent_mix_generate
# Load models
tokenizer = get_tokenizer("Qwen/Qwen2.5-7B-Instruct")
base_model = get_model("Qwen/Qwen2.5-7B-Instruct")
ft_model = get_model("open-thoughts/OpenThinker3-7B")
# Prepare messages
messages = [{"role": "user", "content": "Explain gradient descent simply."}]
# Generate with SLERP at layer 20, beta=0.5
text = latent_mix_generate(
base_model, ft_model, tokenizer,
messages=messages,
mix_layer=20,
beta=0.5,
mode="slerp", # or "lerp"
max_new_tokens=512
)
print(text)
from src.latent_merging import latent_regmean_generate, delta_generate, ActivationSteerer
# RegMean merging
text = latent_regmean_generate(
base_model, ft_model, tokenizer,
messages=messages,
mix_layers=[20, 21, 22, 23],
alpha=0.5
)
# Task Vector (delta steering)
deltas = {20: delta_vector_layer20, 21: delta_vector_layer21}
steerer = ActivationSteerer(base_model, deltas, alpha=0.1)
# Generate with active steering
output = base_model.generate(...)
steerer.remove() # Always clean up hooks
Compare two response sets using GPT-4o-mini as judge:
OPENAI_API_KEY=sk-... python scripts/judgebench_eval.py \
--path-a artifacts/SLERP/latent_0.5_layer20.pkl \
--path-b artifacts/root_pkls/OpenThinker_baseline.pkl \
--judge-model gpt-4o-mini \
--out results/comparison.jsonl
Output: Win rates, ties, statistical summary
h' = (1 - α) * h_A + α * h_B
# Geodesic interpolation on unit sphere with norm restoration
θ = arccos(<u, v>) # u, v are normalized
h' = norm_interp * ((sin((1-α)θ)/sin(θ)) * u + (sin(αθ)/sin(θ)) * v)
h' = mean([h_A, h_B]) - λ * R(h_i)
h' = h + α * (h_FT - h_base)
| Method | Knowledge | Reasoning | Math | Coding | Overall |
|---|---|---|---|---|---|
| SLERP | 59.42 / 40.59 | 100.00 / 0.00 | 86.37 / 13.64 | 98.34 / 1.67 | 74.76 / 25.25 |
| LERP | 98.03 / 1.97 | 91.31 / 8.69 | 98.53 / 1.47 | 100.00 / 0.00 | 97.15 / 2.85 |
| RegMean | 74.06 / 25.94 | 60.00 / 40.00 | 58.82 / 41.18 | 74.19 / 25.81 | 69.82 / 30.18 |
Format: Latent / Weight (win rates %)
Key Finding: Latent merging consistently dominates weight merging across all operators and task categories, with particularly dramatic improvements in reasoning (100% vs 0%) and coding (100% vs 0%).
| Metric | Latent | Weight | Δ |
|---|---|---|---|
| CKA ↑ | 0.83-0.89 | 0.32-0.35 | +0.51 |
| Midness ↑ | 0.77-0.80 | 0.61-0.64 | +0.15 |
| Arc Deviation ↓ | 0.02-0.12 | 0.16-0.32 | -0.16 |
Key Finding: Latent merging preserves representational geometry significantly better, with ~50% improvement in CKA similarity.
Best Configurations:
Large artifacts are not included in this repository. To reproduce experiments:
Model Checkpoints: Download models via Hugging Face or place local checkpoints in desired paths. Specify paths via script arguments.
Response/Merge Results:
artifacts/SLERP/, Lerp/, RegMean/: Scale×step pkl files (*.pkl, *_summary.pkl)artifacts/TaskVector/: latent_merged-TaskVector.pkl, weight_merged-TaskVector.pklartifacts/root_pkls/: Baselines (Qwen_baseline.pkl, OpenThinker_baseline.pkl), merged weights, evaluation resultsMerged Weights: weight_merged-*.pkl, latent_merged-*.pkl stored separately. Reference paths in scripts.
Recommendation: Store large files in external storage (Google Drive, S3, institutional storage) and download as needed.
We use JudgeBench for evaluation because:
This project is licensed under the MIT License - see the LICENSE file for details.