KenteaKe11 commited on
Commit
99eb3d1
·
1 Parent(s): 667baf5

KenT-300M: EMA memory with learned per-layer alpha, 1 WNN per layer, 24 layers

Browse files
README.md CHANGED
@@ -22,28 +22,30 @@ tags:
22
 
23
  # KenT
24
 
25
- KenT is a recurrent language model from the SSM family. KenT concentrates its capacity in compact cells built from a short chain of transformation blocks, and keeps temporal memory as simple as possible.
26
 
27
  > This repo contains `trust_remote_code` code; weights are stored in **FP16**.
28
 
29
  ## Architectural Philosophy
30
 
31
- - **A short chain of compact cells.** KenT stacks a few identical recurrent cells, each containing only two transformation blocks.
32
- The cells are connected in sequence through the representation stream, and the last cell produces the final output.
 
33
  - **Invest in the transformation core.** Capacity is concentrated in the "hidden" part, where features are actually transformed,
34
- rather than in the vocabulary table or in repeating the architecture. The width and depth of this core are scaled together to
35
- stay balanced, rather than pushing a single dimension.
36
  - **Two streams, two roles.** One stream is a representation carrying information about the current token; the other is a memory
37
  stream that aggregates history. The two streams meet only through learned projections and are never mixed directly.
38
  - **Frequency as a fixed foundation, learning where it's needed.** Features are processed in the cosine-coefficient space using a
39
  **fixed, non-learned** operator, interleaved with nonlinear blocks. The fixed part carries built-in structure; the learned part
40
  focuses solely on transforming features.
41
- - **Memory via simple accumulation.** History is summarized with a cumulative average: no dedicated parameters and no implicit
42
- forgetting mechanism. The decoding state has **constant** size regardless of context length.
43
- - **Two separate depth axes.** The depth of feature transformation within a single step and the length of history remembered are
44
- two independent axes: increasing one does not change the other.
45
- - **Chunk-parallelizable.** Because memory is an associative accumulation, a long document can be split into multiple chunks,
46
- processed concurrently, and then merged, yielding the same result as sequential processing.
 
47
 
48
  The points above are **design hypotheses** that need to be verified experimentally, not performance guarantees.
49
  Detailed configuration and full formulas are not described here.
@@ -71,7 +73,7 @@ pip install torch transformers
71
  import torch
72
  from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig
73
 
74
- repo = "Vi-LAB/KenT-100M-Base" # replace with a repo id or a local directory
75
  tok = AutoTokenizer.from_pretrained(repo)
76
  model = AutoModelForCausalLM.from_pretrained(
77
  repo, trust_remote_code=True, dtype=torch.float16
@@ -116,13 +118,13 @@ for token_id in stream(model, ids, gen_cfg):
116
  Command line:
117
 
118
  ```bash
119
- python infer.py --model Vi-LAB/KenT-100M-Base --prompt-file doc.txt --chunk-size 1024
120
  ```
121
 
122
  `cache.clone()` lets you reuse the same prefix to generate multiple different branches without reloading it.
123
 
124
  ## Notes
125
 
126
- - Memory is a uniform cumulative average over every timestep seen; the model does not learn to forget or to select on its own.
127
  - The tokenizer and weights must match `vocab_size` in `config.json`.
128
  - This is a research model; please evaluate it before using it for real-world tasks.
 
22
 
23
  # KenT
24
 
25
+ KenT is a recurrent language model from the SSM family. KenT concentrates its capacity in depth: many compact layers, each with a single transformation block, and a light learned-forgetting memory.
26
 
27
  > This repo contains `trust_remote_code` code; weights are stored in **FP16**.
28
 
29
  ## Architectural Philosophy
30
 
31
+ - **Deep stack of compact layers.** KenT stacks many identical recurrent layers (24 in this ~300M model), each containing exactly
32
+ one transformation block. The layers are connected in sequence through the representation stream, and the last layer produces
33
+ the final output. Depth is prioritized: the deeper, the better, while width is kept at a level that stays balanced with it.
34
  - **Invest in the transformation core.** Capacity is concentrated in the "hidden" part, where features are actually transformed,
35
+ rather than in the vocabulary table. Parameters were scaled from ~100M to ~300M mainly by adding depth, with width unchanged,
36
+ so that both axes stay balanced (width still matters, but is not where the extra capacity went).
37
  - **Two streams, two roles.** One stream is a representation carrying information about the current token; the other is a memory
38
  stream that aggregates history. The two streams meet only through learned projections and are never mixed directly.
39
  - **Frequency as a fixed foundation, learning where it's needed.** Features are processed in the cosine-coefficient space using a
40
  **fixed, non-learned** operator, interleaved with nonlinear blocks. The fixed part carries built-in structure; the learned part
41
  focuses solely on transforming features.
42
+ - **Memory with learned forgetting.** History is summarized with an exponential moving average
43
+ `H_t = (1 - a) H_{t-1} + a u_t`, where `a` is a single learned scalar per layer. Its initial value decreases with depth
44
+ (shallow layers forget fast, deep layers remember longer). The decoding state has **constant** size regardless of context length.
45
+ - **Depth is the main axis.** There is no inner chain of blocks inside a layer any more: feature depth and memory timescale both
46
+ grow with the number of layers.
47
+ - **Chunk-parallelizable.** The moving average is a linear recurrence, so a long document can be split into blocks/chunks,
48
+ processed concurrently, and merged with an affine scan, yielding the same result as sequential processing.
49
 
50
  The points above are **design hypotheses** that need to be verified experimentally, not performance guarantees.
51
  Detailed configuration and full formulas are not described here.
 
73
  import torch
74
  from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig
75
 
76
+ repo = "Vi-LAB/KenT-300M-Base" # replace with a repo id or a local directory
77
  tok = AutoTokenizer.from_pretrained(repo)
78
  model = AutoModelForCausalLM.from_pretrained(
79
  repo, trust_remote_code=True, dtype=torch.float16
 
118
  Command line:
119
 
120
  ```bash
121
+ python infer.py --model Vi-LAB/KenT-300M-Base --prompt-file doc.txt --chunk-size 1024
122
  ```
123
 
124
  `cache.clone()` lets you reuse the same prefix to generate multiple different branches without reloading it.
125
 
126
  ## Notes
127
 
128
+ - Memory is an exponential moving average with a learned per-layer coefficient; it forgets old timesteps but does not select content on its own.
129
  - The tokenizer and weights must match `vocab_size` in `config.json`.
130
  - This is a research model; please evaluate it before using it for real-world tasks.
__pycache__/infer.cpython-310.pyc ADDED
Binary file (7.02 kB). View file
 
__pycache__/model.cpython-310.pyc ADDED
Binary file (22.6 kB). View file
 
config.json CHANGED
@@ -10,19 +10,20 @@
10
  },
11
  "vocab_size": 50257,
12
  "hidden_size": 512,
13
- "num_hidden_layers": 5,
14
  "resolution_size": 8,
15
  "feature_dim": 512,
16
- "num_wnn_blocks": 2,
17
  "intermediate_size": 2048,
18
  "rms_norm_eps": 1e-05,
19
  "use_bias": true,
20
  "initializer_range": 0.02,
21
- "scan_group_size": 8,
22
  "use_cache": true,
23
  "tie_word_embeddings": true,
24
  "pad_token_id": 50256,
25
  "bos_token_id": 50256,
26
  "eos_token_id": 50256,
27
- "dtype": "float16"
28
- }
 
 
 
 
10
  },
11
  "vocab_size": 50257,
12
  "hidden_size": 512,
13
+ "num_hidden_layers": 24,
14
  "resolution_size": 8,
15
  "feature_dim": 512,
 
16
  "intermediate_size": 2048,
17
  "rms_norm_eps": 1e-05,
18
  "use_bias": true,
19
  "initializer_range": 0.02,
 
20
  "use_cache": true,
21
  "tie_word_embeddings": true,
22
  "pad_token_id": 50256,
23
  "bos_token_id": 50256,
24
  "eos_token_id": 50256,
25
+ "dtype": "float16",
26
+ "ema_alpha_first": 0.5,
27
+ "ema_alpha_last": 0.01,
28
+ "ema_block_size": 128
29
+ }
infer.py CHANGED
@@ -4,13 +4,13 @@
4
 
5
  Quy ước:
6
  * Trục batch khi nạp prefix = các chunk liên tiếp của MỘT tài liệu.
7
- * Mỗi layer tính cumsum cục bộ trong chunk (song song), sau đó ghép các chunk bằng
8
- exclusive scan đệ quy; kết quả bằng đúng CMA tuần tự.
9
- * Sau prefix, trạng thái chỉ còn (H, t) mỗi layer -> giải mã O(1) bộ nhớ theo độ dài.
10
 
11
  Ví dụ:
12
- python infer.py --model Vi-LAB/KenT-100M-Base --prompt "Xin chào"
13
- python infer.py --model ./KenT-100M-Base --prompt-file doc.txt --chunk-size 1024
14
  """
15
 
16
  import argparse
@@ -66,7 +66,7 @@ def prefill(
66
  """Nạp prefix 1D `input_ids` (T,). Trả về (logits token cuối (V,), cache).
67
 
68
  Các chunk được xử lý song song theo nhóm tối đa `max_parallel_chunks`; giữa các nhóm,
69
- trạng thái (H, t) được truyền tiếp qua cache. Truyền `cache` có sẵn để nạp tiếp
70
  một đoạn tài liệu mới vào cùng trạng thái.
71
  """
72
  device = next(model.parameters()).device
@@ -131,7 +131,7 @@ def stream(
131
  max_parallel_chunks: Optional[int] = None,
132
  cache=None,
133
  ) -> Iterator[int]:
134
- """Sinh từng token: prefill chunk-song-song, rồi giải mã từng bước với trạng thái (H, t)."""
135
  chunk_size = chunk_size or getattr(gen_cfg, "kent_prefill_chunk_size", None) or 512
136
  max_parallel_chunks = max_parallel_chunks or getattr(gen_cfg, "kent_max_parallel_chunks", None) or 16
137
  device = next(model.parameters()).device
 
4
 
5
  Quy ước:
6
  * Trục batch khi nạp prefix = các chunk liên tiếp của MỘT tài liệu.
7
+ * Mỗi layer tính EMA song song theo khối, các khối/chunk được ghép bằng affine-scan;
8
+ kết quả bằng đúng EMA tuần tự H_t = (1 - a) H_{t-1} + a u_t.
9
+ * Sau prefix, trạng thái chỉ còn H mỗi layer -> giải mã O(1) bộ nhớ theo độ dài.
10
 
11
  Ví dụ:
12
+ python infer.py --model Vi-LAB/KenT-300M-Base --prompt "Xin chào"
13
+ python infer.py --model ./KenT-300M-Base --prompt-file doc.txt --chunk-size 1024
14
  """
15
 
16
  import argparse
 
66
  """Nạp prefix 1D `input_ids` (T,). Trả về (logits token cuối (V,), cache).
67
 
68
  Các chunk được xử lý song song theo nhóm tối đa `max_parallel_chunks`; giữa các nhóm,
69
+ trạng thái H được truyền tiếp qua cache. Truyền `cache` có sẵn để nạp tiếp
70
  một đoạn tài liệu mới vào cùng trạng thái.
71
  """
72
  device = next(model.parameters()).device
 
131
  max_parallel_chunks: Optional[int] = None,
132
  cache=None,
133
  ) -> Iterator[int]:
134
+ """Sinh từng token: prefill chunk-song-song, rồi giải mã từng bước với trạng thái H."""
135
  chunk_size = chunk_size or getattr(gen_cfg, "kent_prefill_chunk_size", None) or 512
136
  max_parallel_chunks = max_parallel_chunks or getattr(gen_cfg, "kent_max_parallel_chunks", None) or 16
137
  device = next(model.parameters()).device
model.py CHANGED
@@ -1,19 +1,24 @@
1
  # coding=utf-8
2
  """KenT: kiến trúc hồi quy hai luồng (representation X_t / temporal-memory H_t).
3
 
4
- Mỗi KenT layer thực hiện một bước:
5
 
6
  V = B_in(Norm_X(X)) # (…, N, d_h)
7
  Z = A_N V # DCT-II cố định trên trục N
8
- for l in 0..L-1: Z = A_N [ Z + SwiGLU(Norm(Z)) ]
9
- H_t = ((t-1) H_{t-1} + u_t) / t # CMA, u_t = Z
10
  X' = X + B_out(Norm_H(H_t))
11
 
12
- Vì CMA là tổng tích lũy nên toàn bộ chuỗi trong một layer có thể tính song song
13
- (cumsum), và prefix dài có thể được chia thành các chunk rồi ghép bằng exclusive
14
- scan đệ quy. Trạng thái giải mã chỉ là (H, t) cho mỗi layer: O(1) theo độ dài.
15
 
16
- Trọng số được lưu ở FP16; mọi phép cộng dồn CMA và normalization chạy ở FP32.
 
 
 
 
 
17
  """
18
 
19
  import math
@@ -39,15 +44,17 @@ class KentConfig(PretrainedConfig):
39
  Args:
40
  vocab_size: kích thước từ vựng.
41
  hidden_size: số chiều của representation stream X_t (d_x).
42
- num_hidden_layers: số KenT cell nối liền nhau (cell cuối sinh đầu ra cuối).
43
  resolution_size: N, độ dài trục mà DCT-II tác động lên.
44
  feature_dim: d_h, số chiều đặc trưng trên mỗi vị trí resolution.
45
- num_wnn_blocks: L, số WNN Block nối tiếp trong MỖI cell.
46
  intermediate_size: kích thước hidden của SwiGLU (trên trục d_h).
47
  rms_norm_eps: epsilon của RMSNorm.
48
  use_bias: dùng bias trong Bilinear / SwiGLU.
49
  initializer_range: độ lệch chuẩn khởi tạo trọng số.
50
- scan_group_size: kích thước nhóm của exclusive scan đệ quy giữa các chunk.
 
 
 
51
  use_cache: trả về KentCache khi suy luận.
52
  """
53
 
@@ -57,15 +64,16 @@ class KentConfig(PretrainedConfig):
57
  self,
58
  vocab_size: int = 50257,
59
  hidden_size: int = 512,
60
- num_hidden_layers: int = 5,
61
  resolution_size: int = 8,
62
  feature_dim: int = 512,
63
- num_wnn_blocks: int = 2,
64
  intermediate_size: int = 2048,
65
  rms_norm_eps: float = 1e-5,
66
  use_bias: bool = True,
67
  initializer_range: float = 0.02,
68
- scan_group_size: int = 8,
 
 
69
  use_cache: bool = True,
70
  pad_token_id: int = 50256,
71
  bos_token_id: int = 50256,
@@ -78,12 +86,13 @@ class KentConfig(PretrainedConfig):
78
  self.num_hidden_layers = num_hidden_layers
79
  self.resolution_size = resolution_size
80
  self.feature_dim = feature_dim
81
- self.num_wnn_blocks = num_wnn_blocks
82
  self.intermediate_size = intermediate_size
83
  self.rms_norm_eps = rms_norm_eps
84
  self.use_bias = use_bias
85
  self.initializer_range = initializer_range
86
- self.scan_group_size = scan_group_size
 
 
87
  self.use_cache = use_cache
88
  super().__init__(
89
  pad_token_id=pad_token_id,
@@ -93,6 +102,14 @@ class KentConfig(PretrainedConfig):
93
  **kwargs,
94
  )
95
 
 
 
 
 
 
 
 
 
96
 
97
  # --------------------------------------------------------------------------- #
98
  # Fixed DCT-II operator
@@ -115,66 +132,66 @@ def dct2(z: torch.Tensor) -> torch.Tensor:
115
 
116
 
117
  # --------------------------------------------------------------------------- #
118
- # CMA: song song theo thời gian + ghép chunk bằng exclusive scan đệ quy
119
  # --------------------------------------------------------------------------- #
120
- def _exclusive_scan(x: torch.Tensor, group: int) -> torch.Tensor:
121
- """Exclusive prefix-sum dọc dim 0, tính đệ quy theo nhóm.
122
 
123
- Các nhóm được scan song song; tổng của từng nhóm lại được scan đệ quy ở tầng trên,
124
- độ sâu đệ quy O(log_group K).
125
  """
126
- k = x.shape[0]
127
- if k <= group:
128
- return torch.cumsum(x, dim=0) - x
129
- pad = (-k) % group
130
- if pad:
131
- x = torch.cat([x, x.new_zeros((pad,) + tuple(x.shape[1:]))], dim=0)
132
- g = x.shape[0] // group
133
- xg = x.reshape((g, group) + tuple(x.shape[1:]))
134
- inner = torch.cumsum(xg, dim=1) - xg # exclusive trong nhóm
135
- outer = _exclusive_scan(xg.sum(dim=1), group) # exclusive giữa các nhóm (đệ quy)
136
- out = inner + outer.unsqueeze(1)
137
- return out.reshape((g * group,) + tuple(x.shape[1:]))[:k]
138
-
139
-
140
- def cma_scan(u, mask, h0, n0):
141
- """CMA song song theo thời gian cho từng hàng batch độc lập.
142
-
143
- u: (B,T,N,D) fp32 | mask: (B,T) long {0,1} | h0: (B,N,D) fp32 | n0: (B,) long
144
- Trả về H (B,T,N,D), h_cuối (B,N,D), n_cuối (B,).
145
- """
146
- s0 = h0 * n0.to(u.dtype)[:, None, None]
147
- s = torch.cumsum(u * mask.to(u.dtype)[:, :, None, None], dim=1) + s0[:, None]
148
- n = torch.cumsum(mask, dim=1) + n0[:, None]
149
- h = s / n.clamp_min(1).to(u.dtype)[:, :, None, None]
150
- return h, h[:, -1].clone(), n[:, -1].clone()
151
 
152
 
153
- def cma_chunk_scan(u, mask, h0, n0, group: int):
154
- """CMA cho K chunk liên tiếp của MỘT tài liệu (trục batch = trục chunk).
155
 
156
- 1. cumsum cục bộ trong từng chunk (song song trên mọi chunk);
157
- 2. tổng từng chunk -> exclusive scan đệ quy -> carry cho mỗi chunk;
158
- 3. cộng trạng thái khởi tạo (h0, n0) rồi chia đếm để được H toàn cục.
159
 
160
- u: (K,C,N,D) | mask: (K,C) | h0: (1,N,D) | n0: (1,)
161
- Trả về H (K,C,N,D), h_cuối (1,N,D), n_cuối (1,).
162
  """
163
- local_s = torch.cumsum(u * mask.to(u.dtype)[:, :, None, None], dim=1)
164
- local_n = torch.cumsum(mask, dim=1)
165
- carry_s = _exclusive_scan(local_s[:, -1], group) + h0 * n0.to(u.dtype)[:, None, None]
166
- carry_n = _exclusive_scan(local_n[:, -1], group) + n0
167
- s = local_s + carry_s[:, None]
168
- n = local_n + carry_n[:, None]
169
- h = s / n.clamp_min(1).to(u.dtype)[:, :, None, None]
170
- return h, h[-1:, -1].clone(), n[-1:, -1].clone()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
 
173
  # --------------------------------------------------------------------------- #
174
- # Cache (state tuple H, t cho từng layer)
175
  # --------------------------------------------------------------------------- #
176
  class KentCache:
177
- """Trạng thái hồi quy của KenT: H (mean, fp32) theo layer và bộ đếm t."""
178
 
179
  def __init__(self, config: KentConfig, batch_size: int = 1, device=None):
180
  shape = (batch_size, config.resolution_size, config.feature_dim)
@@ -182,12 +199,11 @@ class KentCache:
182
  torch.zeros(shape, dtype=torch.float32, device=device)
183
  for _ in range(config.num_hidden_layers)
184
  ]
185
- self.t = torch.zeros(batch_size, dtype=torch.long, device=device)
186
  self.steps = 0 # số vị trí đã nạp (đếm ở phía Python, không cần sync GPU)
187
 
188
  @property
189
  def batch_size(self) -> int:
190
- return self.t.shape[0]
191
 
192
  @property
193
  def seen_tokens(self) -> int:
@@ -199,24 +215,20 @@ class KentCache:
199
  def reset(self):
200
  for h in self.h:
201
  h.zero_()
202
- self.t.zero_()
203
  self.steps = 0
204
 
205
  def clone(self) -> "KentCache":
206
  new = KentCache.__new__(KentCache)
207
  new.h = [h.clone() for h in self.h]
208
- new.t = self.t.clone()
209
  new.steps = self.steps
210
  return new
211
 
212
  def to(self, device):
213
  self.h = [h.to(device) for h in self.h]
214
- self.t = self.t.to(device)
215
  return self
216
 
217
  def reorder_cache(self, beam_idx: torch.LongTensor):
218
  self.h = [h.index_select(0, beam_idx.to(h.device)) for h in self.h]
219
- self.t = self.t.index_select(0, beam_idx.to(self.t.device))
220
 
221
 
222
  # --------------------------------------------------------------------------- #
@@ -273,35 +285,41 @@ class KentWNNBlock(nn.Module):
273
 
274
 
275
  class KentLayer(nn.Module):
276
- """Một bước KenT: (X_t, H_{t-1}, t) -> (X_{t+1}, H_t)."""
277
 
278
- def __init__(self, config: KentConfig):
279
  super().__init__()
280
  self.n = config.resolution_size
281
  self.d = config.feature_dim
282
- self.group = config.scan_group_size
283
  self.norm_x = KentRMSNorm(config.hidden_size, config.rms_norm_eps)
284
  self.b_in = KentBilinear(config.hidden_size, self.n * self.d, config.use_bias)
285
- self.blocks = nn.ModuleList([KentWNNBlock(config) for _ in range(config.num_wnn_blocks)])
286
  self.norm_h = KentRMSNorm(config.feature_dim, config.rms_norm_eps)
287
  self.b_out = KentBilinear(self.n * self.d, config.hidden_size, config.use_bias)
 
 
 
288
 
289
- def forward(self, x, mask, h0, n0, chunked: bool = False):
 
 
 
 
290
  b, t, _ = x.shape
291
  v = self.b_in(self.norm_x(x)).view(b, t, self.n, self.d)
292
- z = dct2(v) # Z^(0) = A_N V
293
- for blk in self.blocks:
294
- z = blk(z)
295
- u = z.float() # CMA cộng dồn ở fp32
296
- if h0 is None:
297
- h0 = u.new_zeros((1 if chunked else b, self.n, self.d))
298
- n0 = torch.zeros(h0.shape[0], dtype=torch.long, device=x.device)
299
- if chunked:
300
- h, h_last, n_last = cma_chunk_scan(u, mask, h0, n0, self.group)
301
  else:
302
- h, h_last, n_last = cma_scan(u, mask, h0, n0)
 
 
 
 
303
  dx = self.b_out(self.norm_h(h).to(x.dtype).reshape(b, t, self.n * self.d))
304
- return x + dx, h_last, n_last
305
 
306
 
307
  # --------------------------------------------------------------------------- #
@@ -342,6 +360,9 @@ class KentPreTrainedModel(PreTrainedModel):
342
  nn.init.normal_(module.weight, mean=0.0, std=std)
343
  elif isinstance(module, KentRMSNorm):
344
  nn.init.ones_(module.weight)
 
 
 
345
  if isinstance(module, KentBilinear) and getattr(module, "_residual_out", False):
346
  # tích của hai nhánh tuyến tính => thu nhỏ mỗi nhánh (2L)^(-1/4)
347
  with torch.no_grad():
@@ -352,7 +373,7 @@ class KentModel(KentPreTrainedModel):
352
  def __init__(self, config: KentConfig):
353
  super().__init__(config)
354
  self.embeddings = nn.Embedding(config.vocab_size, config.hidden_size)
355
- self.layers = nn.ModuleList([KentLayer(config) for _ in range(config.num_hidden_layers)])
356
  for layer in self.layers:
357
  layer.b_out._residual_out = True
358
  self.norm_f = KentRMSNorm(config.hidden_size, config.rms_norm_eps)
@@ -381,7 +402,7 @@ class KentModel(KentPreTrainedModel):
381
  chunked=False: trục batch là các chuỗi độc lập; (B, T).
382
  chunked=True : trục batch là K chunk liên tiếp của MỘT tài liệu; (K, C).
383
  Cache vào/ra có batch size 1 (trạng thái sau chunk cuối).
384
- attention_mask: 1 = token thật, 0 = padding (không đóng góp vào CMA).
385
  """
386
  use_cache = use_cache if use_cache is not None else (self.config.use_cache and not self.training)
387
  output_hidden_states = bool(output_hidden_states)
@@ -405,22 +426,17 @@ class KentModel(KentPreTrainedModel):
405
  cache_params = KentCache(self.config, 1 if chunked else b, x.device)
406
 
407
  all_hidden = () if output_hidden_states else None
408
- n_last = None
409
  for i, layer in enumerate(self.layers):
410
  if output_hidden_states:
411
  all_hidden += (x,)
412
  h0 = cache_params.h[i] if cache_params is not None else None
413
- n0 = cache_params.t if cache_params is not None else None
414
  if self.gradient_checkpointing and self.training:
415
- x, h_last, n_last = self._gradient_checkpointing_func(
416
- layer.__call__, x, mask, h0, n0, chunked
417
- )
418
  else:
419
- x, h_last, n_last = layer(x, mask, h0, n0, chunked)
420
  if use_cache:
421
  cache_params.h[i] = h_last
422
  if use_cache:
423
- cache_params.t = n_last
424
  cache_params.steps += b * t if chunked else t
425
 
426
  x = self.norm_f(x)
@@ -555,4 +571,5 @@ __all__ = [
555
  "KentForCausalLM",
556
  "KentPreTrainedModel",
557
  "dct2",
 
558
  ]
 
1
  # coding=utf-8
2
  """KenT: kiến trúc hồi quy hai luồng (representation X_t / temporal-memory H_t).
3
 
4
+ Mỗi KenT layer thực hiện một bước, với ĐÚNG MỘT WNN Block:
5
 
6
  V = B_in(Norm_X(X)) # (…, N, d_h)
7
  Z = A_N V # DCT-II cố định trên trục N
8
+ u = A_N [ Z + SwiGLU(Norm(Z)) ] # một WNN Block
9
+ H_t = (1 - a) H_{t-1} + a u_t # EMA, a = a_layer học được, riêng từng layer
10
  X' = X + B_out(Norm_H(H_t))
11
 
12
+ Độ sâu đến từ việc xếp chồng nhiều layer (mỗi layer một WNN), không còn chuỗi L WNN trong
13
+ một layer. Bộ nhớ H có cơ chế quên: a_layer = sigmoid(ema_logit) là một số vô hướng học được
14
+ cho mỗi layer; khởi tạo giảm dần theo độ sâu (layer nông quên nhanh, layer sâu nhớ lâu).
15
 
16
+ EMA là một phép hồi quy tuyến tính nên vẫn song song hóa được theo thời gian: trong từng khối
17
+ độ dài `ema_block_size` dùng ma trận phân rã tam giác dưới, các khối được ghép bằng
18
+ affine-scan log-depth. Kết quả bằng đúng hồi quy tuần tự. Trạng thái giải mã chỉ là H cho mỗi
19
+ layer: O(1) theo độ dài.
20
+
21
+ Trọng số được lưu ở FP16; EMA và normalization chạy ở FP32.
22
  """
23
 
24
  import math
 
44
  Args:
45
  vocab_size: kích thước từ vựng.
46
  hidden_size: số chiều của representation stream X_t (d_x).
47
+ num_hidden_layers: số KenT layer xếp chồng (mỗi layer có đúng một WNN Block).
48
  resolution_size: N, độ dài trục mà DCT-II tác động lên.
49
  feature_dim: d_h, số chiều đặc trưng trên mỗi vị trí resolution.
 
50
  intermediate_size: kích thước hidden của SwiGLU (trên trục d_h).
51
  rms_norm_eps: epsilon của RMSNorm.
52
  use_bias: dùng bias trong Bilinear / SwiGLU.
53
  initializer_range: độ lệch chuẩn khởi tạo trọng số.
54
+ ema_alpha_first: a_layer khởi tạo của layer đầu (quên nhanh).
55
+ ema_alpha_last: a_layer khởi tạo của layer cuối (nhớ lâu); các layer ở giữa nội suy
56
+ theo cấp số nhân. a_layer vẫn là tham số học được.
57
+ ema_block_size: độ dài khối của EMA song song (chỉ ảnh hưởng tốc độ/bộ nhớ).
58
  use_cache: trả về KentCache khi suy luận.
59
  """
60
 
 
64
  self,
65
  vocab_size: int = 50257,
66
  hidden_size: int = 512,
67
+ num_hidden_layers: int = 24,
68
  resolution_size: int = 8,
69
  feature_dim: int = 512,
 
70
  intermediate_size: int = 2048,
71
  rms_norm_eps: float = 1e-5,
72
  use_bias: bool = True,
73
  initializer_range: float = 0.02,
74
+ ema_alpha_first: float = 0.5,
75
+ ema_alpha_last: float = 0.01,
76
+ ema_block_size: int = 128,
77
  use_cache: bool = True,
78
  pad_token_id: int = 50256,
79
  bos_token_id: int = 50256,
 
86
  self.num_hidden_layers = num_hidden_layers
87
  self.resolution_size = resolution_size
88
  self.feature_dim = feature_dim
 
89
  self.intermediate_size = intermediate_size
90
  self.rms_norm_eps = rms_norm_eps
91
  self.use_bias = use_bias
92
  self.initializer_range = initializer_range
93
+ self.ema_alpha_first = ema_alpha_first
94
+ self.ema_alpha_last = ema_alpha_last
95
+ self.ema_block_size = ema_block_size
96
  self.use_cache = use_cache
97
  super().__init__(
98
  pad_token_id=pad_token_id,
 
102
  **kwargs,
103
  )
104
 
105
+ def alpha_init(self, layer_idx: int) -> float:
106
+ """a_layer khởi tạo của layer `layer_idx` (giảm theo cấp số nhân theo độ sâu)."""
107
+ n = self.num_hidden_layers
108
+ if n <= 1:
109
+ return float(self.ema_alpha_first)
110
+ r = layer_idx / (n - 1)
111
+ return float(self.ema_alpha_first * (self.ema_alpha_last / self.ema_alpha_first) ** r)
112
+
113
 
114
  # --------------------------------------------------------------------------- #
115
  # Fixed DCT-II operator
 
132
 
133
 
134
  # --------------------------------------------------------------------------- #
135
+ # EMA: song song theo thời gian (khối tam giác + affine-scan giữa các khối)
136
  # --------------------------------------------------------------------------- #
137
+ def _affine_scan(a: torch.Tensor, b: torch.Tensor):
138
+ """Inclusive scan dọc dim 1 của các ánh xạ affine h -> a*h + b (Hillis-Steele, log K bước).
139
 
140
+ a: (B,K) | b: (B,K,F). Phần tử k được áp dụng SAU các phần tử < k.
141
+ Trả về (A, B) sao cho trạng thái sau khối k là A[k]*h_vào + B[k].
142
  """
143
+ k = a.shape[1]
144
+ off = 1
145
+ while off < k:
146
+ b = torch.cat([b[:, :off], a[:, off:, None] * b[:, :-off] + b[:, off:]], dim=1)
147
+ a = torch.cat([a[:, :off], a[:, off:] * a[:, :-off]], dim=1)
148
+ off *= 2
149
+ return a, b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
 
152
+ def ema_scan(u: torch.Tensor, mask: torch.Tensor, h0: torch.Tensor, a: torch.Tensor, block: int = 128):
153
+ """H_t = (1 - a m_t) H_{t-1} + a m_t u_t, tính song song cho mọi t.
154
 
155
+ u: (B,T,N,D) fp32 | mask: (B,T) {0,1} (0 = padding: H giữ nguyên) | h0: (B,N,D) fp32
156
+ a: số vô hướng fp32 trong (0,1).
157
+ Trả về H (B,T,N,D) và h_cuối (B,N,D).
158
 
159
+ Trong một khối: H_t = sum_{s<=t} a m_s [prod_{s<r<=t}(1 - a m_r)] u_s + [prod_{r<=t}(1 - a m_r)] h_vào,
160
+ với tích tính qua hiệu của cumsum log (luôn <= 0 nên ổn định). Các khối được nối bằng affine-scan.
161
  """
162
+ bsz, t, n, d = u.shape
163
+ f = n * d
164
+ blk = max(1, min(block, t))
165
+ pad = (-t) % blk
166
+ uf = u.reshape(bsz, t, f)
167
+ m = mask.to(u.dtype)
168
+ if pad: # padding ở cuối: mask = 0 nên trạng thái không đổi
169
+ uf = F.pad(uf, (0, 0, 0, pad))
170
+ m = F.pad(m, (0, pad))
171
+ k = (t + pad) // blk
172
+ uk = uf.reshape(bsz, k, blk, f)
173
+ am = a * m.reshape(bsz, k, blk) # (B,K,blk)
174
+
175
+ cum = torch.cumsum(torch.log1p(-am), dim=2) # log prod_{r<=t}(1 - a m_r)
176
+ tril = torch.ones(blk, blk, dtype=torch.bool, device=u.device).tril()
177
+ decay = torch.exp((cum.unsqueeze(-1) - cum.unsqueeze(-2)).masked_fill(~tril, float("-inf")))
178
+ local = torch.matmul(decay * am.unsqueeze(-2), uk) # (B,K,blk,F): khối bắt đầu từ H = 0
179
+
180
+ a_blk = torch.exp(cum[..., -1]) # (B,K): hệ số phân rã cả khối
181
+ a_p, b_p = _affine_scan(a_blk, local[:, :, -1])
182
+ h0f = h0.reshape(bsz, 1, f)
183
+ s = a_p.unsqueeze(-1) * h0f + b_p # trạng thái sau mỗi khối
184
+ h_in = torch.cat([h0f, s[:, :-1]], dim=1) # trạng thái vào mỗi khối
185
+ h = local + torch.exp(cum).unsqueeze(-1) * h_in.unsqueeze(2)
186
+ h = h.reshape(bsz, k * blk, f)[:, :t].reshape(bsz, t, n, d)
187
+ return h, s[:, -1].reshape(bsz, n, d)
188
 
189
 
190
  # --------------------------------------------------------------------------- #
191
+ # Cache (trạng thái H cho từng layer)
192
  # --------------------------------------------------------------------------- #
193
  class KentCache:
194
+ """Trạng thái hồi quy của KenT: H (EMA, fp32) theo layer."""
195
 
196
  def __init__(self, config: KentConfig, batch_size: int = 1, device=None):
197
  shape = (batch_size, config.resolution_size, config.feature_dim)
 
199
  torch.zeros(shape, dtype=torch.float32, device=device)
200
  for _ in range(config.num_hidden_layers)
201
  ]
 
202
  self.steps = 0 # số vị trí đã nạp (đếm ở phía Python, không cần sync GPU)
203
 
204
  @property
205
  def batch_size(self) -> int:
206
+ return self.h[0].shape[0]
207
 
208
  @property
209
  def seen_tokens(self) -> int:
 
215
  def reset(self):
216
  for h in self.h:
217
  h.zero_()
 
218
  self.steps = 0
219
 
220
  def clone(self) -> "KentCache":
221
  new = KentCache.__new__(KentCache)
222
  new.h = [h.clone() for h in self.h]
 
223
  new.steps = self.steps
224
  return new
225
 
226
  def to(self, device):
227
  self.h = [h.to(device) for h in self.h]
 
228
  return self
229
 
230
  def reorder_cache(self, beam_idx: torch.LongTensor):
231
  self.h = [h.index_select(0, beam_idx.to(h.device)) for h in self.h]
 
232
 
233
 
234
  # --------------------------------------------------------------------------- #
 
285
 
286
 
287
  class KentLayer(nn.Module):
288
+ """Một bước KenT: (X_t, H_{t-1}) -> (X_{t+1}, H_t), với một WNN Block và EMA học được."""
289
 
290
+ def __init__(self, config: KentConfig, layer_idx: int = 0):
291
  super().__init__()
292
  self.n = config.resolution_size
293
  self.d = config.feature_dim
294
+ self.block_size = config.ema_block_size
295
  self.norm_x = KentRMSNorm(config.hidden_size, config.rms_norm_eps)
296
  self.b_in = KentBilinear(config.hidden_size, self.n * self.d, config.use_bias)
297
+ self.wnn = KentWNNBlock(config)
298
  self.norm_h = KentRMSNorm(config.feature_dim, config.rms_norm_eps)
299
  self.b_out = KentBilinear(self.n * self.d, config.hidden_size, config.use_bias)
300
+ a0 = config.alpha_init(layer_idx)
301
+ self.ema_logit_init = math.log(a0 / (1.0 - a0))
302
+ self.ema_logit = nn.Parameter(torch.tensor(self.ema_logit_init))
303
 
304
+ def alpha(self) -> torch.Tensor:
305
+ """a_layer in (0,1), tính ở fp32."""
306
+ return torch.sigmoid(self.ema_logit.float()).clamp_max(0.999)
307
+
308
+ def forward(self, x, mask, h0, chunked: bool = False):
309
  b, t, _ = x.shape
310
  v = self.b_in(self.norm_x(x)).view(b, t, self.n, self.d)
311
+ u = self.wnn(dct2(v)).float() # Z^(0) = A_N V -> một WNN Block; EMA ở fp32
312
+ if chunked: # trục batch là các chunk liên tiếp: nối thành một chuỗi cho EMA
313
+ u = u.reshape(1, b * t, self.n, self.d)
314
+ m = mask.reshape(1, b * t)
 
 
 
 
 
315
  else:
316
+ m = mask
317
+ if h0 is None:
318
+ h0 = u.new_zeros((u.shape[0], self.n, self.d))
319
+ h, h_last = ema_scan(u, m, h0, self.alpha(), self.block_size)
320
+ h = h.reshape(b, t, self.n, self.d)
321
  dx = self.b_out(self.norm_h(h).to(x.dtype).reshape(b, t, self.n * self.d))
322
+ return x + dx, h_last
323
 
324
 
325
  # --------------------------------------------------------------------------- #
 
360
  nn.init.normal_(module.weight, mean=0.0, std=std)
361
  elif isinstance(module, KentRMSNorm):
362
  nn.init.ones_(module.weight)
363
+ elif isinstance(module, KentLayer):
364
+ with torch.no_grad():
365
+ module.ema_logit.fill_(module.ema_logit_init)
366
  if isinstance(module, KentBilinear) and getattr(module, "_residual_out", False):
367
  # tích của hai nhánh tuyến tính => thu nhỏ mỗi nhánh (2L)^(-1/4)
368
  with torch.no_grad():
 
373
  def __init__(self, config: KentConfig):
374
  super().__init__(config)
375
  self.embeddings = nn.Embedding(config.vocab_size, config.hidden_size)
376
+ self.layers = nn.ModuleList([KentLayer(config, i) for i in range(config.num_hidden_layers)])
377
  for layer in self.layers:
378
  layer.b_out._residual_out = True
379
  self.norm_f = KentRMSNorm(config.hidden_size, config.rms_norm_eps)
 
402
  chunked=False: trục batch là các chuỗi độc lập; (B, T).
403
  chunked=True : trục batch là K chunk liên tiếp của MỘT tài liệu; (K, C).
404
  Cache vào/ra có batch size 1 (trạng thái sau chunk cuối).
405
+ attention_mask: 1 = token thật, 0 = padding (H giữ nguyên tại vị trí đó).
406
  """
407
  use_cache = use_cache if use_cache is not None else (self.config.use_cache and not self.training)
408
  output_hidden_states = bool(output_hidden_states)
 
426
  cache_params = KentCache(self.config, 1 if chunked else b, x.device)
427
 
428
  all_hidden = () if output_hidden_states else None
 
429
  for i, layer in enumerate(self.layers):
430
  if output_hidden_states:
431
  all_hidden += (x,)
432
  h0 = cache_params.h[i] if cache_params is not None else None
 
433
  if self.gradient_checkpointing and self.training:
434
+ x, h_last = self._gradient_checkpointing_func(layer.__call__, x, mask, h0, chunked)
 
 
435
  else:
436
+ x, h_last = layer(x, mask, h0, chunked)
437
  if use_cache:
438
  cache_params.h[i] = h_last
439
  if use_cache:
 
440
  cache_params.steps += b * t if chunked else t
441
 
442
  x = self.norm_f(x)
 
571
  "KentForCausalLM",
572
  "KentPreTrainedModel",
573
  "dct2",
574
+ "ema_scan",
575
  ]