File size: 5,710 Bytes
41b08c0
 
5462185
1c4c0d2
5462185
 
 
1c4c0d2
 
 
 
 
 
 
 
6c5e4f2
1c4c0d2
 
 
 
 
 
41b08c0
5462185
 
 
963b91a
ba698bf
 
 
 
 
6c5e4f2
 
 
 
ba698bf
963b91a
 
ba698bf
 
 
 
 
99eb3d1
 
 
963b91a
6c5e4f2
99eb3d1
 
ba698bf
 
 
 
 
 
 
5462185
ba698bf
 
ebfe05c
 
ba698bf
 
5462185
 
 
 
 
 
 
ba698bf
5462185
 
 
 
 
6c5e4f2
5462185
 
ebfe05c
5462185
ebfe05c
5462185
ba698bf
5462185
 
 
 
 
 
 
 
ba698bf
5462185
 
 
ba698bf
5462185
 
 
ba698bf
5462185
ba698bf
 
5462185
 
 
 
 
 
ba698bf
5462185
 
ba698bf
5462185
 
 
 
ba698bf
5462185
 
6c5e4f2
5462185
 
ba698bf
5462185
ba698bf
5462185
99eb3d1
ba698bf
 
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
---
license: apache-2.0
language:
- en
library_name: transformers
pipeline_tag: text-generation
tags:
- kent
- ssm
- state-space-model
- recurrent
- linear-time
- frequency-domain
- dct
- mlp
- swiglu
- text-generation
- custom_code
- fp16
- base
datasets:
- wikimedia/wikipedia
---

# KenT

KenT is a recurrent language model from the SSM family. KenT concentrates its capacity in a wide spectral representation stacked over a moderate depth: each layer has a single transformation block, plus a light learned-forgetting memory.

> This repo contains `trust_remote_code` code; weights are stored in **FP16**.

## Architectural Philosophy

- **Wide spectrum, moderate depth.** KenT stacks identical recurrent layers (24 in this ~1B model), each containing exactly
  one transformation block (a SwiGLU MLP with hidden size ~8/3 x D). The layers are connected in sequence through the representation stream, and the last layer produces
  the final output. The representation stream feeding `lm_head` is wide (1536), and each layer's Bilinear projection expands it
  into a large resolution axis (N = 24 DCT positions) with enough features per position (D = 256) before the DCT.
- **Invest in the transformation core.** Capacity is concentrated in the "hidden" part, where features are actually transformed,
  rather than in the vocabulary table. Parameters were scaled from ~100M by widening the representation stream and the spectral
  axes (N and D together) and then by adding layers; the MLP inside each block is kept small.
- **Two streams, two roles.** One stream is a representation carrying information about the current token; the other is a memory
  stream that aggregates history. The two streams meet only through learned projections and are never mixed directly.
- **Frequency as a fixed foundation, learning where it's needed.** Features are processed in the cosine-coefficient space using a
  **fixed, non-learned** operator, interleaved with nonlinear blocks. The fixed part carries built-in structure; the learned part
  focuses solely on transforming features.
- **Memory with learned forgetting.** History is summarized with an exponential moving average
  `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
  (shallow layers forget fast, deep layers remember longer). The decoding state has **constant** size regardless of context length.
- **Width and depth together.** There is no inner chain of blocks inside a layer: each layer applies one DCT/MLP block over a
  large N x D grid, and memory timescale varies across the 24 layers.
- **Chunk-parallelizable.** The moving average is a linear recurrence, so a long document can be split into blocks/chunks,
  processed concurrently, and merged with an affine scan, yielding the same result as sequential processing.

The points above are **design hypotheses** that need to be verified experimentally, not performance guarantees.
Detailed configuration and full formulas are not described here.

## Repo Structure

| File | Role |
|---|---|
| `model.py` | Model implementation (`KentConfig`, `KentModel`, `KentForCausalLM`, `KentCache`) |
| `config.json` | Standard model configuration |
| `generation_config.json` | Default generation configuration |
| `tokenizer.json`, `tokenizer_config.json` | Tokenizer |
| `infer.py` | Parallel chunked prefix loading and the generation loop |
| `model.safetensors` | FP16 weights |

## Quickstart

```bash
pip install torch transformers
```

### Standard text generation (`generate`)

```python
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig

repo = "Vi-LAB/KenT-1B-Base"  # replace with a repo id or a local directory
tok = AutoTokenizer.from_pretrained(repo)
model = AutoModelForCausalLM.from_pretrained(
    repo, trust_remote_code=True, dtype=torch.float16
).to("cuda").eval()
gen_cfg = GenerationConfig.from_pretrained(repo)

inputs = tok("Hello, today", return_tensors="pt").to("cuda")
out = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    generation_config=gen_cfg,
)
print(tok.decode(out[0], skip_special_tokens=True))
```

The recurrent state is returned as `cache_params` (similar to other SSM models in `transformers`):

```python
out = model(**inputs, use_cache=True)
cache = out.cache_params                       # recurrent state
out = model(input_ids=next_token, cache_params=cache, use_cache=True)
```

### Long documents: parallel chunked prefix loading

When processing in batches, each row of the batch is **one chunk of the same document**; the chunks are processed
in parallel with the `chunked=True` flag, and the final state is returned in `cache_params`.

```python
from infer import prefill, stream

ids = tok(open("doc.txt", encoding="utf-8").read(), return_tensors="pt").input_ids[0]

# 1) load the prefix only, get the last token's logits and the state
logits, cache = prefill(model, ids, chunk_size=1024, max_parallel_chunks=16)

# 2) load the prefix, then generate (streaming)
for token_id in stream(model, ids, gen_cfg):
    print(tok.decode([token_id]), end="", flush=True)
```

Command line:

```bash
python infer.py --model Vi-LAB/KenT-1B-Base --prompt-file doc.txt --chunk-size 1024
```

`cache.clone()` lets you reuse the same prefix to generate multiple different branches without reloading it.

## Notes

- Memory is an exponential moving average with a learned per-layer coefficient; it forgets old timesteps but does not select content on its own.
- The tokenizer and weights must match `vocab_size` in `config.json`.
- This is a research model; please evaluate it before using it for real-world tasks.