Skip to content

Commit c93057d

Browse files
remi-orvasqu
andauthored
Kimi linear (#48250)
* Config * Finsh config * Modularized the cfg * draft modeling * draft 2 * Experts * Attention * KDA init * Decoder and pretrained * Nits * Done * Auto fixes * Fix bugs * Fix missing mapping * Config done * Conversion mapping, Reshape op, Bugfix * Fix last bugs, gnertion is bad but finishes * Fix activation * Notes * Fix internal import chain * Fixes * Tests * Docs * Small fixes * Nitssssss * Nits * Added mapping for tokenizer * Apply batched suggestions from code review Co-authored-by: Anton Vlasjuk <73884904+vasqu@users.noreply.github.com> * Doc review * MAke fix repo * Inherit torch KDA from GLM * Replaced the gated norm with GLM 5 next * Replace KDA module * Fix decoder * Revert the conversion ops now that we inherit * Review compliance moar * Review end * Text nit * REview (all but tests) * Remove gate lower bound * Fixes to run * Fix decoder forward * Update tests * Fixes * Skip and fixes * Removed a test and style * nit * Update src/transformers/models/kimi_linear/modular_kimi_linear.py Co-authored-by: Anton Vlasjuk <73884904+vasqu@users.noreply.github.com> * Review nits * Revert change * Test expectations * Fixed attribute map oopsie * Useless CODEPATH comment * Code path again * Remove unused var --------- Co-authored-by: Anton Vlasjuk <73884904+vasqu@users.noreply.github.com>
1 parent f62dc9b commit c93057d

21 files changed

Lines changed: 2054 additions & 8 deletions

docs/source/en/_toctree.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,8 @@
727727
title: JetMoe
728728
- local: model_doc/jina_embeddings_v3
729729
title: jina_embeddings_v3
730+
- local: model_doc/kimi_linear
731+
title: KimiLinear
730732
- local: model_doc/laguna
731733
title: Laguna
732734
- local: model_doc/led
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<!--Copyright 2026 the HuggingFace Inc. team. All rights reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
4+
the License. You may obtain a copy of the License at
5+
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
8+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
9+
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
10+
specific language governing permissions and limitations under the License.
11+
12+
⚠️ Note that this file is in Markdown but contains specific syntax for our doc-builder (similar to MDX) that may not be
13+
rendered properly in your Markdown viewer.
14+
15+
-->
16+
*This model was published in HF papers on 2025-10-30 and contributed to Hugging Face Transformers on 2026-09-04.*
17+
18+
## Overview
19+
20+
Kimi Linear is a hybrid linear attention architecture from Moonshot AI, introduced in
21+
[Kimi Linear: An Expressive, Efficient Attention Architecture](https://huggingface.co/papers/2510.26692).
22+
23+
At its core is **Kimi Delta Attention (KDA)**, a refinement of [Gated DeltaNet](https://huggingface.co/papers/2412.06464)
24+
that gives each key channel its own forget gate, so the recurrent state decays per channel instead of per head. KDA is
25+
used in most layers; every fourth layer keeps a full-attention block that reuses DeepSeek-V3's Multi-head Latent
26+
Attention (MLA), and the feed-forward blocks are DeepSeek-V3-style MoE with a shared expert.
27+
28+
The abstract from the paper is the following:
29+
30+
*We introduce Kimi Linear, a hybrid linear attention architecture that, for the first time, outperforms full attention
31+
under fair comparisons across various scenarios -- including short-context, long-context, and reinforcement learning
32+
(RL) scaling regimes. At its core lies Kimi Delta Attention (KDA), an expressive linear attention module that extends
33+
Gated DeltaNet with a finer-grained gating mechanism.*
34+
35+
Two things are worth knowing when reading the modeling code:
36+
37+
- **The model is NoPE.** Every released checkpoint sets `mla_use_nope=True`, so no rotary embedding is applied
38+
anywhere: the KDA layers encode position through their recurrence, and the full-attention layers are left without
39+
positional encoding. The `qk_rope_head_dim` slice still exists in the projections, it is simply never rotated.
40+
- **The layer pattern comes from the checkpoint.** `linear_attn_config` lists `kda_layers` / `full_attn_layers` with
41+
1-based indices; the config converts them into the standard `layer_types` list.
42+
43+
This model was contributed by [Remi Ouazan](https://huggingface.co/ror).
44+
The original code can be found [here](https://github.com/MoonshotAI/Kimi-Linear).
45+
46+
## Usage examples
47+
48+
```python
49+
from transformers import AutoModelForCausalLM, AutoTokenizer
50+
51+
52+
model_name = "moonshotai/Kimi-Linear-48B-A3B-Instruct"
53+
54+
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
55+
tokenizer = AutoTokenizer.from_pretrained(model_name)
56+
57+
messages = [{"role": "user", "content": "Tell me about the french revolution."}]
58+
model_inputs = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt").to(model.device)
59+
60+
generated_ids = model.generate(**model_inputs, max_new_tokens=128)
61+
output_ids = generated_ids[0][len(model_inputs.input_ids[0]) :]
62+
63+
print(tokenizer.decode(output_ids, skip_special_tokens=True))
64+
```
65+
66+
The KDA layers run on a pure PyTorch implementation by default. Installing
67+
[`kernels`](https://github.com/huggingface/kernels) (`pip install -U kernels`) and passing `use_kernels=True`
68+
in `from_pretrained` makes them dispatch to custom kernels instead, which is considerably faster for long sequences.
69+
70+
## KimiLinearConfig
71+
72+
[[autodoc]] KimiLinearConfig
73+
74+
## KimiLinearModel
75+
76+
[[autodoc]] KimiLinearModel
77+
- forward
78+
79+
## KimiLinearForCausalLM
80+
81+
[[autodoc]] KimiLinearForCausalLM
82+
- forward

src/transformers/conversion_mapping.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1833,6 +1833,41 @@ def _build_checkpoint_conversion_mapping():
18331833
WeightRenaming(source_patterns=r"layers\.(\d+)\.transformer_block\.", target_patterns=r"layers.\1.mtp_block."),
18341834
]
18351835

1836+
mapping["kimi_linear"] = [
1837+
# Forget gate weights are attached to the forget gate module instead of the attention
1838+
WeightRenaming(source_patterns=r"self_attn\.f_a_proj\.", target_patterns=r"self_attn.forget_gate.f_a_proj."),
1839+
WeightRenaming(source_patterns=r"self_attn\.f_b_proj\.", target_patterns=r"self_attn.forget_gate.f_b_proj."),
1840+
WeightRenaming(source_patterns=r"self_attn\.dt_bias", target_patterns=r"self_attn.forget_gate.dt_bias"),
1841+
WeightRenaming(source_patterns=r"self_attn\.A_log", target_patterns=r"self_attn.forget_gate.A_log"),
1842+
# Conv weights are stacked before runtime
1843+
WeightConverter(
1844+
source_patterns=[
1845+
"self_attn.q_conv1d.weight",
1846+
"self_attn.k_conv1d.weight",
1847+
"self_attn.v_conv1d.weight",
1848+
],
1849+
target_patterns="self_attn.conv1d.weight",
1850+
operations=[Concatenate(dim=0)],
1851+
),
1852+
# Rename MoEs so they have the same prefix as the MLPs
1853+
WeightRenaming(source_patterns=r"\.block_sparse_moe\.", target_patterns=r"\.mlp\."),
1854+
# Concatenate w1 (gate) and w3 (up) weights into a single weight and merge across experts
1855+
WeightConverter(
1856+
source_patterns=[
1857+
r"\.experts.*.w1.weight",
1858+
r"\.experts.*.w3.weight",
1859+
],
1860+
target_patterns=r"\.experts.gate_up_proj",
1861+
operations=[MergeModulelist(dim=0), Concatenate(dim=1)],
1862+
),
1863+
# Merge w2 (down) weights across experts
1864+
WeightConverter(
1865+
source_patterns=r"\.experts.*.w2.weight",
1866+
target_patterns=r"\.experts.down_proj",
1867+
operations=[MergeModulelist(dim=0)],
1868+
),
1869+
]
1870+
18361871
for model_type, base_pattern in _MODEL_TO_CONVERSION_PATTERN.items():
18371872
if model_type in mapping:
18381873
continue

src/transformers/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@
241241
from .jetmoe import *
242242
from .jina_embeddings_v3 import *
243243
from .kimi_k25 import *
244+
from .kimi_linear import *
244245
from .kosmos2 import *
245246
from .kosmos2_5 import *
246247
from .kyutai_speech_to_text import *

src/transformers/models/auto/auto_mappings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@
333333
("jina_embeddings_v3", "JinaEmbeddingsV3Config"),
334334
("kimi_k25", "Kimi_K25Config"),
335335
("kimi_k25_vision", "Kimi_K25VisionConfig"),
336+
("kimi_linear", "KimiLinearConfig"),
336337
("kosmos-2", "Kosmos2Config"),
337338
("kosmos-2.5", "Kosmos2_5Config"),
338339
("kosmos_2_5_text_model", "Kosmos2_5TextConfig"),

src/transformers/models/auto/modeling_auto.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ class _BaseModelWithGenerate(PreTrainedModel, GenerationMixin):
281281
("jina_embeddings_v3", "JinaEmbeddingsV3Model"),
282282
("kimi_k25", "Kimi_K25Model"),
283283
("kimi_k25_vision", "Kimi_K25VisionModel"),
284+
("kimi_linear", "KimiLinearModel"),
284285
("kosmos-2", "Kosmos2Model"),
285286
("kosmos-2.5", "Kosmos2_5Model"),
286287
("kyutai_speech_to_text", "KyutaiSpeechToTextModel"),
@@ -787,6 +788,7 @@ class _BaseModelWithGenerate(PreTrainedModel, GenerationMixin):
787788
("jais2", "Jais2ForCausalLM"),
788789
("jamba", "JambaForCausalLM"),
789790
("jetmoe", "JetMoeForCausalLM"),
791+
("kimi_linear", "KimiLinearForCausalLM"),
790792
("laguna", "LagunaForCausalLM"),
791793
("lfm2", "Lfm2ForCausalLM"),
792794
("lfm2_moe", "Lfm2MoeForCausalLM"),

src/transformers/models/auto/tokenization_auto.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
("jais2", "GPT2Tokenizer" if is_tokenizers_available() else None),
174174
("jina_embeddings_v3", "XLMRobertaTokenizer" if is_tokenizers_available() else None),
175175
("kimi_k25", "TokenizersBackend" if is_tokenizers_available() else None),
176+
("kimi_linear", "TokenizersBackend" if is_tokenizers_available() else None),
176177
("kosmos-2", "TokenizersBackend" if is_tokenizers_available() else None),
177178
("lasr_ctc", "LasrTokenizer" if is_tokenizers_available() else None),
178179
("lasr_encoder", "LasrTokenizer" if is_tokenizers_available() else None),

src/transformers/models/axk1/modeling_axk1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ def forward(self, x):
143143

144144

145145
class AXK1TopkRouter(nn.Module):
146-
def __init__(self, config):
146+
def __init__(self, config: AXK1Config):
147147
super().__init__()
148148
self.top_k = config.num_experts_per_tok
149149
self.num_experts = config.num_local_experts

src/transformers/models/deepseek_v3/modeling_deepseek_v3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def forward(self, x):
129129

130130

131131
class DeepseekV3TopkRouter(nn.Module):
132-
def __init__(self, config):
132+
def __init__(self, config: DeepseekV3Config):
133133
super().__init__()
134134
self.top_k = config.num_experts_per_tok
135135
self.num_experts = config.num_local_experts

src/transformers/models/deepseek_v3/modular_deepseek_v3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def apply_rotary_pos_emb_interleave(q, k, cos, sin, position_ids=None, unsqueeze
8585

8686

8787
class DeepseekV3TopkRouter(DeepseekV2TopkRouter):
88-
def __init__(self, config):
88+
def __init__(self, config: DeepseekV3Config):
8989
super().__init__(config)
9090
del self.topk_method
9191
self.num_experts = config.num_local_experts

0 commit comments

Comments
 (0)