forked from TheTom/llama-cpp-turboquant
-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathinkling.py
More file actions
350 lines (302 loc) · 16.3 KB
/
Copy pathinkling.py
File metadata and controls
350 lines (302 loc) · 16.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
from __future__ import annotations
from typing import Callable, Iterable, TYPE_CHECKING
if TYPE_CHECKING:
from torch import Tensor
from .base import MmprojModel, ModelBase, TextModel, gguf, logger
@ModelBase.register("InklingForConditionalGeneration")
class InklingModel(TextModel):
model_arch = gguf.MODEL_ARCH.INKLING
undo_permute = False
_SKIP_PREFIXES = ("model.visual.", "model.audio.", "model.mtp.")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# explicit raises (not assert, stripped by python -O) guard the single supported variant
hp = self.hparams
# normalize keys renamed by HF-port re-saved configs back to checkpoint names
if "dense_intermediate_size" not in hp and "moe_intermediate_size" in hp:
hp["dense_intermediate_size"] = hp["intermediate_size"]
hp["intermediate_size"] = hp["moe_intermediate_size"]
if "sconv_kernel_size" not in hp and "conv_kernel_size" in hp:
hp["sconv_kernel_size"] = hp["conv_kernel_size"]
if "dense_mlp_idx" not in hp and hp.get("mlp_layer_types"):
types = hp["mlp_layer_types"]
hp["dense_mlp_idx"] = next((i for i, t in enumerate(types) if t != "dense"), len(types))
if hp.get("gate_activation", "sigmoid") != "sigmoid":
raise NotImplementedError(
f"unsupported gate_activation {hp.get('gate_activation')!r}; only 'sigmoid' is implemented"
)
for flag, want in (
("norm_after_topk", True),
("shared_expert_sink", True),
("use_sconv", True),
("use_embed_norm", True),
("use_gate_bias", True),
("use_global_scale", True),
):
if hp.get(flag, want) is not want:
raise NotImplementedError(f"unsupported {flag}={hp.get(flag)!r}; only {want} is implemented")
if hp.get("q_bias", False) is not False or hp.get("o_bias", False) is not False:
raise NotImplementedError("attention q_bias / o_bias are not supported")
if hp.get("final_logit_softcapping") not in (None, 0, 0.0):
raise NotImplementedError(
f"final_logit_softcapping={hp.get('final_logit_softcapping')!r} is not supported"
)
if hp["swa_head_dim"] != hp["head_dim"]:
raise ValueError(f"swa_head_dim {hp['swa_head_dim']} must equal head_dim {hp['head_dim']}")
if hp["swa_num_attention_heads"] != hp["num_attention_heads"]:
raise ValueError(
f"swa_num_attention_heads {hp['swa_num_attention_heads']} must equal "
f"num_attention_heads {hp['num_attention_heads']}"
)
# context length comes from model_max_length per the design contract
if (mml := hp.get("model_max_length")) is not None:
self.hparams["max_position_embeddings"] = mml
# checked by the base find_hparam list before the MoE "intermediate_size"
self.hparams["prefix_dense_intermediate_size"] = hp["dense_intermediate_size"]
self._local_layer_flags = self._get_local_layer_flags()
self.hparams["num_key_value_heads"] = [
hp["swa_num_key_value_heads"] if is_local else hp["num_key_value_heads"]
for is_local in self._local_layer_flags
]
def _get_local_layer_flags(self) -> list[bool]:
# local_layer_ids is authoritative; a round-tripped layer_types may be stale and must not override it
n_layer = self.hparams["num_hidden_layers"]
local_ids = self.hparams.get("local_layer_ids")
if local_ids is None:
# default: global at id % 6 == 5; omitted/null must not collapse to all-global (explicit [] does)
local_ids = [i for i in range(n_layer) if i % 6 != 5]
local_ids = set(local_ids)
return [i in local_ids for i in range(n_layer)]
def get_vocab_base(self) -> tuple[list[str], list[int], str]:
tokens, toktypes, tokpre = super().get_vocab_base()
# dedicated pre-type: o200k-family regex that keeps combining marks attached to base letters
tokpre = "inkling"
import gguf as _gguf
n_vocab = self.hparams["vocab_size"]
n_unpadded = self.hparams.get("unpadded_vocab_size") or n_vocab
if len(tokens) != n_vocab:
raise ValueError(f"Inkling tokenizer produced {len(tokens)} entries, expected {n_vocab}")
# force-CONTROL special ids from added_tokens_decoder, else the trailing-60 convention
try:
import json as _json
import pathlib as _pl
tc = _json.loads((_pl.Path(self.dir_model) / "tokenizer_config.json").read_text())
special_ids = sorted(int(i) for i, d in tc.get("added_tokens_decoder", {}).items() if d.get("special"))
except Exception:
special_ids = list(range(n_unpadded - 60, n_unpadded))
for tid in special_ids:
if 0 <= tid < n_vocab:
toktypes[tid] = _gguf.TokenType.CONTROL
if any(t != _gguf.TokenType.UNUSED for t in toktypes[n_unpadded:]):
raise ValueError("real tokens found at/above unpadded_vocab_size; padded-vocab mask would hide them")
return tokens, toktypes, tokpre
def set_vocab(self):
self._set_vocab_gpt2()
eos_id = int(self.hparams.get("eos_token_id", 200006))
if eos_id < 199998:
# HF-port configs re-save generic bos/eos defaults; the real EOS lives at 199998+
eos_id = 200006
# 200006 is the SOLE end-of-generation token; <|end_message|> (200010) is an
# intra-turn block separator and must NOT be registered eot/eog
self.gguf_writer.add_eos_token_id(eos_id)
# no BOS is ever prepended; pin bos to EOS so a stale base-tokenizer bos id never surfaces
self.gguf_writer.add_bos_token_id(eos_id)
self.gguf_writer.add_add_bos_token(False)
def set_gguf_parameters(self):
super().set_gguf_parameters()
hp = self.hparams
self.gguf_writer.add_vocab_size(hp["vocab_size"])
self.gguf_writer.add_expert_feed_forward_length(hp["intermediate_size"])
self.gguf_writer.add_expert_shared_count(hp["n_shared_experts"])
self.gguf_writer.add_expert_weights_scale(hp["route_scale"])
self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SIGMOID)
# sliding_window_size is canonical; explicit is-None fallback so a serialized 0 cannot bypass the mismatch check
canonical_window = hp["sliding_window_size"]
sliding_window = hp.get("sliding_window")
if sliding_window is None:
sliding_window = canonical_window
elif sliding_window != canonical_window:
raise ValueError(
f"sliding_window {sliding_window} disagrees with sliding_window_size "
f"{canonical_window!r}"
)
if sliding_window <= 0:
raise ValueError(f"sliding_window must be positive, got {sliding_window}")
self.gguf_writer.add_sliding_window(sliding_window)
# true = local (swa) layer
self.gguf_writer.add_sliding_window_pattern(self._local_layer_flags)
# no RoPE (arch-determined NONE); custom inkling.* keys per INKLING_DESIGN.md
arch = gguf.MODEL_ARCH_NAMES[self.model_arch]
self.gguf_writer.add_uint32(f"{arch}.d_rel", hp["d_rel"])
self.gguf_writer.add_uint32(f"{arch}.rel_extent", hp["rel_extent"])
self.gguf_writer.add_uint32(f"{arch}.rel_extent_swa", sliding_window)
self.gguf_writer.add_uint32(f"{arch}.shortconv_kernel", hp["sconv_kernel_size"])
self.gguf_writer.add_uint32(f"{arch}.dense_block_count", hp["dense_mlp_idx"])
self.gguf_writer.add_float32(f"{arch}.logit_scale_denom", hp["logits_mup_width_multiplier"])
self.gguf_writer.add_uint32(f"{arch}.log_scaling_n_floor", int(hp.get("log_scaling_n_floor") or 0))
self.gguf_writer.add_float32(f"{arch}.log_scaling_alpha", hp.get("log_scaling_alpha", 0.0))
self.gguf_writer.add_uint32(f"{arch}.unpadded_vocab_size", hp["unpadded_vocab_size"])
logger.info(f"gguf: (inkling) swa pattern (true=local) = {self._local_layer_flags}")
logger.info(f"gguf: (inkling) unpadded_vocab_size = {hp['unpadded_vocab_size']}")
@classmethod
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
name, gen = item
if name.startswith(cls._SKIP_PREFIXES):
return None
name = name.replace("model.llm.", "model.")
# parameter has no ".weight"-style suffix in the checkpoint
name = name.replace("rel_logits_proj.proj", "rel_logits_proj.weight")
return super().filter_tensors((name, gen))
@staticmethod
def _deinterleave_w13(w13: Tensor) -> tuple[Tensor, Tensor]:
# interleaved SwiGLU along the output rows: silu(z[..., ::2]) * z[..., 1::2]
gate = w13[..., 0::2, :].contiguous()
up = w13[..., 1::2, :].contiguous()
return gate, up
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
# short convs: [C, 1, K] -> [C, K] (same layout as LFM2 shortconv.conv)
if name.endswith("_sconv.weight"):
data_torch = data_torch.squeeze(1)
return [(self.map_tensor_name(name), data_torch)]
if name.endswith(".mlp.w13_dn.weight"):
assert bid is not None
gate, up = self._deinterleave_w13(data_torch)
return [
(self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE, bid), gate),
(self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP, bid), up),
]
if name.endswith(".mlp.global_scale") or name.endswith(".mlp.gate.global_scale"):
assert bid is not None
return [(self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GSCALE, bid), data_torch.float())]
if name.endswith(".mlp.gate.bias"):
assert bid is not None
return [(self.format_tensor_name(gguf.MODEL_TENSOR.FFN_EXP_PROBS_B, bid, ".bias"), data_torch.float())]
if name.endswith(".mlp.experts.w13_weight"):
assert bid is not None
gate, up = self._deinterleave_w13(data_torch)
return [
(self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE_EXP, bid), gate),
(self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP_EXP, bid), up),
]
if name.endswith(".mlp.experts.w2_weight"):
assert bid is not None
return [(self.format_tensor_name(gguf.MODEL_TENSOR.FFN_DOWN_EXP, bid), data_torch)]
# shared experts stored stacked for mul_mat_id
if name.endswith(".mlp.shared_experts.shared_w13_weight"):
assert bid is not None
gate, up = self._deinterleave_w13(data_torch)
return [
(self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE_SHEXP, bid), gate),
(self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP_SHEXP, bid), up),
]
if name.endswith(".mlp.shared_experts.shared_w2_weight"):
assert bid is not None
return [(self.format_tensor_name(gguf.MODEL_TENSOR.FFN_DOWN_SHEXP, bid), data_torch)]
return [(self.map_tensor_name(name), data_torch)]
def tensor_force_quant(self, name: str, new_name: str, bid: int | None, n_dims: int):
# used in fp32 rel-bias math; keep full precision
if new_name.endswith("attn_rel_proj.weight"):
return gguf.GGMLQuantizationType.F32
# ggml_ssm_conv kernels are F32-only
if ".shortconv_" in new_name:
return gguf.GGMLQuantizationType.F32
return super().tensor_force_quant(name, new_name, bid, n_dims)
@ModelBase.register("InklingForConditionalGeneration")
class InklingMmprojModel(MmprojModel):
"""Export Inkling's hMLP and dMel towers as one mtmd projector."""
has_vision_encoder = True
has_audio_encoder = True
_IMAGE_MEAN = [0.48145466, 0.4578275, 0.40821073]
_IMAGE_STD = [0.26862954, 0.2613026, 0.2757771]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
assert self.hparams_vision is not None
hp = self.hparams_vision
expected = {
"vision_encoder_type": "hmlp",
"patch_size": 40,
"temporal_patch_size": 2,
"n_channels": 3,
"n_layers": 4,
"decoder_dmodel": 6144,
"use_vision_norm": True,
}
for key, want in expected.items():
got = hp.get(key, want)
if got != want:
raise NotImplementedError(
f"Inkling mmproj requires vision_config.{key}={want!r}, got {got!r}"
)
assert self.hparams_audio is not None
ahp = self.hparams_audio
audio_expected = {
"audio_mode": "dmel",
"decoder_dmodel": 6144,
"n_mel_bins": 80,
"mel_vocab_size": 16,
"use_audio_norm": True,
}
for key, want in audio_expected.items():
got = ahp.get(key, want)
if got != want:
raise NotImplementedError(
f"Inkling mmproj requires audio_config.{key}={want!r}, got {got!r}"
)
def set_gguf_parameters(self):
hp = self.hparams_vision
assert hp is not None
self.gguf_writer.add_file_type(self.ftype)
self.gguf_writer.add_clip_has_vision_encoder(True)
self.gguf_writer.add_clip_has_audio_encoder(True)
self.gguf_writer.add_clip_vision_projector_type(gguf.VisionProjectorType.INKLING)
self.gguf_writer.add_clip_audio_projector_type(gguf.VisionProjectorType.INKLING)
self.gguf_writer.add_vision_projection_dim(hp["decoder_dmodel"])
# clip.cpp requires these common fields even though hMLP is not a ViT.
self.gguf_writer.add_vision_image_size(hp["patch_size"])
self.gguf_writer.add_vision_patch_size(hp["patch_size"])
self.gguf_writer.add_vision_embedding_length(hp["n_channels"])
self.gguf_writer.add_vision_feed_forward_length(0)
self.gguf_writer.add_vision_block_count(hp["n_layers"])
self.gguf_writer.add_vision_head_count(1)
self.gguf_writer.add_vision_attention_layernorm_eps(1e-6)
self.gguf_writer.add_vision_image_mean(self._IMAGE_MEAN)
self.gguf_writer.add_vision_image_std(self._IMAGE_STD)
ahp = self.hparams_audio
assert ahp is not None
self.gguf_writer.add_audio_projection_dim(ahp["decoder_dmodel"])
self.gguf_writer.add_audio_embedding_length(ahp["decoder_dmodel"])
self.gguf_writer.add_audio_feed_forward_length(0)
self.gguf_writer.add_audio_block_count(0)
self.gguf_writer.add_audio_head_count(1)
self.gguf_writer.add_audio_attention_layernorm_eps(1e-6)
self.gguf_writer.add_audio_num_mel_bins(ahp["n_mel_bins"])
@classmethod
def filter_tensors(cls, item):
name, gen = item
if not name.startswith(("model.visual.", "visual.", "model.audio.", "audio.")):
return None
return name, gen
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None):
del bid
if name.startswith(("model.audio.", "audio.")):
prefix = "model.audio." if name.startswith("model.audio.") else "audio."
local = name.removeprefix(prefix)
if local == "encoder.weight":
yield "a.dmel.embedding.weight", data_torch
return
if local == "final_norm.weight":
yield "a.dmel.final_norm.weight", data_torch
return
raise ValueError(f"unexpected Inkling audio tensor {name!r}")
prefix = "model.visual." if name.startswith("model.visual.") else "visual."
local = name.removeprefix(prefix)
if local == "final_norm.weight":
yield "v.hmlp.final_norm.weight", data_torch
return
parts = local.split(".")
if len(parts) == 3 and parts[0] == "layers" and parts[2] == "weight":
kind, sep, layer_s = parts[1].partition("_")
if sep and kind in ("linear", "norm") and layer_s.isdigit():
yield f"v.hmlp.{int(layer_s)}.{kind}.weight", data_torch
return
raise ValueError(f"unexpected Inkling vision tensor {name!r}")