Wiself commited on
Commit
99db05a
·
verified ·
1 Parent(s): f5b6f25

Upload vlib/della.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. vlib/della.py +176 -0
vlib/della.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """DELLA magnitude pruning + task-vector combine over voice head tensors.
2
+
3
+ Clean-room port of the DELLA spec (arXiv:2406.11617) in numpy. No mergekit
4
+ dependency, no vendored code: mergekit is LGPL-3.0 and the authors' snapshot
5
+ carries no license grant, so both served as read-only references only.
6
+
7
+ One deliberate fork from upstream mergekit's ``della_magprune``, recorded
8
+ here rather than collapsed: upstream rescales L1 against the whole-tensor
9
+ sum, while ``magprune`` rescales each row to its own pre-prune ``sum|x|``.
10
+ Per-row keeps every output neuron's scale (each head row is one token's
11
+ direction), stays bounded at aggressive sparsity, and keeps row-blocking
12
+ exactly correct (a global rescale would need the full tensor at once).
13
+ Equivalence spot-checks against mergekit therefore expect "close", not
14
+ bit-exact: same family, different rescale locality.
15
+
16
+ Determinism: ``seed`` spawns one child stream per voice
17
+ (``SeedSequence(seed)``), so a seeded run is bit-identical regardless of
18
+ ``block_rows`` — each voice's Bernoulli draws are consumed in row order on
19
+ its own stream. ``seed=None`` gives fresh entropy per voice
20
+ (nondeterministic, as upstream).
21
+ """
22
+
23
+ VALID_RESCALES = ("l1", "inv_p")
24
+
25
+
26
+ def _validate_density_epsilon(density, epsilon):
27
+ if density + epsilon >= 1 or density - epsilon <= 0:
28
+ raise ValueError(
29
+ "della density±epsilon must stay in (0,1): "
30
+ f"got density={density} epsilon={epsilon}"
31
+ )
32
+
33
+
34
+ def _check_rescale(rescale):
35
+ if rescale not in VALID_RESCALES:
36
+ raise ValueError(f"della rescale must be 'l1' or 'inv_p', got {rescale!r}")
37
+
38
+
39
+ def magprune(delta, density=0.5, epsilon=0.1, rescale="l1", rng=None):
40
+ """Rank-weighted Bernoulli prune of one delta, row-wise. Always F32 out.
41
+
42
+ Keep-probability ramps linearly with within-row magnitude rank, from
43
+ ``density-epsilon`` (smallest) to ``density+epsilon`` (largest), then a
44
+ seeded Bernoulli draw decides survivors. ``rescale="l1"`` restores each
45
+ surviving row's ``sum|x|``; ``rescale="inv_p"`` divides each survivor by
46
+ its own keep-prob (paper-faithful, heavy-tailed).
47
+ """
48
+ import numpy as np
49
+ F32 = np.float32
50
+ _check_rescale(rescale)
51
+ a = np.asarray(delta, dtype=F32)
52
+ if density >= 1:
53
+ return a
54
+ if density <= 0:
55
+ return np.zeros_like(a)
56
+ _validate_density_epsilon(density, epsilon)
57
+ orig_shape = a.shape
58
+ w = np.atleast_2d(a)
59
+ order = np.argsort(np.abs(w), axis=-1, kind="stable")
60
+ ranks = np.argsort(order, axis=-1).astype(F32) + F32(1.0)
61
+ rmin = ranks.min(axis=-1, keepdims=True)
62
+ rmax = ranks.max(axis=-1, keepdims=True)
63
+ denom = rmax - rmin
64
+ # Single-column rows have no ordering; fall back to flat density.
65
+ rank_norm = np.divide(ranks - rmin, denom,
66
+ out=np.full_like(ranks, F32(0.5)),
67
+ where=denom > 0)
68
+ rank_norm = np.clip(rank_norm, F32(0.0), F32(1.0))
69
+ d = F32(density)
70
+ e = F32(epsilon)
71
+ probs = (d - e) + rank_norm * (F32(2.0) * e)
72
+ if rng is None:
73
+ rng = np.random.default_rng()
74
+ mask = rng.binomial(1, probs).astype(F32)
75
+ masked = w * mask
76
+ if rescale == "l1":
77
+ before = np.abs(w).sum(axis=-1, keepdims=True)
78
+ after = np.abs(masked).sum(axis=-1, keepdims=True)
79
+ ok = (before >= 1e-7) & (after >= 1e-7)
80
+ scale = np.where(ok, before / np.maximum(after, F32(1e-30)), F32(1.0))
81
+ out = masked * scale
82
+ else: # inv_p
83
+ out = masked / probs
84
+ if not np.all(np.isfinite(out)):
85
+ raise ValueError("della inv_p rescale produced NaN/Inf "
86
+ "- refusing to write a corrupt voice")
87
+ return np.asarray(out.reshape(orig_shape), dtype=F32)
88
+
89
+
90
+ def combine(base, deltas, weights, lambda_=1.0, normalize=True):
91
+ """TIES-sum combine of pruned deltas onto base. F32 in, F32 out.
92
+
93
+ Weight-scale, elect the majority sign per position (ties go positive),
94
+ average the elected (``normalize``) or sum them, scale by ``lambda_``,
95
+ add base. Positions every voice pruned divide by 1, not 0.
96
+ """
97
+ import numpy as np
98
+ F32 = np.float32
99
+ b = np.asarray(base, dtype=F32)
100
+ ds = [np.asarray(d, dtype=F32) for d in deltas]
101
+ if not ds:
102
+ return b
103
+ if len(weights) != len(ds):
104
+ raise ValueError(f"della weights ({len(weights)}) != deltas ({len(ds)})")
105
+ for i, d in enumerate(ds):
106
+ if d.shape != b.shape:
107
+ raise ValueError(f"della delta {i} shape {d.shape} != base {b.shape}")
108
+ wcol = np.asarray(list(weights), dtype=F32).reshape((-1,) + (1,) * b.ndim)
109
+ W = np.stack(ds).astype(F32, copy=False) * wcol
110
+ majority = np.where(W.sum(axis=0) >= 0, F32(1.0), F32(-1.0))
111
+ elected = np.sign(W) == majority
112
+ mixed = np.where(elected, W, F32(0.0)).sum(axis=0)
113
+ divisor = np.where(elected, wcol, F32(0.0)).sum(axis=0)
114
+ divisor = np.where(divisor == 0, F32(1.0), divisor)
115
+ if normalize:
116
+ mixed = mixed / divisor
117
+ if lambda_ != 1:
118
+ mixed = mixed * F32(lambda_)
119
+ return b + mixed
120
+
121
+
122
+ def merge(base, voices, weights=None, density=0.5, epsilon=0.1, lambda_=1.0,
123
+ rescale="l1", normalize=True, seed=None, block_rows=4096):
124
+ """Stream one DELLA merge: per-block (voice-base) prune, then combine.
125
+
126
+ Only ``block_rows`` rows per voice are pruned/combined at once, so the
127
+ full ``n_voices x rows x cols`` delta stack is never materialized; peak
128
+ besides the inputs/output is ``O(block_rows x cols x n_voices)``.
129
+ Blocking is exactly correct: pruning ranks within rows and election
130
+ votes per position, neither crosses a block boundary. Each voice draws
131
+ on its own ``SeedSequence(seed)`` child stream, so repartitioning blocks
132
+ never changes the result. Output is cast back to the base input's dtype.
133
+ """
134
+ import numpy as np
135
+ F32 = np.float32
136
+ _check_rescale(rescale)
137
+ b = np.asarray(base)
138
+ out_dtype = b.dtype
139
+ bw = np.asarray(b, dtype=F32)
140
+ vs = [np.asarray(v) for v in voices]
141
+ if not vs:
142
+ return np.asarray(b, dtype=out_dtype)
143
+ wts = [1.0] * len(vs) if weights is None else list(weights)
144
+ if len(wts) != len(vs):
145
+ raise ValueError(f"della weights ({len(wts)}) != voices ({len(vs)})")
146
+ for i, v in enumerate(vs):
147
+ if v.shape != bw.shape:
148
+ raise ValueError(f"della voice {i} shape {v.shape} != base {bw.shape}")
149
+ if not (density >= 1 or density <= 0):
150
+ _validate_density_epsilon(density, epsilon)
151
+ squeeze = False
152
+ if bw.ndim == 1:
153
+ squeeze = True
154
+ bw = bw[None, :]
155
+ vs = [v[None, :] for v in vs]
156
+ if bw.ndim < 1:
157
+ raise ValueError("della merge needs at least 1-D tensors")
158
+ if seed is None:
159
+ rngs = [np.random.default_rng() for _ in vs]
160
+ else:
161
+ rngs = [np.random.default_rng(s)
162
+ for s in np.random.SeedSequence(seed).spawn(len(vs))]
163
+ vw = [np.asarray(v, dtype=F32) for v in vs]
164
+ out = np.empty_like(bw)
165
+ for start in range(0, bw.shape[0], block_rows):
166
+ sl = slice(start, min(start + block_rows, bw.shape[0]))
167
+ bb = bw[sl]
168
+ pruned = [magprune(v[sl] - bb, density=density, epsilon=epsilon,
169
+ rescale=rescale, rng=rngs[vi])
170
+ for vi, v in enumerate(vw)]
171
+ out[sl] = combine(bb, pruned, wts, lambda_=lambda_, normalize=normalize)
172
+ if squeeze:
173
+ out = out.reshape(b.shape)
174
+ if out_dtype != F32:
175
+ return out.astype(out_dtype, copy=False)
176
+ return out