KenT-300M-Base / README.md
KenteaKe11
KenT-300M: EMA memory with learned per-layer alpha, 1 WNN per layer, 24 layers
99eb3d1
|
Raw History Blame Contribute Delete
5.49 kB
---
license: apache-2.0
language:
- vi
- en
library_name: transformers
pipeline_tag: text-generation
tags:
- kent
- ssm
- state-space-model
- recurrent
- linear-time
- frequency-domain
- dct
- swiglu
- text-generation
- custom_code
- fp16
- base
---
# KenT
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.
> This repo contains `trust_remote_code` code; weights are stored in **FP16**.
## Architectural Philosophy
- **Deep stack of compact layers.** KenT stacks many identical recurrent layers (24 in this ~300M model), each containing exactly
one transformation block. The layers are connected in sequence through the representation stream, and the last layer produces
the final output. Depth is prioritized: the deeper, the better, while width is kept at a level that stays balanced with it.
- **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 to ~300M mainly by adding depth, with width unchanged,
so that both axes stay balanced (width still matters, but is not where the extra capacity went).
- **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.
- **Depth is the main axis.** There is no inner chain of blocks inside a layer any more: feature depth and memory timescale both
grow with the number of 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-300M-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-300M-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.