Views
No views yet
[!CAUTION] ⚠️ Warning: This model can produce narratives and RP that contain violent and graphic erotic content. Adjust your system prompt accordingly, and use Mistral Tekken or ChatML chat template.

multi_fusion merge method.arcee_fusion method by offering alternate importance metrics, inspired from other methods.arcee_fusion) with delta_mag (from generalized_task_arithmetic) and cosine_sim (from model_stock).1architecture: MistralForCausalLM
2base_model: B:\24B\MuXodious--Maginum-Cydoms-24B-absolute-heresy
3models:
4 - model: B:\24B\MuXodious--Maginum-Cydoms-24B-absolute-heresy
5 - model: B:\24B\DarkArtsForge--Morax-24B-v2
6merge_method: multi_fusion # v1
7parameters:
8 tukey_fence: 1.5
9 importance_metric: "delta_mag" # kl_div, delta_mag, cosine_sim, fisher_grad, topk_var
10dtype: float32
11out_dtype: bfloat16
12tokenizer:
13 source: union
14chat_template: auto
15name: 👹 Morax Cydoms 24B1architecture: MistralForCausalLM
2base_model: B:\24B\Morax-Cydoms-24B
3models:
4 - model: B:\24B\Morax-Cydoms-24B
5 - model: B:\24B\Naphula--Slimaki-24B-v1.2
6merge_method: multi_fusion # v1
7parameters:
8 tukey_fence: 1.5
9 importance_metric: "cosine_sim" # kl_div, delta_mag, cosine_sim, fisher_grad, topk_var
10dtype: float32
11out_dtype: bfloat16
12tokenizer:
13 source: union
14chat_template: auto
15name: 🐌 Ślimaki Tavern 24B v1.3arcee_fusion script into a custom multi_fusion with various options.| Metric | Formula | Characteristics | Use Case |
|---|---|---|---|
| kl_div | diff * KL_div(softmax(params), softmax(base)) | Combines magnitude with distributional divergence | When parameter direction matters probabilistically |
| delta_mag | abs(params - base_params) | Pure magnitude of parameter differences | Simple, widely used (TIES/DARE/DELLA) |
| cosine_sim | abs(delta) * (1 + abs(cosine_sim(delta, base))) | Magnitude weighted by alignment with base | When preserving base-aligned changes is important |
| fisher_grad | variance(delta) + eps | Variance along last dimension only | When parameter variability indicates importance |
importance = delta.abs() * (1 + cosine_sim.abs()) . This prioritizes changes that are aligned with the base model's direction in parameter space.fisher_grad metric uses variance directly rather than variance * magnitude (commented out in the code), which follows the SCE approach.registry.py.1from mergekit.merge_methods.arcee_fusion import ArceeFusionMerge
2from mergekit.merge_methods.multi_fusion import MultiFusionMerge
3
4STATIC_MERGE_METHODS: List[MergeMethod] = [
5 LinearMerge(),
6 SlerpMerge(),
7 NuSlerpMerge(),
8 PassthroughMerge(),
9 ModelStockMerge(),
10 ArceeFusionMerge(),
11 MultiFusionMerge(),
12 KarcherMerge(),kl_div should be identical to arcee_fusion.multi_fusion.py1# Copyright (C) 2025 Arcee AI
2# SPDX-License-Identifier: LGPL-3.0-only
3
4from typing import Any, Dict, List, Optional
5
6import torch
7import torch.nn.functional as F
8from typing_extensions import override
9
10from mergekit.architecture import WeightInfo
11from mergekit.common import ModelReference
12from mergekit.graph import Task
13from mergekit.merge_methods.base import (
14 ConfigParameterDef,
15 MergeMethod,
16 MergeTensorInput,
17)
18from mergekit.merge_methods.rectify_embed import rectify_embed_sizes
19
20
21class DynamicThresholdFusion:
22 def approximate_quantiles(self, tensor, q):
23 # Flatten the tensor
24 flat_tensor = tensor.view(-1)
25
26 # If tensor is too large, sample it
27 if flat_tensor.numel() > 1e6:
28 flat_tensor = flat_tensor[torch.randperm(flat_tensor.numel())[:1000000]]
29
30 # Sort the (possibly sampled) tensor
31 sorted_tensor, _ = torch.sort(flat_tensor)
32
33 # Compute quantile indices
34 quantile_indices = (q * (sorted_tensor.numel() - 1)).long()
35
36 # Return quantiles
37 return sorted_tensor[quantile_indices]
38
39 def calculate_dynamic_threshold(self, importance_scores, tukey_fence=1.5):
40 # Approximate median and quantiles
41 median = self.approximate_quantiles(importance_scores, torch.tensor([0.5]))[0]
42 q1, q3 = self.approximate_quantiles(
43 importance_scores, torch.tensor([0.25, 0.75])
44 )
45
46 # Calculate IQR
47 iqr = q3 - q1
48
49 # Set threshold as median + tukey_fence * IQR
50 dynamic_threshold = median + tukey_fence * iqr
51
52 return dynamic_threshold
53
54 def compute_fusion_mask(self, importance_scores, tukey_fence=1.5):
55 threshold = self.calculate_dynamic_threshold(importance_scores, tukey_fence)
56 fusion_mask = (importance_scores >= threshold).float()
57 return fusion_mask, threshold
58
59
60class MultiFusionMergeTask(Task[torch.Tensor]):
61 gather_tensors: MergeTensorInput
62 base_model: ModelReference
63 weight_info: WeightInfo
64 importance_metric: str = "delta_mag"
65 tukey_fence: float = 1.5
66
67 def uses_accelerator(self) -> bool:
68 return True
69
70 def arguments(self) -> Dict[str, Task]:
71 return {"tensors": self.gather_tensors}
72
73 def execute(self, tensors: Dict[ModelReference, torch.Tensor]) -> torch.Tensor:
74 if len(tensors) == 1:
75 return list(tensors.values())[0]
76 elif len(tensors) != 2:
77 raise RuntimeError("MutliFusion merge expects exactly two models")
78 elif self.base_model not in tensors:
79 raise RuntimeError("Base model not in input tensors")
80
81 [a, b] = list(tensors.items())
82 if a[0] != self.base_model:
83 [a, b] = [b, a]
84 prepped_tensors = [a[1], b[1]]
85
86 rectify_embed_sizes(self.weight_info, prepped_tensors)
87
88 importance_scores = self._compute_importance(
89 prepped_tensors[1], prepped_tensors[0]
90 )
91 dynamic_threshold_fusion = DynamicThresholdFusion()
92 fusion_mask, _threshold = dynamic_threshold_fusion.compute_fusion_mask(
93 importance_scores, tukey_fence=self.tukey_fence
94 )
95
96 delta = prepped_tensors[1] - prepped_tensors[0]
97 masked_delta = delta * fusion_mask
98 fused = prepped_tensors[0] + masked_delta
99
100 return fused
101
102 def _compute_importance(
103 self, params: torch.Tensor, base_params: torch.Tensor, eps: float = 1e-8
104 ) -> torch.Tensor:
105 if self.importance_metric == "kl_div":
106 return self._compute_kl_div_importance(params, base_params, eps)
107 elif self.importance_metric == "delta_mag":
108 return self._compute_delta_mag_importance(params, base_params)
109 elif self.importance_metric == "cosine_sim":
110 return self._compute_cosine_sim_importance(params, base_params)
111 elif self.importance_metric == "fisher_grad":
112 return self._compute_fisher_grad_importance(params, base_params)
113 else:
114 raise ValueError(f"Unknown importance metric: {self.importance_metric}")
115
116 def _compute_kl_div_importance(
117 self, params: torch.Tensor, base_params: torch.Tensor, eps: float = 1e-8
118 ) -> torch.Tensor:
119 diff = (params - base_params).abs()
120 p = F.softmax(params, dim=-1) + eps
121 q = F.softmax(base_params, dim=-1) + eps
122 kl_div = torch.sum(p * torch.log(p / q), dim=-1)
123 return diff * kl_div.unsqueeze(-1)
124
125 def _compute_delta_mag_importance(
126 self, params: torch.Tensor, base_params: torch.Tensor
127 ) -> torch.Tensor:
128 # Magnitude of delta - used by TIES/DARE/DELLA
129 delta = params - base_params
130 return delta.abs()
131
132 def _compute_cosine_sim_importance(
133 self, params: torch.Tensor, base_params: torch.Tensor
134 ) -> torch.Tensor:
135 # Cosine similarity based - inspired by Model Stock
136 delta = params - base_params
137 delta_flat = delta.view(-1)
138 base_flat = base_params.view(-1)
139
140 # Compute cosine similarity between delta and base
141 dot_product = torch.dot(delta_flat, base_flat)
142 norm_delta = torch.norm(delta_flat)
143 norm_base = torch.norm(base_flat)
144
145 # Avoid division by zero
146 if norm_delta == 0 or norm_base == 0:
147 return torch.zeros_like(delta)
148
149 cosine_sim = dot_product / (norm_delta * norm_base)
150 # Convert similarity to importance (higher similarity = more important)
151 importance = delta.abs() * (1 + cosine_sim.abs())
152 return importance.view_as(delta)
153
154 def _compute_fisher_grad_importance(
155 self, params: torch.Tensor, base_params: torch.Tensor
156 ) -> torch.Tensor:
157 # Fisher/gradient-based importance - inspired by Karcher/Fisher information
158 # Since we don't have access to gradients/data, we use a proxy based on
159 # the magnitude and variance of the delta
160 delta = params - base_params
161
162 # Compute variance along the last dimension as a proxy for Fisher information
163 if delta.dim() > 1:
164 variance = torch.var(delta, dim=-1, keepdim=True)
165 else:
166 variance = delta.var().unsqueeze(0)
167
168 ## # Importance combines magnitude and variance
169 ## importance = delta.abs() * (variance + 1e-8)
170 ## return importance
171
172 # Use variance directly as importance (SCE-style) rather than variance * magnitude
173 importance = variance + 1e-8
174 return importance
175
176class MultiFusionMerge(MergeMethod):
177 def name(self) -> str:
178 return "multi_fusion"
179
180 @override
181 def pretty_name(self) -> Optional[str]:
182 return "Multi Fusion"
183
184 @override
185 def reference_url(self) -> Optional[str]:
186 return "https://huggingface.co/Naphula/Slimaki-Tavern-24B-v1.3"
187
188 def parameters(self) -> List[ConfigParameterDef]:
189 return [
190 ConfigParameterDef(
191 name="importance_metric",
192 required=False,
193 default_value="delta_mag",
194 ),
195 ConfigParameterDef(
196 name="tukey_fence",
197 required=False,
198 default_value=1.5,
199 )
200 ]
201
202 def make_task(
203 self,
204 output_weight: WeightInfo,
205 tensors: MergeTensorInput,
206 base_model: Optional[ModelReference],
207 parameters: Dict[str, Any],
208 **kwargs,
209 ) -> Task[torch.Tensor]:
210 return MultiFusionMergeTask(
211 gather_tensors=tensors,
212 weight_info=output_weight,
213 base_model=base_model,
214 importance_metric=parameters["importance_metric"],
215 tukey_fence=parameters["tukey_fence"]
216 )