Text Generation
Transformers
Safetensors
English
Arabic
quasar_long
silx-ai
quasar-preview
quasar
foundation-model
Mixture of Experts
18b
2b-active
long-context
bittensor
sn24
decentralized-training
distillation
hybrid-transformer
loop-transformer
safe-nope
drope
conversational
custom_code
Instructions to use mainline777/base_IIXIV with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use mainline777/base_IIXIV with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="mainline777/base_IIXIV", trust_remote_code=True) messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("mainline777/base_IIXIV", trust_remote_code=True, dtype="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use mainline777/base_IIXIV with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "mainline777/base_IIXIV" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "mainline777/base_IIXIV", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/mainline777/base_IIXIV
- SGLang
How to use mainline777/base_IIXIV with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "mainline777/base_IIXIV" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "mainline777/base_IIXIV", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "mainline777/base_IIXIV" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "mainline777/base_IIXIV", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use mainline777/base_IIXIV with Docker Model Runner:
docker model run hf.co/mainline777/base_IIXIV
Commit ·
41865df
0
Parent(s):
Duplicate from silx-ai/Quasar-Preview
Browse filesCo-authored-by: Eyad Gomaa <eyad-silx@users.noreply.huggingface.co>
This view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +37 -0
- README.md +710 -0
- chat_template.jinja +1 -0
- config.json +116 -0
- configuration_quasar_long.py +140 -0
- engram.py +611 -0
- fla/__init__.py +8 -0
- fla/distributed_compat.py +57 -0
- fla/layers/__init__.py +3 -0
- fla/layers/abc.py +231 -0
- fla/layers/attn.py +176 -0
- fla/layers/based.py +93 -0
- fla/layers/bitattn.py +162 -0
- fla/layers/comba.py +328 -0
- fla/layers/delta_net.py +287 -0
- fla/layers/deltaformer.py +152 -0
- fla/layers/forgetting_attn.py +133 -0
- fla/layers/gated_deltanet.py +316 -0
- fla/layers/gated_deltaproduct.py +287 -0
- fla/layers/gla.py +304 -0
- fla/layers/gsa.py +237 -0
- fla/layers/hgrn.py +174 -0
- fla/layers/hgrn2.py +210 -0
- fla/layers/kda.py +277 -0
- fla/layers/lightnet.py +238 -0
- fla/layers/linear_attn.py +196 -0
- fla/layers/log_linear_mamba2.py +684 -0
- fla/layers/mamba.py +395 -0
- fla/layers/mamba2.py +649 -0
- fla/layers/mesa_net.py +221 -0
- fla/layers/mla.py +225 -0
- fla/layers/mom.py +831 -0
- fla/layers/multiscale_retention.py +303 -0
- fla/layers/nsa.py +137 -0
- fla/layers/path_attn.py +216 -0
- fla/layers/quasar.py +439 -0
- fla/layers/rebased.py +144 -0
- fla/layers/rodimus.py +397 -0
- fla/layers/rwkv6.py +360 -0
- fla/layers/rwkv7.py +347 -0
- fla/layers/simple_gla.py +273 -0
- fla/layers/utils.py +218 -0
- fla/models/__init__.py +3 -0
- fla/models/abc/__init__.py +12 -0
- fla/models/abc/configuration_abc.py +105 -0
- fla/models/abc/modeling_abc.py +371 -0
- fla/models/bitnet/__init__.py +12 -0
- fla/models/bitnet/configuration_bitnet.py +81 -0
- fla/models/bitnet/modeling_bitnet.py +395 -0
- fla/models/comba/__init__.py +11 -0
.gitattributes
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
quasar_banner.png filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,710 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
language:
|
| 3 |
+
- en
|
| 4 |
+
- ar
|
| 5 |
+
license: mit
|
| 6 |
+
tags:
|
| 7 |
+
- silx-ai
|
| 8 |
+
- quasar-preview
|
| 9 |
+
- quasar
|
| 10 |
+
- foundation-model
|
| 11 |
+
- moe
|
| 12 |
+
- 18b
|
| 13 |
+
- 2b-active
|
| 14 |
+
- long-context
|
| 15 |
+
- bittensor
|
| 16 |
+
- sn24
|
| 17 |
+
- decentralized-training
|
| 18 |
+
- distillation
|
| 19 |
+
- hybrid-transformer
|
| 20 |
+
- loop-transformer
|
| 21 |
+
- safe-nope
|
| 22 |
+
- drope
|
| 23 |
+
pipeline_tag: text-generation
|
| 24 |
+
library_name: transformers
|
| 25 |
+
---
|
| 26 |
+
|
| 27 |
+
<p align="center">
|
| 28 |
+
<img src="./quasar_banner.png" alt="Quasar-Preview Foundation Model" width="100%">
|
| 29 |
+
</p>
|
| 30 |
+
|
| 31 |
+
# **Quasar-Preview**
|
| 32 |
+
|
| 33 |
+
**Quasar-Preview** is the first public model in SILX AI’s **Quasar Foundation Model** series.
|
| 34 |
+
|
| 35 |
+
It is an early preview checkpoint built to demonstrate the direction of the Quasar architecture at real scale: sparse MoE routing, hybrid recurrent/attention layers, and an experimental long-context configuration designed for future memory-based systems.
|
| 36 |
+
|
| 37 |
+
This is **not the finished Quasar model**.
|
| 38 |
+
|
| 39 |
+
Quasar-Preview is the first public step in a larger series of Quasar models that will continue scaling through decentralized training, distillation, architecture improvements, and long-context research on **Bittensor SN24**.
|
| 40 |
+
|
| 41 |
+
---
|
| 42 |
+
|
| 43 |
+
## TL;DR
|
| 44 |
+
|
| 45 |
+
- **First public Quasar model**
|
| 46 |
+
- **~18B total parameter MoE**
|
| 47 |
+
- **~2B active parameter path**
|
| 48 |
+
- **Experimental 5M-token context configuration**
|
| 49 |
+
- Built with **Loop Transformer + Quasar hybrid attention**
|
| 50 |
+
- Includes **Quasar / Raven / GLA** hybrid layers
|
| 51 |
+
- Designed for **Bittensor SN24 decentralized distillation**
|
| 52 |
+
- Trained on **>1T and <1.5T tokens**
|
| 53 |
+
- Long-context extension path has received **<1B tokens** so far
|
| 54 |
+
- Early preview checkpoint, not a final production/SOTA model
|
| 55 |
+
|
| 56 |
+
Quasar-Preview should be understood as an **architecture preview and foundation checkpoint**, not the final endpoint of the Quasar roadmap.
|
| 57 |
+
|
| 58 |
+
---
|
| 59 |
+
|
| 60 |
+
# Important Note
|
| 61 |
+
|
| 62 |
+
Quasar-Preview is an early model from our broader Quasar model series.
|
| 63 |
+
|
| 64 |
+
It is released to make the architecture public, allow miners and researchers to work with the model, and begin the next phase of decentralized scaling.
|
| 65 |
+
|
| 66 |
+
This model is:
|
| 67 |
+
|
| 68 |
+
- An **early preview checkpoint**
|
| 69 |
+
- The **first model** in a planned series of Quasar models
|
| 70 |
+
- Trained on **>1T and <1.5T tokens**
|
| 71 |
+
- Built for **research, distillation, and SN24 training**
|
| 72 |
+
- Not yet the final Quasar model
|
| 73 |
+
- Not intended to represent the final quality of the Quasar architecture
|
| 74 |
+
|
| 75 |
+
Performance is expected to improve through:
|
| 76 |
+
|
| 77 |
+
- Iterative subnet training
|
| 78 |
+
- Distillation cycles
|
| 79 |
+
- Longer training runs
|
| 80 |
+
- Stronger post-training
|
| 81 |
+
- More long-context extension training
|
| 82 |
+
- Future Quasar architecture updates
|
| 83 |
+
|
| 84 |
+
---
|
| 85 |
+
|
| 86 |
+
# Model Overview
|
| 87 |
+
|
| 88 |
+
| Field | Value |
|
| 89 |
+
| --- | --- |
|
| 90 |
+
| Model Name | Quasar-Preview |
|
| 91 |
+
| Model Family | Quasar Foundation Models |
|
| 92 |
+
| Organization | SILX AI |
|
| 93 |
+
| Model Type | `quasar_long` |
|
| 94 |
+
| Architecture | Quasar Long Hybrid Transformer |
|
| 95 |
+
| Total Parameters | ~18B class |
|
| 96 |
+
| Active Parameters | ~2B class sparse MoE path |
|
| 97 |
+
| Training Stage | Early preview checkpoint |
|
| 98 |
+
| Context Config | Experimental 5M-token config |
|
| 99 |
+
| Long-Context Method | Safe NoPE / DrOPE-style staging |
|
| 100 |
+
| Tokenizer | Quasar tokenizer preserved from checkpoint lineage |
|
| 101 |
+
| Primary Use | Research, distillation, SN24 decentralized training |
|
| 102 |
+
| License | MIT |
|
| 103 |
+
|
| 104 |
+
---
|
| 105 |
+
|
| 106 |
+
# What Is Active In This Checkpoint?
|
| 107 |
+
|
| 108 |
+
Quasar-Preview includes several architecture paths. Some are active in this checkpoint, while others are included for future Quasar versions.
|
| 109 |
+
|
| 110 |
+
| Component | Status in Quasar-Preview |
|
| 111 |
+
| --- | --- |
|
| 112 |
+
| Sparse MoE | Active |
|
| 113 |
+
| Quasar hybrid layers | Active |
|
| 114 |
+
| GLA branch | Active |
|
| 115 |
+
| Raven branch | Active |
|
| 116 |
+
| GQA compatibility attention | Active in this checkpoint |
|
| 117 |
+
| Safe NoPE / DrOPE-style context config | Active |
|
| 118 |
+
| Loop Transformer scaffold | Present |
|
| 119 |
+
| Loop execution | Configured as single-loop |
|
| 120 |
+
| Looped anchor injection | Disabled |
|
| 121 |
+
| Engram memory | Included and loadable, not active by default |
|
| 122 |
+
| 5M context | Config exposed, early long-context training only |
|
| 123 |
+
|
| 124 |
+
The goal of this release is to expose the first working Quasar architecture checkpoint while keeping the model stable for research and SN24 training.
|
| 125 |
+
|
| 126 |
+
---
|
| 127 |
+
|
| 128 |
+
# Quick Start
|
| 129 |
+
|
| 130 |
+
Quasar-Preview uses custom architecture code.
|
| 131 |
+
|
| 132 |
+
Use `trust_remote_code=True` when loading the model.
|
| 133 |
+
|
| 134 |
+
```python
|
| 135 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM
|
| 136 |
+
import torch
|
| 137 |
+
|
| 138 |
+
model_id = "SILX-AI/Quasar-Preview"
|
| 139 |
+
|
| 140 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 141 |
+
model_id,
|
| 142 |
+
trust_remote_code=True
|
| 143 |
+
)
|
| 144 |
+
|
| 145 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 146 |
+
model_id,
|
| 147 |
+
trust_remote_code=True,
|
| 148 |
+
torch_dtype=torch.bfloat16,
|
| 149 |
+
device_map="auto"
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
prompt = "Explain the purpose of long-context models in simple terms."
|
| 153 |
+
|
| 154 |
+
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
|
| 155 |
+
|
| 156 |
+
with torch.no_grad():
|
| 157 |
+
output = model.generate(
|
| 158 |
+
**inputs,
|
| 159 |
+
max_new_tokens=256,
|
| 160 |
+
do_sample=True,
|
| 161 |
+
temperature=0.7,
|
| 162 |
+
top_p=0.9
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
print(tokenizer.decode(output[0], skip_special_tokens=True))
|
| 166 |
+
```
|
| 167 |
+
|
| 168 |
+
## Inference Notes
|
| 169 |
+
|
| 170 |
+
Quasar-Preview is an ~18B total parameter MoE checkpoint. Even though the active path is ~2B parameters, the full checkpoint still requires loading the model weights.
|
| 171 |
+
|
| 172 |
+
Actual memory usage depends on:
|
| 173 |
+
|
| 174 |
+
- Precision
|
| 175 |
+
- Quantization
|
| 176 |
+
- Runtime implementation
|
| 177 |
+
- Sequence length
|
| 178 |
+
- Batch size
|
| 179 |
+
- Device mapping
|
| 180 |
+
- Whether long-context experiments are enabled
|
| 181 |
+
|
| 182 |
+
The 5M context configuration is experimental. Do not assume ordinary inference hardware can run full 5M-token contexts without specialized infrastructure.
|
| 183 |
+
|
| 184 |
+
---
|
| 185 |
+
|
| 186 |
+
# Quasar-Preview Benchmark Snapshot
|
| 187 |
+
|
| 188 |
+
These are early benchmark results from the current Quasar checkpoint lineage.
|
| 189 |
+
|
| 190 |
+
They should be treated as a moving snapshot, not final model quality.
|
| 191 |
+
|
| 192 |
+
| Category | Benchmark | Quasar-Preview |
|
| 193 |
+
| --- | --- | ---: |
|
| 194 |
+
| Knowledge | MMLU (5-shot) | **68.40%** |
|
| 195 |
+
| Knowledge | MMLU-Pro | **33.20%** |
|
| 196 |
+
| Knowledge | GPQA | **25.60%** |
|
| 197 |
+
| Commonsense | ARC Challenge | **63.00%** |
|
| 198 |
+
| Commonsense | ARC Easy | **80.10%** |
|
| 199 |
+
| Commonsense | PIQA | **81.90%** |
|
| 200 |
+
| Commonsense | HellaSwag | **74.00%** |
|
| 201 |
+
| Science | OpenBookQA | **47.00%** |
|
| 202 |
+
| Math | MATH-500 (4-shot) | **71.40%** |
|
| 203 |
+
|
| 204 |
+
## Evaluation Notes
|
| 205 |
+
|
| 206 |
+
These results are provided as an early internal snapshot for the current Quasar-Preview checkpoint lineage.
|
| 207 |
+
|
| 208 |
+
They are not presented as final model quality. Public verification, different harness versions, prompt formats, decoding settings, and evaluation implementations may change the reported numbers.
|
| 209 |
+
|
| 210 |
+
When comparing Quasar-Preview to other models, please report:
|
| 211 |
+
|
| 212 |
+
- Evaluation harness
|
| 213 |
+
- Harness version or commit
|
| 214 |
+
- Prompt format
|
| 215 |
+
- Shot count
|
| 216 |
+
- Decoding settings
|
| 217 |
+
- Whether chain-of-thought prompting was used
|
| 218 |
+
- Exact checkpoint version
|
| 219 |
+
|
| 220 |
+
---
|
| 221 |
+
|
| 222 |
+
# Training Strategy
|
| 223 |
+
|
| 224 |
+
Quasar follows a multi-stage training plan.
|
| 225 |
+
|
| 226 |
+
Quasar-Preview is an early checkpoint from this plan.
|
| 227 |
+
|
| 228 |
+
## Stage 1 — Base Pretraining
|
| 229 |
+
|
| 230 |
+
The base model is trained on a broad corpus to build general next-token prediction, reasoning, and language ability.
|
| 231 |
+
|
| 232 |
+
Goals of this stage:
|
| 233 |
+
|
| 234 |
+
- Stabilize the sparse MoE path
|
| 235 |
+
- Build general language ability
|
| 236 |
+
- Train the hybrid Quasar stack
|
| 237 |
+
- Establish a checkpoint suitable for distillation and subnet training
|
| 238 |
+
|
| 239 |
+
Quasar-Preview has been trained on **>1T and <1.5T tokens** so far.
|
| 240 |
+
|
| 241 |
+
## Stage 2 — Distillation And Capability Training
|
| 242 |
+
|
| 243 |
+
After base training, Quasar-Preview is improved through task distillation and targeted capability training.
|
| 244 |
+
|
| 245 |
+
The goal is to make the checkpoint more useful for:
|
| 246 |
+
|
| 247 |
+
- Reasoning
|
| 248 |
+
- Instruction-following
|
| 249 |
+
- Commonsense tasks
|
| 250 |
+
- Math and science tasks
|
| 251 |
+
- SN24 miner distillation
|
| 252 |
+
- Future post-training
|
| 253 |
+
|
| 254 |
+
This release is designed to be a foundation for continued decentralized improvement rather than the final result.
|
| 255 |
+
|
| 256 |
+
## Stage 3 — Long-Context Extension
|
| 257 |
+
|
| 258 |
+
Quasar is designed to move toward ultra-long-context reasoning and memory.
|
| 259 |
+
|
| 260 |
+
The current checkpoint exposes an experimental **5M-token context configuration** using safe NoPE / DrOPE-style staging.
|
| 261 |
+
|
| 262 |
+
Important: the 5M context path has received **less than 1B tokens** of long-context extension training so far.
|
| 263 |
+
|
| 264 |
+
This means the config is present, but mature 5M-token reasoning quality should not be expected yet.
|
| 265 |
+
|
| 266 |
+
The purpose of this stage is to:
|
| 267 |
+
|
| 268 |
+
- Preserve short-context behavior
|
| 269 |
+
- Avoid damaging the base model during extension
|
| 270 |
+
- Prepare the architecture for future long-context training
|
| 271 |
+
- Enable research on scalable memory and recall
|
| 272 |
+
|
| 273 |
+
---
|
| 274 |
+
|
| 275 |
+
# Quasar Long Hybrid Architecture
|
| 276 |
+
|
| 277 |
+
Quasar is a hybrid transformer architecture designed for long-context research, sparse computation, and decentralized training.
|
| 278 |
+
|
| 279 |
+
It is built around:
|
| 280 |
+
|
| 281 |
+
- A Loop Transformer execution scaffold
|
| 282 |
+
- Sparse Mixture-of-Experts routing
|
| 283 |
+
- Hybrid Quasar / Raven / GLA branch layers
|
| 284 |
+
- Optional anchor-state conditioning
|
| 285 |
+
- Optional Engram n-gram memory
|
| 286 |
+
- Safe NoPE / DrOPE-style long-context configuration
|
| 287 |
+
|
| 288 |
+
Quasar-Preview is the first public checkpoint in this architecture family.
|
| 289 |
+
|
| 290 |
+
---
|
| 291 |
+
|
| 292 |
+
# Technical Specifications
|
| 293 |
+
|
| 294 |
+
| Component | Value |
|
| 295 |
+
| --- | ---: |
|
| 296 |
+
| Total parameters | ~18B |
|
| 297 |
+
| Active parameters | ~2B |
|
| 298 |
+
| Layers | 20 |
|
| 299 |
+
| Hidden size | 2048 |
|
| 300 |
+
| Intermediate size | 5120 |
|
| 301 |
+
| Attention heads | 16 |
|
| 302 |
+
| KV heads | 4 |
|
| 303 |
+
| Head dim | 128 |
|
| 304 |
+
| Vocabulary size | 157,184 |
|
| 305 |
+
| Experts | 256 |
|
| 306 |
+
| Experts per token | 8 |
|
| 307 |
+
| Shared experts | 1 |
|
| 308 |
+
| Active hybrid layers | 4-19 |
|
| 309 |
+
| Raven slots | 64 |
|
| 310 |
+
| Raven top-k | 32 |
|
| 311 |
+
| Engram slots config | 2,000,000 |
|
| 312 |
+
| Loop count config | 1 |
|
| 313 |
+
| Looped injection config | Disabled |
|
| 314 |
+
| Max context config | 5,000,000 |
|
| 315 |
+
| Safe NoPE cutoff | 512 |
|
| 316 |
+
|
| 317 |
+
Compatibility note: this checkpoint includes GQA for the current release path. Future Quasar versions may change this component as the architecture evolves.
|
| 318 |
+
|
| 319 |
+
---
|
| 320 |
+
|
| 321 |
+
# Looped Transformer Path
|
| 322 |
+
|
| 323 |
+
Quasar includes a Loop Transformer execution path.
|
| 324 |
+
|
| 325 |
+
The idea is to reuse the decoder stack across multiple passes, increasing effective computation depth without copying every parameter into a deeper model.
|
| 326 |
+
|
| 327 |
+
The current checkpoint is configured conservatively:
|
| 328 |
+
|
| 329 |
+
```text
|
| 330 |
+
num_loops: 1
|
| 331 |
+
use_looped_injection: false
|
| 332 |
+
```
|
| 333 |
+
|
| 334 |
+
This means Quasar-Preview runs as a single-loop model by default.
|
| 335 |
+
|
| 336 |
+
The loop machinery is still part of the architecture code and can be enabled in future Quasar configurations.
|
| 337 |
+
|
| 338 |
+
When looped injection is enabled, Quasar keeps an anchor snapshot of the input embedding stream, usually called **P**, and injects it back into the hidden state during looped execution.
|
| 339 |
+
|
| 340 |
+
This gives later loop passes a stable reference to the original token stream.
|
| 341 |
+
|
| 342 |
+
The intended future looped path is:
|
| 343 |
+
|
| 344 |
+
```text
|
| 345 |
+
Token IDs
|
| 346 |
+
|
|
| 347 |
+
v
|
| 348 |
+
Embedding Layer
|
| 349 |
+
|
|
| 350 |
+
+--> Anchor P snapshot
|
| 351 |
+
|
|
| 352 |
+
v
|
| 353 |
+
Decoder stack
|
| 354 |
+
|
|
| 355 |
+
v
|
| 356 |
+
Loop pass 1
|
| 357 |
+
|
|
| 358 |
+
+--> inject gated Anchor P
|
| 359 |
+
|
|
| 360 |
+
v
|
| 361 |
+
Loop pass 2 / future passes
|
| 362 |
+
|
|
| 363 |
+
v
|
| 364 |
+
Final hidden state
|
| 365 |
+
```
|
| 366 |
+
|
| 367 |
+
The injection gate is initialized near zero so the model can adapt safely instead of suddenly changing behavior.
|
| 368 |
+
|
| 369 |
+
This gives Quasar a path toward deeper effective reasoning while keeping parameter count controlled.
|
| 370 |
+
|
| 371 |
+
---
|
| 372 |
+
|
| 373 |
+
# Core Data Flow
|
| 374 |
+
|
| 375 |
+
```text
|
| 376 |
+
Token IDs
|
| 377 |
+
|
|
| 378 |
+
v
|
| 379 |
+
Token Embedding
|
| 380 |
+
|
|
| 381 |
+
+--> Optional Anchor P snapshot
|
| 382 |
+
|
|
| 383 |
+
v
|
| 384 |
+
Early Transformer Blocks
|
| 385 |
+
layers 0-3
|
| 386 |
+
|
|
| 387 |
+
v
|
| 388 |
+
Hybrid Quasar Blocks
|
| 389 |
+
layers 4-19
|
| 390 |
+
|
|
| 391 |
+
+--> GQA attention path
|
| 392 |
+
|
|
| 393 |
+
+--> Quasar recurrent / linear path
|
| 394 |
+
|
|
| 395 |
+
+--> Raven slot-memory path
|
| 396 |
+
|
|
| 397 |
+
+--> GLA recurrent path
|
| 398 |
+
|
|
| 399 |
+
v
|
| 400 |
+
Hybrid Add / Branch Merge
|
| 401 |
+
|
|
| 402 |
+
v
|
| 403 |
+
Optional Loop Injection / Next Loop
|
| 404 |
+
|
|
| 405 |
+
v
|
| 406 |
+
RMSNorm
|
| 407 |
+
|
|
| 408 |
+
v
|
| 409 |
+
LM Head
|
| 410 |
+
|
|
| 411 |
+
v
|
| 412 |
+
Next-token logits
|
| 413 |
+
```
|
| 414 |
+
|
| 415 |
+
---
|
| 416 |
+
|
| 417 |
+
# Hybrid Layer Composition
|
| 418 |
+
|
| 419 |
+
The active hybrid layers are:
|
| 420 |
+
|
| 421 |
+
```text
|
| 422 |
+
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
|
| 423 |
+
```
|
| 424 |
+
|
| 425 |
+
The current layerwise branch cycle is:
|
| 426 |
+
|
| 427 |
+
```text
|
| 428 |
+
quasar -> raven -> quasar -> quasar -> gla
|
| 429 |
+
```
|
| 430 |
+
|
| 431 |
+
Across the hybrid stack, this gives:
|
| 432 |
+
|
| 433 |
+
- **Quasar branch:** 10 layers
|
| 434 |
+
- **Raven branch:** 3 layers
|
| 435 |
+
- **GLA branch:** 3 layers
|
| 436 |
+
|
| 437 |
+
The design keeps Quasar as the dominant branch while giving the model targeted recurrent and slot-memory paths.
|
| 438 |
+
|
| 439 |
+
---
|
| 440 |
+
|
| 441 |
+
# Quasar + GLA
|
| 442 |
+
|
| 443 |
+
GLA is used through the bundled Flash Linear Attention stack.
|
| 444 |
+
|
| 445 |
+
The goal of the GLA branch is to give Quasar a fast recurrent sequence-mixing path that is cheaper than full dense attention at long lengths.
|
| 446 |
+
|
| 447 |
+
Current GLA-related config:
|
| 448 |
+
|
| 449 |
+
```text
|
| 450 |
+
hybrid_gla_enabled: true
|
| 451 |
+
hybrid_gla_expand_k: 1.0
|
| 452 |
+
hybrid_gla_expand_v: 1.0
|
| 453 |
+
hybrid_use_short_conv: false
|
| 454 |
+
```
|
| 455 |
+
|
| 456 |
+
GLA is not used as a standalone model here.
|
| 457 |
+
|
| 458 |
+
It is a branch inside Quasar's hybrid layers.
|
| 459 |
+
|
| 460 |
+
---
|
| 461 |
+
|
| 462 |
+
# Raven Design
|
| 463 |
+
|
| 464 |
+
Raven is included as a slot-routed recurrent attention branch.
|
| 465 |
+
|
| 466 |
+
Current Raven config:
|
| 467 |
+
|
| 468 |
+
```text
|
| 469 |
+
hybrid_raven_enabled: true
|
| 470 |
+
hybrid_raven_slots: 64
|
| 471 |
+
hybrid_raven_topk: 32
|
| 472 |
+
hybrid_raven_decay_type: Mamba2
|
| 473 |
+
```
|
| 474 |
+
|
| 475 |
+
Raven routes hidden states through a fixed number of recurrent memory slots.
|
| 476 |
+
|
| 477 |
+
In this checkpoint:
|
| 478 |
+
|
| 479 |
+
- The branch has **64 memory slots**
|
| 480 |
+
- It selects **top-32 routes**
|
| 481 |
+
- It uses a **Mamba2-style decay**
|
| 482 |
+
|
| 483 |
+
Raven gives Quasar a memory-like path where sequence information can be compressed into routed recurrent state instead of relying only on dense attention.
|
| 484 |
+
|
| 485 |
+
---
|
| 486 |
+
|
| 487 |
+
# Engram Design
|
| 488 |
+
|
| 489 |
+
Engram is Quasar's conditional n-gram memory module.
|
| 490 |
+
|
| 491 |
+
It is included in the repository as `engram.py` and supports:
|
| 492 |
+
|
| 493 |
+
- n-gram orders `[2, 3]`
|
| 494 |
+
- 8 Engram heads
|
| 495 |
+
- configurable memory slots
|
| 496 |
+
- Triton hash-table lookup
|
| 497 |
+
- gated projection back into the residual stream
|
| 498 |
+
|
| 499 |
+
Current Engram config:
|
| 500 |
+
|
| 501 |
+
```text
|
| 502 |
+
engram_slots: 2,000,000
|
| 503 |
+
engram_dim: 512
|
| 504 |
+
engram_ngram_orders: [2, 3]
|
| 505 |
+
engram_num_heads: 8
|
| 506 |
+
engram_residual_scale: 0.01
|
| 507 |
+
engram_lr_multiplier: 5.0
|
| 508 |
+
engram_layers: []
|
| 509 |
+
```
|
| 510 |
+
|
| 511 |
+
`engram_layers` is currently empty.
|
| 512 |
+
|
| 513 |
+
This means Engram is included and loadable, but not active by default in Quasar-Preview.
|
| 514 |
+
|
| 515 |
+
Future Quasar versions can enable Engram on selected layers without changing the base model shape.
|
| 516 |
+
|
| 517 |
+
Engram is intended as a fast recall path for repeated local patterns, while the main model focuses on reasoning and generalization.
|
| 518 |
+
|
| 519 |
+
---
|
| 520 |
+
|
| 521 |
+
# Safe NoPE / DrOPE Context Design
|
| 522 |
+
|
| 523 |
+
The current checkpoint uses safe NoPE as the default long-context configuration.
|
| 524 |
+
|
| 525 |
+
Current context config:
|
| 526 |
+
|
| 527 |
+
```text
|
| 528 |
+
use_nope: true
|
| 529 |
+
long_context_mode: rope_short_nope_long
|
| 530 |
+
nope_after_position: 512
|
| 531 |
+
max_position_embeddings: 5,000,000
|
| 532 |
+
max_seq_length: 5,000,000
|
| 533 |
+
max_sequence_length: 5,000,000
|
| 534 |
+
rope_scaling: null
|
| 535 |
+
rope_theta: 10000
|
| 536 |
+
```
|
| 537 |
+
|
| 538 |
+
The behavior is:
|
| 539 |
+
|
| 540 |
+
```text
|
| 541 |
+
Positions 0-511
|
| 542 |
+
-> normal RoPE
|
| 543 |
+
|
| 544 |
+
Positions 512+
|
| 545 |
+
-> NoPE identity rotation
|
| 546 |
+
cos = 1
|
| 547 |
+
sin = 0
|
| 548 |
+
```
|
| 549 |
+
|
| 550 |
+
This is a safe DrOPE-style staging design for positional extension.
|
| 551 |
+
|
| 552 |
+
The goals are:
|
| 553 |
+
|
| 554 |
+
- Preserve short-context behavior
|
| 555 |
+
- Avoid stretching RoPE everywhere
|
| 556 |
+
- Avoid allocating a giant 5M RoPE table
|
| 557 |
+
- Expose a 5M sequence-length configuration
|
| 558 |
+
- Prepare for future long-context training runs
|
| 559 |
+
|
| 560 |
+
Important: the 5M context path has only received **less than 1B tokens** of long-context extension training so far.
|
| 561 |
+
|
| 562 |
+
So high-quality 5M-token reasoning should not be expected yet.
|
| 563 |
+
|
| 564 |
+
This setting is included to expose and continue training the long-context path safely.
|
| 565 |
+
|
| 566 |
+
---
|
| 567 |
+
|
| 568 |
+
# Config Snapshot
|
| 569 |
+
|
| 570 |
+
```json
|
| 571 |
+
{
|
| 572 |
+
"model_type": "quasar_long",
|
| 573 |
+
"architectures": ["QuasarLongForCausalLM"],
|
| 574 |
+
"hidden_size": 2048,
|
| 575 |
+
"intermediate_size": 5120,
|
| 576 |
+
"num_hidden_layers": 20,
|
| 577 |
+
"num_attention_heads": 16,
|
| 578 |
+
"num_key_value_heads": 4,
|
| 579 |
+
"head_dim": 128,
|
| 580 |
+
"vocab_size": 157184,
|
| 581 |
+
"num_experts": 256,
|
| 582 |
+
"num_experts_per_tok": 8,
|
| 583 |
+
"num_shared_experts": 1,
|
| 584 |
+
"num_loops": 1,
|
| 585 |
+
"use_looped_injection": false,
|
| 586 |
+
"hybrid_attention_layers": [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
|
| 587 |
+
"hybrid_branch_layout": "layerwise",
|
| 588 |
+
"hybrid_layerwise_cycle": ["quasar", "raven", "quasar", "quasar", "gla"],
|
| 589 |
+
"hybrid_replacement_mode": "add",
|
| 590 |
+
"hybrid_eval_mode": "hybrid_add",
|
| 591 |
+
"hybrid_quasar_enabled": true,
|
| 592 |
+
"hybrid_raven_enabled": true,
|
| 593 |
+
"hybrid_gla_enabled": true,
|
| 594 |
+
"hybrid_raven_slots": 64,
|
| 595 |
+
"hybrid_raven_topk": 32,
|
| 596 |
+
"use_nope": true,
|
| 597 |
+
"long_context_mode": "rope_short_nope_long",
|
| 598 |
+
"nope_after_position": 512,
|
| 599 |
+
"max_position_embeddings": 5000000,
|
| 600 |
+
"max_seq_length": 5000000,
|
| 601 |
+
"max_sequence_length": 5000000
|
| 602 |
+
}
|
| 603 |
+
```
|
| 604 |
+
|
| 605 |
+
---
|
| 606 |
+
|
| 607 |
+
# Intended Use
|
| 608 |
+
|
| 609 |
+
Quasar-Preview is designed as an early foundation checkpoint for the Quasar ecosystem.
|
| 610 |
+
|
| 611 |
+
It is primarily intended for:
|
| 612 |
+
|
| 613 |
+
- **Bittensor SN24 miners** participating in decentralized training and knowledge distillation
|
| 614 |
+
- **Distillation pipelines** transferring capabilities from stronger teacher models
|
| 615 |
+
- **Research on long-context architectures**
|
| 616 |
+
- **Research on sparse MoE systems**
|
| 617 |
+
- **Hybrid attention research**
|
| 618 |
+
- **Agentic system experiments**
|
| 619 |
+
- **Memory and recall experiments**
|
| 620 |
+
- **Future Quasar model development**
|
| 621 |
+
|
| 622 |
+
This model is best treated as a research and development checkpoint.
|
| 623 |
+
|
| 624 |
+
---
|
| 625 |
+
|
| 626 |
+
# Out-of-Scope Use
|
| 627 |
+
|
| 628 |
+
Quasar-Preview is not intended to be used as:
|
| 629 |
+
|
| 630 |
+
- A final production assistant
|
| 631 |
+
- A safety-aligned chatbot
|
| 632 |
+
- A medical, legal, or financial authority
|
| 633 |
+
- A final benchmark-maximized release
|
| 634 |
+
- Proof of mature 5M-token reasoning quality
|
| 635 |
+
- The final Quasar architecture endpoint
|
| 636 |
+
|
| 637 |
+
The model may produce incorrect, unsafe, biased, or low-quality outputs.
|
| 638 |
+
|
| 639 |
+
Use appropriate evaluation, filtering, and safety layers before any deployment.
|
| 640 |
+
|
| 641 |
+
---
|
| 642 |
+
|
| 643 |
+
# Limitations
|
| 644 |
+
|
| 645 |
+
Quasar-Preview is early.
|
| 646 |
+
|
| 647 |
+
Known limitations:
|
| 648 |
+
|
| 649 |
+
- It is not the finished Quasar model.
|
| 650 |
+
- It is the first model in a broader Quasar series.
|
| 651 |
+
- Long-context behavior is experimental.
|
| 652 |
+
- The 5M-token context is a configuration path, not yet mature 5M-token reasoning quality.
|
| 653 |
+
- The long-context path has received less than 1B tokens of extension training so far.
|
| 654 |
+
- Some architecture modules are included for future versions but disabled in this checkpoint.
|
| 655 |
+
- Engram is included but not active by default.
|
| 656 |
+
- Loop execution is configured as single-loop by default.
|
| 657 |
+
- Benchmarks are early checkpoint-lineage snapshots and require public verification.
|
| 658 |
+
- The model may hallucinate or produce incorrect answers.
|
| 659 |
+
- The model has not completed the full Quasar training roadmap.
|
| 660 |
+
|
| 661 |
+
---
|
| 662 |
+
|
| 663 |
+
# Bittensor SN24
|
| 664 |
+
|
| 665 |
+
Quasar-Preview is designed for the **SN24 Quasar subnet** on Bittensor.
|
| 666 |
+
|
| 667 |
+
The goal is to create a shared architecture where miners can continuously improve the model through distributed knowledge distillation, evaluation, and iterative training.
|
| 668 |
+
|
| 669 |
+
SN24 is intended to support:
|
| 670 |
+
|
| 671 |
+
- Open model improvement
|
| 672 |
+
- Competitive distillation
|
| 673 |
+
- Decentralized training incentives
|
| 674 |
+
- Shared progress on the Quasar architecture
|
| 675 |
+
- Long-context and memory-focused model development
|
| 676 |
+
|
| 677 |
+
Quasar-Preview is the starting checkpoint for this direction.
|
| 678 |
+
|
| 679 |
+
---
|
| 680 |
+
|
| 681 |
+
# Roadmap
|
| 682 |
+
|
| 683 |
+
Quasar-Preview is only the first public model in the Quasar series.
|
| 684 |
+
|
| 685 |
+
Next Quasar models will continue toward:
|
| 686 |
+
|
| 687 |
+
- Larger-scale decentralized training
|
| 688 |
+
- More training tokens
|
| 689 |
+
- Stronger post-training
|
| 690 |
+
- Better reasoning performance
|
| 691 |
+
- More stable long-context behavior
|
| 692 |
+
- More long-context extension training
|
| 693 |
+
- Deeper Loop Transformer experiments
|
| 694 |
+
- More Raven, GLA, and Engram experimentation
|
| 695 |
+
- Improved benchmark performance
|
| 696 |
+
- Stronger agentic and memory capabilities
|
| 697 |
+
|
| 698 |
+
Future releases may change architecture components, routing, loop configuration, long-context training strategy, and active memory modules as the Quasar series evolves.
|
| 699 |
+
|
| 700 |
+
---
|
| 701 |
+
|
| 702 |
+
# Release Statement
|
| 703 |
+
|
| 704 |
+
Quasar-Preview is not the final destination.
|
| 705 |
+
|
| 706 |
+
It is the first public checkpoint in the Quasar model series and the first public proof of the architecture direction at scale.
|
| 707 |
+
|
| 708 |
+
The model is early, but it is real, usable, and ready for research, distillation, and decentralized improvement.
|
| 709 |
+
|
| 710 |
+
This is the beginning of Quasar.
|
chat_template.jinja
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{% for message in messages %}{% set role = message['role'] | lower %}{% if role == 'user' %}{% set role = 'HUMAN' %}{% endif %}{% set role = role | upper %}{{ '<role>' + role + '</role>' + message['content'] }}{% endfor %}{% if add_generation_prompt %}{{ '<role>ASSISTANT</role>' }}{% endif %}
|
config.json
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_name_or_path": "silx-ai/Quasar-T",
|
| 3 |
+
"architectures": [
|
| 4 |
+
"QuasarLongForCausalLM"
|
| 5 |
+
],
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"auto_map": {
|
| 8 |
+
"AutoConfig": "configuration_quasar_long.QuasarLongConfig",
|
| 9 |
+
"AutoModelForCausalLM": "modeling_quasar_long.QuasarLongForCausalLM"
|
| 10 |
+
},
|
| 11 |
+
"dtype": "bfloat16",
|
| 12 |
+
"embedding_dropout": 0.0,
|
| 13 |
+
"engram_dim": 512,
|
| 14 |
+
"engram_layers": [],
|
| 15 |
+
"engram_lr_multiplier": 5.0,
|
| 16 |
+
"engram_ngram_orders": [
|
| 17 |
+
2,
|
| 18 |
+
3
|
| 19 |
+
],
|
| 20 |
+
"engram_num_heads": 8,
|
| 21 |
+
"engram_residual_scale": 0.01,
|
| 22 |
+
"engram_slots": 2000000,
|
| 23 |
+
"eos_token_id": 156892,
|
| 24 |
+
"first_k_dense_replace": 1,
|
| 25 |
+
"head_dim": 128,
|
| 26 |
+
"hidden_act": "silu",
|
| 27 |
+
"hidden_size": 2048,
|
| 28 |
+
"hybrid_alpha_init": 0.8473,
|
| 29 |
+
"hybrid_attention_layers": [
|
| 30 |
+
4,
|
| 31 |
+
5,
|
| 32 |
+
6,
|
| 33 |
+
7,
|
| 34 |
+
8,
|
| 35 |
+
9,
|
| 36 |
+
10,
|
| 37 |
+
11,
|
| 38 |
+
12,
|
| 39 |
+
13,
|
| 40 |
+
14,
|
| 41 |
+
15,
|
| 42 |
+
16,
|
| 43 |
+
17,
|
| 44 |
+
18,
|
| 45 |
+
19
|
| 46 |
+
],
|
| 47 |
+
"hybrid_branch_layout": "layerwise",
|
| 48 |
+
"hybrid_eval_force_branch": "",
|
| 49 |
+
"hybrid_eval_mode": "hybrid_add",
|
| 50 |
+
"hybrid_gla_enabled": true,
|
| 51 |
+
"hybrid_gla_expand_k": 1.0,
|
| 52 |
+
"hybrid_gla_expand_v": 1.0,
|
| 53 |
+
"hybrid_layerwise_cycle": [
|
| 54 |
+
"quasar",
|
| 55 |
+
"raven",
|
| 56 |
+
"quasar",
|
| 57 |
+
"quasar",
|
| 58 |
+
"gla"
|
| 59 |
+
],
|
| 60 |
+
"hybrid_quasar_enabled": true,
|
| 61 |
+
"hybrid_raven_decay_type": "Mamba2",
|
| 62 |
+
"hybrid_raven_enabled": true,
|
| 63 |
+
"hybrid_raven_slots": 64,
|
| 64 |
+
"hybrid_raven_topk": 32,
|
| 65 |
+
"hybrid_replacement_mode": "add",
|
| 66 |
+
"hybrid_use_short_conv": false,
|
| 67 |
+
"initializer_range": 0.02,
|
| 68 |
+
"intermediate_size": 5120,
|
| 69 |
+
"long_context_mode": "rope_short_nope_long",
|
| 70 |
+
"max_position_embeddings": 5000000,
|
| 71 |
+
"max_seq_length": 5000000,
|
| 72 |
+
"max_sequence_length": 5000000,
|
| 73 |
+
"max_window_layers": 20,
|
| 74 |
+
"model_type": "quasar_long",
|
| 75 |
+
"moe_intermediate_size": 512,
|
| 76 |
+
"moe_router_enable_expert_bias": true,
|
| 77 |
+
"moe_shared_expert_intermediate_size": 512,
|
| 78 |
+
"mtp_loss_scaling_factor": 0,
|
| 79 |
+
"n_group": 8,
|
| 80 |
+
"nope_after_position": 512,
|
| 81 |
+
"norm_topk_prob": true,
|
| 82 |
+
"num_attention_heads": 16,
|
| 83 |
+
"num_experts": 256,
|
| 84 |
+
"num_experts_per_tok": 8,
|
| 85 |
+
"num_hidden_layers": 20,
|
| 86 |
+
"num_key_value_heads": 4,
|
| 87 |
+
"num_loops": 1,
|
| 88 |
+
"num_nextn_predict_layers": 0,
|
| 89 |
+
"num_shared_experts": 1,
|
| 90 |
+
"output_dropout": 0.0,
|
| 91 |
+
"output_router_logits": false,
|
| 92 |
+
"pad_token_id": 156892,
|
| 93 |
+
"partial_rotary_factor": 0.5,
|
| 94 |
+
"rms_norm_eps": 1e-06,
|
| 95 |
+
"rope_parameters": {
|
| 96 |
+
"partial_rotary_factor": 0.5,
|
| 97 |
+
"rope_theta": 10000,
|
| 98 |
+
"rope_type": "default"
|
| 99 |
+
},
|
| 100 |
+
"rope_scaling": null,
|
| 101 |
+
"rope_theta": 10000,
|
| 102 |
+
"routed_scaling_factor": 2.5,
|
| 103 |
+
"router_dtype": "fp32",
|
| 104 |
+
"score_function": "sigmoid",
|
| 105 |
+
"tie_word_embeddings": false,
|
| 106 |
+
"topk_group": 4,
|
| 107 |
+
"transformers_version": "5.10.2",
|
| 108 |
+
"use_bias": false,
|
| 109 |
+
"use_cache": true,
|
| 110 |
+
"use_looped_injection": false,
|
| 111 |
+
"use_nope": true,
|
| 112 |
+
"use_qk_norm": true,
|
| 113 |
+
"use_qkv_bias": false,
|
| 114 |
+
"use_rmsnorm": true,
|
| 115 |
+
"vocab_size": 157184
|
| 116 |
+
}
|
configuration_quasar_long.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Quasar Long model configuration"""
|
| 2 |
+
|
| 3 |
+
from transformers.configuration_utils import PretrainedConfig
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class QuasarLongConfig(PretrainedConfig):
|
| 7 |
+
model_type = "quasar_long"
|
| 8 |
+
|
| 9 |
+
def __init__(
|
| 10 |
+
self,
|
| 11 |
+
vocab_size=157184,
|
| 12 |
+
hidden_size=2048,
|
| 13 |
+
intermediate_size=5120,
|
| 14 |
+
num_hidden_layers=20,
|
| 15 |
+
num_attention_heads=16,
|
| 16 |
+
num_key_value_heads=4,
|
| 17 |
+
hidden_act="silu",
|
| 18 |
+
use_qkv_bias=False, # quasar legacy
|
| 19 |
+
use_bias=False, # quasar legacy
|
| 20 |
+
rms_norm_eps=1e-06,
|
| 21 |
+
tie_word_embeddings=False, # PretrainedConfig key, here change default value.
|
| 22 |
+
embedding_dropout=0.0,
|
| 23 |
+
attention_dropout=0.0,
|
| 24 |
+
output_dropout=0.0,
|
| 25 |
+
initializer_range=0.02,
|
| 26 |
+
max_position_embeddings=32768,
|
| 27 |
+
rope_theta=600000.0,
|
| 28 |
+
use_cache=True,
|
| 29 |
+
max_window_layers=20,
|
| 30 |
+
rope_scaling=None,
|
| 31 |
+
pad_token_id=156892,
|
| 32 |
+
eos_token_id=156892,
|
| 33 |
+
num_experts=256,
|
| 34 |
+
num_shared_experts=1,
|
| 35 |
+
num_experts_per_tok=8,
|
| 36 |
+
n_group=8,
|
| 37 |
+
topk_group=4,
|
| 38 |
+
moe_intermediate_size=512,
|
| 39 |
+
first_k_dense_replace=1,
|
| 40 |
+
head_dim=128,
|
| 41 |
+
output_router_logits=False,
|
| 42 |
+
use_qk_norm=True,
|
| 43 |
+
num_nextn_predict_layers=0,
|
| 44 |
+
mtp_loss_scaling_factor=0,
|
| 45 |
+
moe_router_enable_expert_bias=True,
|
| 46 |
+
routed_scaling_factor=1.0,
|
| 47 |
+
hybrid_attention_layers=None,
|
| 48 |
+
hybrid_alpha_init=-15.0,
|
| 49 |
+
hybrid_gla_expand_k=1.0,
|
| 50 |
+
hybrid_gla_expand_v=1.0,
|
| 51 |
+
hybrid_use_short_conv=False,
|
| 52 |
+
hybrid_quasar_enabled=True,
|
| 53 |
+
hybrid_gla_enabled=True,
|
| 54 |
+
hybrid_branch_layout="mixed",
|
| 55 |
+
hybrid_layerwise_cycle=None,
|
| 56 |
+
# ── Looped Transformer ────────────────────────────────────────────────
|
| 57 |
+
num_loops=1,
|
| 58 |
+
use_looped_injection=False,
|
| 59 |
+
# ── Engram Conditional Memory ─────────────────────────────────────────
|
| 60 |
+
# engram_layers=[] → module disabled (zero overhead, backward-compatible).
|
| 61 |
+
engram_layers=None,
|
| 62 |
+
engram_dim=512,
|
| 63 |
+
engram_slots=2_000_000,
|
| 64 |
+
engram_num_heads=8,
|
| 65 |
+
engram_ngram_orders=None,
|
| 66 |
+
engram_lr_multiplier=5.0,
|
| 67 |
+
use_nope=False,
|
| 68 |
+
long_context_mode="rope_short_nope_long",
|
| 69 |
+
nope_after_position=512,
|
| 70 |
+
max_seq_length=None,
|
| 71 |
+
max_sequence_length=None,
|
| 72 |
+
**kwargs,
|
| 73 |
+
):
|
| 74 |
+
self.num_hidden_layers = num_hidden_layers
|
| 75 |
+
self.vocab_size = vocab_size
|
| 76 |
+
self.hidden_size = hidden_size
|
| 77 |
+
self.intermediate_size = intermediate_size
|
| 78 |
+
self.num_attention_heads = num_attention_heads
|
| 79 |
+
self.num_key_value_heads = num_key_value_heads
|
| 80 |
+
self.hidden_act = hidden_act
|
| 81 |
+
self.use_qkv_bias = use_qkv_bias
|
| 82 |
+
self.use_bias = use_bias
|
| 83 |
+
self.rms_norm_eps = rms_norm_eps
|
| 84 |
+
self.embedding_dropout = embedding_dropout
|
| 85 |
+
self.attention_dropout = attention_dropout
|
| 86 |
+
self.output_dropout = output_dropout
|
| 87 |
+
self.num_nextn_predict_layers = num_nextn_predict_layers
|
| 88 |
+
self.mtp_loss_scaling_factor = mtp_loss_scaling_factor
|
| 89 |
+
self.initializer_range = initializer_range
|
| 90 |
+
self.max_position_embeddings = max_position_embeddings
|
| 91 |
+
self.rope_theta = rope_theta
|
| 92 |
+
self.use_cache = use_cache
|
| 93 |
+
self.max_window_layers = max_window_layers
|
| 94 |
+
self.head_dim = head_dim or self.hidden_size // self.num_attention_heads
|
| 95 |
+
self.rope_scaling = rope_scaling
|
| 96 |
+
self.use_qk_norm = use_qk_norm
|
| 97 |
+
self.moe_router_enable_expert_bias = moe_router_enable_expert_bias
|
| 98 |
+
self.routed_scaling_factor = routed_scaling_factor
|
| 99 |
+
self.hybrid_attention_layers = hybrid_attention_layers or []
|
| 100 |
+
self.hybrid_alpha_init = hybrid_alpha_init
|
| 101 |
+
self.hybrid_gla_expand_k = hybrid_gla_expand_k
|
| 102 |
+
self.hybrid_gla_expand_v = hybrid_gla_expand_v
|
| 103 |
+
self.hybrid_use_short_conv = hybrid_use_short_conv
|
| 104 |
+
self.hybrid_quasar_enabled = hybrid_quasar_enabled
|
| 105 |
+
self.hybrid_gla_enabled = hybrid_gla_enabled
|
| 106 |
+
self.hybrid_branch_layout = hybrid_branch_layout
|
| 107 |
+
self.hybrid_layerwise_cycle = list(hybrid_layerwise_cycle) if hybrid_layerwise_cycle is not None else [
|
| 108 |
+
"quasar",
|
| 109 |
+
"raven",
|
| 110 |
+
"gla",
|
| 111 |
+
]
|
| 112 |
+
|
| 113 |
+
# Looped Transformer
|
| 114 |
+
self.num_loops = num_loops
|
| 115 |
+
self.use_looped_injection = use_looped_injection
|
| 116 |
+
|
| 117 |
+
# Engram Conditional Memory
|
| 118 |
+
self.engram_layers = list(engram_layers) if engram_layers is not None else []
|
| 119 |
+
self.engram_dim = engram_dim
|
| 120 |
+
self.engram_slots = engram_slots
|
| 121 |
+
self.engram_num_heads = engram_num_heads
|
| 122 |
+
self.engram_ngram_orders = list(engram_ngram_orders) if engram_ngram_orders is not None else [2, 3]
|
| 123 |
+
self.engram_lr_multiplier = engram_lr_multiplier
|
| 124 |
+
self.use_nope = use_nope
|
| 125 |
+
self.long_context_mode = long_context_mode
|
| 126 |
+
self.nope_after_position = int(nope_after_position)
|
| 127 |
+
self.max_seq_length = int(max_seq_length) if max_seq_length is not None else None
|
| 128 |
+
self.max_sequence_length = int(max_sequence_length) if max_sequence_length is not None else None
|
| 129 |
+
|
| 130 |
+
# MoE configs
|
| 131 |
+
self.num_experts = num_experts
|
| 132 |
+
self.num_shared_experts = num_shared_experts
|
| 133 |
+
self.num_experts_per_tok = num_experts_per_tok
|
| 134 |
+
self.n_group = n_group
|
| 135 |
+
self.topk_group = topk_group
|
| 136 |
+
self.moe_intermediate_size = moe_intermediate_size
|
| 137 |
+
self.first_k_dense_replace = first_k_dense_replace
|
| 138 |
+
self.output_router_logits = output_router_logits
|
| 139 |
+
|
| 140 |
+
super().__init__(pad_token_id=pad_token_id, eos_token_id=eos_token_id, tie_word_embeddings=tie_word_embeddings, **kwargs)
|
engram.py
ADDED
|
@@ -0,0 +1,611 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
EngramModule: Conditional N-gram Memory for Quasar-RoPE
|
| 3 |
+
Implements Engram from DeepSeek-AI (arXiv:2601.07372).
|
| 4 |
+
|
| 5 |
+
Design constraints:
|
| 6 |
+
- No Python loops over T (sequence length) or B (batch).
|
| 7 |
+
- N-gram extraction via torch.unfold (single vectorized op).
|
| 8 |
+
- Hash computed via vectorized XOR reduction (loop over n=2..3 only, compile-time constant).
|
| 9 |
+
- Embedding lookup via batched advanced indexing — no loop over T.
|
| 10 |
+
- Optional Triton kernel fuses hash + lookup + accumulation into a single SRAM pass.
|
| 11 |
+
- Zero output at init: conv.weight=0, out_proj uses deep Trinity init.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
import math
|
| 15 |
+
import os
|
| 16 |
+
import torch
|
| 17 |
+
import torch.nn as nn
|
| 18 |
+
import torch.nn.functional as F
|
| 19 |
+
|
| 20 |
+
try:
|
| 21 |
+
import triton
|
| 22 |
+
import triton.language as tl
|
| 23 |
+
HAS_TRITON = True
|
| 24 |
+
except ImportError:
|
| 25 |
+
HAS_TRITON = False
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
# ─────────────────────────────────────────────────────────────────────────────
|
| 29 |
+
# Helpers
|
| 30 |
+
# ─────────────────────────────────────────────────────────────────────────────
|
| 31 |
+
|
| 32 |
+
def _next_prime(n: int) -> int:
|
| 33 |
+
"""Smallest prime >= n."""
|
| 34 |
+
def _is_prime(x: int) -> bool:
|
| 35 |
+
if x < 2:
|
| 36 |
+
return False
|
| 37 |
+
if x == 2:
|
| 38 |
+
return True
|
| 39 |
+
if x % 2 == 0:
|
| 40 |
+
return False
|
| 41 |
+
for i in range(3, int(x ** 0.5) + 1, 2):
|
| 42 |
+
if x % i == 0:
|
| 43 |
+
return False
|
| 44 |
+
return True
|
| 45 |
+
n = max(n, 2)
|
| 46 |
+
while not _is_prime(n):
|
| 47 |
+
n += 1
|
| 48 |
+
return n
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
# ─────────────────────────────────────────────────────────────────────────────
|
| 52 |
+
# Triton Kernel: Fused N-gram Hash + Embedding Lookup
|
| 53 |
+
#
|
| 54 |
+
# Grid: (B, T). Each program handles one (batch, position) pair.
|
| 55 |
+
# For each of the `num_tables` embedding tables:
|
| 56 |
+
# 1. Load the suffix n-gram ending at position t (causal, no future tokens).
|
| 57 |
+
# 2. Compute XOR-multiplicative hash (loop over n ≤ 3, constexpr-unrolled).
|
| 58 |
+
# 3. Index into the embedding table and write directly to output.
|
| 59 |
+
# One SRAM pass — no intermediate [B,T,n] tensor, no round-trip to HBM.
|
| 60 |
+
# ─────────────────────────────────────────────────────────────────────────────
|
| 61 |
+
|
| 62 |
+
if HAS_TRITON:
|
| 63 |
+
@triton.jit
|
| 64 |
+
def _engram_hash_lookup_kernel(
|
| 65 |
+
# [B, T] canonical token IDs (int32 on device)
|
| 66 |
+
canonical_ptr, stride_cb, stride_ct,
|
| 67 |
+
# [B, T, num_tables * d_slot] output (bfloat16)
|
| 68 |
+
output_ptr, stride_ob, stride_ot,
|
| 69 |
+
# [num_tables, M, d_slot] embedding tables (float32)
|
| 70 |
+
tables_ptr, stride_tn, stride_tm, stride_td,
|
| 71 |
+
# [num_tables] per-table seeds (int64)
|
| 72 |
+
seeds_ptr,
|
| 73 |
+
# [num_ngram_orders] ngram order values, e.g. [2, 3]
|
| 74 |
+
ngrams_ptr,
|
| 75 |
+
# Scalars
|
| 76 |
+
B, T: tl.constexpr, M, d_slot: tl.constexpr,
|
| 77 |
+
num_tables: tl.constexpr,
|
| 78 |
+
num_ngram_orders: tl.constexpr, # ≤ 4
|
| 79 |
+
num_heads: tl.constexpr, # ≤ 16
|
| 80 |
+
MAX_N: tl.constexpr, # max(ngram_orders), e.g. 3
|
| 81 |
+
BLOCK_D: tl.constexpr, # power-of-2 ≥ d_slot
|
| 82 |
+
):
|
| 83 |
+
b_idx = tl.program_id(0)
|
| 84 |
+
t_idx = tl.program_id(1)
|
| 85 |
+
|
| 86 |
+
d_offs = tl.arange(0, BLOCK_D)
|
| 87 |
+
d_mask = d_offs < d_slot
|
| 88 |
+
|
| 89 |
+
# Pre-load the last MAX_N canonical tokens ending at t_idx (causal).
|
| 90 |
+
# Positions before 0 are treated as padding (0). We unroll to pure scalars for Triton compatibility.
|
| 91 |
+
# Crucial Safety: clamp pos using tl.where to ensure pointer arithmetic is never negative.
|
| 92 |
+
c0 = tl.full((), 0, dtype=tl.int64)
|
| 93 |
+
c1 = tl.full((), 0, dtype=tl.int64)
|
| 94 |
+
c2 = tl.full((), 0, dtype=tl.int64)
|
| 95 |
+
c3 = tl.full((), 0, dtype=tl.int64)
|
| 96 |
+
|
| 97 |
+
if MAX_N >= 1:
|
| 98 |
+
pos_raw = t_idx - (MAX_N - 1 - 0)
|
| 99 |
+
valid = pos_raw >= 0
|
| 100 |
+
pos = tl.where(valid, pos_raw, 0)
|
| 101 |
+
tok = tl.load(
|
| 102 |
+
canonical_ptr + b_idx * stride_cb + pos * stride_ct,
|
| 103 |
+
mask=valid, other=0,
|
| 104 |
+
)
|
| 105 |
+
c0 = tl.where(valid, tok.to(tl.int64), tl.full((), 0, dtype=tl.int64))
|
| 106 |
+
if MAX_N >= 2:
|
| 107 |
+
pos_raw = t_idx - (MAX_N - 1 - 1)
|
| 108 |
+
valid = pos_raw >= 0
|
| 109 |
+
pos = tl.where(valid, pos_raw, 0)
|
| 110 |
+
tok = tl.load(
|
| 111 |
+
canonical_ptr + b_idx * stride_cb + pos * stride_ct,
|
| 112 |
+
mask=valid, other=0,
|
| 113 |
+
)
|
| 114 |
+
c1 = tl.where(valid, tok.to(tl.int64), tl.full((), 0, dtype=tl.int64))
|
| 115 |
+
if MAX_N >= 3:
|
| 116 |
+
pos_raw = t_idx - (MAX_N - 1 - 2)
|
| 117 |
+
valid = pos_raw >= 0
|
| 118 |
+
pos = tl.where(valid, pos_raw, 0)
|
| 119 |
+
tok = tl.load(
|
| 120 |
+
canonical_ptr + b_idx * stride_cb + pos * stride_ct,
|
| 121 |
+
mask=valid, other=0,
|
| 122 |
+
)
|
| 123 |
+
c2 = tl.where(valid, tok.to(tl.int64), tl.full((), 0, dtype=tl.int64))
|
| 124 |
+
if MAX_N >= 4:
|
| 125 |
+
pos_raw = t_idx - (MAX_N - 1 - 3)
|
| 126 |
+
valid = pos_raw >= 0
|
| 127 |
+
pos = tl.where(valid, pos_raw, 0)
|
| 128 |
+
tok = tl.load(
|
| 129 |
+
canonical_ptr + b_idx * stride_cb + pos * stride_ct,
|
| 130 |
+
mask=valid, other=0,
|
| 131 |
+
)
|
| 132 |
+
c3 = tl.where(valid, tok.to(tl.int64), tl.full((), 0, dtype=tl.int64))
|
| 133 |
+
|
| 134 |
+
# Iterate over all tables; loop bounds are constexpr → fully unrolled by compiler.
|
| 135 |
+
for n_ord in tl.static_range(4): # ≤ num_ngram_orders
|
| 136 |
+
if n_ord < num_ngram_orders:
|
| 137 |
+
n = tl.load(ngrams_ptr + n_ord).to(tl.int32)
|
| 138 |
+
|
| 139 |
+
for k in tl.static_range(16): # ≤ num_heads
|
| 140 |
+
if k < num_heads:
|
| 141 |
+
# Safety: Compute unique table_idx directly from static loop indices to avoid mutable variable register compilation bugs.
|
| 142 |
+
table_idx = n_ord * num_heads + k
|
| 143 |
+
seed = tl.load(seeds_ptr + table_idx).to(tl.int64)
|
| 144 |
+
|
| 145 |
+
# XOR-multiplicative hash over the suffix n-gram.
|
| 146 |
+
# loop over MAX_N positions; positions outside the suffix are skipped.
|
| 147 |
+
h = seed
|
| 148 |
+
for i in tl.static_range(MAX_N):
|
| 149 |
+
include = i >= (MAX_N - n)
|
| 150 |
+
tok = tl.full((), 0, dtype=tl.int64)
|
| 151 |
+
if i == 0:
|
| 152 |
+
tok = c0
|
| 153 |
+
elif i == 1:
|
| 154 |
+
tok = c1
|
| 155 |
+
elif i == 2:
|
| 156 |
+
tok = c2
|
| 157 |
+
elif i == 3:
|
| 158 |
+
tok = c3
|
| 159 |
+
new_h = h * 2654435761 ^ tok
|
| 160 |
+
h = tl.where(include, new_h, h)
|
| 161 |
+
|
| 162 |
+
# Clamp absolute value of hash using tl.where for maximum Triton version safety
|
| 163 |
+
idx = tl.where(h >= 0, h, -h) % M
|
| 164 |
+
|
| 165 |
+
# Load d_slot floats from embed_tables[table_idx, idx]
|
| 166 |
+
emb_base = table_idx * stride_tn + idx * stride_tm
|
| 167 |
+
emb = tl.load(
|
| 168 |
+
tables_ptr + emb_base + d_offs * stride_td,
|
| 169 |
+
mask=d_mask, other=0.0,
|
| 170 |
+
)
|
| 171 |
+
|
| 172 |
+
# Write to output[b, t, table_idx*d_slot : (table_idx+1)*d_slot]
|
| 173 |
+
out_base = b_idx * stride_ob + t_idx * stride_ot + table_idx * d_slot
|
| 174 |
+
tl.store(
|
| 175 |
+
output_ptr + out_base + d_offs,
|
| 176 |
+
emb.to(output_ptr.dtype.element_ty),
|
| 177 |
+
mask=d_mask,
|
| 178 |
+
)
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
# ─────────────────────────────────────────────────────────────────────────────
|
| 182 |
+
# Custom Autograd Function for Fused Triton Training Lookup
|
| 183 |
+
# ─────────────────────────────────────────────────────────────────────────────
|
| 184 |
+
|
| 185 |
+
class FusedEngramLookupFunction(torch.autograd.Function):
|
| 186 |
+
@staticmethod
|
| 187 |
+
def forward(
|
| 188 |
+
ctx,
|
| 189 |
+
canonical,
|
| 190 |
+
embed_tables,
|
| 191 |
+
seeds,
|
| 192 |
+
ngram_orders_buf,
|
| 193 |
+
M,
|
| 194 |
+
d_slot,
|
| 195 |
+
num_tables,
|
| 196 |
+
num_ngram_orders,
|
| 197 |
+
num_heads,
|
| 198 |
+
ngram_orders,
|
| 199 |
+
):
|
| 200 |
+
ctx.save_for_backward(canonical, seeds, ngram_orders_buf)
|
| 201 |
+
ctx.M = M
|
| 202 |
+
ctx.d_slot = d_slot
|
| 203 |
+
ctx.num_tables = num_tables
|
| 204 |
+
ctx.num_ngram_orders = num_ngram_orders
|
| 205 |
+
ctx.num_heads = num_heads
|
| 206 |
+
ctx.ngram_orders = ngram_orders
|
| 207 |
+
ctx.embed_tables_shape = embed_tables.shape
|
| 208 |
+
|
| 209 |
+
B, T = canonical.shape
|
| 210 |
+
BLOCK_D = triton.next_power_of_2(d_slot)
|
| 211 |
+
|
| 212 |
+
out = torch.empty(
|
| 213 |
+
B, T, num_tables * d_slot,
|
| 214 |
+
device=canonical.device, dtype=embed_tables.dtype,
|
| 215 |
+
)
|
| 216 |
+
|
| 217 |
+
tables = embed_tables.contiguous()
|
| 218 |
+
|
| 219 |
+
_engram_hash_lookup_kernel[(B, T)](
|
| 220 |
+
canonical.int().contiguous(), canonical.stride(0), canonical.stride(1),
|
| 221 |
+
out, out.stride(0), out.stride(1),
|
| 222 |
+
tables, tables.stride(0), tables.stride(1), tables.stride(2),
|
| 223 |
+
seeds.contiguous(),
|
| 224 |
+
ngram_orders_buf.contiguous(),
|
| 225 |
+
B, T, M, d_slot,
|
| 226 |
+
num_tables, num_ngram_orders, num_heads,
|
| 227 |
+
MAX_N=max(ngram_orders),
|
| 228 |
+
BLOCK_D=BLOCK_D,
|
| 229 |
+
)
|
| 230 |
+
return out
|
| 231 |
+
|
| 232 |
+
@staticmethod
|
| 233 |
+
def backward(ctx, grad_output):
|
| 234 |
+
canonical, seeds, ngram_orders_buf = ctx.saved_tensors
|
| 235 |
+
B, T = canonical.shape
|
| 236 |
+
device = canonical.device
|
| 237 |
+
|
| 238 |
+
# 1. Re-compute hashes for each table in vectorized form
|
| 239 |
+
all_hashes = torch.empty(ctx.num_tables, B * T, dtype=torch.long, device=device)
|
| 240 |
+
table_idx = 0
|
| 241 |
+
for n_idx, n in enumerate(ctx.ngram_orders):
|
| 242 |
+
padded = F.pad(canonical, (n - 1, 0), value=0)
|
| 243 |
+
ngrams = padded.unfold(dimension=1, size=n, step=1)
|
| 244 |
+
for k in range(ctx.num_heads):
|
| 245 |
+
seed = int(seeds[table_idx].item())
|
| 246 |
+
h = torch.full(ngrams.shape[:2], seed, dtype=torch.long, device=device)
|
| 247 |
+
for i in range(ngrams.shape[-1]):
|
| 248 |
+
h = h * 2654435761 ^ ngrams[..., i]
|
| 249 |
+
h = h.abs() % ctx.M
|
| 250 |
+
all_hashes[table_idx] = h.view(B * T)
|
| 251 |
+
table_idx += 1
|
| 252 |
+
|
| 253 |
+
# 2. Reshape and permute grad_output from [B, T, num_tables * d_slot] back to [num_tables, B * T, d_slot]
|
| 254 |
+
grad_out_reshaped = grad_output.reshape(B, T, ctx.num_tables, ctx.d_slot).permute(2, 0, 1, 3).reshape(ctx.num_tables, B * T, ctx.d_slot)
|
| 255 |
+
|
| 256 |
+
# 3. Accumulate gradients into grad_embed_tables using PyTorch's native CUDA-optimized index_put_ scatter-add
|
| 257 |
+
grad_embed_tables = torch.zeros(ctx.embed_tables_shape, dtype=grad_output.dtype, device=device)
|
| 258 |
+
tbl_idx = torch.arange(ctx.num_tables, device=device).unsqueeze(1).expand(ctx.num_tables, B * T)
|
| 259 |
+
|
| 260 |
+
grad_embed_tables.index_put_((tbl_idx, all_hashes), grad_out_reshaped, accumulate=True)
|
| 261 |
+
|
| 262 |
+
# Return gradients matching forward arguments (None for non-tensor / constant arguments)
|
| 263 |
+
return None, grad_embed_tables, None, None, None, None, None, None, None, None
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
# ─────────────────────────────────────────────────────────────────────────────
|
| 267 |
+
# Lightweight RMSNorm (standalone; avoids circular import from quasar_rope)
|
| 268 |
+
# ─────────────────────────────────────────────────────────────────────────────
|
| 269 |
+
|
| 270 |
+
class _RMSNorm(nn.Module):
|
| 271 |
+
def __init__(self, dim: int, eps: float = 1e-6):
|
| 272 |
+
super().__init__()
|
| 273 |
+
self.weight = nn.Parameter(torch.ones(dim))
|
| 274 |
+
self.eps = eps
|
| 275 |
+
|
| 276 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 277 |
+
dtype = x.dtype
|
| 278 |
+
x = x.float()
|
| 279 |
+
x = x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
|
| 280 |
+
return (self.weight * x).to(dtype)
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
# ─────────────────────────────────────────────────────────────────────────────
|
| 284 |
+
# EngramModule
|
| 285 |
+
# ─────────────────────────────────────────────────────────────────────────────
|
| 286 |
+
|
| 287 |
+
class EngramModule(nn.Module):
|
| 288 |
+
"""
|
| 289 |
+
Engram Conditional Memory Module (DeepSeek-AI, arXiv:2601.07372).
|
| 290 |
+
|
| 291 |
+
Replaces expensive attention layers for static N-gram patterns with
|
| 292 |
+
O(1) hash-table lookups gated into the hidden state.
|
| 293 |
+
|
| 294 |
+
All operations are fully vectorized — no Python loops over T or B:
|
| 295 |
+
• N-gram extraction: torch.unfold (single op)
|
| 296 |
+
• Hash computation: vectorized XOR accumulation (loop over n=2..3 only)
|
| 297 |
+
• Embedding lookup: batched advanced indexing (single gather)
|
| 298 |
+
• Conv: nn.Conv1d with causal pad + slice
|
| 299 |
+
"""
|
| 300 |
+
|
| 301 |
+
def __init__(
|
| 302 |
+
self,
|
| 303 |
+
vocab_size: int,
|
| 304 |
+
d_model: int,
|
| 305 |
+
d_mem: int,
|
| 306 |
+
num_heads: int = 8,
|
| 307 |
+
ngram_orders: list = None,
|
| 308 |
+
target_slots: int = 5_700_000,
|
| 309 |
+
n_layers: int = 24,
|
| 310 |
+
):
|
| 311 |
+
super().__init__()
|
| 312 |
+
|
| 313 |
+
if ngram_orders is None:
|
| 314 |
+
ngram_orders = [2, 3]
|
| 315 |
+
|
| 316 |
+
self.vocab_size = vocab_size
|
| 317 |
+
self.d_model = d_model
|
| 318 |
+
self.d_mem = d_mem
|
| 319 |
+
self.num_heads = num_heads
|
| 320 |
+
self.ngram_orders = list(ngram_orders)
|
| 321 |
+
self.num_ngram_orders = len(ngram_orders)
|
| 322 |
+
self.num_tables = self.num_ngram_orders * num_heads
|
| 323 |
+
self.n_layers = n_layers
|
| 324 |
+
|
| 325 |
+
# ── A. Tokenizer Compression Buffer ──────────────────────────────────
|
| 326 |
+
# Surjective P: V → V', ~23% compression.
|
| 327 |
+
# Deterministic multiplicative hash — no tokenizer object needed at
|
| 328 |
+
# construction time (avoids FSDP serialization problems).
|
| 329 |
+
compressed_size = max(1, int(vocab_size * 0.77))
|
| 330 |
+
self.compressed_vocab_size = compressed_size
|
| 331 |
+
token_map = (
|
| 332 |
+
torch.arange(vocab_size, dtype=torch.long) * 2654435761
|
| 333 |
+
) % compressed_size
|
| 334 |
+
self.register_buffer('token_map', token_map)
|
| 335 |
+
|
| 336 |
+
# ── B. Embedding Tables ───────────────────────────────────────────────
|
| 337 |
+
# All num_tables share the same prime size M for vectorized indexing.
|
| 338 |
+
slots_per_table = max(1, target_slots // self.num_tables)
|
| 339 |
+
self.M = _next_prime(slots_per_table)
|
| 340 |
+
self.d_slot = max(16, d_mem // max(1, self.num_tables))
|
| 341 |
+
self.total_embed_dim = self.num_tables * self.d_slot
|
| 342 |
+
|
| 343 |
+
# Single parameter tensor — enables batched advanced-index gather.
|
| 344 |
+
self.embed_tables = nn.Parameter(
|
| 345 |
+
torch.empty(self.num_tables, self.M, self.d_slot)
|
| 346 |
+
)
|
| 347 |
+
|
| 348 |
+
# Per-table hashing seeds (non-trainable).
|
| 349 |
+
seeds = torch.randint(1, 2 ** 31 - 1, (self.num_tables,), dtype=torch.long)
|
| 350 |
+
self.register_buffer('seeds', seeds)
|
| 351 |
+
|
| 352 |
+
# N-gram order list as a buffer for the Triton kernel.
|
| 353 |
+
self.register_buffer(
|
| 354 |
+
'ngram_orders_buf',
|
| 355 |
+
torch.tensor(self.ngram_orders, dtype=torch.long),
|
| 356 |
+
)
|
| 357 |
+
|
| 358 |
+
# ── C. Projection total_embed_dim → d_mem ────────────────────────────
|
| 359 |
+
self.embed_proj = nn.Linear(self.total_embed_dim, d_mem, bias=False)
|
| 360 |
+
|
| 361 |
+
# ── D. Context-aware gating ───────────────────────────────────────────
|
| 362 |
+
self.q_proj = nn.Linear(d_model, d_mem, bias=False)
|
| 363 |
+
self.W_K = nn.Linear(d_mem, d_mem, bias=False)
|
| 364 |
+
self.W_V = nn.Linear(d_mem, d_mem, bias=False)
|
| 365 |
+
|
| 366 |
+
# ── E. Causal depthwise Conv1d ────────────────────────────────────────
|
| 367 |
+
# kernel=4, dilation=3 → causal receptive field = 1 + (4-1)*3 = 10
|
| 368 |
+
self.kernel_size = 4
|
| 369 |
+
self.dilation = 3
|
| 370 |
+
self.conv_norm = _RMSNorm(d_mem)
|
| 371 |
+
self.conv = nn.Conv1d(
|
| 372 |
+
d_mem, d_mem,
|
| 373 |
+
kernel_size=self.kernel_size,
|
| 374 |
+
dilation=self.dilation,
|
| 375 |
+
groups=d_mem, # depthwise
|
| 376 |
+
bias=False,
|
| 377 |
+
)
|
| 378 |
+
|
| 379 |
+
# ── F. Output projection d_mem → d_model ─────────────────────────────
|
| 380 |
+
self.out_proj = nn.Linear(d_mem, d_model, bias=False)
|
| 381 |
+
|
| 382 |
+
# Triton eligible when compile-time bounds fit the kernel
|
| 383 |
+
self._triton_ok = (
|
| 384 |
+
HAS_TRITON
|
| 385 |
+
and self.num_ngram_orders <= 4
|
| 386 |
+
and num_heads <= 16
|
| 387 |
+
and max(ngram_orders) <= 3
|
| 388 |
+
)
|
| 389 |
+
self.triton_training = True
|
| 390 |
+
|
| 391 |
+
self._init_weights()
|
| 392 |
+
|
| 393 |
+
# ── Initialization ────────────────────────────────────────────────────────
|
| 394 |
+
|
| 395 |
+
def _init_weights(self):
|
| 396 |
+
# 1. Deterministic buffer re-population (bypasses meta-device empty uninitialized memory)
|
| 397 |
+
if hasattr(self, "token_map") and self.token_map is not None:
|
| 398 |
+
dev = "cpu" if self.token_map.device.type == "meta" else self.token_map.device
|
| 399 |
+
t_map = (torch.arange(self.vocab_size, dtype=torch.long, device=dev) * 2654435761) % self.compressed_vocab_size
|
| 400 |
+
self.token_map.data.copy_(t_map)
|
| 401 |
+
|
| 402 |
+
if hasattr(self, "seeds") and self.seeds is not None:
|
| 403 |
+
# Deterministic hash seeds across all ranks
|
| 404 |
+
g = torch.Generator().manual_seed(42)
|
| 405 |
+
dev = "cpu" if self.seeds.device.type == "meta" else self.seeds.device
|
| 406 |
+
s_t = torch.randint(1, 2 ** 31 - 1, (self.num_tables,), dtype=torch.long, device=dev, generator=g)
|
| 407 |
+
self.seeds.data.copy_(s_t)
|
| 408 |
+
|
| 409 |
+
if hasattr(self, "ngram_orders_buf") and self.ngram_orders_buf is not None:
|
| 410 |
+
dev = "cpu" if self.ngram_orders_buf.device.type == "meta" else self.ngram_orders_buf.device
|
| 411 |
+
ord_buf = torch.tensor(self.ngram_orders, dtype=torch.long, device=dev)
|
| 412 |
+
self.ngram_orders_buf.data.copy_(ord_buf)
|
| 413 |
+
|
| 414 |
+
trinity_std = 0.5 / math.sqrt(self.d_model)
|
| 415 |
+
scale_factor = 1.0 / math.sqrt(2 * self.n_layers)
|
| 416 |
+
|
| 417 |
+
# 2. Deep init on output → zero-init to guarantee exactly zero output at step 0
|
| 418 |
+
nn.init.zeros_(self.out_proj.weight)
|
| 419 |
+
# Gating projections: standard Trinity
|
| 420 |
+
nn.init.normal_(self.q_proj.weight, std=trinity_std)
|
| 421 |
+
nn.init.normal_(self.W_K.weight, std=trinity_std)
|
| 422 |
+
nn.init.normal_(self.W_V.weight, std=trinity_std)
|
| 423 |
+
# embed_proj: standard Trinity
|
| 424 |
+
nn.init.normal_(self.embed_proj.weight, std=trinity_std)
|
| 425 |
+
# Conv: zero init → identity pass-through at step 0
|
| 426 |
+
nn.init.zeros_(self.conv.weight)
|
| 427 |
+
# Embedding tables: small normal (paper standard)
|
| 428 |
+
nn.init.normal_(self.embed_tables, std=0.01)
|
| 429 |
+
# Conv norm: fill with ones
|
| 430 |
+
if hasattr(self.conv_norm, "weight") and self.conv_norm.weight is not None:
|
| 431 |
+
nn.init.ones_(self.conv_norm.weight)
|
| 432 |
+
|
| 433 |
+
# Check for any non-finite initialization values
|
| 434 |
+
for name, p in [("out_proj", self.out_proj.weight), ("q_proj", self.q_proj.weight),
|
| 435 |
+
("W_K", self.W_K.weight), ("W_V", self.W_V.weight),
|
| 436 |
+
("embed_proj", self.embed_proj.weight), ("conv", self.conv.weight),
|
| 437 |
+
("embed_tables", self.embed_tables), ("conv_norm", self.conv_norm.weight)]:
|
| 438 |
+
if p.device.type != "meta":
|
| 439 |
+
if not torch.isfinite(p).all():
|
| 440 |
+
print(f"[engram-init-warn] Parameter {name} contains non-finite values! Re-initializing with zeros.", flush=True)
|
| 441 |
+
nn.init.zeros_(p)
|
| 442 |
+
|
| 443 |
+
# ── Core helpers ──────────────────────────────────────────────────────────
|
| 444 |
+
|
| 445 |
+
@staticmethod
|
| 446 |
+
def _hash_ngrams(ngrams: torch.Tensor, table_size: int, seed: int) -> torch.Tensor:
|
| 447 |
+
"""
|
| 448 |
+
Vectorized XOR-multiplicative hash.
|
| 449 |
+
ngrams: [B, T, n] — n ∈ {2, 3}, compile-time constant.
|
| 450 |
+
Returns: [B, T] — indices into embedding table.
|
| 451 |
+
No loop over T or B; only loops over n (≤ 3).
|
| 452 |
+
"""
|
| 453 |
+
h = torch.full(ngrams.shape[:2], seed, dtype=torch.long, device=ngrams.device)
|
| 454 |
+
for i in range(ngrams.shape[-1]): # n iterations, NOT T
|
| 455 |
+
h = h * 2654435761 ^ ngrams[..., i]
|
| 456 |
+
return h.abs() % table_size
|
| 457 |
+
|
| 458 |
+
def _lookup_pytorch(self, canonical: torch.Tensor) -> torch.Tensor:
|
| 459 |
+
"""
|
| 460 |
+
Pure-PyTorch path: fully vectorized, no T/B loops.
|
| 461 |
+
|
| 462 |
+
Steps:
|
| 463 |
+
1. For each n-gram order, extract suffix n-grams via unfold → [B, T, n]
|
| 464 |
+
2. Hash all (n, k) pairs → [num_tables, B, T]
|
| 465 |
+
3. Batched advanced-index gather from embed_tables → [num_tables, B*T, d_slot]
|
| 466 |
+
4. Reshape to [B, T, total_embed_dim]
|
| 467 |
+
"""
|
| 468 |
+
B, T = canonical.shape
|
| 469 |
+
device = canonical.device
|
| 470 |
+
|
| 471 |
+
# Step 1+2: collect hashes for all tables — loop over num_tables (≤ 32, not over T)
|
| 472 |
+
all_hashes = torch.empty(self.num_tables, B * T, dtype=torch.long, device=device)
|
| 473 |
+
table_idx = 0
|
| 474 |
+
seeds_cpu = self.seeds.cpu().tolist() if hasattr(self, "seeds") and self.seeds is not None else []
|
| 475 |
+
for n_idx, n in enumerate(self.ngram_orders): # 2 or 3 iterations
|
| 476 |
+
# Vectorized n-gram extraction: unfold over T → [B, T, n]
|
| 477 |
+
padded = F.pad(canonical, (n - 1, 0), value=0) # [B, T+n-1]
|
| 478 |
+
ngrams = padded.unfold(dimension=1, size=n, step=1) # [B, T, n]
|
| 479 |
+
|
| 480 |
+
for k in range(self.num_heads): # num_heads iterations (≤ 16)
|
| 481 |
+
seed = seeds_cpu[table_idx] if table_idx < len(seeds_cpu) else 42
|
| 482 |
+
h = self._hash_ngrams(ngrams, self.M, seed) # [B, T]
|
| 483 |
+
all_hashes[table_idx] = h.view(B * T)
|
| 484 |
+
table_idx += 1
|
| 485 |
+
|
| 486 |
+
# Step 3: Single batched gather — no loop over T
|
| 487 |
+
# embed_tables: [num_tables, M, d_slot]
|
| 488 |
+
# all_hashes: [num_tables, B*T]
|
| 489 |
+
# Expand table index for advanced indexing
|
| 490 |
+
tbl_idx = torch.arange(self.num_tables, device=device).unsqueeze(1).expand(
|
| 491 |
+
self.num_tables, B * T
|
| 492 |
+
) # [num_tables, B*T]
|
| 493 |
+
embeddings = self.embed_tables[tbl_idx, all_hashes] # [num_tables, B*T, d_slot]
|
| 494 |
+
|
| 495 |
+
# Step 4: Reshape to [B, T, total_embed_dim]
|
| 496 |
+
embeddings = embeddings.permute(1, 0, 2) # [B*T, num_tables, d_slot]
|
| 497 |
+
return embeddings.reshape(B, T, self.total_embed_dim)
|
| 498 |
+
|
| 499 |
+
def _lookup_triton(self, canonical: torch.Tensor) -> torch.Tensor:
|
| 500 |
+
"""
|
| 501 |
+
Triton path: fused hash + lookup in a single SRAM pass.
|
| 502 |
+
Uses FusedEngramLookupFunction to support exact backward auto-differentiation in training.
|
| 503 |
+
"""
|
| 504 |
+
return FusedEngramLookupFunction.apply(
|
| 505 |
+
canonical,
|
| 506 |
+
self.embed_tables,
|
| 507 |
+
self.seeds,
|
| 508 |
+
self.ngram_orders_buf,
|
| 509 |
+
self.M,
|
| 510 |
+
self.d_slot,
|
| 511 |
+
self.num_tables,
|
| 512 |
+
self.num_ngram_orders,
|
| 513 |
+
self.num_heads,
|
| 514 |
+
self.ngram_orders,
|
| 515 |
+
)
|
| 516 |
+
|
| 517 |
+
# ── Forward ───────────────────────────────────────────────────────────────
|
| 518 |
+
|
| 519 |
+
def forward(
|
| 520 |
+
self,
|
| 521 |
+
input_ids: torch.Tensor, # [B, T] raw token IDs
|
| 522 |
+
hidden_states: torch.Tensor, # [B, T, d_model]
|
| 523 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 524 |
+
"""
|
| 525 |
+
Returns:
|
| 526 |
+
engram_out : [B, T, d_model] — to add to residual stream
|
| 527 |
+
alpha_mean : scalar tensor — mean gate value for LatentMemory suppression
|
| 528 |
+
"""
|
| 529 |
+
B, T = input_ids.shape
|
| 530 |
+
orig_dtype = hidden_states.dtype
|
| 531 |
+
|
| 532 |
+
# ── A. Token compression ─────────────────────────────────────────────
|
| 533 |
+
# Single gather op — no loop
|
| 534 |
+
canonical = self.token_map[input_ids.clamp(0, self.vocab_size - 1)] # [B, T]
|
| 535 |
+
|
| 536 |
+
# ── B+C. Hash → lookup → project to d_mem ───────────────────────────
|
| 537 |
+
use_triton_lookup = self._triton_ok and canonical.is_cuda and (
|
| 538 |
+
self.training is False or bool(getattr(self, "triton_training", False))
|
| 539 |
+
)
|
| 540 |
+
if use_triton_lookup:
|
| 541 |
+
raw_embed = self._lookup_triton(canonical) # [B, T, total_embed_dim]
|
| 542 |
+
else:
|
| 543 |
+
raw_embed = self._lookup_pytorch(canonical) # [B, T, total_embed_dim]
|
| 544 |
+
|
| 545 |
+
raw_embed = raw_embed.to(orig_dtype)
|
| 546 |
+
|
| 547 |
+
debug_engram = bool(int(os.environ.get("ENGRAM_DEBUG", "0")))
|
| 548 |
+
if debug_engram:
|
| 549 |
+
print(f"[engram-debug] embed_tables: finite={torch.isfinite(self.embed_tables).all().item()} min={self.embed_tables.float().min().item():.6g} max={self.embed_tables.float().max().item():.6g}", flush=True)
|
| 550 |
+
print(f"[engram-debug] hidden_states: finite={torch.isfinite(hidden_states).all().item()} min={hidden_states.float().min().item():.6g} max={hidden_states.float().max().item():.6g}", flush=True)
|
| 551 |
+
print(f"[engram-debug] raw_embed: finite={torch.isfinite(raw_embed).all().item()} min={raw_embed.float().min().item():.6g} max={raw_embed.float().max().item():.6g}", flush=True)
|
| 552 |
+
|
| 553 |
+
raw_embed = torch.nan_to_num(raw_embed, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-10.0, 10.0)
|
| 554 |
+
e_t = self.embed_proj(raw_embed) # [B, T, d_mem]
|
| 555 |
+
if debug_engram:
|
| 556 |
+
print(f"[engram-debug] e_t: finite={torch.isfinite(e_t).all().item()} min={e_t.float().min().item():.6g} max={e_t.float().max().item():.6g}", flush=True)
|
| 557 |
+
e_t = torch.nan_to_num(e_t, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
|
| 558 |
+
|
| 559 |
+
# ── D. Context-aware gating ──────────────────────────────────────────
|
| 560 |
+
h_proj = self.q_proj(hidden_states) # [B, T, d_mem]
|
| 561 |
+
k_t = self.W_K(e_t) # [B, T, d_mem]
|
| 562 |
+
v_t = self.W_V(e_t) # [B, T, d_mem]
|
| 563 |
+
if debug_engram:
|
| 564 |
+
print(f"[engram-debug] h_proj: finite={torch.isfinite(h_proj).all().item()} min={h_proj.float().min().item():.6g} max={h_proj.float().max().item():.6g}", flush=True)
|
| 565 |
+
print(f"[engram-debug] k_t: finite={torch.isfinite(k_t).all().item()} min={k_t.float().min().item():.6g} max={k_t.float().max().item():.6g}", flush=True)
|
| 566 |
+
print(f"[engram-debug] v_t: finite={torch.isfinite(v_t).all().item()} min={v_t.float().min().item():.6g} max={v_t.float().max().item():.6g}", flush=True)
|
| 567 |
+
|
| 568 |
+
h_proj = torch.nan_to_num(h_proj, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
|
| 569 |
+
k_t = torch.nan_to_num(k_t, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
|
| 570 |
+
v_t = torch.nan_to_num(v_t, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
|
| 571 |
+
|
| 572 |
+
# L2-normalize for stability (matches Quasar key normalization)
|
| 573 |
+
q_norm = F.normalize(h_proj.float(), dim=-1, eps=1e-6).to(orig_dtype)
|
| 574 |
+
k_norm = F.normalize(k_t.float(), dim=-1, eps=1e-6).to(orig_dtype)
|
| 575 |
+
|
| 576 |
+
# Scalar gate per token per position
|
| 577 |
+
alpha_logits = (q_norm * k_norm).sum(-1, keepdim=True).float() / math.sqrt(self.d_mem)
|
| 578 |
+
alpha_t = torch.sigmoid(alpha_logits.clamp_(-30.0, 30.0)).to(orig_dtype) # [B, T, 1]
|
| 579 |
+
if debug_engram:
|
| 580 |
+
print(f"[engram-debug] alpha_t: finite={torch.isfinite(alpha_t).all().item()} min={alpha_t.float().min().item():.6g} max={alpha_t.float().max().item():.6g}", flush=True)
|
| 581 |
+
gated = alpha_t * v_t # [B, T, d_mem]
|
| 582 |
+
gated = torch.nan_to_num(gated, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
|
| 583 |
+
|
| 584 |
+
# ── E. Causal depthwise conv ─────────────────────────────────────────
|
| 585 |
+
# Fully vectorized: F.pad + Conv1d + slice — no loop over T
|
| 586 |
+
causal_pad = (self.kernel_size - 1) * self.dilation
|
| 587 |
+
g_norm = self.conv_norm(gated) # [B, T, d_mem]
|
| 588 |
+
if debug_engram:
|
| 589 |
+
print(f"[engram-debug] gated: finite={torch.isfinite(gated).all().item()} min={gated.float().min().item():.6g} max={gated.float().max().item():.6g}", flush=True)
|
| 590 |
+
print(f"[engram-debug] conv_norm.weight: finite={torch.isfinite(self.conv_norm.weight).all().item()} min={self.conv_norm.weight.float().min().item():.6g} max={self.conv_norm.weight.float().max().item():.6g}", flush=True)
|
| 591 |
+
print(f"[engram-debug] g_norm: finite={torch.isfinite(g_norm).all().item()} min={g_norm.float().min().item():.6g} max={g_norm.float().max().item():.6g}", flush=True)
|
| 592 |
+
g_norm = torch.nan_to_num(g_norm, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
|
| 593 |
+
g_t = g_norm.transpose(1, 2) # [B, d_mem, T]
|
| 594 |
+
g_t = F.pad(g_t, (causal_pad, 0)) # [B, d_mem, T+pad]
|
| 595 |
+
g_t = self.conv(g_t)[..., :T] # [B, d_mem, T]
|
| 596 |
+
g_t = F.silu(g_t).transpose(1, 2) # [B, T, d_mem]
|
| 597 |
+
Y = g_t + gated # residual
|
| 598 |
+
if debug_engram:
|
| 599 |
+
print(f"[engram-debug] Y: finite={torch.isfinite(Y).all().item()} min={Y.float().min().item():.6g} max={Y.float().max().item():.6g}", flush=True)
|
| 600 |
+
Y = torch.nan_to_num(Y, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
|
| 601 |
+
|
| 602 |
+
# ── F. Output projection ─────────────────────────────────────────────
|
| 603 |
+
engram_out = self.out_proj(Y) # [B, T, d_model]
|
| 604 |
+
if debug_engram:
|
| 605 |
+
print(f"[engram-debug] engram_out: finite={torch.isfinite(engram_out).all().item()} min={engram_out.float().min().item():.6g} max={engram_out.float().max().item():.6g}", flush=True)
|
| 606 |
+
engram_out = torch.nan_to_num(engram_out, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
|
| 607 |
+
|
| 608 |
+
# alpha_mean: mean gate activity — used by LatentMemory for suppression
|
| 609 |
+
alpha_mean = alpha_t.squeeze(-1) # [B, T]
|
| 610 |
+
|
| 611 |
+
return engram_out, alpha_mean
|
fla/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Lightweight package initializer for the vendored FLA subset used by Quasar.
|
| 2 |
+
#
|
| 3 |
+
# The upstream file eagerly imports every layer and model, which is slow and can
|
| 4 |
+
# hang on fresh training containers while optional kernels are being resolved.
|
| 5 |
+
# Import concrete modules directly, e.g. `from fla.layers.quasar import ...`.
|
| 6 |
+
|
| 7 |
+
__version__ = "0.1.0"
|
| 8 |
+
__all__ = []
|
fla/distributed_compat.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
"""
|
| 3 |
+
Centralized compatibility module for torch.distributed imports.
|
| 4 |
+
All distributed-related imports should go through here to handle environments
|
| 5 |
+
where distributed tensor APIs are not available.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
|
| 10 |
+
# DeviceMesh
|
| 11 |
+
try:
|
| 12 |
+
from torch.distributed import DeviceMesh
|
| 13 |
+
except ImportError:
|
| 14 |
+
try:
|
| 15 |
+
from torch.distributed.device_mesh import DeviceMesh
|
| 16 |
+
except ImportError:
|
| 17 |
+
DeviceMesh = None
|
| 18 |
+
|
| 19 |
+
# DTensor
|
| 20 |
+
try:
|
| 21 |
+
from torch.distributed.tensor import DTensor
|
| 22 |
+
except (ImportError, AttributeError):
|
| 23 |
+
DTensor = None
|
| 24 |
+
|
| 25 |
+
# Replicate, Shard, distribute_module, Placement
|
| 26 |
+
try:
|
| 27 |
+
from torch.distributed.tensor import Placement, Replicate, Shard, distribute_module
|
| 28 |
+
except (ImportError, AttributeError):
|
| 29 |
+
Placement = Replicate = Shard = distribute_module = None
|
| 30 |
+
|
| 31 |
+
# ParallelStyle
|
| 32 |
+
try:
|
| 33 |
+
from torch.distributed.tensor.parallel import ParallelStyle
|
| 34 |
+
except (ImportError, AttributeError):
|
| 35 |
+
ParallelStyle = None
|
| 36 |
+
|
| 37 |
+
# Convenience flag
|
| 38 |
+
HAS_DISTRIBUTED = all([
|
| 39 |
+
DeviceMesh is not None,
|
| 40 |
+
DTensor is not None,
|
| 41 |
+
Placement is not None,
|
| 42 |
+
Replicate is not None,
|
| 43 |
+
Shard is not None,
|
| 44 |
+
distribute_module is not None,
|
| 45 |
+
ParallelStyle is not None,
|
| 46 |
+
])
|
| 47 |
+
|
| 48 |
+
__all__ = [
|
| 49 |
+
'DeviceMesh',
|
| 50 |
+
'DTensor',
|
| 51 |
+
'Placement',
|
| 52 |
+
'Replicate',
|
| 53 |
+
'Shard',
|
| 54 |
+
'distribute_module',
|
| 55 |
+
'ParallelStyle',
|
| 56 |
+
'HAS_DISTRIBUTED',
|
| 57 |
+
]
|
fla/layers/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Keep layer package imports lazy. Import specific layer modules directly.
|
| 2 |
+
|
| 3 |
+
__all__ = []
|
fla/layers/abc.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
|
| 12 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 13 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, RotaryEmbedding, ShortConvolution
|
| 14 |
+
from fla.modules.activations import swiglu, swish
|
| 15 |
+
from fla.ops.abc.chunk import chunk_abc
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from fla.models.utils import Cache
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class ABCAttention(nn.Module):
|
| 22 |
+
|
| 23 |
+
def __init__(
|
| 24 |
+
self,
|
| 25 |
+
hidden_size: int = 1024,
|
| 26 |
+
expand_k: float = 0.5,
|
| 27 |
+
expand_v: float = 1.0,
|
| 28 |
+
num_heads: int = 4,
|
| 29 |
+
use_short_conv: bool = False,
|
| 30 |
+
conv_size: int = 4,
|
| 31 |
+
conv_bias: bool = False,
|
| 32 |
+
num_slots: int | None = None,
|
| 33 |
+
elementwise_affine: bool | None = True,
|
| 34 |
+
norm_eps: float = 1e-5,
|
| 35 |
+
gate_low_rank_dim: int = 16,
|
| 36 |
+
gate_logit_normalizer: int = 16,
|
| 37 |
+
use_rope: bool = True,
|
| 38 |
+
use_input_gate: bool = False,
|
| 39 |
+
use_output_gate: bool = True,
|
| 40 |
+
use_norm: bool = True,
|
| 41 |
+
clamp_min: float | None = -32,
|
| 42 |
+
clamp_max: float | None = 32,
|
| 43 |
+
layer_idx: int | None = None,
|
| 44 |
+
**kwargs,
|
| 45 |
+
) -> ABCAttention:
|
| 46 |
+
super().__init__()
|
| 47 |
+
|
| 48 |
+
self.hidden_size = hidden_size
|
| 49 |
+
self.expand_k = expand_k
|
| 50 |
+
self.expand_v = expand_v
|
| 51 |
+
self.num_heads = num_heads
|
| 52 |
+
self.key_dim = int(self.hidden_size * self.expand_k)
|
| 53 |
+
self.value_dim = int(self.hidden_size * self.expand_v)
|
| 54 |
+
self.head_k_dim = self.key_dim // self.num_heads
|
| 55 |
+
self.head_v_dim = self.value_dim // self.num_heads
|
| 56 |
+
|
| 57 |
+
self.use_short_conv = use_short_conv
|
| 58 |
+
self.conv_size = conv_size
|
| 59 |
+
self.conv_bias = conv_bias
|
| 60 |
+
|
| 61 |
+
self.gate_low_rank_dim = gate_low_rank_dim
|
| 62 |
+
self.gate_logit_normalizer = gate_logit_normalizer
|
| 63 |
+
|
| 64 |
+
self.use_rope = use_rope
|
| 65 |
+
self.use_input_gate = use_input_gate
|
| 66 |
+
self.use_output_gate = use_output_gate
|
| 67 |
+
self.use_norm = use_norm
|
| 68 |
+
|
| 69 |
+
if num_slots is None:
|
| 70 |
+
num_slots = self.head_k_dim
|
| 71 |
+
self.num_slots = num_slots
|
| 72 |
+
|
| 73 |
+
self.norm_eps = norm_eps
|
| 74 |
+
|
| 75 |
+
self.clamp_min = clamp_min
|
| 76 |
+
self.clamp_max = clamp_max
|
| 77 |
+
self.layer_idx = layer_idx
|
| 78 |
+
|
| 79 |
+
if layer_idx is None:
|
| 80 |
+
warnings.warn(
|
| 81 |
+
f"Instantiating {self.__class__.__name__} without passing `layer_idx` is not recommended and will "
|
| 82 |
+
"to errors during the forward call, if caching is used. Please make sure to provide a `layer_idx` "
|
| 83 |
+
"when creating this class.",
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
self.q_proj = nn.Linear(self.hidden_size, self.key_dim, bias=False)
|
| 87 |
+
self.k_proj = nn.Linear(self.hidden_size, self.key_dim, bias=False)
|
| 88 |
+
self.v_proj = nn.Linear(self.hidden_size, self.value_dim, bias=False)
|
| 89 |
+
|
| 90 |
+
if use_output_gate:
|
| 91 |
+
self.g_proj = nn.Linear(self.hidden_size, self.value_dim, bias=False)
|
| 92 |
+
self.s_proj = nn.Linear(self.hidden_size, self.num_heads * self.num_slots, bias=False)
|
| 93 |
+
self.o_proj = nn.Linear(self.value_dim, self.hidden_size, bias=False)
|
| 94 |
+
|
| 95 |
+
if use_short_conv:
|
| 96 |
+
self.conv_size = conv_size
|
| 97 |
+
self.q_conv1d = ShortConvolution(
|
| 98 |
+
hidden_size=self.key_dim,
|
| 99 |
+
kernel_size=conv_size,
|
| 100 |
+
bias=conv_bias,
|
| 101 |
+
activation='silu',
|
| 102 |
+
)
|
| 103 |
+
self.k_conv1d = ShortConvolution(
|
| 104 |
+
hidden_size=self.key_dim,
|
| 105 |
+
kernel_size=conv_size,
|
| 106 |
+
bias=conv_bias,
|
| 107 |
+
activation='silu',
|
| 108 |
+
)
|
| 109 |
+
self.v_conv1d = ShortConvolution(
|
| 110 |
+
hidden_size=self.value_dim,
|
| 111 |
+
kernel_size=conv_size,
|
| 112 |
+
bias=conv_bias,
|
| 113 |
+
activation='silu',
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
if self.use_norm:
|
| 117 |
+
if self.use_output_gate:
|
| 118 |
+
self.g_norm = FusedRMSNormGated(
|
| 119 |
+
hidden_size=self.head_v_dim,
|
| 120 |
+
elementwise_affine=elementwise_affine,
|
| 121 |
+
eps=norm_eps,
|
| 122 |
+
)
|
| 123 |
+
else:
|
| 124 |
+
self.g_norm = RMSNorm(
|
| 125 |
+
hidden_size=self.head_v_dim,
|
| 126 |
+
elementwise_affine=elementwise_affine,
|
| 127 |
+
eps=norm_eps,
|
| 128 |
+
dtype=torch.float32,
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
if self.use_rope:
|
| 132 |
+
self.rotary = RotaryEmbedding(self.head_k_dim)
|
| 133 |
+
|
| 134 |
+
def forward(
|
| 135 |
+
self,
|
| 136 |
+
hidden_states: torch.Tensor,
|
| 137 |
+
attention_mask: torch.Tensor | None = None,
|
| 138 |
+
past_key_values: Cache | None = None,
|
| 139 |
+
use_cache: bool | None = False,
|
| 140 |
+
output_attentions: bool | None = False,
|
| 141 |
+
**kwargs,
|
| 142 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 143 |
+
if attention_mask is not None:
|
| 144 |
+
assert len(attention_mask.shape) == 2, (
|
| 145 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 146 |
+
"for padding purposes (0 indicating padding). "
|
| 147 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 151 |
+
|
| 152 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 153 |
+
if cu_seqlens is not None:
|
| 154 |
+
raise NotImplementedError("Training with cu_seqlens is not supported yet for ABCAttention")
|
| 155 |
+
if self.use_short_conv:
|
| 156 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 157 |
+
if last_state is not None:
|
| 158 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 159 |
+
conv_mask = attention_mask[:, -hidden_states.shape[1]:] if attention_mask is not None else None
|
| 160 |
+
q, conv_state_q = self.q_conv1d(
|
| 161 |
+
x=self.q_proj(hidden_states),
|
| 162 |
+
mask=conv_mask,
|
| 163 |
+
cache=conv_state_q,
|
| 164 |
+
output_final_state=use_cache,
|
| 165 |
+
cu_seqlens=cu_seqlens,
|
| 166 |
+
)
|
| 167 |
+
k, conv_state_k = self.k_conv1d(
|
| 168 |
+
x=self.k_proj(hidden_states),
|
| 169 |
+
mask=conv_mask,
|
| 170 |
+
cache=conv_state_k,
|
| 171 |
+
output_final_state=use_cache,
|
| 172 |
+
cu_seqlens=cu_seqlens,
|
| 173 |
+
)
|
| 174 |
+
v, conv_state_v = self.v_conv1d(
|
| 175 |
+
x=self.v_proj(hidden_states),
|
| 176 |
+
mask=conv_mask,
|
| 177 |
+
cache=conv_state_v,
|
| 178 |
+
output_final_state=use_cache,
|
| 179 |
+
cu_seqlens=cu_seqlens,
|
| 180 |
+
)
|
| 181 |
+
else:
|
| 182 |
+
q = self.q_proj(hidden_states)
|
| 183 |
+
k = self.k_proj(hidden_states)
|
| 184 |
+
v = self.v_proj(hidden_states)
|
| 185 |
+
|
| 186 |
+
if self.use_input_gate:
|
| 187 |
+
q, k, v = map(lambda x: swish(x), (q, k, v))
|
| 188 |
+
# dealing with left-padding
|
| 189 |
+
if attention_mask is not None:
|
| 190 |
+
v = v.mul_(attention_mask[:, -v.shape[-2]:, None])
|
| 191 |
+
|
| 192 |
+
q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
|
| 193 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 194 |
+
if self.use_rope:
|
| 195 |
+
seqlen_offset = 0
|
| 196 |
+
if past_key_values is not None:
|
| 197 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 198 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset)
|
| 199 |
+
|
| 200 |
+
s = rearrange(self.s_proj(hidden_states), '... (h m) -> ... h m', m=self.num_slots)
|
| 201 |
+
s = s.clamp_(self.clamp_min, self.clamp_max)
|
| 202 |
+
|
| 203 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 204 |
+
o, recurrent_state = chunk_abc(
|
| 205 |
+
q=q,
|
| 206 |
+
k=k,
|
| 207 |
+
v=v,
|
| 208 |
+
s=s,
|
| 209 |
+
initial_state=recurrent_state,
|
| 210 |
+
output_final_state=use_cache,
|
| 211 |
+
)
|
| 212 |
+
update_layer_cache(
|
| 213 |
+
self,
|
| 214 |
+
past_key_values,
|
| 215 |
+
recurrent_state=recurrent_state,
|
| 216 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 217 |
+
offset=q.shape[1],
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
if self.use_norm and not self.use_output_gate:
|
| 221 |
+
o = self.g_norm(o)
|
| 222 |
+
elif self.use_output_gate:
|
| 223 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 224 |
+
o = self.g_norm(o, g) if self.use_norm else swiglu(g, o)
|
| 225 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 226 |
+
o = self.o_proj(o)
|
| 227 |
+
|
| 228 |
+
return o, None, past_key_values
|
| 229 |
+
|
| 230 |
+
def state_size(self, seq_len: int = 2048):
|
| 231 |
+
return 2 * self.num_slots * self.hidden_size
|
fla/layers/attn.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
from transformers.utils import logging
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import pad_input, unpad_input
|
| 14 |
+
from fla.modules import RMSNorm, RotaryEmbedding
|
| 15 |
+
from fla.ops.utils.index import prepare_lens_from_mask
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from fla.models.utils import Cache
|
| 19 |
+
|
| 20 |
+
try:
|
| 21 |
+
from flash_attn import flash_attn_func, flash_attn_varlen_func
|
| 22 |
+
except ImportError:
|
| 23 |
+
warnings.warn(
|
| 24 |
+
"Flash Attention is not installed. Please install it via `pip install flash-attn --no-build-isolation`",
|
| 25 |
+
category=ImportWarning,
|
| 26 |
+
)
|
| 27 |
+
flash_attn_func = None
|
| 28 |
+
|
| 29 |
+
logger = logging.get_logger(__name__)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class Attention(nn.Module):
|
| 33 |
+
|
| 34 |
+
def __init__(
|
| 35 |
+
self,
|
| 36 |
+
hidden_size: int = 2048,
|
| 37 |
+
num_heads: int = 32,
|
| 38 |
+
num_kv_heads: int | None = None,
|
| 39 |
+
qkv_bias: bool = False,
|
| 40 |
+
qk_norm: bool = False,
|
| 41 |
+
window_size: int | None = None,
|
| 42 |
+
rope_theta: float | None = 10000.,
|
| 43 |
+
max_position_embeddings: int | None = None,
|
| 44 |
+
use_nope: bool = False,
|
| 45 |
+
layer_idx: int = None,
|
| 46 |
+
):
|
| 47 |
+
super().__init__()
|
| 48 |
+
|
| 49 |
+
self.hidden_size = hidden_size
|
| 50 |
+
self.num_heads = num_heads
|
| 51 |
+
if num_kv_heads is None:
|
| 52 |
+
self.num_kv_heads = self.num_heads
|
| 53 |
+
else:
|
| 54 |
+
self.num_kv_heads = num_kv_heads
|
| 55 |
+
self.num_kv_groups = num_heads // self.num_kv_heads
|
| 56 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 57 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 58 |
+
self.qkv_bias = qkv_bias
|
| 59 |
+
self.qk_norm = qk_norm
|
| 60 |
+
|
| 61 |
+
self.window_size = window_size
|
| 62 |
+
self.rope_theta = rope_theta
|
| 63 |
+
self.max_position_embeddings = max_position_embeddings
|
| 64 |
+
self.use_nope = use_nope
|
| 65 |
+
self.layer_idx = layer_idx
|
| 66 |
+
|
| 67 |
+
if flash_attn_func is None:
|
| 68 |
+
raise ImportError("Please install Flash Attention via `pip install flash-attn --no-build-isolation` first")
|
| 69 |
+
|
| 70 |
+
self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
|
| 71 |
+
self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 72 |
+
self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 73 |
+
self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 74 |
+
|
| 75 |
+
if qk_norm:
|
| 76 |
+
self.q_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 77 |
+
self.k_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 78 |
+
|
| 79 |
+
self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
|
| 80 |
+
|
| 81 |
+
def forward(
|
| 82 |
+
self,
|
| 83 |
+
hidden_states: torch.Tensor,
|
| 84 |
+
attention_mask: torch.LongTensor | None = None,
|
| 85 |
+
past_key_values: Cache | None = None,
|
| 86 |
+
output_attentions: bool = False,
|
| 87 |
+
use_cache: bool = False,
|
| 88 |
+
**kwargs,
|
| 89 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 90 |
+
if attention_mask is not None:
|
| 91 |
+
assert len(attention_mask.shape) == 2, (
|
| 92 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 93 |
+
"for padding purposes (0 indicating padding). "
|
| 94 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
batch_size, q_len, _ = hidden_states.size()
|
| 98 |
+
|
| 99 |
+
q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 100 |
+
k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 101 |
+
v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 102 |
+
|
| 103 |
+
if self.qk_norm:
|
| 104 |
+
q, k = self.q_norm(q), self.k_norm(k)
|
| 105 |
+
|
| 106 |
+
# equivalent to cu_seqlens in `flash_attn`
|
| 107 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 108 |
+
|
| 109 |
+
seqlen_offset, max_seqlen = 0, q_len
|
| 110 |
+
if past_key_values is not None:
|
| 111 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 112 |
+
max_seqlen = q.shape[1] + seqlen_offset
|
| 113 |
+
|
| 114 |
+
if attention_mask is not None:
|
| 115 |
+
# to deliminate the offsets of padding tokens
|
| 116 |
+
seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
|
| 117 |
+
max_seqlen = q.shape[1] + max(seqlen_offset)
|
| 118 |
+
|
| 119 |
+
if self.max_position_embeddings is not None:
|
| 120 |
+
max_seqlen = max(max_seqlen, self.max_position_embeddings)
|
| 121 |
+
if not self.use_nope:
|
| 122 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
|
| 123 |
+
|
| 124 |
+
if past_key_values is not None:
|
| 125 |
+
cache_has_content = past_key_values.get_seq_length(self.layer_idx) > 0
|
| 126 |
+
k_cached, v_cached = past_key_values.update(
|
| 127 |
+
attn_state=(k.flatten(-2, -1), v.flatten(-2, -1)),
|
| 128 |
+
layer_idx=self.layer_idx,
|
| 129 |
+
offset=q_len,
|
| 130 |
+
cache_kwargs=dict(window_size=self.window_size),
|
| 131 |
+
)['attn_state']
|
| 132 |
+
if cache_has_content:
|
| 133 |
+
k, v = k_cached, v_cached
|
| 134 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 135 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 136 |
+
|
| 137 |
+
# Contains at least one padding token in the sequence
|
| 138 |
+
if attention_mask is not None:
|
| 139 |
+
if q.shape[1] == 1 and self.window_size is not None:
|
| 140 |
+
attention_mask = attention_mask[:, -self.window_size:]
|
| 141 |
+
q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(q, (k, v), attention_mask, q_len)
|
| 142 |
+
cu_seqlens_q, cu_seqlens_k = cu_seqlens
|
| 143 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 144 |
+
o = flash_attn_varlen_func(
|
| 145 |
+
q, k, v,
|
| 146 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 147 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 148 |
+
max_seqlen_q=max_seqlen_q,
|
| 149 |
+
max_seqlen_k=max_seqlen_k,
|
| 150 |
+
causal=True,
|
| 151 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 152 |
+
)
|
| 153 |
+
o = pad_input(o, indices_q, batch_size, q_len)
|
| 154 |
+
elif cu_seqlens is not None:
|
| 155 |
+
o = flash_attn_varlen_func(
|
| 156 |
+
q.squeeze(0), k.squeeze(0), v.squeeze(0),
|
| 157 |
+
cu_seqlens_q=cu_seqlens,
|
| 158 |
+
cu_seqlens_k=cu_seqlens,
|
| 159 |
+
max_seqlen_q=max_seqlen,
|
| 160 |
+
max_seqlen_k=max_seqlen,
|
| 161 |
+
causal=True,
|
| 162 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 163 |
+
).unsqueeze(0)
|
| 164 |
+
else:
|
| 165 |
+
o = flash_attn_func(
|
| 166 |
+
q, k, v,
|
| 167 |
+
causal=True,
|
| 168 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 169 |
+
)
|
| 170 |
+
o = o.reshape(batch_size, q_len, -1)
|
| 171 |
+
o = self.o_proj(o)
|
| 172 |
+
|
| 173 |
+
if not output_attentions:
|
| 174 |
+
attentions = None
|
| 175 |
+
|
| 176 |
+
return o, attentions, past_key_values
|
fla/layers/based.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
Linear attention in Based.
|
| 5 |
+
https://github.com/HazyResearch/zoology/blob/main/zoology/mixers/based.py
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
|
| 12 |
+
from fla.modules.feature_map import TaylorFeatureMap
|
| 13 |
+
from fla.ops.based import parallel_based
|
| 14 |
+
from fla.ops.linear_attn import chunk_linear_attn, fused_chunk_linear_attn
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class BasedLinearAttention(nn.Module):
|
| 18 |
+
|
| 19 |
+
def __init__(
|
| 20 |
+
self,
|
| 21 |
+
hidden_size: int,
|
| 22 |
+
feature_dim: int = 16,
|
| 23 |
+
num_key_value_heads: int = 12,
|
| 24 |
+
num_heads: int = 12,
|
| 25 |
+
feature_name: str = "taylor_exp",
|
| 26 |
+
eps: float = 1e-12,
|
| 27 |
+
causal: bool = True,
|
| 28 |
+
mode: str = "parallel",
|
| 29 |
+
):
|
| 30 |
+
super().__init__()
|
| 31 |
+
|
| 32 |
+
self.hidden_size = hidden_size
|
| 33 |
+
self.mode = mode
|
| 34 |
+
self.feature_name = feature_name
|
| 35 |
+
self.feature_dim = feature_dim
|
| 36 |
+
self.num_key_value_heads = num_key_value_heads
|
| 37 |
+
self.num_heads = num_heads
|
| 38 |
+
self.head_dim = self.hidden_size // self.num_key_value_heads
|
| 39 |
+
assert self.hidden_size % self.head_dim == 0
|
| 40 |
+
self.causal = causal
|
| 41 |
+
|
| 42 |
+
self.q_proj = nn.Linear(self.hidden_size, self.feature_dim * self.num_heads, bias=False)
|
| 43 |
+
self.k_proj = nn.Linear(self.hidden_size, self.feature_dim * self.num_heads, bias=False)
|
| 44 |
+
self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
|
| 45 |
+
self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
|
| 46 |
+
self.dropout = nn.Identity()
|
| 47 |
+
self.feature_map = TaylorFeatureMap(feature_dim)
|
| 48 |
+
self.eps = eps
|
| 49 |
+
|
| 50 |
+
def forward(self, hidden_states: torch.Tensor, **kwargs):
|
| 51 |
+
mode = self.mode
|
| 52 |
+
q, k, v = self.q_proj(hidden_states), self.k_proj(hidden_states), self.v_proj(hidden_states)
|
| 53 |
+
q, k, v = map(lambda x: rearrange(x, "... (h d) -> ... h d", d=self.head_dim), [q, k, v])
|
| 54 |
+
if mode == "fused_chunk":
|
| 55 |
+
q, k = self.feature_map(q), self.feature_map(k)
|
| 56 |
+
o, _ = fused_chunk_linear_attn(q, k, v, normalize=True, scale=1)
|
| 57 |
+
elif mode == 'chunk':
|
| 58 |
+
q, k = self.feature_map(q), self.feature_map(k)
|
| 59 |
+
o, _ = chunk_linear_attn(q, k, v, normalize=True, scale=1)
|
| 60 |
+
elif mode == 'parallel':
|
| 61 |
+
assert q.shape[-1] <= 128
|
| 62 |
+
o = parallel_based(q, k, v, scale=1, use_norm=True)
|
| 63 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 64 |
+
o = self.o_proj(o)
|
| 65 |
+
o = self.dropout(o)
|
| 66 |
+
return o
|
| 67 |
+
|
| 68 |
+
def forward_reference(self, hidden_states: torch.Tensor, **kwargs):
|
| 69 |
+
"""
|
| 70 |
+
x (torch.Tensor): tensor of shape (b, d, t)
|
| 71 |
+
y (torch.Tensor): tensor of shape (b, d, t)
|
| 72 |
+
"""
|
| 73 |
+
# hidden_states = hidden_states.transpose(1, 2)
|
| 74 |
+
b, t, _ = hidden_states.size()
|
| 75 |
+
q, k, v = self.q_proj(hidden_states), self.k_proj(hidden_states), self.v_proj(hidden_states)
|
| 76 |
+
|
| 77 |
+
q = q.view(b, t, self.num_heads, self.feature_dim).transpose(1, 2)
|
| 78 |
+
k = k.view(b, t, self.num_key_value_heads, self.feature_dim).transpose(1, 2)
|
| 79 |
+
v = v.view(b, t, self.num_key_value_heads, self.head_dim).transpose(1, 2)
|
| 80 |
+
|
| 81 |
+
# Linear attention
|
| 82 |
+
q, k = self.feature_map(q), self.feature_map(k)
|
| 83 |
+
q, k, v = q.unsqueeze(-2), k.unsqueeze(-2), v.unsqueeze(-1)
|
| 84 |
+
|
| 85 |
+
# Compute attention
|
| 86 |
+
if self.causal:
|
| 87 |
+
y = ((q * (k * v).cumsum(2)).sum(-1) / ((q * k.cumsum(2)).sum(-1) + self.eps))
|
| 88 |
+
else:
|
| 89 |
+
y = ((q * (k * v).sum(2, True)).sum(-1) / ((q * k.sum(2, True)).sum(-1) + self.eps))
|
| 90 |
+
y = rearrange(y, 'b h t d -> b t (h d)')
|
| 91 |
+
y = self.o_proj(y.to(hidden_states.dtype))
|
| 92 |
+
y = self.dropout(y)
|
| 93 |
+
return y.to(hidden_states.dtype)
|
fla/layers/bitattn.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
from transformers.utils import logging
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import pad_input, unpad_input
|
| 14 |
+
from fla.modules import RotaryEmbedding
|
| 15 |
+
from fla.modules.fused_bitlinear import FusedBitLinear
|
| 16 |
+
from fla.ops.utils.index import prepare_lens_from_mask
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from fla.models.utils import Cache
|
| 20 |
+
|
| 21 |
+
try:
|
| 22 |
+
from flash_attn import flash_attn_func, flash_attn_varlen_func
|
| 23 |
+
except ImportError:
|
| 24 |
+
warnings.warn(
|
| 25 |
+
"Flash Attention is not installed. Please install it via `pip install flash-attn --no-build-isolation`",
|
| 26 |
+
category=ImportWarning,
|
| 27 |
+
)
|
| 28 |
+
flash_attn_func = None
|
| 29 |
+
|
| 30 |
+
logger = logging.get_logger(__name__)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class BitAttention(nn.Module):
|
| 34 |
+
|
| 35 |
+
def __init__(
|
| 36 |
+
self,
|
| 37 |
+
hidden_size: int = 2048,
|
| 38 |
+
num_heads: int = 32,
|
| 39 |
+
num_kv_heads: int | None = None,
|
| 40 |
+
window_size: int | None = None,
|
| 41 |
+
rope_theta: float | None = 10000.,
|
| 42 |
+
max_position_embeddings: int | None = None,
|
| 43 |
+
norm_eps: float = 1e-5,
|
| 44 |
+
layer_idx: int = None,
|
| 45 |
+
):
|
| 46 |
+
super().__init__()
|
| 47 |
+
|
| 48 |
+
self.num_heads = num_heads
|
| 49 |
+
if num_kv_heads is None:
|
| 50 |
+
self.num_kv_heads = self.num_heads
|
| 51 |
+
else:
|
| 52 |
+
self.num_kv_heads = num_kv_heads
|
| 53 |
+
self.num_kv_groups = num_heads // self.num_kv_heads
|
| 54 |
+
self.hidden_size = hidden_size
|
| 55 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 56 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 57 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 58 |
+
self.window_size = window_size
|
| 59 |
+
self.rope_theta = rope_theta
|
| 60 |
+
self.max_position_embeddings = max_position_embeddings
|
| 61 |
+
self.layer_idx = layer_idx
|
| 62 |
+
|
| 63 |
+
self.q_proj = FusedBitLinear(self.hidden_size, self.hidden_size, bias=False)
|
| 64 |
+
self.k_proj = FusedBitLinear(self.hidden_size, self.kv_dim, bias=False)
|
| 65 |
+
self.v_proj = FusedBitLinear(self.hidden_size, self.kv_dim, bias=False)
|
| 66 |
+
self.o_proj = FusedBitLinear(self.hidden_size, self.hidden_size, bias=False)
|
| 67 |
+
|
| 68 |
+
self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
|
| 69 |
+
|
| 70 |
+
def forward(
|
| 71 |
+
self,
|
| 72 |
+
hidden_states: torch.Tensor,
|
| 73 |
+
attention_mask: torch.LongTensor | None = None,
|
| 74 |
+
past_key_values: Cache | None = None,
|
| 75 |
+
output_attentions: bool = False,
|
| 76 |
+
use_cache: bool = False,
|
| 77 |
+
**kwargs,
|
| 78 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 79 |
+
if attention_mask is not None:
|
| 80 |
+
assert len(attention_mask.shape) == 2, (
|
| 81 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 82 |
+
"for padding purposes (0 indicating padding). "
|
| 83 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
batch_size, q_len, _ = hidden_states.size()
|
| 87 |
+
|
| 88 |
+
q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 89 |
+
k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 90 |
+
v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 91 |
+
|
| 92 |
+
# equivalent to cu_seqlens in `flash_attn`
|
| 93 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 94 |
+
|
| 95 |
+
seqlen_offset, max_seqlen = 0, q_len
|
| 96 |
+
if past_key_values is not None:
|
| 97 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 98 |
+
max_seqlen = q.shape[1] + seqlen_offset
|
| 99 |
+
|
| 100 |
+
if attention_mask is not None:
|
| 101 |
+
# to deliminate the offsets of padding tokens
|
| 102 |
+
seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
|
| 103 |
+
max_seqlen = q.shape[1] + max(seqlen_offset)
|
| 104 |
+
|
| 105 |
+
if self.max_position_embeddings is not None:
|
| 106 |
+
max_seqlen = max(max_seqlen, self.max_position_embeddings)
|
| 107 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
|
| 108 |
+
|
| 109 |
+
if past_key_values is not None:
|
| 110 |
+
cache_has_content = past_key_values.get_seq_length(self.layer_idx) > 0
|
| 111 |
+
k_cached, v_cached = past_key_values.update(
|
| 112 |
+
attn_state=(k.flatten(-2, -1), v.flatten(-2, -1)),
|
| 113 |
+
layer_idx=self.layer_idx,
|
| 114 |
+
offset=q_len,
|
| 115 |
+
cache_kwargs=dict(window_size=self.window_size),
|
| 116 |
+
)['attn_state']
|
| 117 |
+
if cache_has_content:
|
| 118 |
+
k, v = k_cached, v_cached
|
| 119 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 120 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 121 |
+
|
| 122 |
+
if flash_attn_func is None:
|
| 123 |
+
raise ImportError("Please install Flash Attention via `pip install flash-attn --no-build-isolation` first")
|
| 124 |
+
|
| 125 |
+
# Contains at least one padding token in the sequence
|
| 126 |
+
if attention_mask is not None:
|
| 127 |
+
q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(q, (k, v), attention_mask, q_len)
|
| 128 |
+
cu_seqlens_q, cu_seqlens_k = cu_seqlens
|
| 129 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 130 |
+
o = flash_attn_varlen_func(
|
| 131 |
+
q, k, v,
|
| 132 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 133 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 134 |
+
max_seqlen_q=max_seqlen_q,
|
| 135 |
+
max_seqlen_k=max_seqlen_k,
|
| 136 |
+
causal=True,
|
| 137 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 138 |
+
)
|
| 139 |
+
o = pad_input(o, indices_q, batch_size, q_len)
|
| 140 |
+
elif cu_seqlens is not None:
|
| 141 |
+
o = flash_attn_varlen_func(
|
| 142 |
+
q.squeeze(0), k.squeeze(0), v.squeeze(0),
|
| 143 |
+
cu_seqlens_q=cu_seqlens,
|
| 144 |
+
cu_seqlens_k=cu_seqlens,
|
| 145 |
+
max_seqlen_q=max_seqlen,
|
| 146 |
+
max_seqlen_k=max_seqlen,
|
| 147 |
+
causal=True,
|
| 148 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 149 |
+
).unsqueeze(0)
|
| 150 |
+
else:
|
| 151 |
+
o = flash_attn_func(
|
| 152 |
+
q, k, v,
|
| 153 |
+
causal=True,
|
| 154 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 155 |
+
)
|
| 156 |
+
o = o.reshape(batch_size, q_len, -1)
|
| 157 |
+
o = self.o_proj(o)
|
| 158 |
+
|
| 159 |
+
if not output_attentions:
|
| 160 |
+
attentions = None
|
| 161 |
+
|
| 162 |
+
return o, attentions, past_key_values
|
fla/layers/comba.py
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import math
|
| 6 |
+
import warnings
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
from einops import rearrange, repeat
|
| 12 |
+
from torch.nn import functional as F
|
| 13 |
+
|
| 14 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 15 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 16 |
+
from fla.ops.comba import chunk_comba, fused_recurrent_comba
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from transformers.processing_utils import Unpack
|
| 20 |
+
|
| 21 |
+
from fla.models.utils import Cache
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class Comba(nn.Module):
|
| 25 |
+
"""
|
| 26 |
+
The layer implementaion for [Comba: Improving Bilinear RNNs with Closed-loop Control](https://arxiv.org/abs/2506.02475).
|
| 27 |
+
|
| 28 |
+
Similar to Mamba2 and Gated-DeltaNet, each layer contains around 6*hidden_size*hidden_size parameters.
|
| 29 |
+
|
| 30 |
+
Parameter alloation when use_output_gate=True:
|
| 31 |
+
- 0.75 * hidden_size * hidden_size for the q_proj and k_proj each
|
| 32 |
+
- 1.5 * hidden_size * hidden_size for the v_proj, g_proj and o_proj each
|
| 33 |
+
- Others are ignorably small.
|
| 34 |
+
- In total = 0.75 * 2 + 1.5 * 3 = 6 * hidden_size * hidden_size
|
| 35 |
+
NOTE: num_heads * head_dim = 0.75 * hidden_size, please make sure to set the correct num_heads and head_dim.
|
| 36 |
+
|
| 37 |
+
Parameter allocation when use_output_gate=False:
|
| 38 |
+
- 1 * hidden_size * hidden_size for the q_proj and k_proj each
|
| 39 |
+
- 2 * hidden_size * hidden_size for the v_proj and o_proj each
|
| 40 |
+
- Others are ignorably small.
|
| 41 |
+
- In total = 1 * 2 + 2 * 2 = 6 * hidden_size * hidden_size
|
| 42 |
+
|
| 43 |
+
Args:
|
| 44 |
+
hidden_size (int, Optional):
|
| 45 |
+
The hidden size of the input. Default: 2048.
|
| 46 |
+
expand_v (float, Optional):
|
| 47 |
+
The expansion ratio for the value dim. Default: 2.0.
|
| 48 |
+
head_dim (int, Optional):
|
| 49 |
+
The dimension of each head. Default: 256.
|
| 50 |
+
num_heads (int, Optional):
|
| 51 |
+
The number of heads. Default: 4.
|
| 52 |
+
num_v_heads (int, Optional):
|
| 53 |
+
The number of heads for the value projection, equal to `num_heads` if `None`.
|
| 54 |
+
GVA is applied if `num_v_heads` > `num_heads`. Default: `None`.
|
| 55 |
+
mode (str, Optional):
|
| 56 |
+
Which Gated DeltaNet kernel to use.
|
| 57 |
+
Currently available: `chunk` and `fused_recurrent`.
|
| 58 |
+
Default: `chunk`.
|
| 59 |
+
use_beta (bool, Optional):
|
| 60 |
+
Whether to use beta. Default: `True`.
|
| 61 |
+
use_output_gate (bool, Optional):
|
| 62 |
+
Whether to use output gate. Default: `True`.
|
| 63 |
+
use_output_correction (bool, Optional):
|
| 64 |
+
Whether to use <q-dk>. Default: `True`.
|
| 65 |
+
use_short_conv (bool, Optional):
|
| 66 |
+
Whether to use short convolutions. Default: `True`.
|
| 67 |
+
conv_size (int, Optional):
|
| 68 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 69 |
+
conv_bias (bool, Optional):
|
| 70 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 71 |
+
layer_idx (int, Optional):
|
| 72 |
+
The index of the layer. Default: None.
|
| 73 |
+
norm_eps (float, Optional):
|
| 74 |
+
The epsilon value for the normalization layer. Default: 1e-5.
|
| 75 |
+
"""
|
| 76 |
+
|
| 77 |
+
def __init__(
|
| 78 |
+
self,
|
| 79 |
+
hidden_size: int = 2048,
|
| 80 |
+
expand_v: float = 2,
|
| 81 |
+
head_dim: int = 256,
|
| 82 |
+
num_heads: int = 6,
|
| 83 |
+
num_v_heads: int = None,
|
| 84 |
+
mode: str = 'chunk',
|
| 85 |
+
use_short_conv: bool = True,
|
| 86 |
+
use_output_gate: bool = True,
|
| 87 |
+
use_output_correction: bool = True,
|
| 88 |
+
use_inner_decay: bool = True,
|
| 89 |
+
correction_factor: float = 1.,
|
| 90 |
+
conv_size: int = 4,
|
| 91 |
+
conv_bias: bool = False,
|
| 92 |
+
layer_idx: int = None,
|
| 93 |
+
norm_eps: float = 1e-5,
|
| 94 |
+
**kwargs,
|
| 95 |
+
) -> Comba:
|
| 96 |
+
super().__init__()
|
| 97 |
+
|
| 98 |
+
self.mode = mode
|
| 99 |
+
|
| 100 |
+
self.hidden_size = hidden_size
|
| 101 |
+
self.expand_v = expand_v
|
| 102 |
+
|
| 103 |
+
self.use_short_conv = use_short_conv
|
| 104 |
+
self.use_output_gate = use_output_gate
|
| 105 |
+
self.use_output_correction = use_output_correction
|
| 106 |
+
self.use_inner_decay = use_inner_decay
|
| 107 |
+
self.conv_size = conv_size
|
| 108 |
+
self.conv_bias = conv_bias
|
| 109 |
+
|
| 110 |
+
self.head_dim = head_dim
|
| 111 |
+
self.num_heads = num_heads
|
| 112 |
+
self.num_v_heads = num_v_heads if num_v_heads is not None else num_heads
|
| 113 |
+
|
| 114 |
+
self.head_k_dim = head_dim
|
| 115 |
+
self.head_v_dim = int(self.head_dim * self.expand_v)
|
| 116 |
+
self.key_dim = int(self.num_heads * self.head_k_dim)
|
| 117 |
+
self.value_dim = int(self.num_v_heads * self.head_v_dim)
|
| 118 |
+
self.layer_idx = layer_idx
|
| 119 |
+
|
| 120 |
+
# Consistency check: Ensure expand_v produces integer values
|
| 121 |
+
if not math.isclose(self.num_v_heads * self.head_dim * expand_v, self.value_dim, rel_tol=1e-5):
|
| 122 |
+
raise ValueError(
|
| 123 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by key_dim={self.key_dim}. "
|
| 124 |
+
f"Resulting value_dim would be {self.num_v_heads * self.head_dim * expand_v}, which is invalid for nn.Linear.",
|
| 125 |
+
)
|
| 126 |
+
if self.num_v_heads > self.num_heads and self.num_v_heads % self.num_heads != 0:
|
| 127 |
+
raise ValueError(
|
| 128 |
+
f"num_v_heads={self.num_v_heads} must be divisible by num_heads={self.num_heads}.",
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
if not math.isclose(head_dim * expand_v, self.head_v_dim, rel_tol=1e-5):
|
| 132 |
+
raise ValueError(
|
| 133 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by head_dim={head_dim}. "
|
| 134 |
+
f"Resulting head_v_dim would be {head_dim * expand_v}, which is invalid for FusedRMSNormGated.",
|
| 135 |
+
)
|
| 136 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 137 |
+
|
| 138 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 139 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 140 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 141 |
+
self.a_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
|
| 142 |
+
self.b_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
|
| 143 |
+
|
| 144 |
+
if use_inner_decay:
|
| 145 |
+
self.decay = nn.Parameter(torch.ones(self.num_heads))
|
| 146 |
+
|
| 147 |
+
if use_output_correction:
|
| 148 |
+
warnings.warn(
|
| 149 |
+
"The correction_factor is set to 1 by default similar to Mamba2. "
|
| 150 |
+
"However, we find that sometimes correction_factor = 0.02 works better for small-scale models. "
|
| 151 |
+
"In practice, we recommend trying both settings. ",
|
| 152 |
+
)
|
| 153 |
+
self.D = nn.Parameter(torch.ones(self.num_heads) * correction_factor)
|
| 154 |
+
self.D._no_weight_decay = True
|
| 155 |
+
|
| 156 |
+
A = torch.empty(self.num_v_heads, dtype=torch.float32).uniform_(0, 16)
|
| 157 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 158 |
+
self.A_log._no_weight_decay = True
|
| 159 |
+
# hard coded for now
|
| 160 |
+
dt_min = 0.001
|
| 161 |
+
dt_max = 0.1
|
| 162 |
+
dt_init_floor = 1e-4
|
| 163 |
+
dt = torch.exp(
|
| 164 |
+
torch.rand(self.num_v_heads) * (math.log(dt_max) - math.log(dt_min))
|
| 165 |
+
+ math.log(dt_min),
|
| 166 |
+
)
|
| 167 |
+
dt = torch.clamp(dt, min=dt_init_floor)
|
| 168 |
+
# Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
|
| 169 |
+
inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| 170 |
+
self.dt_bias = nn.Parameter(inv_dt)
|
| 171 |
+
# Just to be explicit. Without this we already don't put wd on dt_bias because of the check
|
| 172 |
+
# name.endswith("bias") in param_grouping.py
|
| 173 |
+
self.dt_bias._no_weight_decay = True
|
| 174 |
+
|
| 175 |
+
if use_short_conv:
|
| 176 |
+
self.conv_size = conv_size
|
| 177 |
+
self.q_conv1d = ShortConvolution(
|
| 178 |
+
hidden_size=self.key_dim,
|
| 179 |
+
kernel_size=conv_size,
|
| 180 |
+
bias=conv_bias,
|
| 181 |
+
activation='silu',
|
| 182 |
+
)
|
| 183 |
+
self.k_conv1d = ShortConvolution(
|
| 184 |
+
hidden_size=self.key_dim,
|
| 185 |
+
kernel_size=conv_size,
|
| 186 |
+
bias=conv_bias,
|
| 187 |
+
activation='silu',
|
| 188 |
+
)
|
| 189 |
+
self.v_conv1d = ShortConvolution(
|
| 190 |
+
hidden_size=self.value_dim,
|
| 191 |
+
kernel_size=conv_size,
|
| 192 |
+
bias=conv_bias,
|
| 193 |
+
activation='silu',
|
| 194 |
+
)
|
| 195 |
+
else:
|
| 196 |
+
warnings.warn(
|
| 197 |
+
"ShortConvolution is crucial to the performance. "
|
| 198 |
+
"Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
|
| 199 |
+
)
|
| 200 |
+
if use_output_gate:
|
| 201 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 202 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, activation='sigmoid', eps=norm_eps)
|
| 203 |
+
else:
|
| 204 |
+
self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
|
| 205 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 206 |
+
|
| 207 |
+
def forward(
|
| 208 |
+
self,
|
| 209 |
+
hidden_states: torch.Tensor,
|
| 210 |
+
attention_mask: torch.Tensor | None = None,
|
| 211 |
+
past_key_values: Cache | None = None,
|
| 212 |
+
use_cache: bool | None = False,
|
| 213 |
+
output_attentions: bool | None = False,
|
| 214 |
+
**kwargs: Unpack[dict],
|
| 215 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 216 |
+
if attention_mask is not None:
|
| 217 |
+
assert len(attention_mask.shape) == 2, (
|
| 218 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 219 |
+
"for padding purposes (0 indicating padding). "
|
| 220 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 224 |
+
# change to inference mode.
|
| 225 |
+
mode = 'fused_recurrent' if (q_len <= 64 and not self.training) else self.mode
|
| 226 |
+
if self.training:
|
| 227 |
+
assert mode == 'chunk', "Only chunk mode is supported in training."
|
| 228 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 229 |
+
|
| 230 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 231 |
+
if attention_mask is not None:
|
| 232 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 233 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 234 |
+
|
| 235 |
+
if self.use_short_conv:
|
| 236 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 237 |
+
if last_state is not None:
|
| 238 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 239 |
+
q, conv_state_q = self.q_conv1d(
|
| 240 |
+
x=self.q_proj(hidden_states),
|
| 241 |
+
cache=conv_state_q,
|
| 242 |
+
output_final_state=use_cache,
|
| 243 |
+
cu_seqlens=cu_seqlens,
|
| 244 |
+
)
|
| 245 |
+
k, conv_state_k = self.k_conv1d(
|
| 246 |
+
x=self.k_proj(hidden_states),
|
| 247 |
+
cache=conv_state_k,
|
| 248 |
+
output_final_state=use_cache,
|
| 249 |
+
cu_seqlens=cu_seqlens,
|
| 250 |
+
)
|
| 251 |
+
v, conv_state_v = self.v_conv1d(
|
| 252 |
+
x=self.v_proj(hidden_states),
|
| 253 |
+
cache=conv_state_v,
|
| 254 |
+
output_final_state=use_cache,
|
| 255 |
+
cu_seqlens=cu_seqlens,
|
| 256 |
+
)
|
| 257 |
+
else:
|
| 258 |
+
q = F.silu(self.q_proj(hidden_states))
|
| 259 |
+
k = F.silu(self.k_proj(hidden_states))
|
| 260 |
+
v = F.silu(self.v_proj(hidden_states))
|
| 261 |
+
|
| 262 |
+
q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
|
| 263 |
+
|
| 264 |
+
if self.use_inner_decay:
|
| 265 |
+
p = k * self.decay[None, None, :, None].sigmoid()
|
| 266 |
+
else:
|
| 267 |
+
p = k
|
| 268 |
+
|
| 269 |
+
if self.use_output_correction:
|
| 270 |
+
q = q - self.D[None, None, :, None] * p
|
| 271 |
+
|
| 272 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 273 |
+
|
| 274 |
+
if self.num_v_heads > self.num_heads:
|
| 275 |
+
q, k = map(lambda x: repeat(x, '... h d -> ... (h g) d', g=self.num_v_heads // self.num_heads), (q, k))
|
| 276 |
+
|
| 277 |
+
beta = self.b_proj(hidden_states).sigmoid()
|
| 278 |
+
g = -self.A_log.float().exp() * F.softplus(self.a_proj(hidden_states).float() + self.dt_bias)
|
| 279 |
+
|
| 280 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 281 |
+
if mode == 'chunk':
|
| 282 |
+
o, recurrent_state = chunk_comba(
|
| 283 |
+
q=q,
|
| 284 |
+
k=k,
|
| 285 |
+
v=v,
|
| 286 |
+
p=p,
|
| 287 |
+
g=g,
|
| 288 |
+
beta=beta,
|
| 289 |
+
initial_state=recurrent_state,
|
| 290 |
+
output_final_state=use_cache,
|
| 291 |
+
cu_seqlens=cu_seqlens,
|
| 292 |
+
use_qk_l2norm_in_kernel=True,
|
| 293 |
+
)
|
| 294 |
+
elif mode == 'fused_recurrent':
|
| 295 |
+
o, recurrent_state = fused_recurrent_comba(
|
| 296 |
+
q=q,
|
| 297 |
+
k=k,
|
| 298 |
+
v=v,
|
| 299 |
+
p=p,
|
| 300 |
+
g=g,
|
| 301 |
+
beta=beta,
|
| 302 |
+
initial_state=recurrent_state,
|
| 303 |
+
output_final_state=use_cache,
|
| 304 |
+
cu_seqlens=cu_seqlens,
|
| 305 |
+
use_qk_l2norm_in_kernel=True,
|
| 306 |
+
)
|
| 307 |
+
else:
|
| 308 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 309 |
+
|
| 310 |
+
update_layer_cache(
|
| 311 |
+
self,
|
| 312 |
+
past_key_values,
|
| 313 |
+
recurrent_state=recurrent_state,
|
| 314 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 315 |
+
offset=q_len,
|
| 316 |
+
)
|
| 317 |
+
|
| 318 |
+
if self.use_output_gate:
|
| 319 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 320 |
+
o = self.o_norm(o, g)
|
| 321 |
+
else:
|
| 322 |
+
o = self.o_norm(o)
|
| 323 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 324 |
+
o = self.o_proj(o)
|
| 325 |
+
if attention_mask is not None:
|
| 326 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 327 |
+
|
| 328 |
+
return o, None, past_key_values
|
fla/layers/delta_net.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
from torch.nn import functional as F
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 14 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 15 |
+
from fla.ops.delta_rule import chunk_delta_rule, fused_recurrent_delta_rule
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from transformers.processing_utils import Unpack
|
| 19 |
+
|
| 20 |
+
from fla.models.utils import Cache
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def elu_p1(x):
|
| 24 |
+
return (F.elu(x, 1., False) + 1.).to(x)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def sum_norm(x):
|
| 28 |
+
return (x / x.sum(-1, keepdim=True)).to(x)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class DeltaNet(nn.Module):
|
| 32 |
+
r"""
|
| 33 |
+
The layer implementaion for [Parallelizing Linear Transformers with the Delta Rule over Sequence Length](https://arxiv.org/abs/2406.06484). # noqa:
|
| 34 |
+
DeltaNet was originally proposed in [Linear Transformers Are Secretly Fast Weight Programmers](https://arxiv.org/abs/2102.11174). # noqa
|
| 35 |
+
|
| 36 |
+
Args:
|
| 37 |
+
mode (str, Optional):
|
| 38 |
+
Which DeltaNet kernel to use.
|
| 39 |
+
Currently available: `chunk`, `fused_recurrent`, and `fused_chunk`.
|
| 40 |
+
Default: `chunk`.
|
| 41 |
+
hidden_size (int, Optional):
|
| 42 |
+
The hidden size of the input. Default: 1024.
|
| 43 |
+
expand_k (float, Optional):
|
| 44 |
+
The expansion ratio for the key dim. Default: 1.0.
|
| 45 |
+
expand_v (float, Optional):
|
| 46 |
+
The expansion ratio for the value dim. Default: 1.0.
|
| 47 |
+
num_heads (int, Optional):
|
| 48 |
+
The number of heads. Default: 4.
|
| 49 |
+
use_beta (bool, Optional):
|
| 50 |
+
Whether to use beta. Default: `True`.
|
| 51 |
+
use_gate (bool, Optional):
|
| 52 |
+
Whether to use output gate. Default: `False`.
|
| 53 |
+
use_short_conv (bool, Optional):
|
| 54 |
+
Whether to use short convolutions. Default: `True`.
|
| 55 |
+
conv_size (int, Optional):
|
| 56 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 57 |
+
conv_bias (bool, Optional):
|
| 58 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 59 |
+
allow_neg_eigval (bool, Optional):
|
| 60 |
+
Allow negative eigenvalues. Default: `False`. If set to `True`, the beta will be multiplied by 2.
|
| 61 |
+
See reference: [Unlocking State-Tracking in Linear RNNs Through Negative Eigenvalues](https://arxiv.org/abs/2411.12537)
|
| 62 |
+
layer_idx (int, Optional):
|
| 63 |
+
The index of the layer. Default: None.
|
| 64 |
+
norm_eps (float, Optional):
|
| 65 |
+
The epsilon value for the layernorm/rmsnorm layer. Default: 1e-5.
|
| 66 |
+
qk_activation (str, Optional):
|
| 67 |
+
The activation function for the query and key. Default: `silu`.
|
| 68 |
+
qk_norm (str, Optional):
|
| 69 |
+
The normalization method for the query and key. Default: `l2`.
|
| 70 |
+
"""
|
| 71 |
+
|
| 72 |
+
def __init__(
|
| 73 |
+
self,
|
| 74 |
+
mode: str = 'chunk',
|
| 75 |
+
d_model: int = None,
|
| 76 |
+
hidden_size: int = 1024,
|
| 77 |
+
expand_k: float = 1.0,
|
| 78 |
+
expand_v: float = 1.0,
|
| 79 |
+
num_heads: int = 4,
|
| 80 |
+
use_beta: bool = True,
|
| 81 |
+
use_gate: bool = False,
|
| 82 |
+
use_short_conv: bool = True,
|
| 83 |
+
conv_size: int = 4,
|
| 84 |
+
conv_bias: bool = False,
|
| 85 |
+
allow_neg_eigval: bool = False,
|
| 86 |
+
layer_idx: int = None,
|
| 87 |
+
qk_activation: str = 'silu',
|
| 88 |
+
qk_norm: str = 'l2',
|
| 89 |
+
norm_eps: float = 1e-5,
|
| 90 |
+
**kwargs,
|
| 91 |
+
) -> DeltaNet:
|
| 92 |
+
super().__init__()
|
| 93 |
+
|
| 94 |
+
self.mode = mode
|
| 95 |
+
self.qk_activation = qk_activation
|
| 96 |
+
self.qk_norm = qk_norm
|
| 97 |
+
|
| 98 |
+
assert self.qk_activation in ['silu', 'relu', 'elu', 'identity']
|
| 99 |
+
assert self.qk_norm in ['l2', 'sum']
|
| 100 |
+
|
| 101 |
+
if d_model is not None:
|
| 102 |
+
hidden_size = d_model
|
| 103 |
+
self.hidden_size = hidden_size
|
| 104 |
+
self.expand_k = expand_k
|
| 105 |
+
self.expand_v = expand_v
|
| 106 |
+
self.num_heads = num_heads
|
| 107 |
+
self.use_gate = use_gate
|
| 108 |
+
self.use_short_conv = use_short_conv
|
| 109 |
+
self.conv_size = conv_size
|
| 110 |
+
self.conv_bias = conv_bias
|
| 111 |
+
self.allow_neg_eigval = allow_neg_eigval
|
| 112 |
+
|
| 113 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 114 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 115 |
+
self.head_k_dim = self.key_dim // num_heads
|
| 116 |
+
self.head_v_dim = self.value_dim // num_heads
|
| 117 |
+
self.layer_idx = layer_idx
|
| 118 |
+
|
| 119 |
+
if mode == 'fused_chunk':
|
| 120 |
+
raise NotImplementedError("fused_chunk_delta_rule is now deprecated. Please use `chunk_delta_rule` instead.")
|
| 121 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 122 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 123 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 124 |
+
|
| 125 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 126 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 127 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 128 |
+
|
| 129 |
+
self.use_beta = use_beta
|
| 130 |
+
if self.use_beta:
|
| 131 |
+
self.b_proj = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 132 |
+
if use_short_conv:
|
| 133 |
+
self.conv_size = conv_size
|
| 134 |
+
self.q_conv1d = ShortConvolution(
|
| 135 |
+
hidden_size=self.key_dim,
|
| 136 |
+
kernel_size=conv_size,
|
| 137 |
+
bias=conv_bias,
|
| 138 |
+
activation='silu' if qk_activation == 'silu' else None,
|
| 139 |
+
)
|
| 140 |
+
self.k_conv1d = ShortConvolution(
|
| 141 |
+
hidden_size=self.key_dim,
|
| 142 |
+
kernel_size=conv_size,
|
| 143 |
+
bias=conv_bias,
|
| 144 |
+
activation='silu' if qk_activation == 'silu' else None,
|
| 145 |
+
)
|
| 146 |
+
self.v_conv1d = ShortConvolution(
|
| 147 |
+
hidden_size=self.value_dim,
|
| 148 |
+
kernel_size=conv_size,
|
| 149 |
+
bias=conv_bias,
|
| 150 |
+
activation='silu',
|
| 151 |
+
)
|
| 152 |
+
else:
|
| 153 |
+
warnings.warn(
|
| 154 |
+
"ShortConvolution is crucial to the performance. "
|
| 155 |
+
"Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
|
| 156 |
+
)
|
| 157 |
+
if use_gate:
|
| 158 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 159 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
|
| 160 |
+
else:
|
| 161 |
+
self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
|
| 162 |
+
|
| 163 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 164 |
+
|
| 165 |
+
def forward(
|
| 166 |
+
self,
|
| 167 |
+
hidden_states: torch.Tensor,
|
| 168 |
+
attention_mask: torch.Tensor | None = None,
|
| 169 |
+
past_key_values: Cache | None = None,
|
| 170 |
+
use_cache: bool | None = False,
|
| 171 |
+
output_attentions: bool | None = False,
|
| 172 |
+
**kwargs: Unpack[dict],
|
| 173 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 174 |
+
if attention_mask is not None:
|
| 175 |
+
assert len(attention_mask.shape) == 2, (
|
| 176 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 177 |
+
"for padding purposes (0 indicating padding). "
|
| 178 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 179 |
+
)
|
| 180 |
+
|
| 181 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 182 |
+
# change to inference mode.
|
| 183 |
+
mode = 'fused_recurrent' if q_len <= 64 else self.mode
|
| 184 |
+
|
| 185 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 186 |
+
|
| 187 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 188 |
+
if attention_mask is not None:
|
| 189 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 190 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 191 |
+
|
| 192 |
+
if self.use_short_conv:
|
| 193 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 194 |
+
if last_state is not None:
|
| 195 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 196 |
+
q, conv_state_q = self.q_conv1d(
|
| 197 |
+
x=self.q_proj(hidden_states),
|
| 198 |
+
cache=conv_state_q,
|
| 199 |
+
output_final_state=use_cache,
|
| 200 |
+
cu_seqlens=cu_seqlens,
|
| 201 |
+
)
|
| 202 |
+
k, conv_state_k = self.k_conv1d(
|
| 203 |
+
x=self.k_proj(hidden_states),
|
| 204 |
+
cache=conv_state_k,
|
| 205 |
+
output_final_state=use_cache,
|
| 206 |
+
cu_seqlens=cu_seqlens,
|
| 207 |
+
)
|
| 208 |
+
v, conv_state_v = self.v_conv1d(
|
| 209 |
+
x=self.v_proj(hidden_states),
|
| 210 |
+
cache=conv_state_v,
|
| 211 |
+
output_final_state=use_cache,
|
| 212 |
+
cu_seqlens=cu_seqlens,
|
| 213 |
+
)
|
| 214 |
+
else:
|
| 215 |
+
q = self.q_proj(hidden_states)
|
| 216 |
+
k = self.k_proj(hidden_states)
|
| 217 |
+
if self.qk_activation == 'silu':
|
| 218 |
+
q, k = F.silu(q), F.silu(k)
|
| 219 |
+
v = F.silu(self.v_proj(hidden_states))
|
| 220 |
+
|
| 221 |
+
q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
|
| 222 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 223 |
+
if self.qk_activation != 'silu':
|
| 224 |
+
if self.qk_activation == 'relu':
|
| 225 |
+
q, k = q.relu(), k.relu()
|
| 226 |
+
elif self.qk_activation == 'elu':
|
| 227 |
+
q, k = elu_p1(q), elu_p1(k)
|
| 228 |
+
elif self.qk_activation != 'identity':
|
| 229 |
+
raise NotImplementedError
|
| 230 |
+
|
| 231 |
+
if self.qk_norm == 'sum':
|
| 232 |
+
q = sum_norm(q).to(q)
|
| 233 |
+
k = sum_norm(k).to(k)
|
| 234 |
+
|
| 235 |
+
if self.use_beta:
|
| 236 |
+
beta = self.b_proj(hidden_states).sigmoid()
|
| 237 |
+
else:
|
| 238 |
+
beta = torch.ones_like(q[..., 0])
|
| 239 |
+
|
| 240 |
+
if self.allow_neg_eigval:
|
| 241 |
+
beta = beta * 2.
|
| 242 |
+
|
| 243 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 244 |
+
if mode == 'fused_recurrent':
|
| 245 |
+
o, recurrent_state = fused_recurrent_delta_rule(
|
| 246 |
+
q=q,
|
| 247 |
+
k=k,
|
| 248 |
+
v=v,
|
| 249 |
+
beta=beta,
|
| 250 |
+
initial_state=recurrent_state,
|
| 251 |
+
output_final_state=use_cache,
|
| 252 |
+
cu_seqlens=cu_seqlens,
|
| 253 |
+
use_qk_l2norm_in_kernel=(self.qk_norm == 'l2'),
|
| 254 |
+
)
|
| 255 |
+
elif mode == 'chunk':
|
| 256 |
+
o, recurrent_state = chunk_delta_rule(
|
| 257 |
+
q=q,
|
| 258 |
+
k=k,
|
| 259 |
+
v=v,
|
| 260 |
+
beta=beta,
|
| 261 |
+
initial_state=recurrent_state,
|
| 262 |
+
output_final_state=use_cache,
|
| 263 |
+
cu_seqlens=cu_seqlens,
|
| 264 |
+
use_qk_l2norm_in_kernel=(self.qk_norm == 'l2'),
|
| 265 |
+
)
|
| 266 |
+
else:
|
| 267 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 268 |
+
|
| 269 |
+
update_layer_cache(
|
| 270 |
+
self,
|
| 271 |
+
past_key_values,
|
| 272 |
+
recurrent_state=recurrent_state,
|
| 273 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 274 |
+
offset=q_len,
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
if self.use_gate:
|
| 278 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 279 |
+
o = self.o_norm(o, g)
|
| 280 |
+
else:
|
| 281 |
+
o = self.o_norm(o)
|
| 282 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 283 |
+
o = self.o_proj(o)
|
| 284 |
+
if attention_mask is not None:
|
| 285 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 286 |
+
|
| 287 |
+
return o, None, past_key_values
|
fla/layers/deltaformer.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from einops import rearrange
|
| 10 |
+
from transformers.utils import logging
|
| 11 |
+
|
| 12 |
+
from fla.modules import RMSNorm, RotaryEmbedding
|
| 13 |
+
from fla.ops.deltaformer import deltaformer_attn
|
| 14 |
+
from fla.ops.utils.index import prepare_lens_from_mask
|
| 15 |
+
|
| 16 |
+
if TYPE_CHECKING:
|
| 17 |
+
from fla.models.utils import Cache
|
| 18 |
+
|
| 19 |
+
logger = logging.get_logger(__name__)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class DeltaFormerAttention(nn.Module):
|
| 23 |
+
|
| 24 |
+
r"""
|
| 25 |
+
The layer implementation for DeltaFormer,
|
| 26 |
+
[Understanding Transformer from the Perspective of Associative Memory]
|
| 27 |
+
(https://arxiv.org/pdf/2505.19488).
|
| 28 |
+
|
| 29 |
+
Notes
|
| 30 |
+
- DeltaFormer attention is implemented with Triton kernels in `fla.ops.deltaformer` and is tuned
|
| 31 |
+
for typical head dimensions (e.g., 64/128). It currently supports fixed-length inputs.
|
| 32 |
+
- For variable-length inputs (padding masks), the deltaformer computation falls back to using the
|
| 33 |
+
fixed-length path, while the second stage (softmax attention over U) uses FlashAttention's
|
| 34 |
+
varlen path when an attention mask is provided.
|
| 35 |
+
- K/V grouping (GQA) is supported natively by FlashAttention via `num_kv_heads`.
|
| 36 |
+
- Uses K-K similarity in deltaformer computation instead of Q-K similarity for better performance.
|
| 37 |
+
|
| 38 |
+
Args:
|
| 39 |
+
hidden_size (int, Optional):
|
| 40 |
+
The hidden size of the input. Default: 2048.
|
| 41 |
+
num_heads (int, Optional):
|
| 42 |
+
The number of attention heads. Default: 32.
|
| 43 |
+
num_kv_heads (int, Optional):
|
| 44 |
+
The number of key/value heads for grouped-query attention. If None, equals `num_heads`.
|
| 45 |
+
Default: None.
|
| 46 |
+
qkv_bias (bool, Optional):
|
| 47 |
+
Whether to use bias for Q/K/V projections. Default: False.
|
| 48 |
+
qk_norm (bool, Optional):
|
| 49 |
+
Whether to apply per-head RMSNorm to Q and K before attention. Default: False.
|
| 50 |
+
rope_theta (float, Optional):
|
| 51 |
+
The base frequency for rotary position embedding. Default: 10000.
|
| 52 |
+
max_position_embeddings (int, Optional):
|
| 53 |
+
The maximum position embeddings. Default: None.
|
| 54 |
+
layer_idx (int, Optional):
|
| 55 |
+
The index of the layer (used for cache compatibility). Default: None.
|
| 56 |
+
"""
|
| 57 |
+
|
| 58 |
+
def __init__(
|
| 59 |
+
self,
|
| 60 |
+
hidden_size: int = 2048,
|
| 61 |
+
num_heads: int = 32,
|
| 62 |
+
num_kv_heads: int | None = None,
|
| 63 |
+
qkv_bias: bool = False,
|
| 64 |
+
qk_norm: bool = False,
|
| 65 |
+
rope_theta: float = 10000.,
|
| 66 |
+
max_position_embeddings: int | None = None,
|
| 67 |
+
layer_idx: int | None = None,
|
| 68 |
+
):
|
| 69 |
+
super().__init__()
|
| 70 |
+
|
| 71 |
+
self.hidden_size = hidden_size
|
| 72 |
+
self.num_heads = num_heads
|
| 73 |
+
self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
|
| 74 |
+
self.num_kv_groups = num_heads // self.num_kv_heads
|
| 75 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 76 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 77 |
+
self.qkv_bias = qkv_bias
|
| 78 |
+
self.qk_norm = qk_norm
|
| 79 |
+
self.rope_theta = rope_theta
|
| 80 |
+
self.max_position_embeddings = max_position_embeddings
|
| 81 |
+
self.layer_idx = layer_idx
|
| 82 |
+
|
| 83 |
+
self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
|
| 84 |
+
self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 85 |
+
self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 86 |
+
self.b_proj = nn.Linear(self.hidden_size, self.num_heads, bias=True)
|
| 87 |
+
self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 88 |
+
|
| 89 |
+
if qk_norm:
|
| 90 |
+
self.q_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 91 |
+
self.k_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 92 |
+
|
| 93 |
+
self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
|
| 94 |
+
|
| 95 |
+
def forward(
|
| 96 |
+
self,
|
| 97 |
+
hidden_states: torch.Tensor,
|
| 98 |
+
attention_mask: torch.LongTensor | None = None,
|
| 99 |
+
past_key_values: Cache | None = None,
|
| 100 |
+
output_attentions: bool = False,
|
| 101 |
+
use_cache: bool = False,
|
| 102 |
+
**kwargs,
|
| 103 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 104 |
+
attentions = None
|
| 105 |
+
if attention_mask is not None:
|
| 106 |
+
assert len(attention_mask.shape) == 2, (
|
| 107 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 108 |
+
"for padding purposes (0 indicating padding). "
|
| 109 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
batch_size, q_len, _ = hidden_states.size()
|
| 113 |
+
|
| 114 |
+
q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 115 |
+
k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 116 |
+
v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 117 |
+
beta = self.b_proj(hidden_states)
|
| 118 |
+
|
| 119 |
+
if self.qk_norm:
|
| 120 |
+
q, k = self.q_norm(q), self.k_norm(k)
|
| 121 |
+
|
| 122 |
+
cu_seqlens_kw = kwargs.get('cu_seqlens')
|
| 123 |
+
seqlen_offset, max_seqlen = 0, q_len
|
| 124 |
+
if past_key_values is not None:
|
| 125 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 126 |
+
max_seqlen = q_len + seqlen_offset
|
| 127 |
+
|
| 128 |
+
if attention_mask is not None:
|
| 129 |
+
seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
|
| 130 |
+
max_seqlen = q_len + max(seqlen_offset)
|
| 131 |
+
|
| 132 |
+
if self.max_position_embeddings is not None:
|
| 133 |
+
max_seqlen = max(max_seqlen, self.max_position_embeddings)
|
| 134 |
+
|
| 135 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens_kw)
|
| 136 |
+
|
| 137 |
+
o = deltaformer_attn(
|
| 138 |
+
q=q,
|
| 139 |
+
k=k,
|
| 140 |
+
v=v,
|
| 141 |
+
beta=beta,
|
| 142 |
+
attention_mask=attention_mask,
|
| 143 |
+
cu_seqlens=cu_seqlens_kw,
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
o = o.reshape(batch_size, q_len, -1)
|
| 147 |
+
o = self.o_proj(o)
|
| 148 |
+
|
| 149 |
+
if not output_attentions:
|
| 150 |
+
attentions = None
|
| 151 |
+
|
| 152 |
+
return o, attentions, past_key_values
|
fla/layers/forgetting_attn.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
import torch.utils.checkpoint
|
| 11 |
+
from einops import rearrange
|
| 12 |
+
from transformers.utils import logging
|
| 13 |
+
|
| 14 |
+
from fla.layers.utils import pad_input, unpad_input
|
| 15 |
+
from fla.modules import GroupNorm
|
| 16 |
+
from fla.ops.attn.decoding import attn_decoding_one_step
|
| 17 |
+
from fla.ops.forgetting_attn.parallel import parallel_forgetting_attn
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
from fla.models.utils import Cache
|
| 21 |
+
|
| 22 |
+
logger = logging.get_logger(__name__)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class ForgettingAttention(nn.Module):
|
| 26 |
+
|
| 27 |
+
def __init__(
|
| 28 |
+
self,
|
| 29 |
+
hidden_size: int = 2048,
|
| 30 |
+
num_heads: int = 32,
|
| 31 |
+
num_kv_heads: int | None = None,
|
| 32 |
+
qkv_bias: bool = False,
|
| 33 |
+
qk_norm: bool = False,
|
| 34 |
+
window_size: int | None = None,
|
| 35 |
+
use_output_gate: bool = False,
|
| 36 |
+
layer_idx: int = None,
|
| 37 |
+
):
|
| 38 |
+
super().__init__()
|
| 39 |
+
|
| 40 |
+
self.hidden_size = hidden_size
|
| 41 |
+
self.num_heads = num_heads
|
| 42 |
+
if num_kv_heads is None:
|
| 43 |
+
self.num_kv_heads = self.num_heads
|
| 44 |
+
else:
|
| 45 |
+
self.num_kv_heads = num_kv_heads
|
| 46 |
+
self.num_kv_groups = num_heads // self.num_kv_heads
|
| 47 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 48 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 49 |
+
self.qkv_bias = qkv_bias
|
| 50 |
+
self.qk_norm = qk_norm
|
| 51 |
+
|
| 52 |
+
self.window_size = window_size
|
| 53 |
+
self.use_output_gate = use_output_gate
|
| 54 |
+
self.layer_idx = layer_idx
|
| 55 |
+
|
| 56 |
+
self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
|
| 57 |
+
self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 58 |
+
self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 59 |
+
self.f_proj = nn.Linear(self.hidden_size, self.num_heads, bias=True)
|
| 60 |
+
|
| 61 |
+
if use_output_gate:
|
| 62 |
+
self.g_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 63 |
+
self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 64 |
+
|
| 65 |
+
if qk_norm:
|
| 66 |
+
self.q_norm = GroupNorm(
|
| 67 |
+
num_groups=self.num_heads,
|
| 68 |
+
hidden_size=self.hidden_size,
|
| 69 |
+
is_rms_norm=True,
|
| 70 |
+
)
|
| 71 |
+
self.k_norm = GroupNorm(
|
| 72 |
+
num_groups=self.num_kv_heads,
|
| 73 |
+
hidden_size=self.kv_dim,
|
| 74 |
+
is_rms_norm=True,
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
def forward(
|
| 78 |
+
self,
|
| 79 |
+
hidden_states: torch.Tensor,
|
| 80 |
+
attention_mask: torch.LongTensor | None = None,
|
| 81 |
+
past_key_values: Cache | None = None,
|
| 82 |
+
output_attentions: bool = False,
|
| 83 |
+
use_cache: bool = False,
|
| 84 |
+
**kwargs,
|
| 85 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 86 |
+
if attention_mask is not None:
|
| 87 |
+
assert len(attention_mask.shape) == 2, (
|
| 88 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 89 |
+
"for padding purposes (0 indicating padding). "
|
| 90 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
batch_size, q_len, _ = hidden_states.size()
|
| 94 |
+
|
| 95 |
+
q, k, v = self.q_proj(hidden_states), self.k_proj(hidden_states), self.v_proj(hidden_states)
|
| 96 |
+
f = F.logsigmoid(self.f_proj(hidden_states).float())
|
| 97 |
+
if self.qk_norm:
|
| 98 |
+
q, k = self.q_norm(q), self.k_norm(k)
|
| 99 |
+
|
| 100 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 101 |
+
if past_key_values is not None:
|
| 102 |
+
assert cu_seqlens is None, "cu_seqlens should not be provided when past_key_values is not None"
|
| 103 |
+
state = past_key_values.update(
|
| 104 |
+
attn_state=(k, v, f),
|
| 105 |
+
layer_idx=self.layer_idx,
|
| 106 |
+
offset=q_len,
|
| 107 |
+
cache_kwargs=dict(window_size=self.window_size),
|
| 108 |
+
)
|
| 109 |
+
k, v, f = state['attn_state']
|
| 110 |
+
|
| 111 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_dim)
|
| 112 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 113 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 114 |
+
|
| 115 |
+
if attention_mask is not None:
|
| 116 |
+
q, (k, v, f), indices_q, cu_seqlens, max_seq_lens = unpad_input(q, (k, v, f), attention_mask, q_len, keepdim=True)
|
| 117 |
+
_, cu_seqlens_k = cu_seqlens
|
| 118 |
+
cu_seqlens = cu_seqlens_k
|
| 119 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 120 |
+
if max_seqlen_q != max_seqlen_k:
|
| 121 |
+
assert max_seqlen_q == 1, "only support q_len == 1 for decoding"
|
| 122 |
+
o = attn_decoding_one_step(q, k, v, f, cu_seqlens=cu_seqlens)
|
| 123 |
+
else:
|
| 124 |
+
o = parallel_forgetting_attn(q, k, v, f, cu_seqlens=cu_seqlens)
|
| 125 |
+
else:
|
| 126 |
+
o = parallel_forgetting_attn(q, k, v, f, cu_seqlens=cu_seqlens)
|
| 127 |
+
if attention_mask is not None:
|
| 128 |
+
o = pad_input(o.squeeze(0), indices_q, batch_size, q_len)
|
| 129 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 130 |
+
if self.use_output_gate:
|
| 131 |
+
o = self.g_proj(hidden_states).sigmoid() * o
|
| 132 |
+
o = self.o_proj(o)
|
| 133 |
+
return o, None, past_key_values
|
fla/layers/gated_deltanet.py
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import math
|
| 6 |
+
import warnings
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
from einops import rearrange, repeat
|
| 12 |
+
from torch.nn import functional as F
|
| 13 |
+
|
| 14 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 15 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 16 |
+
from fla.ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from transformers.processing_utils import Unpack
|
| 20 |
+
|
| 21 |
+
from fla.models.utils import Cache
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@torch.compile
|
| 25 |
+
def elu_p1(x):
|
| 26 |
+
return (F.elu(x, 1., False) + 1.).to(x)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@torch.compile
|
| 30 |
+
def sum_norm(x):
|
| 31 |
+
return (x / x.sum(-1, keepdim=True)).to(x)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class GatedDeltaNet(nn.Module):
|
| 35 |
+
"""
|
| 36 |
+
The layer implementaion for [Gated Delta Networks: Improving Mamba2 with Delta Rule](https://arxiv.org/abs/2412.06464). # noqa
|
| 37 |
+
|
| 38 |
+
Similar to Mamba2, each layer contains around 6*hidden_size*hidden_size parameters.
|
| 39 |
+
|
| 40 |
+
Parameter alloation when use_gate=True:
|
| 41 |
+
- 0.75 * hidden_size * hidden_size for the q_proj and k_proj each
|
| 42 |
+
- 1.5 * hidden_size * hidden_size for the v_proj, g_proj and o_proj each
|
| 43 |
+
- Others are ignorably small.
|
| 44 |
+
- In total = 0.75 * 2 + 1.5 * 3 = 6 * hidden_size * hidden_size
|
| 45 |
+
NOTE: num_heads * head_dim = 0.75 * hidden_size, please make sure to set the correct num_heads and head_dim.
|
| 46 |
+
|
| 47 |
+
Parameter allocation when use_gate=False:
|
| 48 |
+
- 1 * hidden_size * hidden_size for the q_proj and k_proj each
|
| 49 |
+
- 2 * hidden_size * hidden_size for the v_proj and o_proj each
|
| 50 |
+
- Others are ignorably small.
|
| 51 |
+
- In total = 1 * 2 + 2 * 2 = 6 * hidden_size * hidden_size
|
| 52 |
+
|
| 53 |
+
Args:
|
| 54 |
+
hidden_size (int, Optional):
|
| 55 |
+
The hidden size of the input. Default: 2048.
|
| 56 |
+
expand_v (float, Optional):
|
| 57 |
+
The expansion ratio for the value dim. Default: 2.0.
|
| 58 |
+
head_dim (int, Optional):
|
| 59 |
+
The dimension of each head. Default: 256.
|
| 60 |
+
num_heads (int, Optional):
|
| 61 |
+
The number of heads. Default: 4.
|
| 62 |
+
num_v_heads (int, Optional):
|
| 63 |
+
The number of heads for the value projection, equal to `num_heads` if `None`.
|
| 64 |
+
GVA is applied if `num_v_heads` > `num_heads`. Default: `None`.
|
| 65 |
+
mode (str, Optional):
|
| 66 |
+
Which Gated DeltaNet kernel to use.
|
| 67 |
+
Currently available: `chunk` and `fused_recurrent`.
|
| 68 |
+
Default: `chunk`.
|
| 69 |
+
use_beta (bool, Optional):
|
| 70 |
+
Whether to use beta. Default: `True`.
|
| 71 |
+
use_gate (bool, Optional):
|
| 72 |
+
Whether to use output gate. Default: `True`.
|
| 73 |
+
use_short_conv (bool, Optional):
|
| 74 |
+
Whether to use short convolutions. Default: `True`.
|
| 75 |
+
allow_neg_eigval (bool, Optional):
|
| 76 |
+
Allow negative eigenvalues. Default: `False`. If set to `True`, the beta will be multiplied by 2.
|
| 77 |
+
See reference: [Unlocking State-Tracking in Linear RNNs Through Negative Eigenvalues](https://arxiv.org/abs/2411.12537)
|
| 78 |
+
conv_size (int, Optional):
|
| 79 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 80 |
+
conv_bias (bool, Optional):
|
| 81 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 82 |
+
layer_idx (int, Optional):
|
| 83 |
+
The index of the layer. Default: None.
|
| 84 |
+
norm_eps (float, Optional):
|
| 85 |
+
The epsilon value for the normalization layer. Default: 1e-5.
|
| 86 |
+
"""
|
| 87 |
+
|
| 88 |
+
def __init__(
|
| 89 |
+
self,
|
| 90 |
+
hidden_size: int = 2048,
|
| 91 |
+
expand_v: float = 2,
|
| 92 |
+
head_dim: int = 256,
|
| 93 |
+
num_heads: int = 6,
|
| 94 |
+
num_v_heads: int = None,
|
| 95 |
+
mode: str = 'chunk',
|
| 96 |
+
use_gate: bool = True,
|
| 97 |
+
use_short_conv: bool = True,
|
| 98 |
+
allow_neg_eigval: bool = False,
|
| 99 |
+
conv_size: int = 4,
|
| 100 |
+
conv_bias: bool = False,
|
| 101 |
+
layer_idx: int = None,
|
| 102 |
+
norm_eps: float = 1e-5,
|
| 103 |
+
**kwargs,
|
| 104 |
+
) -> GatedDeltaNet:
|
| 105 |
+
super().__init__()
|
| 106 |
+
|
| 107 |
+
self.mode = mode
|
| 108 |
+
self.allow_neg_eigval = allow_neg_eigval
|
| 109 |
+
self.hidden_size = hidden_size
|
| 110 |
+
self.expand_v = expand_v
|
| 111 |
+
|
| 112 |
+
self.use_gate = use_gate
|
| 113 |
+
self.use_short_conv = use_short_conv
|
| 114 |
+
self.conv_size = conv_size
|
| 115 |
+
self.conv_bias = conv_bias
|
| 116 |
+
|
| 117 |
+
self.head_dim = head_dim
|
| 118 |
+
self.num_heads = num_heads
|
| 119 |
+
self.num_v_heads = num_v_heads if num_v_heads is not None else num_heads
|
| 120 |
+
|
| 121 |
+
self.head_k_dim = head_dim
|
| 122 |
+
self.head_v_dim = int(self.head_dim * self.expand_v)
|
| 123 |
+
self.key_dim = int(self.num_heads * self.head_k_dim)
|
| 124 |
+
self.value_dim = int(self.num_v_heads * self.head_v_dim)
|
| 125 |
+
self.layer_idx = layer_idx
|
| 126 |
+
|
| 127 |
+
# Consistency check: Ensure expand_v produces integer values
|
| 128 |
+
if not math.isclose(self.num_v_heads * self.head_dim * expand_v, self.value_dim, rel_tol=1e-5):
|
| 129 |
+
raise ValueError(
|
| 130 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by key_dim={self.key_dim}. "
|
| 131 |
+
f"Resulting value_dim would be {self.num_v_heads * self.head_dim * expand_v}, which is invalid for nn.Linear.",
|
| 132 |
+
)
|
| 133 |
+
if self.num_v_heads > self.num_heads and self.num_v_heads % self.num_heads != 0:
|
| 134 |
+
raise ValueError(
|
| 135 |
+
f"num_v_heads={self.num_v_heads} must be divisible by num_heads={self.num_heads}.",
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
if not math.isclose(head_dim * expand_v, self.head_v_dim, rel_tol=1e-5):
|
| 139 |
+
raise ValueError(
|
| 140 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by head_dim={head_dim}. "
|
| 141 |
+
f"Resulting head_v_dim would be {head_dim * expand_v}, which is invalid for FusedRMSNormGated.",
|
| 142 |
+
)
|
| 143 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 144 |
+
|
| 145 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 146 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 147 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 148 |
+
self.a_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
|
| 149 |
+
self.b_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
|
| 150 |
+
|
| 151 |
+
A = torch.empty(self.num_v_heads, dtype=torch.float32).uniform_(0, 16)
|
| 152 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 153 |
+
self.A_log._no_weight_decay = True
|
| 154 |
+
# hard coded for now
|
| 155 |
+
dt_min = 0.001
|
| 156 |
+
dt_max = 0.1
|
| 157 |
+
dt_init_floor = 1e-4
|
| 158 |
+
dt = torch.exp(
|
| 159 |
+
torch.rand(self.num_v_heads) * (math.log(dt_max) - math.log(dt_min))
|
| 160 |
+
+ math.log(dt_min),
|
| 161 |
+
)
|
| 162 |
+
dt = torch.clamp(dt, min=dt_init_floor)
|
| 163 |
+
# Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
|
| 164 |
+
inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| 165 |
+
self.dt_bias = nn.Parameter(inv_dt)
|
| 166 |
+
# Just to be explicit. Without this we already don't put wd on dt_bias because of the check
|
| 167 |
+
# name.endswith("bias") in param_grouping.py
|
| 168 |
+
self.dt_bias._no_weight_decay = True
|
| 169 |
+
|
| 170 |
+
if use_short_conv:
|
| 171 |
+
self.conv_size = conv_size
|
| 172 |
+
self.q_conv1d = ShortConvolution(
|
| 173 |
+
hidden_size=self.key_dim,
|
| 174 |
+
kernel_size=conv_size,
|
| 175 |
+
bias=conv_bias,
|
| 176 |
+
activation='silu',
|
| 177 |
+
)
|
| 178 |
+
self.k_conv1d = ShortConvolution(
|
| 179 |
+
hidden_size=self.key_dim,
|
| 180 |
+
kernel_size=conv_size,
|
| 181 |
+
bias=conv_bias,
|
| 182 |
+
activation='silu',
|
| 183 |
+
)
|
| 184 |
+
self.v_conv1d = ShortConvolution(
|
| 185 |
+
hidden_size=self.value_dim,
|
| 186 |
+
kernel_size=conv_size,
|
| 187 |
+
bias=conv_bias,
|
| 188 |
+
activation='silu',
|
| 189 |
+
)
|
| 190 |
+
else:
|
| 191 |
+
warnings.warn(
|
| 192 |
+
"ShortConvolution is crucial to the performance. "
|
| 193 |
+
"Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
|
| 194 |
+
)
|
| 195 |
+
if use_gate:
|
| 196 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 197 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
|
| 198 |
+
else:
|
| 199 |
+
self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
|
| 200 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 201 |
+
|
| 202 |
+
def forward(
|
| 203 |
+
self,
|
| 204 |
+
hidden_states: torch.Tensor,
|
| 205 |
+
attention_mask: torch.Tensor | None = None,
|
| 206 |
+
past_key_values: Cache | None = None,
|
| 207 |
+
use_cache: bool | None = False,
|
| 208 |
+
output_attentions: bool | None = False,
|
| 209 |
+
**kwargs: Unpack[dict],
|
| 210 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 211 |
+
if attention_mask is not None:
|
| 212 |
+
assert len(attention_mask.shape) == 2, (
|
| 213 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 214 |
+
"for padding purposes (0 indicating padding). "
|
| 215 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 219 |
+
# change to inference mode.
|
| 220 |
+
mode = 'fused_recurrent' if (q_len <= 64 and not self.training) else self.mode
|
| 221 |
+
if self.training:
|
| 222 |
+
assert mode == 'chunk', "Only chunk mode is supported in training."
|
| 223 |
+
|
| 224 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 225 |
+
|
| 226 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 227 |
+
if attention_mask is not None:
|
| 228 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 229 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 230 |
+
|
| 231 |
+
if self.use_short_conv:
|
| 232 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 233 |
+
if last_state is not None:
|
| 234 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 235 |
+
q, conv_state_q = self.q_conv1d(
|
| 236 |
+
x=self.q_proj(hidden_states),
|
| 237 |
+
cache=conv_state_q,
|
| 238 |
+
output_final_state=use_cache,
|
| 239 |
+
cu_seqlens=cu_seqlens,
|
| 240 |
+
)
|
| 241 |
+
k, conv_state_k = self.k_conv1d(
|
| 242 |
+
x=self.k_proj(hidden_states),
|
| 243 |
+
cache=conv_state_k,
|
| 244 |
+
output_final_state=use_cache,
|
| 245 |
+
cu_seqlens=cu_seqlens,
|
| 246 |
+
)
|
| 247 |
+
v, conv_state_v = self.v_conv1d(
|
| 248 |
+
x=self.v_proj(hidden_states),
|
| 249 |
+
cache=conv_state_v,
|
| 250 |
+
output_final_state=use_cache,
|
| 251 |
+
cu_seqlens=cu_seqlens,
|
| 252 |
+
)
|
| 253 |
+
else:
|
| 254 |
+
q = F.silu(self.q_proj(hidden_states))
|
| 255 |
+
k = F.silu(self.k_proj(hidden_states))
|
| 256 |
+
v = F.silu(self.v_proj(hidden_states))
|
| 257 |
+
|
| 258 |
+
q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
|
| 259 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 260 |
+
|
| 261 |
+
if self.num_v_heads > self.num_heads:
|
| 262 |
+
q, k = map(lambda x: repeat(x, '... h d -> ... (h g) d', g=self.num_v_heads // self.num_heads), (q, k))
|
| 263 |
+
|
| 264 |
+
beta = self.b_proj(hidden_states).sigmoid()
|
| 265 |
+
if self.allow_neg_eigval:
|
| 266 |
+
beta = beta * 2.
|
| 267 |
+
|
| 268 |
+
g = -self.A_log.float().exp() * F.softplus(self.a_proj(hidden_states).float() + self.dt_bias)
|
| 269 |
+
|
| 270 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 271 |
+
if mode == 'chunk':
|
| 272 |
+
o, recurrent_state = chunk_gated_delta_rule(
|
| 273 |
+
q=q,
|
| 274 |
+
k=k,
|
| 275 |
+
v=v,
|
| 276 |
+
g=g,
|
| 277 |
+
beta=beta,
|
| 278 |
+
initial_state=recurrent_state,
|
| 279 |
+
output_final_state=use_cache,
|
| 280 |
+
cu_seqlens=cu_seqlens,
|
| 281 |
+
use_qk_l2norm_in_kernel=True,
|
| 282 |
+
)
|
| 283 |
+
elif mode == 'fused_recurrent':
|
| 284 |
+
o, recurrent_state = fused_recurrent_gated_delta_rule(
|
| 285 |
+
q=q,
|
| 286 |
+
k=k,
|
| 287 |
+
v=v,
|
| 288 |
+
g=g,
|
| 289 |
+
beta=beta,
|
| 290 |
+
initial_state=recurrent_state,
|
| 291 |
+
output_final_state=use_cache,
|
| 292 |
+
cu_seqlens=cu_seqlens,
|
| 293 |
+
use_qk_l2norm_in_kernel=True,
|
| 294 |
+
)
|
| 295 |
+
else:
|
| 296 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 297 |
+
|
| 298 |
+
update_layer_cache(
|
| 299 |
+
self,
|
| 300 |
+
past_key_values,
|
| 301 |
+
recurrent_state=recurrent_state,
|
| 302 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 303 |
+
offset=q_len,
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
+
if self.use_gate:
|
| 307 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 308 |
+
o = self.o_norm(o, g)
|
| 309 |
+
else:
|
| 310 |
+
o = self.o_norm(o)
|
| 311 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 312 |
+
o = self.o_proj(o)
|
| 313 |
+
if attention_mask is not None:
|
| 314 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 315 |
+
|
| 316 |
+
return o, None, past_key_values
|
fla/layers/gated_deltaproduct.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import math
|
| 6 |
+
import warnings
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
from einops import rearrange, repeat
|
| 12 |
+
from torch.nn import functional as F
|
| 13 |
+
|
| 14 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 15 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 16 |
+
from fla.ops.gated_delta_product import chunk_gated_delta_product
|
| 17 |
+
from fla.ops.gated_delta_rule import fused_recurrent_gated_delta_rule
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
from transformers.processing_utils import Unpack
|
| 21 |
+
|
| 22 |
+
from fla.models.utils import Cache
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class GatedDeltaProduct(nn.Module):
|
| 26 |
+
"""
|
| 27 |
+
Generalized version of GatedDoubleDeltaNet that supports arbitrary number of householder transformations.
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
def __init__(
|
| 31 |
+
self,
|
| 32 |
+
hidden_size: int = 2048,
|
| 33 |
+
expand_v: float = 2,
|
| 34 |
+
head_dim: int = 256,
|
| 35 |
+
num_heads: int = 6,
|
| 36 |
+
num_v_heads: int = None,
|
| 37 |
+
mode: str = 'chunk',
|
| 38 |
+
use_output_gate: bool = True,
|
| 39 |
+
use_short_conv: bool = True,
|
| 40 |
+
conv_size: int = 4,
|
| 41 |
+
conv_bias: bool = False,
|
| 42 |
+
layer_idx: int = None,
|
| 43 |
+
norm_eps: float = 1e-5,
|
| 44 |
+
use_forget_gate: bool = True,
|
| 45 |
+
allow_neg_eigval: bool = True,
|
| 46 |
+
num_householder: int = 2,
|
| 47 |
+
**kwargs,
|
| 48 |
+
) -> GatedDeltaProduct:
|
| 49 |
+
super().__init__()
|
| 50 |
+
|
| 51 |
+
self.mode = mode
|
| 52 |
+
|
| 53 |
+
self.hidden_size = hidden_size
|
| 54 |
+
self.expand_v = expand_v
|
| 55 |
+
|
| 56 |
+
self.use_forget_gate = use_forget_gate
|
| 57 |
+
self.allow_neg_eigval = allow_neg_eigval
|
| 58 |
+
self.num_householder = num_householder
|
| 59 |
+
self.use_output_gate = use_output_gate
|
| 60 |
+
self.use_short_conv = use_short_conv
|
| 61 |
+
self.conv_size = conv_size
|
| 62 |
+
self.conv_bias = conv_bias
|
| 63 |
+
|
| 64 |
+
self.head_dim = head_dim
|
| 65 |
+
self.num_heads = num_heads
|
| 66 |
+
self.num_v_heads = num_v_heads if num_v_heads is not None else num_heads
|
| 67 |
+
|
| 68 |
+
self.head_k_dim = head_dim
|
| 69 |
+
self.head_v_dim = int(self.head_dim * self.expand_v)
|
| 70 |
+
self.key_dim = int(self.num_heads * self.head_k_dim)
|
| 71 |
+
self.value_dim = int(self.num_v_heads * self.head_v_dim)
|
| 72 |
+
self.layer_idx = layer_idx
|
| 73 |
+
|
| 74 |
+
# Consistency check: Ensure expand_v produces integer values
|
| 75 |
+
if not math.isclose(self.num_v_heads * self.head_dim * expand_v, self.value_dim, rel_tol=1e-5):
|
| 76 |
+
raise ValueError(
|
| 77 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by key_dim={self.key_dim}. "
|
| 78 |
+
f"Resulting value_dim would be {self.num_v_heads * self.head_dim * expand_v}, which is invalid for nn.Linear.",
|
| 79 |
+
)
|
| 80 |
+
if self.num_v_heads > self.num_heads and self.num_v_heads % self.num_heads != 0:
|
| 81 |
+
raise ValueError(
|
| 82 |
+
f"num_v_heads={self.num_v_heads} must be divisible by num_heads={self.num_heads}.",
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
if not math.isclose(head_dim * expand_v, self.head_v_dim, rel_tol=1e-5):
|
| 86 |
+
raise ValueError(
|
| 87 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by head_dim={head_dim}. "
|
| 88 |
+
f"Resulting head_v_dim would be {head_dim * expand_v}, which is invalid for FusedRMSNormGated.",
|
| 89 |
+
)
|
| 90 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 91 |
+
|
| 92 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 93 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim * num_householder, bias=False)
|
| 94 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim * num_householder, bias=False)
|
| 95 |
+
self.b_proj = nn.Linear(hidden_size, self.num_v_heads * num_householder, bias=False)
|
| 96 |
+
|
| 97 |
+
if self.use_forget_gate:
|
| 98 |
+
self.a_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
|
| 99 |
+
A = torch.empty(self.num_v_heads, dtype=torch.float32).uniform_(0, 16)
|
| 100 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 101 |
+
self.A_log._no_weight_decay = True
|
| 102 |
+
# hard coded for now
|
| 103 |
+
dt_min = 0.001
|
| 104 |
+
dt_max = 0.1
|
| 105 |
+
dt_init_floor = 1e-4
|
| 106 |
+
dt = torch.exp(
|
| 107 |
+
torch.rand(self.num_v_heads) * (math.log(dt_max) - math.log(dt_min))
|
| 108 |
+
+ math.log(dt_min),
|
| 109 |
+
)
|
| 110 |
+
dt = torch.clamp(dt, min=dt_init_floor)
|
| 111 |
+
# Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
|
| 112 |
+
inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| 113 |
+
self.dt_bias = nn.Parameter(inv_dt)
|
| 114 |
+
# Just to be explicit. Without this we already don't put wd on dt_bias because of the check
|
| 115 |
+
# name.endswith("bias") in param_grouping.py
|
| 116 |
+
self.dt_bias._no_weight_decay = True
|
| 117 |
+
|
| 118 |
+
if use_short_conv:
|
| 119 |
+
self.conv_size = conv_size
|
| 120 |
+
self.q_conv1d = ShortConvolution(
|
| 121 |
+
hidden_size=self.key_dim,
|
| 122 |
+
kernel_size=conv_size,
|
| 123 |
+
bias=conv_bias,
|
| 124 |
+
activation='silu',
|
| 125 |
+
)
|
| 126 |
+
self.k_conv1d = ShortConvolution(
|
| 127 |
+
hidden_size=self.key_dim * num_householder,
|
| 128 |
+
kernel_size=conv_size,
|
| 129 |
+
bias=conv_bias,
|
| 130 |
+
activation='silu',
|
| 131 |
+
)
|
| 132 |
+
self.v_conv1d = ShortConvolution(
|
| 133 |
+
hidden_size=self.value_dim * num_householder,
|
| 134 |
+
kernel_size=conv_size,
|
| 135 |
+
bias=conv_bias,
|
| 136 |
+
activation='silu',
|
| 137 |
+
)
|
| 138 |
+
else:
|
| 139 |
+
warnings.warn(
|
| 140 |
+
"ShortConvolution is crucial to the performance. "
|
| 141 |
+
"Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
|
| 142 |
+
)
|
| 143 |
+
if use_output_gate:
|
| 144 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 145 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
|
| 146 |
+
else:
|
| 147 |
+
self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
|
| 148 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 149 |
+
|
| 150 |
+
def _initialize_weights(self, module: nn.Module):
|
| 151 |
+
if getattr(module, "_is_hf_initialized", False):
|
| 152 |
+
return
|
| 153 |
+
if isinstance(module, nn.Linear):
|
| 154 |
+
nn.init.xavier_uniform_(module.weight, gain=2 ** -2.5)
|
| 155 |
+
if module.bias is not None:
|
| 156 |
+
nn.init.zeros_(module.bias)
|
| 157 |
+
module._is_hf_initialized = True
|
| 158 |
+
|
| 159 |
+
def forward(
|
| 160 |
+
self,
|
| 161 |
+
hidden_states: torch.Tensor,
|
| 162 |
+
attention_mask: torch.Tensor | None = None,
|
| 163 |
+
past_key_values: Cache | None = None,
|
| 164 |
+
use_cache: bool | None = False,
|
| 165 |
+
output_attentions: bool | None = False,
|
| 166 |
+
**kwargs: Unpack[dict],
|
| 167 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 168 |
+
if attention_mask is not None:
|
| 169 |
+
assert len(attention_mask.shape) == 2, (
|
| 170 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 171 |
+
"for padding purposes (0 indicating padding). "
|
| 172 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 176 |
+
# change to inference mode.
|
| 177 |
+
mode = 'fused_recurrent' if (q_len <= 64 and not self.training) else self.mode
|
| 178 |
+
if self.training:
|
| 179 |
+
assert mode == 'chunk', "Only chunk mode is supported in training."
|
| 180 |
+
|
| 181 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 182 |
+
|
| 183 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 184 |
+
if attention_mask is not None:
|
| 185 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 186 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 187 |
+
|
| 188 |
+
if self.use_short_conv:
|
| 189 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 190 |
+
if last_state is not None:
|
| 191 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 192 |
+
q, conv_state_q = self.q_conv1d(
|
| 193 |
+
x=self.q_proj(hidden_states),
|
| 194 |
+
cache=conv_state_q,
|
| 195 |
+
output_final_state=use_cache,
|
| 196 |
+
cu_seqlens=cu_seqlens,
|
| 197 |
+
)
|
| 198 |
+
k, conv_state_k = self.k_conv1d(
|
| 199 |
+
x=self.k_proj(hidden_states),
|
| 200 |
+
cache=conv_state_k,
|
| 201 |
+
output_final_state=use_cache,
|
| 202 |
+
cu_seqlens=cu_seqlens,
|
| 203 |
+
)
|
| 204 |
+
v, conv_state_v = self.v_conv1d(
|
| 205 |
+
x=self.v_proj(hidden_states),
|
| 206 |
+
cache=conv_state_v,
|
| 207 |
+
output_final_state=use_cache,
|
| 208 |
+
cu_seqlens=cu_seqlens,
|
| 209 |
+
)
|
| 210 |
+
else:
|
| 211 |
+
q = F.silu(self.q_proj(hidden_states))
|
| 212 |
+
k = F.silu(self.k_proj(hidden_states))
|
| 213 |
+
v = F.silu(self.v_proj(hidden_states))
|
| 214 |
+
|
| 215 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 216 |
+
k = rearrange(k, '... t (n h d) -> ... (t n) h d', n=self.num_householder, d=self.head_k_dim)
|
| 217 |
+
v = rearrange(v, '... t (n h d) -> ... (t n) h d', n=self.num_householder, d=self.head_v_dim)
|
| 218 |
+
|
| 219 |
+
if self.num_v_heads > self.num_heads:
|
| 220 |
+
q, k = map(lambda x: repeat(x, '... h d -> ... (h g) d', g=self.num_v_heads // self.num_heads), (q, k))
|
| 221 |
+
|
| 222 |
+
beta = self.b_proj(hidden_states).sigmoid()
|
| 223 |
+
if self.allow_neg_eigval:
|
| 224 |
+
beta = beta * 2.
|
| 225 |
+
|
| 226 |
+
beta = rearrange(beta, '... t (n h) -> ... (t n) h', n=self.num_householder)
|
| 227 |
+
if self.use_forget_gate:
|
| 228 |
+
g = -self.A_log.float().exp() * F.softplus(self.a_proj(hidden_states).float() + self.dt_bias)
|
| 229 |
+
else:
|
| 230 |
+
g = None
|
| 231 |
+
|
| 232 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 233 |
+
if mode == 'chunk':
|
| 234 |
+
o, recurrent_state = chunk_gated_delta_product(
|
| 235 |
+
q=q,
|
| 236 |
+
k=k,
|
| 237 |
+
v=v,
|
| 238 |
+
g=g,
|
| 239 |
+
beta=beta,
|
| 240 |
+
initial_state=recurrent_state,
|
| 241 |
+
output_final_state=use_cache,
|
| 242 |
+
cu_seqlens=cu_seqlens,
|
| 243 |
+
num_householder=self.num_householder,
|
| 244 |
+
use_qk_l2norm_in_kernel=True,
|
| 245 |
+
)
|
| 246 |
+
|
| 247 |
+
elif mode == 'fused_recurrent':
|
| 248 |
+
if self.use_forget_gate:
|
| 249 |
+
g_new = g.new_zeros(g.shape[0], g.shape[1], self.num_householder, g.shape[2])
|
| 250 |
+
g_new[:, :, 0] = g
|
| 251 |
+
g = rearrange(g_new, '... t n h -> ... (t n) h')
|
| 252 |
+
|
| 253 |
+
q_new = q.new_zeros(q.shape[0], q.shape[1], self.num_householder, q.shape[2], q.shape[3])
|
| 254 |
+
q_new[:, :, -1] = q
|
| 255 |
+
q = rearrange(q_new, '... t n h d-> ... (t n) h d')
|
| 256 |
+
o, recurrent_state = fused_recurrent_gated_delta_rule(
|
| 257 |
+
q=q,
|
| 258 |
+
k=k,
|
| 259 |
+
v=v,
|
| 260 |
+
g=g,
|
| 261 |
+
beta=beta,
|
| 262 |
+
initial_state=recurrent_state,
|
| 263 |
+
output_final_state=use_cache,
|
| 264 |
+
cu_seqlens=cu_seqlens * self.num_householder if cu_seqlens is not None else None,
|
| 265 |
+
use_qk_l2norm_in_kernel=True,
|
| 266 |
+
)
|
| 267 |
+
o = rearrange(o, '... (t n) h d -> ... t n h d', n=self.num_householder)[..., -1, :, :].contiguous()
|
| 268 |
+
|
| 269 |
+
update_layer_cache(
|
| 270 |
+
self,
|
| 271 |
+
past_key_values,
|
| 272 |
+
recurrent_state=recurrent_state,
|
| 273 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 274 |
+
offset=q_len,
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
if self.use_output_gate:
|
| 278 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 279 |
+
o = self.o_norm(o, g)
|
| 280 |
+
else:
|
| 281 |
+
o = self.o_norm(o)
|
| 282 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 283 |
+
o = self.o_proj(o)
|
| 284 |
+
if attention_mask is not None:
|
| 285 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 286 |
+
|
| 287 |
+
return o, None, past_key_values
|
fla/layers/gla.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
from einops import rearrange, repeat
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 14 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 15 |
+
from fla.modules.activations import ACT2FN
|
| 16 |
+
from fla.ops.gla import chunk_gla, fused_chunk_gla, fused_recurrent_gla
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from transformers.processing_utils import Unpack
|
| 20 |
+
|
| 21 |
+
from fla.models.utils import Cache
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class GatedLinearAttention(nn.Module):
|
| 25 |
+
r"""
|
| 26 |
+
The layer implementaion for [Gated Linear Attention Transformers with Hardware-Efficient Training](https://arxiv.org/abs/2312.06635). # noqa
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
mode (str, Optional):
|
| 30 |
+
Which GLA kernel to use.
|
| 31 |
+
Currently available: `chunk`, `fused_recurrent`, and `fused_chunk`.
|
| 32 |
+
Default: `chunk`.
|
| 33 |
+
hidden_size (int, Optional):
|
| 34 |
+
The hidden size of the input. Default: 1024.
|
| 35 |
+
expand_k (float, Optional):
|
| 36 |
+
The expansion ratio for the key dim. Default: 0.5.
|
| 37 |
+
expand_v (float, Optional):
|
| 38 |
+
The expansion ratio for the value dim. Default: 1.0.
|
| 39 |
+
num_heads (int, Optional):
|
| 40 |
+
The number of heads. Default: 4.
|
| 41 |
+
num_kv_heads (int, Optional):
|
| 42 |
+
The number of key/value heads, used for MQA. Default: None.
|
| 43 |
+
feature_map (str, Optional):
|
| 44 |
+
Feature map function applied to queries/keys. Default: None.
|
| 45 |
+
use_short_conv (bool, Optional):
|
| 46 |
+
Whether to use short convolutions. Default: `False`.
|
| 47 |
+
conv_size (int, Optional):
|
| 48 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 49 |
+
conv_bias (bool, Optional):
|
| 50 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 51 |
+
use_output_gate (bool, Optional):
|
| 52 |
+
Whether to use output gate. Default: `True`.
|
| 53 |
+
gate_fn (str, Optional):
|
| 54 |
+
The activation function for the output gate. Default: `swish`.
|
| 55 |
+
elementwise_affine (bool, Optional):
|
| 56 |
+
If `True`, applies elementwise affine to LayerNorm with learnable parameters. Default: `True`.
|
| 57 |
+
norm_eps (float, Optional):
|
| 58 |
+
The epsilon value for the layernorm/rmsnorm layer. Default: 1e-5.
|
| 59 |
+
gate_logit_normalizer (int, Optional):
|
| 60 |
+
The normalizer for the gate logits, appied after `logsigmoid`. Default: 16.
|
| 61 |
+
gate_low_rank_dim (int, Optional):
|
| 62 |
+
The low rank dim for the gate projection. Default: 16.
|
| 63 |
+
clamp_min (float, Optional):
|
| 64 |
+
The minimum value for the gate logits. Default: None.
|
| 65 |
+
fuse_norm (bool, Optional):
|
| 66 |
+
Whether to fuse the norm and the output gate for better memory footprint. Default: `True`.
|
| 67 |
+
layer_idx (int, Optional):
|
| 68 |
+
The index of the layer. Default: None.
|
| 69 |
+
"""
|
| 70 |
+
|
| 71 |
+
def __init__(
|
| 72 |
+
self,
|
| 73 |
+
mode: str = 'chunk',
|
| 74 |
+
hidden_size: int = 1024,
|
| 75 |
+
expand_k: float = 0.5,
|
| 76 |
+
expand_v: float = 1.0,
|
| 77 |
+
num_heads: int = 4,
|
| 78 |
+
num_kv_heads: int | None = None,
|
| 79 |
+
feature_map: str | None = None,
|
| 80 |
+
use_short_conv: bool = False,
|
| 81 |
+
conv_size: int = 4,
|
| 82 |
+
conv_bias: bool = False,
|
| 83 |
+
use_output_gate: bool = True,
|
| 84 |
+
gate_fn: str = 'swish',
|
| 85 |
+
elementwise_affine: bool | None = True,
|
| 86 |
+
norm_eps: float = 1e-5,
|
| 87 |
+
gate_logit_normalizer: int = 16,
|
| 88 |
+
gate_low_rank_dim: int = 16,
|
| 89 |
+
clamp_min: float | None = None,
|
| 90 |
+
fuse_norm: bool = True,
|
| 91 |
+
layer_idx: int = None,
|
| 92 |
+
) -> GatedLinearAttention:
|
| 93 |
+
super().__init__()
|
| 94 |
+
|
| 95 |
+
self.mode = mode
|
| 96 |
+
self.hidden_size = hidden_size
|
| 97 |
+
self.expand_k = expand_k
|
| 98 |
+
self.expand_v = expand_v
|
| 99 |
+
self.num_heads = num_heads
|
| 100 |
+
self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
|
| 101 |
+
self.num_kv_groups = self.num_heads // self.num_kv_heads
|
| 102 |
+
self.feature_map_fn = ACT2FN[feature_map] if feature_map is not None else None
|
| 103 |
+
|
| 104 |
+
self.use_short_conv = use_short_conv
|
| 105 |
+
self.conv_size = conv_size
|
| 106 |
+
self.conv_bias = conv_bias
|
| 107 |
+
self.use_output_gate = use_output_gate
|
| 108 |
+
|
| 109 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 110 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 111 |
+
self.key_dim_per_group = self.key_dim // self.num_kv_groups
|
| 112 |
+
self.value_dim_per_group = self.value_dim // self.num_kv_groups
|
| 113 |
+
self.clamp_min = clamp_min
|
| 114 |
+
self.layer_idx = layer_idx
|
| 115 |
+
|
| 116 |
+
assert mode in ['chunk', 'fused_recurrent', 'fused_chunk'], f"Not supported mode `{mode}`."
|
| 117 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 118 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 119 |
+
|
| 120 |
+
self.head_k_dim = self.key_dim // num_heads
|
| 121 |
+
self.head_v_dim = self.value_dim // num_heads
|
| 122 |
+
|
| 123 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 124 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim_per_group, bias=False)
|
| 125 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim_per_group, bias=False)
|
| 126 |
+
if self.use_output_gate:
|
| 127 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 128 |
+
|
| 129 |
+
if use_short_conv:
|
| 130 |
+
self.conv_size = conv_size
|
| 131 |
+
self.q_conv1d = ShortConvolution(
|
| 132 |
+
hidden_size=self.key_dim,
|
| 133 |
+
kernel_size=conv_size,
|
| 134 |
+
bias=conv_bias,
|
| 135 |
+
activation='silu',
|
| 136 |
+
)
|
| 137 |
+
self.k_conv1d = ShortConvolution(
|
| 138 |
+
hidden_size=self.key_dim_per_group,
|
| 139 |
+
kernel_size=conv_size,
|
| 140 |
+
bias=conv_bias,
|
| 141 |
+
activation='silu',
|
| 142 |
+
)
|
| 143 |
+
self.v_conv1d = ShortConvolution(
|
| 144 |
+
hidden_size=self.value_dim_per_group,
|
| 145 |
+
kernel_size=conv_size,
|
| 146 |
+
bias=conv_bias,
|
| 147 |
+
activation='silu',
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
self.gk_proj = nn.Sequential(nn.Linear(hidden_size, gate_low_rank_dim, bias=False),
|
| 151 |
+
nn.Linear(gate_low_rank_dim, self.key_dim_per_group, bias=True))
|
| 152 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 153 |
+
|
| 154 |
+
if gate_fn == 'swish' and fuse_norm and use_output_gate:
|
| 155 |
+
self.g_norm_swish_gate = FusedRMSNormGated(
|
| 156 |
+
hidden_size=self.head_v_dim,
|
| 157 |
+
elementwise_affine=elementwise_affine,
|
| 158 |
+
eps=norm_eps,
|
| 159 |
+
)
|
| 160 |
+
self.fuse_norm_and_gate = True
|
| 161 |
+
else:
|
| 162 |
+
self.fuse_norm_and_gate = False
|
| 163 |
+
self.g_norm = RMSNorm(
|
| 164 |
+
hidden_size=self.head_v_dim,
|
| 165 |
+
elementwise_affine=elementwise_affine,
|
| 166 |
+
eps=norm_eps,
|
| 167 |
+
dtype=torch.float32
|
| 168 |
+
)
|
| 169 |
+
self.gate_fn = ACT2FN[gate_fn]
|
| 170 |
+
|
| 171 |
+
self.gate_logit_normalizer = gate_logit_normalizer
|
| 172 |
+
|
| 173 |
+
def reset_parameters(self) -> None:
|
| 174 |
+
for module in self.children():
|
| 175 |
+
reset = getattr(module, "reset_parameters", None)
|
| 176 |
+
if callable(reset):
|
| 177 |
+
reset()
|
| 178 |
+
|
| 179 |
+
def forward(
|
| 180 |
+
self,
|
| 181 |
+
hidden_states: torch.Tensor,
|
| 182 |
+
attention_mask: torch.Tensor | None = None,
|
| 183 |
+
past_key_values: Cache | None = None,
|
| 184 |
+
use_cache: bool | None = False,
|
| 185 |
+
output_attentions: bool | None = False,
|
| 186 |
+
**kwargs: Unpack[dict],
|
| 187 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 188 |
+
if attention_mask is not None:
|
| 189 |
+
assert len(attention_mask.shape) == 2, (
|
| 190 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 191 |
+
"for padding purposes (0 indicating padding). "
|
| 192 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 196 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 197 |
+
|
| 198 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 199 |
+
|
| 200 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 201 |
+
if attention_mask is not None:
|
| 202 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 203 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 204 |
+
|
| 205 |
+
if self.use_short_conv:
|
| 206 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 207 |
+
if last_state is not None:
|
| 208 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 209 |
+
q, conv_state_q = self.q_conv1d(
|
| 210 |
+
x=self.q_proj(hidden_states),
|
| 211 |
+
cache=conv_state_q,
|
| 212 |
+
output_final_state=use_cache,
|
| 213 |
+
cu_seqlens=cu_seqlens,
|
| 214 |
+
)
|
| 215 |
+
k, conv_state_k = self.k_conv1d(
|
| 216 |
+
x=self.k_proj(hidden_states),
|
| 217 |
+
cache=conv_state_k,
|
| 218 |
+
output_final_state=use_cache,
|
| 219 |
+
cu_seqlens=cu_seqlens,
|
| 220 |
+
)
|
| 221 |
+
v, conv_state_v = self.v_conv1d(
|
| 222 |
+
x=self.v_proj(hidden_states),
|
| 223 |
+
cache=conv_state_v,
|
| 224 |
+
output_final_state=use_cache,
|
| 225 |
+
cu_seqlens=cu_seqlens,
|
| 226 |
+
)
|
| 227 |
+
else:
|
| 228 |
+
q = self.q_proj(hidden_states)
|
| 229 |
+
k = self.k_proj(hidden_states)
|
| 230 |
+
v = self.v_proj(hidden_states)
|
| 231 |
+
gk = self.gk_proj(hidden_states)
|
| 232 |
+
|
| 233 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 234 |
+
if self.num_kv_groups > 1:
|
| 235 |
+
k, gk = (repeat(x, '... (h d) -> ... (h g) d', g=self.num_kv_groups, d=self.head_k_dim) for x in (k, gk))
|
| 236 |
+
v = repeat(v, '... (h d) -> ... (h g) d', g=self.num_kv_groups, d=self.head_v_dim)
|
| 237 |
+
else:
|
| 238 |
+
k, gk = (rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim) for x in (k, gk))
|
| 239 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 240 |
+
|
| 241 |
+
gk = F.logsigmoid(gk) / self.gate_logit_normalizer
|
| 242 |
+
if self.clamp_min is not None:
|
| 243 |
+
gk = torch.clamp_min(gk, self.clamp_min)
|
| 244 |
+
|
| 245 |
+
if self.feature_map_fn is not None:
|
| 246 |
+
q, k = map(self.feature_map_fn, (q, k))
|
| 247 |
+
|
| 248 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 249 |
+
if mode == 'fused_recurrent':
|
| 250 |
+
o, recurrent_state = fused_recurrent_gla(
|
| 251 |
+
q=q,
|
| 252 |
+
k=k,
|
| 253 |
+
v=v,
|
| 254 |
+
gk=gk,
|
| 255 |
+
initial_state=recurrent_state,
|
| 256 |
+
output_final_state=use_cache,
|
| 257 |
+
cu_seqlens=cu_seqlens,
|
| 258 |
+
)
|
| 259 |
+
elif mode == 'fused_chunk':
|
| 260 |
+
o, recurrent_state = fused_chunk_gla(
|
| 261 |
+
q=q,
|
| 262 |
+
k=k,
|
| 263 |
+
v=v,
|
| 264 |
+
g=gk,
|
| 265 |
+
initial_state=recurrent_state,
|
| 266 |
+
output_final_state=use_cache,
|
| 267 |
+
)
|
| 268 |
+
elif mode == 'chunk':
|
| 269 |
+
o, recurrent_state = chunk_gla(
|
| 270 |
+
q=q,
|
| 271 |
+
k=k,
|
| 272 |
+
v=v,
|
| 273 |
+
g=gk,
|
| 274 |
+
initial_state=recurrent_state,
|
| 275 |
+
output_final_state=use_cache,
|
| 276 |
+
cu_seqlens=cu_seqlens,
|
| 277 |
+
)
|
| 278 |
+
else:
|
| 279 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 280 |
+
|
| 281 |
+
update_layer_cache(
|
| 282 |
+
self,
|
| 283 |
+
past_key_values,
|
| 284 |
+
recurrent_state=recurrent_state,
|
| 285 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 286 |
+
offset=q_len,
|
| 287 |
+
)
|
| 288 |
+
|
| 289 |
+
if self.use_output_gate:
|
| 290 |
+
g = self.g_proj(hidden_states)
|
| 291 |
+
if self.fuse_norm_and_gate:
|
| 292 |
+
g = rearrange(g, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 293 |
+
o = self.g_norm_swish_gate(o, g)
|
| 294 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 295 |
+
else:
|
| 296 |
+
o = rearrange(self.g_norm(o), '... h d -> ... (h d)')
|
| 297 |
+
o = o * self.gate_fn(g)
|
| 298 |
+
else:
|
| 299 |
+
o = rearrange(self.g_norm(o), '... h d -> ... (h d)')
|
| 300 |
+
o = self.o_proj(o)
|
| 301 |
+
if attention_mask is not None:
|
| 302 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 303 |
+
|
| 304 |
+
return o, None, past_key_values
|
fla/layers/gsa.py
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
from einops import rearrange, repeat
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 14 |
+
from fla.modules import RMSNorm, ShortConvolution
|
| 15 |
+
from fla.modules.feature_map import ReLUFeatureMap, SwishFeatureMap, T2RFeatureMap
|
| 16 |
+
from fla.modules.layernorm import rms_norm_linear
|
| 17 |
+
from fla.ops.gsa import chunk_gsa, fused_recurrent_gsa
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
from transformers.processing_utils import Unpack
|
| 21 |
+
|
| 22 |
+
from fla.models.utils import Cache
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class GatedSlotAttention(nn.Module):
|
| 26 |
+
|
| 27 |
+
def __init__(
|
| 28 |
+
self,
|
| 29 |
+
mode: str = 'chunk',
|
| 30 |
+
hidden_size: int = 1024,
|
| 31 |
+
expand_k: float = 1.,
|
| 32 |
+
expand_v: float = 1.,
|
| 33 |
+
num_heads: int = 4,
|
| 34 |
+
num_kv_heads: int | None = None,
|
| 35 |
+
use_short_conv: bool = False,
|
| 36 |
+
conv_size: int = 4,
|
| 37 |
+
conv_bias: bool = False,
|
| 38 |
+
num_slots: int | None = None,
|
| 39 |
+
elementwise_affine: bool | None = True,
|
| 40 |
+
norm_eps: float = 1e-5,
|
| 41 |
+
gate_logit_normalizer: int = 8,
|
| 42 |
+
feature_map: str = 'swish',
|
| 43 |
+
use_output_gate: bool = False,
|
| 44 |
+
use_norm: bool = True,
|
| 45 |
+
layer_idx: int | None = None,
|
| 46 |
+
scale: float | None = 1.,
|
| 47 |
+
**kwargs,
|
| 48 |
+
) -> GatedSlotAttention:
|
| 49 |
+
super().__init__()
|
| 50 |
+
|
| 51 |
+
self.mode = mode
|
| 52 |
+
self.hidden_size = hidden_size
|
| 53 |
+
self.expand_k = expand_k
|
| 54 |
+
self.expand_v = expand_v
|
| 55 |
+
self.num_heads = num_heads
|
| 56 |
+
self.num_kv_heads = num_heads if num_kv_heads is None else num_kv_heads
|
| 57 |
+
self.num_kv_groups = self.num_heads // self.num_kv_heads
|
| 58 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 59 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 60 |
+
self.key_dim_per_group = self.key_dim // self.num_kv_groups
|
| 61 |
+
self.value_dim_per_group = self.value_dim // self.num_kv_groups
|
| 62 |
+
self.head_k_dim = self.key_dim // self.num_heads
|
| 63 |
+
self.head_v_dim = self.value_dim // self.num_heads
|
| 64 |
+
|
| 65 |
+
self.use_short_conv = use_short_conv
|
| 66 |
+
self.conv_size = conv_size
|
| 67 |
+
self.conv_bias = conv_bias
|
| 68 |
+
|
| 69 |
+
self.gate_logit_normalizer = gate_logit_normalizer
|
| 70 |
+
|
| 71 |
+
self.use_output_gate = use_output_gate
|
| 72 |
+
self.use_norm = use_norm
|
| 73 |
+
self.scale = scale
|
| 74 |
+
|
| 75 |
+
if num_slots is None:
|
| 76 |
+
num_slots = self.head_k_dim
|
| 77 |
+
self.num_slots = num_slots
|
| 78 |
+
|
| 79 |
+
self.layer_idx = layer_idx
|
| 80 |
+
|
| 81 |
+
if layer_idx is None:
|
| 82 |
+
warnings.warn(
|
| 83 |
+
f"Instantiating {self.__class__.__name__} without passing `layer_idx` is not recommended and will "
|
| 84 |
+
"to errors during the forward call, if caching is used. Please make sure to provide a `layer_idx` "
|
| 85 |
+
"when creating this class.",
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
self.register_module('feature_map', None)
|
| 89 |
+
if feature_map == 'swish':
|
| 90 |
+
self.feature_map = SwishFeatureMap()
|
| 91 |
+
elif feature_map == 'relu':
|
| 92 |
+
self.feature_map = ReLUFeatureMap()
|
| 93 |
+
elif feature_map == 't2r':
|
| 94 |
+
self.feature_map = T2RFeatureMap(self.head_k_dim, self.head_k_dim)
|
| 95 |
+
else:
|
| 96 |
+
raise NotImplementedError(f"Feature map `{feature_map}` is not supported now.")
|
| 97 |
+
|
| 98 |
+
self.q_proj = nn.Linear(self.hidden_size, self.key_dim, bias=False)
|
| 99 |
+
self.k_proj = nn.Linear(self.hidden_size, self.key_dim_per_group, bias=False)
|
| 100 |
+
self.v_proj = nn.Linear(self.hidden_size, self.value_dim_per_group, bias=False)
|
| 101 |
+
self.f_proj = nn.Linear(self.hidden_size, self.num_kv_heads * self.num_slots, bias=False)
|
| 102 |
+
|
| 103 |
+
if use_short_conv:
|
| 104 |
+
self.conv_size = conv_size
|
| 105 |
+
self.q_conv1d = ShortConvolution(
|
| 106 |
+
hidden_size=self.key_dim,
|
| 107 |
+
kernel_size=conv_size,
|
| 108 |
+
bias=conv_bias,
|
| 109 |
+
activation='silu',
|
| 110 |
+
)
|
| 111 |
+
self.k_conv1d = ShortConvolution(
|
| 112 |
+
hidden_size=self.key_dim_per_group,
|
| 113 |
+
kernel_size=conv_size,
|
| 114 |
+
bias=conv_bias,
|
| 115 |
+
activation='silu',
|
| 116 |
+
)
|
| 117 |
+
self.v_conv1d = ShortConvolution(
|
| 118 |
+
hidden_size=self.value_dim_per_group,
|
| 119 |
+
kernel_size=conv_size,
|
| 120 |
+
bias=conv_bias,
|
| 121 |
+
activation='silu',
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
self.g_norm = RMSNorm(self.hidden_size, elementwise_affine, eps=norm_eps, dtype=torch.float32)
|
| 125 |
+
self.o_proj = nn.Linear(self.value_dim, self.hidden_size, bias=False)
|
| 126 |
+
|
| 127 |
+
def forward(
|
| 128 |
+
self,
|
| 129 |
+
hidden_states: torch.Tensor,
|
| 130 |
+
attention_mask: torch.Tensor | None = None,
|
| 131 |
+
past_key_values: Cache | None = None,
|
| 132 |
+
use_cache: bool | None = False,
|
| 133 |
+
output_attentions: bool | None = False,
|
| 134 |
+
**kwargs: Unpack[dict],
|
| 135 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 136 |
+
if attention_mask is not None:
|
| 137 |
+
assert len(attention_mask.shape) == 2, (
|
| 138 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 139 |
+
"for padding purposes (0 indicating padding). "
|
| 140 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 144 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 145 |
+
|
| 146 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 147 |
+
|
| 148 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 149 |
+
if attention_mask is not None:
|
| 150 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 151 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 152 |
+
|
| 153 |
+
if self.use_short_conv:
|
| 154 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 155 |
+
if last_state is not None:
|
| 156 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 157 |
+
q, conv_state_q = self.q_conv1d(
|
| 158 |
+
x=self.q_proj(hidden_states),
|
| 159 |
+
cache=conv_state_q,
|
| 160 |
+
output_final_state=use_cache,
|
| 161 |
+
cu_seqlens=cu_seqlens,
|
| 162 |
+
)
|
| 163 |
+
k, conv_state_k = self.k_conv1d(
|
| 164 |
+
x=self.k_proj(hidden_states),
|
| 165 |
+
cache=conv_state_k,
|
| 166 |
+
output_final_state=use_cache,
|
| 167 |
+
cu_seqlens=cu_seqlens,
|
| 168 |
+
)
|
| 169 |
+
v, conv_state_v = self.v_conv1d(
|
| 170 |
+
x=self.v_proj(hidden_states),
|
| 171 |
+
cache=conv_state_v,
|
| 172 |
+
output_final_state=use_cache,
|
| 173 |
+
cu_seqlens=cu_seqlens,
|
| 174 |
+
)
|
| 175 |
+
else:
|
| 176 |
+
q = self.q_proj(hidden_states)
|
| 177 |
+
k = self.k_proj(hidden_states)
|
| 178 |
+
v = self.v_proj(hidden_states)
|
| 179 |
+
f = self.f_proj(hidden_states)
|
| 180 |
+
|
| 181 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 182 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 183 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 184 |
+
f = rearrange(f, '... (h m) -> ... h m', m=self.num_slots)
|
| 185 |
+
|
| 186 |
+
if self.feature_map is not None:
|
| 187 |
+
q, k = map(lambda x: self.feature_map(x), (q, k))
|
| 188 |
+
v = F.silu(v)
|
| 189 |
+
|
| 190 |
+
f = F.logsigmoid(f) / self.gate_logit_normalizer
|
| 191 |
+
s = (1 - f.exp()).to(f.dtype)
|
| 192 |
+
|
| 193 |
+
if self.num_kv_groups > 1:
|
| 194 |
+
k, v, f, s = map(lambda x: repeat(x, '... h d -> ... (h g) d', g=self.num_kv_groups), (k, v, f, s))
|
| 195 |
+
|
| 196 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 197 |
+
if mode == 'fused_recurrent':
|
| 198 |
+
o, recurrent_state = fused_recurrent_gsa(
|
| 199 |
+
q=q,
|
| 200 |
+
k=k,
|
| 201 |
+
v=v,
|
| 202 |
+
s=s,
|
| 203 |
+
g=f,
|
| 204 |
+
initial_state=recurrent_state,
|
| 205 |
+
output_final_state=use_cache,
|
| 206 |
+
scale=self.scale,
|
| 207 |
+
cu_seqlens=cu_seqlens,
|
| 208 |
+
)
|
| 209 |
+
elif mode == 'chunk':
|
| 210 |
+
o, recurrent_state = chunk_gsa(
|
| 211 |
+
q=q,
|
| 212 |
+
k=k,
|
| 213 |
+
v=v,
|
| 214 |
+
s=s,
|
| 215 |
+
g=f,
|
| 216 |
+
initial_state=recurrent_state,
|
| 217 |
+
output_final_state=use_cache,
|
| 218 |
+
scale=self.scale,
|
| 219 |
+
cu_seqlens=cu_seqlens,
|
| 220 |
+
)
|
| 221 |
+
else:
|
| 222 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 223 |
+
|
| 224 |
+
update_layer_cache(
|
| 225 |
+
self,
|
| 226 |
+
past_key_values,
|
| 227 |
+
recurrent_state=recurrent_state,
|
| 228 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 229 |
+
offset=q_len,
|
| 230 |
+
)
|
| 231 |
+
|
| 232 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 233 |
+
o = rms_norm_linear(F.silu(o), self.g_norm.weight, self.g_norm.bias, self.o_proj.weight, self.o_proj.bias)
|
| 234 |
+
if attention_mask is not None:
|
| 235 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 236 |
+
|
| 237 |
+
return o, None, past_key_values
|
fla/layers/hgrn.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
# "Hierarchically Gated Recurrent Neural Network for Sequence Modeling" [https://arxiv.org/abs/2311.04823]
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 14 |
+
from fla.modules import FusedRMSNormGated, ShortConvolution
|
| 15 |
+
from fla.modules.activations import swiglu
|
| 16 |
+
from fla.ops.hgrn import chunk_hgrn, fused_recurrent_hgrn
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from transformers.processing_utils import Unpack
|
| 20 |
+
|
| 21 |
+
from fla.models.utils import Cache
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class HGRNAttention(nn.Module):
|
| 25 |
+
|
| 26 |
+
def __init__(
|
| 27 |
+
self,
|
| 28 |
+
mode: str = 'chunk',
|
| 29 |
+
hidden_size: int = 1024,
|
| 30 |
+
expand_ratio: int | None = 1,
|
| 31 |
+
use_short_conv: bool = False,
|
| 32 |
+
conv_size: int = 4,
|
| 33 |
+
conv_bias: bool = False,
|
| 34 |
+
elementwise_affine: bool | None = True,
|
| 35 |
+
norm_eps: float = 1e-5,
|
| 36 |
+
layer_idx: int = None,
|
| 37 |
+
) -> HGRNAttention:
|
| 38 |
+
super().__init__()
|
| 39 |
+
|
| 40 |
+
self.mode = mode
|
| 41 |
+
self.hidden_size = hidden_size
|
| 42 |
+
self.expand_ratio = expand_ratio
|
| 43 |
+
self.input_dim = int(hidden_size * expand_ratio)
|
| 44 |
+
|
| 45 |
+
self.use_short_conv = use_short_conv
|
| 46 |
+
self.conv_size = conv_size
|
| 47 |
+
self.conv_bias = conv_bias
|
| 48 |
+
|
| 49 |
+
self.layer_idx = layer_idx
|
| 50 |
+
|
| 51 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 52 |
+
|
| 53 |
+
self.i_proj = nn.Linear(hidden_size, self.input_dim, bias=False)
|
| 54 |
+
self.f_proj = nn.Linear(hidden_size, self.input_dim, bias=False)
|
| 55 |
+
self.g_proj = nn.Linear(hidden_size, self.input_dim, bias=False)
|
| 56 |
+
|
| 57 |
+
if use_short_conv:
|
| 58 |
+
self.conv_size = conv_size
|
| 59 |
+
self.f_conv1d = ShortConvolution(
|
| 60 |
+
hidden_size=self.input_dim,
|
| 61 |
+
kernel_size=conv_size,
|
| 62 |
+
bias=conv_bias,
|
| 63 |
+
activation=None,
|
| 64 |
+
)
|
| 65 |
+
self.i_conv1d = ShortConvolution(
|
| 66 |
+
hidden_size=self.input_dim,
|
| 67 |
+
kernel_size=conv_size,
|
| 68 |
+
bias=conv_bias,
|
| 69 |
+
activation=None,
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
self.g_norm = FusedRMSNormGated(
|
| 73 |
+
hidden_size=self.input_dim,
|
| 74 |
+
elementwise_affine=elementwise_affine,
|
| 75 |
+
eps=norm_eps,
|
| 76 |
+
)
|
| 77 |
+
self.o_proj = nn.Linear(self.input_dim, hidden_size, bias=False)
|
| 78 |
+
|
| 79 |
+
def forward(
|
| 80 |
+
self,
|
| 81 |
+
hidden_states: torch.Tensor,
|
| 82 |
+
attention_mask: torch.Tensor | None = None,
|
| 83 |
+
past_key_values: Cache | None = None,
|
| 84 |
+
use_cache: bool | None = False,
|
| 85 |
+
output_attentions: bool | None = False,
|
| 86 |
+
lower_bound: torch.Tensor | None = None,
|
| 87 |
+
**kwargs: Unpack[dict],
|
| 88 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 89 |
+
if attention_mask is not None:
|
| 90 |
+
assert len(attention_mask.shape) == 2, (
|
| 91 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 92 |
+
"for padding purposes (0 indicating padding). "
|
| 93 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
# launching the triton kernel for just one token will actually be slower
|
| 97 |
+
mode = 'fused_recurrent' if not self.training and hidden_states.shape[1] <= 64 else self.mode
|
| 98 |
+
|
| 99 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 100 |
+
|
| 101 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 102 |
+
if self.use_short_conv:
|
| 103 |
+
conv_state_i, conv_state_f = None, None
|
| 104 |
+
if last_state is not None:
|
| 105 |
+
conv_state_i, conv_state_f = last_state['conv_state']
|
| 106 |
+
conv_mask = attention_mask[:, -hidden_states.shape[1]:] if attention_mask is not None else None
|
| 107 |
+
i, conv_state_i = self.i_conv1d(
|
| 108 |
+
x=self.i_proj(hidden_states),
|
| 109 |
+
mask=conv_mask,
|
| 110 |
+
cache=conv_state_i,
|
| 111 |
+
output_final_state=use_cache,
|
| 112 |
+
cu_seqlens=cu_seqlens,
|
| 113 |
+
)
|
| 114 |
+
f, conv_state_f = self.f_conv1d(
|
| 115 |
+
x=self.f_proj(hidden_states),
|
| 116 |
+
mask=conv_mask,
|
| 117 |
+
cache=conv_state_f,
|
| 118 |
+
output_final_state=use_cache,
|
| 119 |
+
cu_seqlens=cu_seqlens,
|
| 120 |
+
)
|
| 121 |
+
else:
|
| 122 |
+
i = self.i_proj(hidden_states)
|
| 123 |
+
f = self.f_proj(hidden_states)
|
| 124 |
+
|
| 125 |
+
f = F.logsigmoid(f)
|
| 126 |
+
# the lower bound for the first layer is zero
|
| 127 |
+
if lower_bound is not None and self.layer_idx > 0:
|
| 128 |
+
f = torch.logaddexp(lower_bound.log(), torch.log1p(-lower_bound) + f).to(f)
|
| 129 |
+
i = swiglu(i, 1 - f.exp())
|
| 130 |
+
|
| 131 |
+
# dealing with left-padding
|
| 132 |
+
if attention_mask is not None:
|
| 133 |
+
i = i.mul(attention_mask[:, -i.shape[-2]:, None])
|
| 134 |
+
|
| 135 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 136 |
+
if mode == 'chunk':
|
| 137 |
+
if cu_seqlens is not None:
|
| 138 |
+
raise NotImplementedError("Chunk mode does not support variable-length sequences.")
|
| 139 |
+
o, recurrent_state = chunk_hgrn(
|
| 140 |
+
x=i,
|
| 141 |
+
g=f,
|
| 142 |
+
initial_state=recurrent_state,
|
| 143 |
+
output_final_state=use_cache,
|
| 144 |
+
)
|
| 145 |
+
elif mode == 'fused_recurrent':
|
| 146 |
+
o, recurrent_state = fused_recurrent_hgrn(
|
| 147 |
+
x=i,
|
| 148 |
+
g=f,
|
| 149 |
+
initial_state=recurrent_state,
|
| 150 |
+
output_final_state=use_cache,
|
| 151 |
+
cu_seqlens=cu_seqlens,
|
| 152 |
+
)
|
| 153 |
+
else:
|
| 154 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 155 |
+
|
| 156 |
+
update_layer_cache(
|
| 157 |
+
self,
|
| 158 |
+
past_key_values,
|
| 159 |
+
recurrent_state=recurrent_state,
|
| 160 |
+
conv_state=(conv_state_i, conv_state_f) if self.use_short_conv else None,
|
| 161 |
+
offset=i.shape[1],
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
o = self.g_norm(o, self.g_proj(hidden_states))
|
| 165 |
+
o = self.o_proj(o)
|
| 166 |
+
|
| 167 |
+
return o, None, past_key_values
|
| 168 |
+
|
| 169 |
+
def state_size(self, **kwargs) -> int:
|
| 170 |
+
state_size = self.hidden_size
|
| 171 |
+
for module in self.children():
|
| 172 |
+
if isinstance(module, ShortConvolution):
|
| 173 |
+
state_size += module.state_size
|
| 174 |
+
return state_size
|
fla/layers/hgrn2.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
# "HGRN2: Gated Linear RNNs with State Expansion"[https://arxiv.org/abs/2404.07904]
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from einops import rearrange
|
| 13 |
+
|
| 14 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 15 |
+
from fla.modules import RMSNorm, ShortConvolution
|
| 16 |
+
from fla.modules.activations import swish
|
| 17 |
+
from fla.modules.layernorm import rms_norm_linear
|
| 18 |
+
from fla.ops.gla import chunk_gla, fused_chunk_gla, fused_recurrent_gla
|
| 19 |
+
|
| 20 |
+
if TYPE_CHECKING:
|
| 21 |
+
from transformers.processing_utils import Unpack
|
| 22 |
+
|
| 23 |
+
from fla.models.utils import Cache
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class HGRN2Attention(nn.Module):
|
| 27 |
+
|
| 28 |
+
def __init__(
|
| 29 |
+
self,
|
| 30 |
+
mode: str = 'chunk',
|
| 31 |
+
hidden_size: int = 1024,
|
| 32 |
+
num_heads: int | None = None,
|
| 33 |
+
expand_ratio: int | None = 128,
|
| 34 |
+
use_short_conv: bool = False,
|
| 35 |
+
conv_size: int = 4,
|
| 36 |
+
conv_bias: bool = False,
|
| 37 |
+
elementwise_affine: bool | None = True,
|
| 38 |
+
norm_eps: float = 1e-5,
|
| 39 |
+
layer_idx: int = None,
|
| 40 |
+
) -> HGRN2Attention:
|
| 41 |
+
super().__init__()
|
| 42 |
+
|
| 43 |
+
self.mode = mode
|
| 44 |
+
self.hidden_size = hidden_size
|
| 45 |
+
|
| 46 |
+
if expand_ratio is not None:
|
| 47 |
+
num_heads = hidden_size // expand_ratio
|
| 48 |
+
elif expand_ratio is None and num_heads is not None:
|
| 49 |
+
expand_ratio = hidden_size // num_heads
|
| 50 |
+
elif expand_ratio is None and num_heads is None:
|
| 51 |
+
raise RuntimeError("One of `expand_ratio` or `num_heads` should be provided.")
|
| 52 |
+
self.num_heads = num_heads
|
| 53 |
+
self.expand_ratio = expand_ratio
|
| 54 |
+
|
| 55 |
+
self.use_short_conv = use_short_conv
|
| 56 |
+
self.conv_size = conv_size
|
| 57 |
+
self.conv_bias = conv_bias
|
| 58 |
+
|
| 59 |
+
self.forget_dim = int(self.num_heads * self.expand_ratio)
|
| 60 |
+
self.input_dim = hidden_size
|
| 61 |
+
self.layer_idx = layer_idx
|
| 62 |
+
|
| 63 |
+
assert mode in ['chunk', 'fused_recurrent', 'fused_chunk'], f"Not supported mode `{mode}`."
|
| 64 |
+
assert self.forget_dim % num_heads == 0, f"forget dim must be divisible by num_heads of {num_heads}"
|
| 65 |
+
assert self.input_dim % num_heads == 0, f"input dim must be divisible by num_heads of {num_heads}"
|
| 66 |
+
|
| 67 |
+
self.head_f_dim = self.expand_ratio
|
| 68 |
+
self.head_i_dim = self.hidden_size // num_heads
|
| 69 |
+
|
| 70 |
+
self.q_proj = nn.Linear(hidden_size, self.forget_dim, bias=False)
|
| 71 |
+
self.f_proj = nn.Linear(hidden_size, self.forget_dim, bias=False)
|
| 72 |
+
self.i_proj = nn.Linear(hidden_size, self.input_dim, bias=False)
|
| 73 |
+
|
| 74 |
+
if use_short_conv:
|
| 75 |
+
self.conv_size = conv_size
|
| 76 |
+
self.q_conv1d = ShortConvolution(
|
| 77 |
+
hidden_size=self.forget_dim,
|
| 78 |
+
kernel_size=conv_size,
|
| 79 |
+
bias=conv_bias,
|
| 80 |
+
activation=None,
|
| 81 |
+
)
|
| 82 |
+
self.f_conv1d = ShortConvolution(
|
| 83 |
+
hidden_size=self.forget_dim,
|
| 84 |
+
kernel_size=conv_size,
|
| 85 |
+
bias=conv_bias,
|
| 86 |
+
activation=None,
|
| 87 |
+
)
|
| 88 |
+
self.i_conv1d = ShortConvolution(
|
| 89 |
+
hidden_size=self.input_dim,
|
| 90 |
+
kernel_size=conv_size,
|
| 91 |
+
bias=conv_bias,
|
| 92 |
+
activation=None,
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
self.g_norm = RMSNorm(hidden_size=self.hidden_size, elementwise_affine=elementwise_affine,
|
| 96 |
+
eps=norm_eps, dtype=torch.float32)
|
| 97 |
+
self.o_proj = nn.Linear(self.input_dim, hidden_size, bias=False)
|
| 98 |
+
|
| 99 |
+
def forward(
|
| 100 |
+
self,
|
| 101 |
+
hidden_states: torch.Tensor,
|
| 102 |
+
attention_mask: torch.Tensor | None = None,
|
| 103 |
+
past_key_values: Cache | None = None,
|
| 104 |
+
use_cache: bool | None = False,
|
| 105 |
+
output_attentions: bool | None = False,
|
| 106 |
+
lower_bound: torch.Tensor | None = None,
|
| 107 |
+
**kwargs: Unpack[dict],
|
| 108 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 109 |
+
if attention_mask is not None:
|
| 110 |
+
assert len(attention_mask.shape) == 2, (
|
| 111 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 112 |
+
"for padding purposes (0 indicating padding). "
|
| 113 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 117 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 118 |
+
|
| 119 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 120 |
+
|
| 121 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 122 |
+
if attention_mask is not None:
|
| 123 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 124 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 125 |
+
|
| 126 |
+
if self.use_short_conv:
|
| 127 |
+
conv_state_q, conv_state_f, conv_state_i = None, None, None
|
| 128 |
+
if last_state is not None:
|
| 129 |
+
conv_state_q, conv_state_f, conv_state_i = last_state['conv_state']
|
| 130 |
+
q, conv_state_q = self.q_conv1d(
|
| 131 |
+
x=self.q_proj(hidden_states),
|
| 132 |
+
cache=conv_state_q,
|
| 133 |
+
output_final_state=use_cache,
|
| 134 |
+
cu_seqlens=cu_seqlens,
|
| 135 |
+
)
|
| 136 |
+
f, conv_state_f = self.f_conv1d(
|
| 137 |
+
x=self.f_proj(hidden_states),
|
| 138 |
+
cache=conv_state_f,
|
| 139 |
+
output_final_state=use_cache,
|
| 140 |
+
cu_seqlens=cu_seqlens,
|
| 141 |
+
)
|
| 142 |
+
i, conv_state_i = self.i_conv1d(
|
| 143 |
+
x=self.i_proj(hidden_states),
|
| 144 |
+
cache=conv_state_i,
|
| 145 |
+
output_final_state=use_cache,
|
| 146 |
+
cu_seqlens=cu_seqlens,
|
| 147 |
+
)
|
| 148 |
+
else:
|
| 149 |
+
q = self.q_proj(hidden_states)
|
| 150 |
+
f = self.f_proj(hidden_states)
|
| 151 |
+
i = self.i_proj(hidden_states)
|
| 152 |
+
|
| 153 |
+
q = swish(q)
|
| 154 |
+
|
| 155 |
+
g = F.logsigmoid(f)
|
| 156 |
+
# the lower bound for the first layer is zero
|
| 157 |
+
if lower_bound is not None and self.layer_idx > 0:
|
| 158 |
+
g = torch.logaddexp(lower_bound.log(), torch.log1p(-lower_bound) + g)
|
| 159 |
+
k = 1 - g.exp()
|
| 160 |
+
|
| 161 |
+
q, k, g = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_f_dim), (q, k.to(i), g))
|
| 162 |
+
i = rearrange(i, '... (h d) -> ... h d', d=self.head_i_dim)
|
| 163 |
+
|
| 164 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 165 |
+
if mode == 'fused_recurrent':
|
| 166 |
+
o, recurrent_state = fused_recurrent_gla(
|
| 167 |
+
q=q,
|
| 168 |
+
k=k,
|
| 169 |
+
v=i,
|
| 170 |
+
gk=g,
|
| 171 |
+
initial_state=recurrent_state,
|
| 172 |
+
output_final_state=use_cache,
|
| 173 |
+
cu_seqlens=cu_seqlens,
|
| 174 |
+
)
|
| 175 |
+
elif mode == 'fused_chunk':
|
| 176 |
+
o, recurrent_state = fused_chunk_gla(
|
| 177 |
+
q=q,
|
| 178 |
+
k=k,
|
| 179 |
+
v=i,
|
| 180 |
+
g=g,
|
| 181 |
+
initial_state=recurrent_state,
|
| 182 |
+
output_final_state=use_cache,
|
| 183 |
+
)
|
| 184 |
+
elif mode == 'chunk':
|
| 185 |
+
o, recurrent_state = chunk_gla(
|
| 186 |
+
q=q,
|
| 187 |
+
k=k,
|
| 188 |
+
v=i,
|
| 189 |
+
g=g,
|
| 190 |
+
initial_state=recurrent_state,
|
| 191 |
+
output_final_state=use_cache,
|
| 192 |
+
cu_seqlens=cu_seqlens,
|
| 193 |
+
)
|
| 194 |
+
else:
|
| 195 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 196 |
+
|
| 197 |
+
update_layer_cache(
|
| 198 |
+
self,
|
| 199 |
+
past_key_values,
|
| 200 |
+
recurrent_state=recurrent_state,
|
| 201 |
+
conv_state=(conv_state_q, conv_state_f, conv_state_i) if self.use_short_conv else None,
|
| 202 |
+
offset=q_len,
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 206 |
+
o = rms_norm_linear(o, self.g_norm.weight, self.g_norm.bias, self.o_proj.weight, self.o_proj.bias)
|
| 207 |
+
if attention_mask is not None:
|
| 208 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 209 |
+
|
| 210 |
+
return o, None, past_key_values
|
fla/layers/kda.py
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import math
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange, repeat
|
| 11 |
+
from torch.nn import functional as F
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 14 |
+
from fla.modules import FusedRMSNormGated, ShortConvolution
|
| 15 |
+
from fla.ops.kda import chunk_kda, fused_recurrent_kda
|
| 16 |
+
from fla.ops.kda.gate import fused_kda_gate
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from transformers.processing_utils import Unpack
|
| 20 |
+
|
| 21 |
+
from fla.models.utils import Cache
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class KimiDeltaAttention(nn.Module):
|
| 25 |
+
"""
|
| 26 |
+
Kimi Delta Attention (KDA) layer implementation.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
hidden_size (int, Optional):
|
| 30 |
+
The hidden size of the input. Default: 2048.
|
| 31 |
+
expand_v (float, Optional):
|
| 32 |
+
The expansion ratio for the value dimension. Default: 1.0.
|
| 33 |
+
head_dim (int, Optional):
|
| 34 |
+
The dimension of each head. Default: 128.
|
| 35 |
+
num_heads (int, Optional):
|
| 36 |
+
The number of heads. Default: 16.
|
| 37 |
+
num_v_heads (int, Optional):
|
| 38 |
+
The number of heads for the value projection, equal to `num_heads` if `None`.
|
| 39 |
+
GVA (Grouped Value Attention) is applied if `num_v_heads` > `num_heads`. Default: `None`.
|
| 40 |
+
mode (str, Optional):
|
| 41 |
+
Which Kimi Delta Attention kernel to use.
|
| 42 |
+
Currently available: `chunk` and `fused_recurrent`.
|
| 43 |
+
Default: `chunk`.
|
| 44 |
+
use_short_conv (bool, Optional):
|
| 45 |
+
Whether to use short convolutions. Default: `True`.
|
| 46 |
+
allow_neg_eigval (bool, Optional):
|
| 47 |
+
Allow negative eigenvalues. Default: `False`. If set to `True`, the beta will be multiplied by 2.
|
| 48 |
+
See reference:
|
| 49 |
+
[Unlocking State-Tracking in Linear RNNs Through Negative Eigenvalues](https://arxiv.org/abs/2411.12537)
|
| 50 |
+
conv_size (int, Optional):
|
| 51 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 52 |
+
conv_bias (bool, Optional):
|
| 53 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 54 |
+
layer_idx (int, Optional):
|
| 55 |
+
The index of the layer. Default: None.
|
| 56 |
+
norm_eps (float, Optional):
|
| 57 |
+
The epsilon value for the normalization layer. Default: 1e-5.
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
def __init__(
|
| 61 |
+
self,
|
| 62 |
+
hidden_size: int = 2048,
|
| 63 |
+
expand_v: float = 1,
|
| 64 |
+
head_dim: int = 128,
|
| 65 |
+
num_heads: int = 16,
|
| 66 |
+
num_v_heads: int = None,
|
| 67 |
+
mode: str = "chunk",
|
| 68 |
+
use_short_conv: bool = True,
|
| 69 |
+
allow_neg_eigval: bool = False,
|
| 70 |
+
conv_size: int = 4,
|
| 71 |
+
conv_bias: bool = False,
|
| 72 |
+
layer_idx: int = None,
|
| 73 |
+
norm_eps: float = 1e-5,
|
| 74 |
+
**kwargs,
|
| 75 |
+
) -> KimiDeltaAttention:
|
| 76 |
+
super().__init__()
|
| 77 |
+
|
| 78 |
+
self.mode = mode
|
| 79 |
+
self.allow_neg_eigval = allow_neg_eigval
|
| 80 |
+
self.hidden_size = hidden_size
|
| 81 |
+
self.expand_v = expand_v
|
| 82 |
+
|
| 83 |
+
self.use_short_conv = use_short_conv
|
| 84 |
+
self.conv_size = conv_size
|
| 85 |
+
self.conv_bias = conv_bias
|
| 86 |
+
|
| 87 |
+
self.head_dim = head_dim
|
| 88 |
+
self.num_heads = num_heads
|
| 89 |
+
self.num_v_heads = num_v_heads if num_v_heads is not None else num_heads
|
| 90 |
+
|
| 91 |
+
self.head_k_dim = head_dim
|
| 92 |
+
self.head_v_dim = int(self.head_dim * self.expand_v)
|
| 93 |
+
self.key_dim = int(self.num_heads * self.head_k_dim)
|
| 94 |
+
self.value_dim = int(self.num_v_heads * self.head_v_dim)
|
| 95 |
+
self.layer_idx = layer_idx
|
| 96 |
+
|
| 97 |
+
# Consistency check: Ensure expand_v produces integer values
|
| 98 |
+
if not math.isclose(self.num_v_heads * self.head_dim * expand_v, self.value_dim, rel_tol=1e-5):
|
| 99 |
+
raise ValueError(
|
| 100 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by key_dim={self.key_dim}. "
|
| 101 |
+
f"Resulting value_dim would be {self.num_v_heads * self.head_dim * expand_v}, which is invalid for nn.Linear.",
|
| 102 |
+
)
|
| 103 |
+
if self.num_v_heads > self.num_heads and self.num_v_heads % self.num_heads != 0:
|
| 104 |
+
raise ValueError(
|
| 105 |
+
f"num_v_heads={self.num_v_heads} must be divisible by num_heads={self.num_heads}.",
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
if not math.isclose(head_dim * expand_v, self.head_v_dim, rel_tol=1e-5):
|
| 109 |
+
raise ValueError(
|
| 110 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by head_dim={head_dim}. "
|
| 111 |
+
f"Resulting head_v_dim would be {head_dim * expand_v}, which is invalid for FusedRMSNormGated.",
|
| 112 |
+
)
|
| 113 |
+
assert mode in ["chunk", "fused_recurrent"], f"Not supported mode `{mode}`."
|
| 114 |
+
|
| 115 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 116 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 117 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 118 |
+
|
| 119 |
+
if use_short_conv:
|
| 120 |
+
self.q_conv1d = ShortConvolution(
|
| 121 |
+
hidden_size=self.key_dim,
|
| 122 |
+
kernel_size=conv_size,
|
| 123 |
+
bias=conv_bias,
|
| 124 |
+
activation="silu",
|
| 125 |
+
)
|
| 126 |
+
self.k_conv1d = ShortConvolution(
|
| 127 |
+
hidden_size=self.key_dim,
|
| 128 |
+
kernel_size=conv_size,
|
| 129 |
+
bias=conv_bias,
|
| 130 |
+
activation="silu",
|
| 131 |
+
)
|
| 132 |
+
self.v_conv1d = ShortConvolution(
|
| 133 |
+
hidden_size=self.value_dim,
|
| 134 |
+
kernel_size=conv_size,
|
| 135 |
+
bias=conv_bias,
|
| 136 |
+
activation="silu",
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
self.f_proj = nn.Sequential(
|
| 140 |
+
nn.Linear(hidden_size, self.head_v_dim, bias=False),
|
| 141 |
+
nn.Linear(self.head_v_dim, self.key_dim, bias=False),
|
| 142 |
+
)
|
| 143 |
+
self.b_proj = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 144 |
+
|
| 145 |
+
self.A_log = nn.Parameter(torch.log(torch.empty(self.num_heads, dtype=torch.float32).uniform_(1, 16)))
|
| 146 |
+
self.A_log._no_weight_decay = True
|
| 147 |
+
dt = torch.exp(
|
| 148 |
+
torch.rand(self.key_dim, dtype=torch.float32) * (math.log(0.1) - math.log(0.001)) + math.log(0.001)
|
| 149 |
+
).clamp(min=1e-4)
|
| 150 |
+
inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| 151 |
+
self.dt_bias = nn.Parameter(inv_dt)
|
| 152 |
+
self.dt_bias._no_weight_decay = True
|
| 153 |
+
|
| 154 |
+
self.g_proj = nn.Sequential(
|
| 155 |
+
nn.Linear(hidden_size, self.head_v_dim, bias=False),
|
| 156 |
+
nn.Linear(self.head_v_dim, self.value_dim, bias=True),
|
| 157 |
+
)
|
| 158 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, activation="sigmoid", eps=norm_eps)
|
| 159 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 160 |
+
|
| 161 |
+
def forward(
|
| 162 |
+
self,
|
| 163 |
+
hidden_states: torch.Tensor,
|
| 164 |
+
attention_mask: torch.Tensor | None = None,
|
| 165 |
+
past_key_values: Cache | None = None,
|
| 166 |
+
use_cache: bool | None = False,
|
| 167 |
+
output_attentions: bool | None = False,
|
| 168 |
+
**kwargs: Unpack[dict],
|
| 169 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 170 |
+
if attention_mask is not None:
|
| 171 |
+
assert len(attention_mask.shape) == 2, (
|
| 172 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 173 |
+
"for padding purposes (0 indicating padding). "
|
| 174 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 178 |
+
# change to inference mode.
|
| 179 |
+
mode = "fused_recurrent" if (q_len <= 64 and not self.training) else self.mode
|
| 180 |
+
if self.training:
|
| 181 |
+
assert mode == "chunk", "Only chunk mode is supported in training."
|
| 182 |
+
|
| 183 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 184 |
+
|
| 185 |
+
cu_seqlens = kwargs.get("cu_seqlens")
|
| 186 |
+
if attention_mask is not None:
|
| 187 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 188 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 189 |
+
|
| 190 |
+
if self.use_short_conv:
|
| 191 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 192 |
+
if last_state is not None:
|
| 193 |
+
conv_state_q, conv_state_k, conv_state_v = last_state["conv_state"]
|
| 194 |
+
q, conv_state_q = self.q_conv1d(
|
| 195 |
+
x=self.q_proj(hidden_states),
|
| 196 |
+
cache=conv_state_q,
|
| 197 |
+
output_final_state=use_cache,
|
| 198 |
+
cu_seqlens=cu_seqlens,
|
| 199 |
+
)
|
| 200 |
+
k, conv_state_k = self.k_conv1d(
|
| 201 |
+
x=self.k_proj(hidden_states),
|
| 202 |
+
cache=conv_state_k,
|
| 203 |
+
output_final_state=use_cache,
|
| 204 |
+
cu_seqlens=cu_seqlens,
|
| 205 |
+
)
|
| 206 |
+
v, conv_state_v = self.v_conv1d(
|
| 207 |
+
x=self.v_proj(hidden_states),
|
| 208 |
+
cache=conv_state_v,
|
| 209 |
+
output_final_state=use_cache,
|
| 210 |
+
cu_seqlens=cu_seqlens,
|
| 211 |
+
)
|
| 212 |
+
else:
|
| 213 |
+
q = F.silu(self.q_proj(hidden_states))
|
| 214 |
+
k = F.silu(self.k_proj(hidden_states))
|
| 215 |
+
v = F.silu(self.v_proj(hidden_states))
|
| 216 |
+
|
| 217 |
+
g = self.f_proj(hidden_states)
|
| 218 |
+
beta = self.b_proj(hidden_states).sigmoid()
|
| 219 |
+
|
| 220 |
+
q, k, g = (rearrange(x, "... (h d) -> ... h d", d=self.head_k_dim) for x in (q, k, g))
|
| 221 |
+
v = rearrange(v, "... (h d) -> ... h d", d=self.head_v_dim)
|
| 222 |
+
|
| 223 |
+
# for multi-value attention, we repeat the inputs for simplicity.
|
| 224 |
+
if self.num_v_heads > self.num_heads:
|
| 225 |
+
q, k, g = (repeat(x, "... h d -> ... (h g) d", g=self.num_v_heads // self.num_heads) for x in (q, k, g))
|
| 226 |
+
beta = repeat(beta, "... h -> ... (h g)", g=self.num_v_heads // self.num_heads)
|
| 227 |
+
|
| 228 |
+
if self.allow_neg_eigval:
|
| 229 |
+
beta = beta * 2.0
|
| 230 |
+
|
| 231 |
+
recurrent_state = last_state["recurrent_state"] if last_state is not None else None
|
| 232 |
+
if mode == "chunk":
|
| 233 |
+
o, recurrent_state = chunk_kda(
|
| 234 |
+
q=q,
|
| 235 |
+
k=k,
|
| 236 |
+
v=v,
|
| 237 |
+
g=g,
|
| 238 |
+
beta=beta,
|
| 239 |
+
A_log=self.A_log,
|
| 240 |
+
dt_bias=self.dt_bias,
|
| 241 |
+
initial_state=recurrent_state,
|
| 242 |
+
output_final_state=use_cache,
|
| 243 |
+
use_qk_l2norm_in_kernel=True,
|
| 244 |
+
use_gate_in_kernel=True,
|
| 245 |
+
cu_seqlens=cu_seqlens,
|
| 246 |
+
)
|
| 247 |
+
elif mode == "fused_recurrent":
|
| 248 |
+
g = fused_kda_gate(g=g, A_log=self.A_log, dt_bias=self.dt_bias)
|
| 249 |
+
o, recurrent_state = fused_recurrent_kda(
|
| 250 |
+
q=q,
|
| 251 |
+
k=k,
|
| 252 |
+
v=v,
|
| 253 |
+
g=g,
|
| 254 |
+
beta=beta,
|
| 255 |
+
initial_state=recurrent_state,
|
| 256 |
+
output_final_state=use_cache,
|
| 257 |
+
use_qk_l2norm_in_kernel=True,
|
| 258 |
+
cu_seqlens=cu_seqlens,
|
| 259 |
+
)
|
| 260 |
+
else:
|
| 261 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 262 |
+
|
| 263 |
+
update_layer_cache(
|
| 264 |
+
self,
|
| 265 |
+
past_key_values,
|
| 266 |
+
recurrent_state=recurrent_state,
|
| 267 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 268 |
+
offset=q_len,
|
| 269 |
+
)
|
| 270 |
+
|
| 271 |
+
o = self.o_norm(o, rearrange(self.g_proj(hidden_states), "... (h d) -> ... h d", d=self.head_v_dim))
|
| 272 |
+
o = rearrange(o, "b t h d -> b t (h d)")
|
| 273 |
+
o = self.o_proj(o)
|
| 274 |
+
if attention_mask is not None:
|
| 275 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 276 |
+
|
| 277 |
+
return o, None, past_key_values
|
fla/layers/lightnet.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
# ["You Only Scan Once: Efficient Multi-dimension Sequential Modeling with LightNet"](https://arxiv.org/abs/2405.21022)
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from einops import rearrange
|
| 13 |
+
|
| 14 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 15 |
+
from fla.modules import FusedRMSNormGated, ShortConvolution
|
| 16 |
+
from fla.modules.fused_norm_gate import rms_norm_swish_gate_linear
|
| 17 |
+
from fla.ops.gla import chunk_gla, fused_recurrent_gla
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
from transformers.processing_utils import Unpack
|
| 21 |
+
|
| 22 |
+
from fla.models.utils import Cache
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class LightNetAttention(nn.Module):
|
| 26 |
+
|
| 27 |
+
def __init__(
|
| 28 |
+
self,
|
| 29 |
+
mode: str = 'chunk',
|
| 30 |
+
hidden_size: int = 1024,
|
| 31 |
+
num_heads: int | None = None,
|
| 32 |
+
expand_ratio: int | None = 128,
|
| 33 |
+
use_short_conv: bool = False,
|
| 34 |
+
conv_size: int = 4,
|
| 35 |
+
conv_bias: bool = False,
|
| 36 |
+
gate_low_rank_dim: int = 128,
|
| 37 |
+
elementwise_affine: bool | None = True,
|
| 38 |
+
norm_eps: float = 1e-5,
|
| 39 |
+
layer_idx: int = None,
|
| 40 |
+
) -> LightNetAttention:
|
| 41 |
+
super().__init__()
|
| 42 |
+
|
| 43 |
+
self.mode = mode
|
| 44 |
+
self.hidden_size = hidden_size
|
| 45 |
+
|
| 46 |
+
if expand_ratio is None and num_heads is not None:
|
| 47 |
+
expand_ratio = hidden_size // num_heads
|
| 48 |
+
elif expand_ratio is not None and num_heads is None:
|
| 49 |
+
num_heads = hidden_size // expand_ratio
|
| 50 |
+
elif expand_ratio is None and num_heads is None:
|
| 51 |
+
raise RuntimeError("One of `expand_ratio` or `num_heads` should be provided.")
|
| 52 |
+
self.num_heads = num_heads
|
| 53 |
+
self.expand_ratio = expand_ratio
|
| 54 |
+
|
| 55 |
+
self.use_short_conv = use_short_conv
|
| 56 |
+
self.conv_size = conv_size
|
| 57 |
+
self.conv_bias = conv_bias
|
| 58 |
+
|
| 59 |
+
self.key_dim = int(self.num_heads * self.expand_ratio)
|
| 60 |
+
self.value_dim = hidden_size
|
| 61 |
+
self.gate_low_rank_dim = gate_low_rank_dim
|
| 62 |
+
self.layer_idx = layer_idx
|
| 63 |
+
|
| 64 |
+
assert mode in ['chunk', 'fused_chunk'], f"Not supported mode `{mode}`."
|
| 65 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 66 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 67 |
+
|
| 68 |
+
self.head_f_dim = self.expand_ratio
|
| 69 |
+
self.head_i_dim = self.hidden_size // num_heads
|
| 70 |
+
|
| 71 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 72 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 73 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 74 |
+
|
| 75 |
+
if use_short_conv:
|
| 76 |
+
self.conv_size = conv_size
|
| 77 |
+
self.q_conv1d = ShortConvolution(
|
| 78 |
+
hidden_size=self.key_dim,
|
| 79 |
+
kernel_size=conv_size,
|
| 80 |
+
bias=conv_bias,
|
| 81 |
+
activation=None,
|
| 82 |
+
)
|
| 83 |
+
self.k_conv1d = ShortConvolution(
|
| 84 |
+
hidden_size=self.key_dim,
|
| 85 |
+
kernel_size=conv_size,
|
| 86 |
+
bias=conv_bias,
|
| 87 |
+
activation=None,
|
| 88 |
+
)
|
| 89 |
+
self.v_conv1d = ShortConvolution(
|
| 90 |
+
hidden_size=self.value_dim,
|
| 91 |
+
kernel_size=conv_size,
|
| 92 |
+
bias=conv_bias,
|
| 93 |
+
activation=None,
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
self.g_proj = nn.Sequential(
|
| 97 |
+
nn.Linear(hidden_size, gate_low_rank_dim, bias=False),
|
| 98 |
+
nn.Linear(gate_low_rank_dim, hidden_size, bias=False),
|
| 99 |
+
)
|
| 100 |
+
self.g_norm = FusedRMSNormGated(
|
| 101 |
+
hidden_size=hidden_size,
|
| 102 |
+
elementwise_affine=elementwise_affine,
|
| 103 |
+
eps=norm_eps,
|
| 104 |
+
)
|
| 105 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 106 |
+
|
| 107 |
+
def forward(
|
| 108 |
+
self,
|
| 109 |
+
hidden_states: torch.Tensor,
|
| 110 |
+
attention_mask: torch.Tensor | None = None,
|
| 111 |
+
past_key_values: Cache | None = None,
|
| 112 |
+
use_cache: bool | None = False,
|
| 113 |
+
output_attentions: bool | None = False,
|
| 114 |
+
**kwargs: Unpack[dict],
|
| 115 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 116 |
+
if attention_mask is not None:
|
| 117 |
+
assert len(attention_mask.shape) == 2, (
|
| 118 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 119 |
+
"for padding purposes (0 indicating padding). "
|
| 120 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
# launching the triton kernel for just one token will actually be slower
|
| 124 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 125 |
+
|
| 126 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 127 |
+
|
| 128 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 129 |
+
if self.use_short_conv:
|
| 130 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 131 |
+
if last_state is not None:
|
| 132 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 133 |
+
conv_mask = attention_mask[:, -hidden_states.shape[1]:] if attention_mask is not None else None
|
| 134 |
+
q, conv_state_q = self.q_conv1d(
|
| 135 |
+
x=self.q_proj(hidden_states),
|
| 136 |
+
mask=conv_mask,
|
| 137 |
+
cache=conv_state_q,
|
| 138 |
+
output_final_state=use_cache,
|
| 139 |
+
cu_seqlens=cu_seqlens,
|
| 140 |
+
)
|
| 141 |
+
k, conv_state_k = self.k_conv1d(
|
| 142 |
+
x=self.k_proj(hidden_states),
|
| 143 |
+
mask=conv_mask,
|
| 144 |
+
cache=conv_state_k,
|
| 145 |
+
output_final_state=use_cache,
|
| 146 |
+
cu_seqlens=cu_seqlens,
|
| 147 |
+
)
|
| 148 |
+
v, conv_state_v = self.v_conv1d(
|
| 149 |
+
x=self.v_proj(hidden_states),
|
| 150 |
+
mask=conv_mask,
|
| 151 |
+
cache=conv_state_v,
|
| 152 |
+
output_final_state=use_cache,
|
| 153 |
+
cu_seqlens=cu_seqlens,
|
| 154 |
+
)
|
| 155 |
+
else:
|
| 156 |
+
q = self.q_proj(hidden_states)
|
| 157 |
+
k = self.k_proj(hidden_states)
|
| 158 |
+
v = self.v_proj(hidden_states)
|
| 159 |
+
|
| 160 |
+
# dealing with left-padding
|
| 161 |
+
if attention_mask is not None:
|
| 162 |
+
v = v.mul(attention_mask[:, -v.shape[-2]:, None])
|
| 163 |
+
|
| 164 |
+
q = F.silu(q)
|
| 165 |
+
q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_f_dim), (q, k))
|
| 166 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_i_dim)
|
| 167 |
+
# TODO: this 2 steps took huge amount of time, which should be optimized
|
| 168 |
+
last_z = last_state['ffn_state'] if last_state is not None and last_state.get('ffn_state') is not None else None
|
| 169 |
+
if last_z is not None:
|
| 170 |
+
# Decode path: continue logcumsumexp from cached state
|
| 171 |
+
z = torch.logaddexp(last_z, k.float())
|
| 172 |
+
k, g = torch.exp(k - z).to(k.dtype), (last_z - z).to(k.dtype)
|
| 173 |
+
else:
|
| 174 |
+
# Prefill path: mask padding positions to -inf so they don't affect logcumsumexp
|
| 175 |
+
if cu_seqlens is not None:
|
| 176 |
+
raise NotImplementedError("LightNet does not support variable-length sequences for now.")
|
| 177 |
+
k_float = k.float()
|
| 178 |
+
if attention_mask is not None:
|
| 179 |
+
pad_mask = attention_mask[:, -k.shape[1]:, None, None] # (B, T, 1, 1)
|
| 180 |
+
k_for_z = k_float.masked_fill(pad_mask == 0, float('-inf'))
|
| 181 |
+
else:
|
| 182 |
+
k_for_z = k_float
|
| 183 |
+
z = k_for_z.logcumsumexp(1)
|
| 184 |
+
k_new = torch.exp(k_float - z)
|
| 185 |
+
g_new = torch.cat((z[:, :1], z[:, :-1]), 1) - z
|
| 186 |
+
# NaN/inf arise at fully-masked positions (-inf - (-inf)), zero them out
|
| 187 |
+
k = torch.nan_to_num(k_new, nan=0.0, posinf=0.0).to(k.dtype)
|
| 188 |
+
g = torch.nan_to_num(g_new, nan=0.0, posinf=0.0, neginf=0.0).to(k.dtype)
|
| 189 |
+
|
| 190 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 191 |
+
if mode == 'fused_recurrent':
|
| 192 |
+
o, recurrent_state = fused_recurrent_gla(
|
| 193 |
+
q=q,
|
| 194 |
+
k=k,
|
| 195 |
+
v=v,
|
| 196 |
+
gk=g,
|
| 197 |
+
initial_state=recurrent_state,
|
| 198 |
+
output_final_state=use_cache,
|
| 199 |
+
cu_seqlens=cu_seqlens,
|
| 200 |
+
)
|
| 201 |
+
elif mode == 'chunk':
|
| 202 |
+
o, recurrent_state = chunk_gla(
|
| 203 |
+
q=q,
|
| 204 |
+
k=k,
|
| 205 |
+
v=v,
|
| 206 |
+
g=g,
|
| 207 |
+
initial_state=recurrent_state,
|
| 208 |
+
output_final_state=use_cache,
|
| 209 |
+
cu_seqlens=cu_seqlens,
|
| 210 |
+
)
|
| 211 |
+
else:
|
| 212 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 213 |
+
|
| 214 |
+
update_layer_cache(
|
| 215 |
+
self,
|
| 216 |
+
past_key_values,
|
| 217 |
+
recurrent_state=recurrent_state,
|
| 218 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 219 |
+
ffn_state=z[:, -1:],
|
| 220 |
+
offset=q.shape[1],
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
o = rms_norm_swish_gate_linear(
|
| 224 |
+
rearrange(o, 'b t h d -> b t (h d)'),
|
| 225 |
+
self.g_proj(hidden_states),
|
| 226 |
+
self.g_norm.weight,
|
| 227 |
+
self.g_norm.bias,
|
| 228 |
+
self.o_proj.weight,
|
| 229 |
+
self.o_proj.bias,
|
| 230 |
+
)
|
| 231 |
+
return o, None, past_key_values
|
| 232 |
+
|
| 233 |
+
def state_size(self, **kwargs) -> int:
|
| 234 |
+
state_size = self.key_dim * self.head_i_dim
|
| 235 |
+
for module in self.children():
|
| 236 |
+
if isinstance(module, ShortConvolution):
|
| 237 |
+
state_size += module.state_size
|
| 238 |
+
return state_size
|
fla/layers/linear_attn.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
from einops import rearrange, repeat
|
| 11 |
+
|
| 12 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 13 |
+
from fla.modules import RMSNorm
|
| 14 |
+
from fla.modules.feature_map import DPFPFeatureMap, HadamardFeatureMap, HedgehogFeatureMap, T2RFeatureMap
|
| 15 |
+
from fla.ops.linear_attn import chunk_linear_attn, fused_chunk_linear_attn, fused_recurrent_linear_attn
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from fla.models.utils import Cache
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class LinearAttention(nn.Module):
|
| 22 |
+
|
| 23 |
+
def __init__(
|
| 24 |
+
self,
|
| 25 |
+
mode: str = 'chunk',
|
| 26 |
+
hidden_size: str = 1024,
|
| 27 |
+
expand_k: float = 1.0,
|
| 28 |
+
expand_v: float = 1.0,
|
| 29 |
+
num_heads: int = 8,
|
| 30 |
+
num_kv_heads: int | None = None,
|
| 31 |
+
feature_map: str = 'elementwise_product',
|
| 32 |
+
tie_feature_map_qk: bool = False,
|
| 33 |
+
output_norm: str = 'rmsnorm',
|
| 34 |
+
norm_q: bool = False,
|
| 35 |
+
norm_k: bool = False,
|
| 36 |
+
do_feature_map_norm: bool = False,
|
| 37 |
+
elementwise_affine: bool = True,
|
| 38 |
+
norm_eps: float = 1e-5,
|
| 39 |
+
layer_idx: int | None = None,
|
| 40 |
+
**kwargs,
|
| 41 |
+
):
|
| 42 |
+
super().__init__()
|
| 43 |
+
|
| 44 |
+
self.hidden_size = hidden_size
|
| 45 |
+
self.mode = mode
|
| 46 |
+
self.num_heads = num_heads
|
| 47 |
+
self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
|
| 48 |
+
self.num_kv_groups = self.num_heads // self.num_kv_heads
|
| 49 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 50 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 51 |
+
self.key_dim_per_group = self.key_dim // self.num_kv_groups
|
| 52 |
+
self.value_dim_per_group = self.value_dim // self.num_kv_groups
|
| 53 |
+
|
| 54 |
+
assert mode in ['chunk', 'fused_chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 55 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 56 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 57 |
+
|
| 58 |
+
self.head_k_dim = self.key_dim // num_heads
|
| 59 |
+
self.head_v_dim = self.value_dim // num_heads
|
| 60 |
+
self.do_feature_map_norm = do_feature_map_norm
|
| 61 |
+
self.layer_idx = layer_idx
|
| 62 |
+
|
| 63 |
+
if feature_map == 'hedgehog':
|
| 64 |
+
if tie_feature_map_qk:
|
| 65 |
+
self.feature_map_q = self.feature_map_k = HedgehogFeatureMap(head_dim=self.head_k_dim)
|
| 66 |
+
else:
|
| 67 |
+
self.feature_map_q = HedgehogFeatureMap(head_dim=self.head_k_dim)
|
| 68 |
+
self.feature_map_k = HedgehogFeatureMap(head_dim=self.head_k_dim)
|
| 69 |
+
|
| 70 |
+
elif feature_map == 't2r':
|
| 71 |
+
if tie_feature_map_qk:
|
| 72 |
+
self.feature_map_q = self.feature_map_k = T2RFeatureMap(head_dim=self.head_k_dim)
|
| 73 |
+
else:
|
| 74 |
+
self.feature_map_q = T2RFeatureMap(head_dim=self.head_k_dim)
|
| 75 |
+
self.feature_map_k = T2RFeatureMap(head_dim=self.head_k_dim)
|
| 76 |
+
|
| 77 |
+
elif feature_map == 'elementwise_product':
|
| 78 |
+
if tie_feature_map_qk:
|
| 79 |
+
self.feature_map_q = self.feature_map_k = HadamardFeatureMap(head_dim=self.head_k_dim)
|
| 80 |
+
else:
|
| 81 |
+
self.feature_map_q = HadamardFeatureMap(head_dim=self.head_k_dim)
|
| 82 |
+
self.feature_map_k = HadamardFeatureMap(head_dim=self.head_k_dim)
|
| 83 |
+
|
| 84 |
+
elif feature_map == 'dpfp':
|
| 85 |
+
self.feature_map_q = DPFPFeatureMap(head_dim=self.head_k_dim)
|
| 86 |
+
self.feature_map_k = DPFPFeatureMap(head_dim=self.head_k_dim)
|
| 87 |
+
|
| 88 |
+
elif feature_map == 'elu':
|
| 89 |
+
def elu(x):
|
| 90 |
+
return F.elu(x) + 1
|
| 91 |
+
self.feature_map_q = elu
|
| 92 |
+
self.feature_map_k = elu
|
| 93 |
+
|
| 94 |
+
elif feature_map == 'relu':
|
| 95 |
+
self.feature_map_q = nn.ReLU()
|
| 96 |
+
self.feature_map_k = nn.ReLU()
|
| 97 |
+
|
| 98 |
+
elif feature_map == 'identity':
|
| 99 |
+
self.feature_map_q = nn.Identity()
|
| 100 |
+
self.feature_map_k = nn.Identity()
|
| 101 |
+
else:
|
| 102 |
+
raise NotImplementedError(f"Not supported feature map `{feature_map}`.")
|
| 103 |
+
|
| 104 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 105 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim_per_group, bias=False)
|
| 106 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim_per_group, bias=False)
|
| 107 |
+
|
| 108 |
+
if output_norm == 'rmsnorm':
|
| 109 |
+
self.norm = RMSNorm(hidden_size=self.head_v_dim, elementwise_affine=elementwise_affine,
|
| 110 |
+
eps=norm_eps, dtype=torch.float32)
|
| 111 |
+
elif output_norm == 'identity':
|
| 112 |
+
self.norm = nn.Identity()
|
| 113 |
+
else:
|
| 114 |
+
raise NotImplementedError(f"Not supported output norm `{output_norm}`.")
|
| 115 |
+
|
| 116 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 117 |
+
|
| 118 |
+
self.norm_q = norm_q
|
| 119 |
+
self.norm_k = norm_k
|
| 120 |
+
|
| 121 |
+
def forward(
|
| 122 |
+
self,
|
| 123 |
+
hidden_states: torch.Tensor,
|
| 124 |
+
attention_mask: torch.Tensor | None = None,
|
| 125 |
+
past_key_values: Cache | None = None,
|
| 126 |
+
use_cache: bool | None = False,
|
| 127 |
+
output_attentions: bool | None = False,
|
| 128 |
+
**kwargs,
|
| 129 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 130 |
+
# Match other recurrent layers: use the recurrent kernel for decode/small chunks.
|
| 131 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 132 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 133 |
+
|
| 134 |
+
q = self.q_proj(hidden_states)
|
| 135 |
+
k = self.k_proj(hidden_states)
|
| 136 |
+
v = self.v_proj(hidden_states)
|
| 137 |
+
|
| 138 |
+
if attention_mask is not None:
|
| 139 |
+
v = v.mul(attention_mask[:, -v.shape[-2]:, None])
|
| 140 |
+
|
| 141 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 142 |
+
if self.num_kv_groups > 1:
|
| 143 |
+
k = repeat(k, '... (h d) -> ... (h g) d', d=self.head_k_dim, g=self.num_kv_groups)
|
| 144 |
+
v = repeat(v, '... (h d) -> ... (h g) d', d=self.head_v_dim, g=self.num_kv_groups)
|
| 145 |
+
else:
|
| 146 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 147 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 148 |
+
|
| 149 |
+
q = self.feature_map_q(q)
|
| 150 |
+
k = self.feature_map_k(k)
|
| 151 |
+
|
| 152 |
+
if self.norm_q:
|
| 153 |
+
q = q / (q.sum(-1, True) + 1e-4)
|
| 154 |
+
if self.norm_k:
|
| 155 |
+
k = k / (k.sum(-1, True) + 1e-4)
|
| 156 |
+
|
| 157 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 158 |
+
if mode == 'chunk':
|
| 159 |
+
o, final_state = chunk_linear_attn(
|
| 160 |
+
q=q,
|
| 161 |
+
k=k,
|
| 162 |
+
v=v,
|
| 163 |
+
initial_state=recurrent_state,
|
| 164 |
+
output_final_state=use_cache,
|
| 165 |
+
normalize=self.do_feature_map_norm,
|
| 166 |
+
)
|
| 167 |
+
elif mode == 'fused_chunk':
|
| 168 |
+
o, final_state = fused_chunk_linear_attn(
|
| 169 |
+
q=q,
|
| 170 |
+
k=k,
|
| 171 |
+
v=v,
|
| 172 |
+
initial_state=recurrent_state,
|
| 173 |
+
output_final_state=use_cache,
|
| 174 |
+
normalize=self.do_feature_map_norm,
|
| 175 |
+
)
|
| 176 |
+
elif mode == 'fused_recurrent':
|
| 177 |
+
o, final_state = fused_recurrent_linear_attn(
|
| 178 |
+
q=q,
|
| 179 |
+
k=k,
|
| 180 |
+
v=v,
|
| 181 |
+
initial_state=recurrent_state,
|
| 182 |
+
output_final_state=use_cache,
|
| 183 |
+
normalize=self.do_feature_map_norm,
|
| 184 |
+
)
|
| 185 |
+
else:
|
| 186 |
+
raise NotImplementedError
|
| 187 |
+
update_layer_cache(
|
| 188 |
+
self,
|
| 189 |
+
past_key_values,
|
| 190 |
+
recurrent_state=final_state,
|
| 191 |
+
offset=q.shape[1],
|
| 192 |
+
)
|
| 193 |
+
o = self.norm(o)
|
| 194 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 195 |
+
o = self.o_proj(o)
|
| 196 |
+
return o, None, past_key_values
|
fla/layers/log_linear_mamba2.py
ADDED
|
@@ -0,0 +1,684 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
from typing import TYPE_CHECKING
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
import torch.nn as nn
|
| 8 |
+
from einops import rearrange
|
| 9 |
+
from transformers.activations import ACT2FN
|
| 10 |
+
from transformers.utils import logging
|
| 11 |
+
|
| 12 |
+
from fla.layers.mamba2 import apply_mask_to_padding_states, causal_conv1d_fn, causal_conv1d_update, is_fast_path_available
|
| 13 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 14 |
+
from fla.modules.layernorm_gated import RMSNormGated, rmsnorm_fn
|
| 15 |
+
from fla.ops.log_linear_attn.chunk import LogLinearAttentionState, chunk_log_linear_attn
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from fla.models.utils import Cache
|
| 19 |
+
|
| 20 |
+
logger = logging.get_logger(__name__)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def ceil_log(x: int, b: int) -> int:
|
| 24 |
+
return math.ceil(math.log(x, b))
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def get_num_levels(length: int, base: int) -> int:
|
| 28 |
+
return ceil_log(length, base) + 1
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
MAX_SEQUENCE_LENGTH = 2048 * 8
|
| 32 |
+
LAMBDA_LEVEL_BASE = 2
|
| 33 |
+
MAX_NUM_LEVELS = get_num_levels(length=MAX_SEQUENCE_LENGTH, base=LAMBDA_LEVEL_BASE)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def hmamba_chunk_scan_combined(
|
| 37 |
+
x: torch.Tensor,
|
| 38 |
+
dt: torch.Tensor,
|
| 39 |
+
A: torch.Tensor,
|
| 40 |
+
B: torch.Tensor,
|
| 41 |
+
C: torch.Tensor,
|
| 42 |
+
dl: torch.Tensor,
|
| 43 |
+
L: torch.Tensor,
|
| 44 |
+
chunk_size: int,
|
| 45 |
+
D: torch.Tensor | None = None,
|
| 46 |
+
z: torch.Tensor | None = None,
|
| 47 |
+
dt_bias: torch.Tensor | None = None,
|
| 48 |
+
initial_states: LogLinearAttentionState | None = None,
|
| 49 |
+
seq_idx: torch.Tensor | None = None,
|
| 50 |
+
cu_seqlens: torch.Tensor | None = None,
|
| 51 |
+
dt_softplus: bool = False,
|
| 52 |
+
dt_limit: tuple[float, float] = (0.0, float("inf")),
|
| 53 |
+
return_final_states: bool = False,
|
| 54 |
+
):
|
| 55 |
+
if z is not None:
|
| 56 |
+
raise NotImplementedError
|
| 57 |
+
if seq_idx is not None:
|
| 58 |
+
raise NotImplementedError
|
| 59 |
+
if cu_seqlens is not None:
|
| 60 |
+
raise NotImplementedError
|
| 61 |
+
if dt_softplus is not True:
|
| 62 |
+
raise NotImplementedError
|
| 63 |
+
if tuple(dt_limit) != (0.0, float("inf")):
|
| 64 |
+
raise NotImplementedError
|
| 65 |
+
if chunk_size != 64:
|
| 66 |
+
raise NotImplementedError
|
| 67 |
+
if not B.shape == C.shape:
|
| 68 |
+
raise ValueError("B and C must have the same shape")
|
| 69 |
+
|
| 70 |
+
if D is not None:
|
| 71 |
+
if D.dim() != 1:
|
| 72 |
+
raise ValueError
|
| 73 |
+
D = rearrange(D, "h -> 1 1 h 1")
|
| 74 |
+
D_residual = x * D
|
| 75 |
+
|
| 76 |
+
if dt_bias is not None:
|
| 77 |
+
dt = dt + rearrange(dt_bias, "h -> 1 1 h")
|
| 78 |
+
if dt_softplus:
|
| 79 |
+
dt = torch.nn.functional.softplus(dt)
|
| 80 |
+
if dt_limit != (0.0, float("inf")):
|
| 81 |
+
dt = torch.clamp(dt, min=dt_limit[0], max=dt_limit[1])
|
| 82 |
+
x = (x * rearrange(dt, "b l h -> b l h 1")).to(x.dtype)
|
| 83 |
+
A = rearrange(A, "h -> 1 1 h") * dt
|
| 84 |
+
|
| 85 |
+
L = torch.nn.functional.softplus(rearrange(L, "h ell -> 1 1 h ell") * dl).to(L.dtype)
|
| 86 |
+
|
| 87 |
+
y, state = chunk_log_linear_attn(
|
| 88 |
+
q=C,
|
| 89 |
+
k=B,
|
| 90 |
+
v=x,
|
| 91 |
+
g=A,
|
| 92 |
+
level_scales=L,
|
| 93 |
+
initial_state=initial_states,
|
| 94 |
+
output_final_state=return_final_states,
|
| 95 |
+
cu_seqlens=cu_seqlens,
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
if D is not None:
|
| 99 |
+
y = y + D_residual
|
| 100 |
+
|
| 101 |
+
return y, state
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def hmamba_split_conv1d_scan_combined(
|
| 105 |
+
zxbcdtdl: torch.Tensor,
|
| 106 |
+
conv1d_weight: torch.Tensor,
|
| 107 |
+
conv1d_bias: torch.Tensor,
|
| 108 |
+
dt_bias: torch.Tensor,
|
| 109 |
+
A: torch.Tensor,
|
| 110 |
+
L: torch.Tensor,
|
| 111 |
+
D: torch.Tensor,
|
| 112 |
+
chunk_size: int,
|
| 113 |
+
initial_states: torch.Tensor | None = None,
|
| 114 |
+
seq_idx: torch.Tensor | None = None,
|
| 115 |
+
dt_limit: tuple[float, float] = (0.0, float("inf")),
|
| 116 |
+
return_final_states: bool = False,
|
| 117 |
+
activation: str = "silu",
|
| 118 |
+
rmsnorm_weight: torch.Tensor | None = None,
|
| 119 |
+
rmsnorm_eps: float = 1e-6,
|
| 120 |
+
outproj_weight: torch.Tensor | None = None,
|
| 121 |
+
outproj_bias: torch.Tensor | None = None,
|
| 122 |
+
headdim: int | None = None,
|
| 123 |
+
ngroups: int = 1,
|
| 124 |
+
norm_before_gate: bool = True,
|
| 125 |
+
conv1d_fn=None,
|
| 126 |
+
conv_backend: str = "cuda",
|
| 127 |
+
) -> torch.Tensor:
|
| 128 |
+
"""
|
| 129 |
+
Argument:
|
| 130 |
+
zxbcdtdl: (batch, seqlen, 2 * dim + 2 * ngroups * dstate + nheads) where dim == nheads * headdim
|
| 131 |
+
conv1d_weight: (dim + 2 * ngroups * dstate, width)
|
| 132 |
+
conv1d_bias: (dim + 2 * ngroups * dstate,)
|
| 133 |
+
dt_bias: (nheads,)
|
| 134 |
+
A: (nheads)
|
| 135 |
+
L: (nheads, nlevels)
|
| 136 |
+
D: (nheads, headdim) or (nheads,)
|
| 137 |
+
initial_states: (batch, nheads, headdim, dstate)
|
| 138 |
+
seq_idx: (batch, seqlen), int32
|
| 139 |
+
rmsnorm_weight: (dim,)
|
| 140 |
+
outproj_weight: (out_dim, dim)
|
| 141 |
+
outproj_bias: (out_dim,)
|
| 142 |
+
headdim: if D is 1D, headdim must be passed in
|
| 143 |
+
norm_before_gate: if True, we do RMSNorm(x) * F.silu(z). If False, we do RMSNorm(x * F.silu(z))
|
| 144 |
+
Return:
|
| 145 |
+
out: (batch, seqlen, dim)
|
| 146 |
+
"""
|
| 147 |
+
if initial_states is not None:
|
| 148 |
+
raise NotImplementedError
|
| 149 |
+
if seq_idx is not None:
|
| 150 |
+
raise NotImplementedError
|
| 151 |
+
if dt_limit != (0.0, float("inf")):
|
| 152 |
+
raise NotImplementedError
|
| 153 |
+
if return_final_states is not False:
|
| 154 |
+
raise NotImplementedError
|
| 155 |
+
if norm_before_gate is not False:
|
| 156 |
+
raise NotImplementedError
|
| 157 |
+
if rmsnorm_weight is None:
|
| 158 |
+
raise NotImplementedError
|
| 159 |
+
if activation not in ["silu", "swish"]:
|
| 160 |
+
raise NotImplementedError
|
| 161 |
+
|
| 162 |
+
batch, seqlen, _ = zxbcdtdl.shape
|
| 163 |
+
dlambda = L.shape[-1]
|
| 164 |
+
(nheads,) = D.shape
|
| 165 |
+
dim = nheads * headdim
|
| 166 |
+
dstate = (zxbcdtdl.shape[-1] - 2 * dim - nheads - nheads * dlambda) // ngroups // 2
|
| 167 |
+
|
| 168 |
+
if D.dim() != 1:
|
| 169 |
+
raise ValueError
|
| 170 |
+
if headdim is None:
|
| 171 |
+
raise ValueError
|
| 172 |
+
if nheads % ngroups != 0:
|
| 173 |
+
raise ValueError
|
| 174 |
+
if zxbcdtdl.shape != (
|
| 175 |
+
batch,
|
| 176 |
+
seqlen,
|
| 177 |
+
2 * dim + 2 * ngroups * dstate + nheads + nheads * dlambda,
|
| 178 |
+
):
|
| 179 |
+
raise ValueError
|
| 180 |
+
if dt_bias.shape != (nheads,):
|
| 181 |
+
raise ValueError
|
| 182 |
+
if A.shape != (nheads,):
|
| 183 |
+
raise ValueError
|
| 184 |
+
if L.shape != (nheads, dlambda):
|
| 185 |
+
raise ValueError
|
| 186 |
+
if D.shape != (nheads,):
|
| 187 |
+
raise ValueError
|
| 188 |
+
if rmsnorm_weight is None:
|
| 189 |
+
raise ValueError
|
| 190 |
+
|
| 191 |
+
zxBCdtl_splits = [dim, dim + 2 * ngroups * dstate, nheads, nheads * dlambda]
|
| 192 |
+
xBC_splits = [dim, ngroups * dstate, ngroups * dstate]
|
| 193 |
+
z, xBC, dt, dl = torch.split(zxbcdtdl, zxBCdtl_splits, dim=-1)
|
| 194 |
+
_conv_fn = conv1d_fn if conv1d_fn is not None else causal_conv1d_fn
|
| 195 |
+
_conv_out = _conv_fn(
|
| 196 |
+
rearrange(xBC, "b s d -> b d s"),
|
| 197 |
+
conv1d_weight,
|
| 198 |
+
bias=conv1d_bias,
|
| 199 |
+
activation=activation,
|
| 200 |
+
seq_idx=seq_idx,
|
| 201 |
+
)
|
| 202 |
+
if conv_backend == 'triton':
|
| 203 |
+
_conv_out = _conv_out[0]
|
| 204 |
+
xBC = rearrange(_conv_out, "b d s -> b s d")
|
| 205 |
+
x, B, C = torch.split(xBC, xBC_splits, dim=-1)
|
| 206 |
+
x = rearrange(x, "b l (h p) -> b l h p", h=nheads, p=headdim)
|
| 207 |
+
B = rearrange(B, "b l (g n) -> b l g n", g=ngroups, n=dstate)
|
| 208 |
+
C = rearrange(C, "b l (g n) -> b l g n", g=ngroups, n=dstate)
|
| 209 |
+
dl = rearrange(dl, "b l (h ell) -> b l h ell", h=nheads, ell=dlambda)
|
| 210 |
+
y, _ = hmamba_chunk_scan_combined(
|
| 211 |
+
x=x,
|
| 212 |
+
dt=dt,
|
| 213 |
+
A=A,
|
| 214 |
+
B=B,
|
| 215 |
+
C=C,
|
| 216 |
+
dl=dl,
|
| 217 |
+
L=L,
|
| 218 |
+
chunk_size=chunk_size,
|
| 219 |
+
D=D,
|
| 220 |
+
z=z if rmsnorm_weight is None else None,
|
| 221 |
+
dt_bias=dt_bias,
|
| 222 |
+
dt_softplus=True,
|
| 223 |
+
seq_idx=seq_idx,
|
| 224 |
+
cu_seqlens=None,
|
| 225 |
+
dt_limit=dt_limit,
|
| 226 |
+
return_final_states=return_final_states,
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
y = rearrange(y, "b l h p -> b l (h p)")
|
| 230 |
+
if rmsnorm_weight is not None:
|
| 231 |
+
y = rmsnorm_fn(
|
| 232 |
+
x=y,
|
| 233 |
+
weight=rmsnorm_weight,
|
| 234 |
+
bias=None,
|
| 235 |
+
z=z,
|
| 236 |
+
eps=rmsnorm_eps,
|
| 237 |
+
group_size=None,
|
| 238 |
+
norm_before_gate=False,
|
| 239 |
+
)
|
| 240 |
+
out = torch.nn.functional.linear(y, outproj_weight, outproj_bias)
|
| 241 |
+
return out
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
class LogLinearMamba2(nn.Module):
|
| 245 |
+
"""
|
| 246 |
+
Compute ∆, A, B, C, and D the state space parameters and compute the `contextualized_states`.
|
| 247 |
+
A, D are input independent (see Mamba paper [1] Section 3.5.2 "Interpretation of A" for why A isn't selective)
|
| 248 |
+
∆, B, C are input-dependent (this is a key difference between Mamba and the linear time invariant S4,
|
| 249 |
+
and is why Mamba is called **selective** state spaces)
|
| 250 |
+
"""
|
| 251 |
+
|
| 252 |
+
def __init__(
|
| 253 |
+
self,
|
| 254 |
+
num_heads: int,
|
| 255 |
+
head_dim: int = 64,
|
| 256 |
+
hidden_size: int = 2048,
|
| 257 |
+
state_size: int = 128,
|
| 258 |
+
expand: int = 2,
|
| 259 |
+
n_groups: int = 1,
|
| 260 |
+
conv_kernel: int = 4,
|
| 261 |
+
use_conv_bias: bool = False,
|
| 262 |
+
hidden_act: str = "silu",
|
| 263 |
+
rms_norm: bool = True,
|
| 264 |
+
chunk_size: int = 64,
|
| 265 |
+
time_step_rank: float = 256,
|
| 266 |
+
time_step_limit: tuple[float, float] = (0.0, float("inf")),
|
| 267 |
+
time_step_min: float = 0.001,
|
| 268 |
+
time_step_max: float = 0.1,
|
| 269 |
+
use_bias: bool = True,
|
| 270 |
+
norm_eps: float = 1e-5,
|
| 271 |
+
layer_idx: int = None,
|
| 272 |
+
backend: str = "cuda",
|
| 273 |
+
):
|
| 274 |
+
super().__init__()
|
| 275 |
+
self.num_heads = num_heads
|
| 276 |
+
self.hidden_size = hidden_size
|
| 277 |
+
self.ssm_state_size = state_size
|
| 278 |
+
self.conv_kernel_size = conv_kernel
|
| 279 |
+
self.intermediate_size = int(expand * self.hidden_size)
|
| 280 |
+
self.time_step_rank = int(time_step_rank)
|
| 281 |
+
self.layer_idx = layer_idx
|
| 282 |
+
self.use_conv_bias = use_conv_bias
|
| 283 |
+
self.activation = hidden_act
|
| 284 |
+
self.act = ACT2FN[hidden_act]
|
| 285 |
+
|
| 286 |
+
self.layer_norm_epsilon = norm_eps
|
| 287 |
+
self.rms_norm = rms_norm
|
| 288 |
+
|
| 289 |
+
self.n_groups = n_groups
|
| 290 |
+
self.head_dim = head_dim
|
| 291 |
+
self.chunk_size = chunk_size
|
| 292 |
+
|
| 293 |
+
self.time_step_limit = time_step_limit
|
| 294 |
+
self.time_step_min = time_step_min
|
| 295 |
+
self.time_step_max = time_step_max
|
| 296 |
+
|
| 297 |
+
self.conv_dim = self.intermediate_size + 2 * self.n_groups * self.ssm_state_size
|
| 298 |
+
self.conv1d = nn.Conv1d(
|
| 299 |
+
in_channels=self.conv_dim,
|
| 300 |
+
out_channels=self.conv_dim,
|
| 301 |
+
bias=use_conv_bias,
|
| 302 |
+
kernel_size=conv_kernel,
|
| 303 |
+
groups=self.conv_dim,
|
| 304 |
+
padding=conv_kernel - 1,
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
self.num_lambda_dims = MAX_NUM_LEVELS
|
| 308 |
+
self.lambda_level_module = None
|
| 309 |
+
|
| 310 |
+
# projection of the input hidden states
|
| 311 |
+
projection_size = (
|
| 312 |
+
self.intermediate_size
|
| 313 |
+
+ self.conv_dim
|
| 314 |
+
+ self.num_heads * (self.num_lambda_dims + 1)
|
| 315 |
+
)
|
| 316 |
+
self.in_proj = nn.Linear(
|
| 317 |
+
self.hidden_size,
|
| 318 |
+
projection_size,
|
| 319 |
+
bias=use_bias,
|
| 320 |
+
)
|
| 321 |
+
# selective projection used to make dt, B and C input dependant
|
| 322 |
+
|
| 323 |
+
# time step projection (discretization)
|
| 324 |
+
# instantiate once and copy inv_dt in init_weights of PretrainedModel
|
| 325 |
+
self.dt_bias = nn.Parameter(torch.ones(self.num_heads))
|
| 326 |
+
|
| 327 |
+
# S4D real initialization. These are not discretized!
|
| 328 |
+
# The core is to load them, compute the discrete states, then write the updated state. Keeps the memory bounded
|
| 329 |
+
A = torch.arange(1, self.num_heads + 1)
|
| 330 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 331 |
+
self.A_log._no_weight_decay = True
|
| 332 |
+
|
| 333 |
+
self.lambda_mode = "positive"
|
| 334 |
+
L = torch.ones(self.num_heads, self.num_lambda_dims)
|
| 335 |
+
self.L = nn.Parameter(L)
|
| 336 |
+
self.L._no_weight_decay = True
|
| 337 |
+
|
| 338 |
+
self.norm = RMSNormGated(
|
| 339 |
+
self.intermediate_size, eps=self.layer_norm_epsilon, norm_before_gate=False,
|
| 340 |
+
)
|
| 341 |
+
self.D = nn.Parameter(torch.ones(self.num_heads))
|
| 342 |
+
self.D._no_weight_decay = True
|
| 343 |
+
|
| 344 |
+
self.out_proj = nn.Linear(
|
| 345 |
+
self.intermediate_size, self.hidden_size, bias=use_bias,
|
| 346 |
+
)
|
| 347 |
+
self.use_bias = use_bias
|
| 348 |
+
|
| 349 |
+
if not is_fast_path_available:
|
| 350 |
+
logger.warning_once(
|
| 351 |
+
"The fast path is not available because one of "
|
| 352 |
+
"`(selective_state_update, causal_conv1d_fn, causal_conv1d_update)` is None. "
|
| 353 |
+
"Falling back to the naive implementation. "
|
| 354 |
+
"To install follow https://github.com/state-spaces/mamba/#installation and"
|
| 355 |
+
"https://github.com/Dao-AILab/causal-conv1d",
|
| 356 |
+
)
|
| 357 |
+
import os
|
| 358 |
+
backend = os.environ.get('FLA_CONV_BACKEND', backend)
|
| 359 |
+
assert backend in ['cuda', 'triton'], f"Unsupported backend: {backend}"
|
| 360 |
+
if backend == 'cuda' and causal_conv1d_fn is None:
|
| 361 |
+
logger.warning_once(
|
| 362 |
+
"The CUDA backend is not available because `causal_conv1d` is None. "
|
| 363 |
+
"Falling back to the Triton backend. "
|
| 364 |
+
"To install follow https://github.com/Dao-AILab/causal-conv1d",
|
| 365 |
+
)
|
| 366 |
+
backend = 'triton'
|
| 367 |
+
if backend == 'triton':
|
| 368 |
+
from fla.modules.convolution import causal_conv1d as causal_conv1d_triton
|
| 369 |
+
from fla.modules.convolution import causal_conv1d_update as causal_conv1d_update_triton
|
| 370 |
+
self.causal_conv1d_fn = causal_conv1d_triton
|
| 371 |
+
self.causal_conv1d_update = causal_conv1d_update_triton
|
| 372 |
+
logger.warning(
|
| 373 |
+
"LogLinearMamba2 does not recommend using Triton's conv1d backend, "
|
| 374 |
+
"as it is untested and may contain bugs.",
|
| 375 |
+
)
|
| 376 |
+
else:
|
| 377 |
+
self.causal_conv1d_fn = causal_conv1d_fn
|
| 378 |
+
self.causal_conv1d_update = causal_conv1d_update
|
| 379 |
+
self.backend = backend
|
| 380 |
+
|
| 381 |
+
def cuda_kernels_forward(
|
| 382 |
+
self,
|
| 383 |
+
hidden_states: torch.Tensor,
|
| 384 |
+
last_state: dict | None = None,
|
| 385 |
+
use_cache: bool = False,
|
| 386 |
+
attention_mask: torch.Tensor | None = None,
|
| 387 |
+
):
|
| 388 |
+
if self.activation not in ["silu", "swish"]:
|
| 389 |
+
raise ValueError
|
| 390 |
+
|
| 391 |
+
# 1. Gated MLP's linear projection
|
| 392 |
+
# Only apply padding mask during prefill (last_state is None).
|
| 393 |
+
# During decode, attention_mask has shape (B, accumulated_len) which
|
| 394 |
+
# mismatches hidden_states (B, 1, D).
|
| 395 |
+
hidden_states = apply_mask_to_padding_states(
|
| 396 |
+
hidden_states=hidden_states,
|
| 397 |
+
attention_mask=attention_mask if last_state is None else None,
|
| 398 |
+
)
|
| 399 |
+
projected_states = self.in_proj(hidden_states)
|
| 400 |
+
|
| 401 |
+
# Set up dimensions for reshapes later
|
| 402 |
+
batch_size, seq_len, _ = hidden_states.shape
|
| 403 |
+
groups_time_state_size = self.n_groups * self.ssm_state_size
|
| 404 |
+
d_mlp = (
|
| 405 |
+
projected_states.shape[-1]
|
| 406 |
+
- 2 * self.intermediate_size
|
| 407 |
+
- 2 * self.n_groups * self.ssm_state_size
|
| 408 |
+
- self.num_heads * (self.num_lambda_dims + 1)
|
| 409 |
+
) // 2
|
| 410 |
+
if d_mlp != 0:
|
| 411 |
+
raise ValueError
|
| 412 |
+
|
| 413 |
+
# Single step calculations via cache
|
| 414 |
+
if last_state is not None:
|
| 415 |
+
if hidden_states.shape[1] != 1:
|
| 416 |
+
raise ValueError("LogLinearMamba2 cached decoding only supports a single new token per step.")
|
| 417 |
+
|
| 418 |
+
gate, xBC, dt, dl = torch.split(
|
| 419 |
+
projected_states.squeeze(1),
|
| 420 |
+
[
|
| 421 |
+
self.intermediate_size,
|
| 422 |
+
self.conv_dim,
|
| 423 |
+
self.num_heads,
|
| 424 |
+
self.num_heads * self.num_lambda_dims,
|
| 425 |
+
],
|
| 426 |
+
dim=-1,
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
# 2. Convolution sequence transformation
|
| 430 |
+
conv_state = last_state['conv_state']
|
| 431 |
+
xBC = self.causal_conv1d_update(
|
| 432 |
+
xBC,
|
| 433 |
+
conv_state,
|
| 434 |
+
rearrange(self.conv1d.weight, "d 1 w -> d w"),
|
| 435 |
+
self.conv1d.bias,
|
| 436 |
+
self.activation,
|
| 437 |
+
)
|
| 438 |
+
|
| 439 |
+
x, B, C = torch.split(
|
| 440 |
+
xBC,
|
| 441 |
+
[
|
| 442 |
+
self.intermediate_size,
|
| 443 |
+
groups_time_state_size,
|
| 444 |
+
groups_time_state_size,
|
| 445 |
+
],
|
| 446 |
+
dim=-1,
|
| 447 |
+
)
|
| 448 |
+
|
| 449 |
+
# 3. SSM transformation
|
| 450 |
+
A = -torch.exp(self.A_log.float()) # (nheads,)
|
| 451 |
+
B = rearrange(
|
| 452 |
+
B,
|
| 453 |
+
"b (g n) -> b g n",
|
| 454 |
+
b=batch_size,
|
| 455 |
+
g=self.n_groups,
|
| 456 |
+
n=self.ssm_state_size,
|
| 457 |
+
)
|
| 458 |
+
C = rearrange(
|
| 459 |
+
C,
|
| 460 |
+
"b (g n) -> b g n",
|
| 461 |
+
b=batch_size,
|
| 462 |
+
g=self.n_groups,
|
| 463 |
+
n=self.ssm_state_size,
|
| 464 |
+
)
|
| 465 |
+
x_reshaped = rearrange(
|
| 466 |
+
x,
|
| 467 |
+
"b (h p) -> b h p",
|
| 468 |
+
b=batch_size,
|
| 469 |
+
h=self.num_heads,
|
| 470 |
+
p=self.head_dim,
|
| 471 |
+
)
|
| 472 |
+
dl_reshaped = rearrange(
|
| 473 |
+
dl,
|
| 474 |
+
"b (h ell) -> b h ell",
|
| 475 |
+
b=batch_size,
|
| 476 |
+
h=self.num_heads,
|
| 477 |
+
ell=self.num_lambda_dims,
|
| 478 |
+
)
|
| 479 |
+
y, hssm_state = hmamba_chunk_scan_combined(
|
| 480 |
+
x_reshaped,
|
| 481 |
+
dt=dt,
|
| 482 |
+
A=A,
|
| 483 |
+
B=B,
|
| 484 |
+
C=C,
|
| 485 |
+
dl=dl_reshaped,
|
| 486 |
+
L=self.L,
|
| 487 |
+
D=self.D,
|
| 488 |
+
z=None,
|
| 489 |
+
dt_bias=self.dt_bias,
|
| 490 |
+
dt_softplus=True,
|
| 491 |
+
initial_states=last_state['recurrent_state'],
|
| 492 |
+
return_final_states=True,
|
| 493 |
+
)
|
| 494 |
+
y = rearrange(
|
| 495 |
+
y,
|
| 496 |
+
"b h p -> b (h p)",
|
| 497 |
+
b=batch_size,
|
| 498 |
+
h=self.num_heads,
|
| 499 |
+
p=self.head_dim,
|
| 500 |
+
)
|
| 501 |
+
y = self.norm(y, gate)
|
| 502 |
+
|
| 503 |
+
# 4. Final linear projection
|
| 504 |
+
out = self.out_proj(y)[:, None, ...]
|
| 505 |
+
return out, conv_state, hssm_state
|
| 506 |
+
|
| 507 |
+
# Fused calculations or step by step if no initialized cache is found
|
| 508 |
+
else:
|
| 509 |
+
A = -torch.exp(
|
| 510 |
+
self.A_log.float(),
|
| 511 |
+
) # (num_heads) or (intermediate_size, state_size)
|
| 512 |
+
dt_limit_kwargs = (
|
| 513 |
+
{}
|
| 514 |
+
if self.time_step_limit == (0.0, float("inf"))
|
| 515 |
+
else {"dt_limit": self.time_step_limit}
|
| 516 |
+
)
|
| 517 |
+
|
| 518 |
+
# 2-4. Fused kernel for conv1d, SSM, and the final projection
|
| 519 |
+
if self.training and not use_cache:
|
| 520 |
+
out = torch.utils.checkpoint.checkpoint(
|
| 521 |
+
hmamba_split_conv1d_scan_combined,
|
| 522 |
+
use_reentrant=False,
|
| 523 |
+
# function arguments
|
| 524 |
+
zxbcdtdl=projected_states,
|
| 525 |
+
conv1d_weight=rearrange(self.conv1d.weight, "d 1 w -> d w"),
|
| 526 |
+
conv1d_bias=self.conv1d.bias,
|
| 527 |
+
dt_bias=self.dt_bias,
|
| 528 |
+
A=A,
|
| 529 |
+
L=self.L,
|
| 530 |
+
D=self.D,
|
| 531 |
+
chunk_size=self.chunk_size,
|
| 532 |
+
conv1d_fn=self.causal_conv1d_fn,
|
| 533 |
+
conv_backend=self.backend,
|
| 534 |
+
seq_idx=None, # was seq_idx
|
| 535 |
+
activation=self.activation,
|
| 536 |
+
rmsnorm_weight=self.norm.weight,
|
| 537 |
+
rmsnorm_eps=self.norm.eps,
|
| 538 |
+
outproj_weight=self.out_proj.weight,
|
| 539 |
+
outproj_bias=self.out_proj.bias,
|
| 540 |
+
headdim=self.head_dim,
|
| 541 |
+
ngroups=self.n_groups,
|
| 542 |
+
norm_before_gate=False,
|
| 543 |
+
return_final_states=False,
|
| 544 |
+
**dt_limit_kwargs,
|
| 545 |
+
)
|
| 546 |
+
return out, None, None
|
| 547 |
+
|
| 548 |
+
else:
|
| 549 |
+
gate, xBC, dt, dl = torch.split(
|
| 550 |
+
projected_states,
|
| 551 |
+
[
|
| 552 |
+
self.intermediate_size,
|
| 553 |
+
self.conv_dim,
|
| 554 |
+
self.num_heads,
|
| 555 |
+
self.num_heads * self.num_lambda_dims,
|
| 556 |
+
],
|
| 557 |
+
dim=-1,
|
| 558 |
+
)
|
| 559 |
+
|
| 560 |
+
# 2. Convolution sequence transformation
|
| 561 |
+
# Init cache
|
| 562 |
+
masked_xBC = apply_mask_to_padding_states(xBC, attention_mask)
|
| 563 |
+
new_conv_state = None
|
| 564 |
+
if use_cache:
|
| 565 |
+
xBC_t = rearrange(masked_xBC, "b l d -> b d l")
|
| 566 |
+
new_conv_state = torch.nn.functional.pad(
|
| 567 |
+
xBC_t,
|
| 568 |
+
(self.conv_kernel_size - xBC_t.shape[-1], 0),
|
| 569 |
+
)
|
| 570 |
+
|
| 571 |
+
_conv1d_output = self.causal_conv1d_fn(
|
| 572 |
+
x=xBC.transpose(1, 2),
|
| 573 |
+
weight=rearrange(self.conv1d.weight, "d 1 w -> d w"),
|
| 574 |
+
bias=self.conv1d.bias,
|
| 575 |
+
activation=self.activation,
|
| 576 |
+
)
|
| 577 |
+
if self.backend == 'cuda':
|
| 578 |
+
xBC = _conv1d_output.transpose(1, 2)
|
| 579 |
+
elif self.backend == 'triton':
|
| 580 |
+
xBC = _conv1d_output[0].transpose(1, 2).contiguous()
|
| 581 |
+
else:
|
| 582 |
+
raise ValueError(f"Unsupported backend: {self.backend}")
|
| 583 |
+
|
| 584 |
+
xBC = apply_mask_to_padding_states(
|
| 585 |
+
hidden_states=xBC,
|
| 586 |
+
attention_mask=attention_mask,
|
| 587 |
+
)
|
| 588 |
+
|
| 589 |
+
x, B, C = torch.split(
|
| 590 |
+
xBC,
|
| 591 |
+
[
|
| 592 |
+
self.intermediate_size,
|
| 593 |
+
groups_time_state_size,
|
| 594 |
+
groups_time_state_size,
|
| 595 |
+
],
|
| 596 |
+
dim=-1,
|
| 597 |
+
)
|
| 598 |
+
|
| 599 |
+
# 3. SSM transformation
|
| 600 |
+
y, hssm_state = hmamba_chunk_scan_combined(
|
| 601 |
+
rearrange(
|
| 602 |
+
x,
|
| 603 |
+
"b l (h p) -> b l h p",
|
| 604 |
+
b=batch_size,
|
| 605 |
+
l=seq_len,
|
| 606 |
+
p=self.head_dim,
|
| 607 |
+
),
|
| 608 |
+
dt=dt,
|
| 609 |
+
A=A,
|
| 610 |
+
B=rearrange(
|
| 611 |
+
B,
|
| 612 |
+
"b l (g n) -> b l g n",
|
| 613 |
+
b=batch_size,
|
| 614 |
+
l=seq_len,
|
| 615 |
+
g=self.n_groups,
|
| 616 |
+
),
|
| 617 |
+
C=rearrange(
|
| 618 |
+
C,
|
| 619 |
+
"b l (g n) -> b l g n",
|
| 620 |
+
b=batch_size,
|
| 621 |
+
l=seq_len,
|
| 622 |
+
g=self.n_groups,
|
| 623 |
+
),
|
| 624 |
+
dl=rearrange(
|
| 625 |
+
dl,
|
| 626 |
+
"b l (h ell) -> b l h ell",
|
| 627 |
+
b=batch_size,
|
| 628 |
+
h=self.num_heads,
|
| 629 |
+
ell=self.num_lambda_dims,
|
| 630 |
+
),
|
| 631 |
+
L=self.L,
|
| 632 |
+
chunk_size=self.chunk_size,
|
| 633 |
+
D=self.D,
|
| 634 |
+
z=None,
|
| 635 |
+
seq_idx=None,
|
| 636 |
+
return_final_states=True,
|
| 637 |
+
dt_bias=self.dt_bias,
|
| 638 |
+
dt_softplus=True,
|
| 639 |
+
**dt_limit_kwargs,
|
| 640 |
+
)
|
| 641 |
+
|
| 642 |
+
y = rearrange(
|
| 643 |
+
y,
|
| 644 |
+
"b l h p -> b l (h p)",
|
| 645 |
+
b=batch_size,
|
| 646 |
+
l=seq_len,
|
| 647 |
+
h=self.num_heads,
|
| 648 |
+
p=self.head_dim,
|
| 649 |
+
)
|
| 650 |
+
# Multiply "gate" branch and apply extra normalization layer
|
| 651 |
+
y = self.norm(y, gate)
|
| 652 |
+
|
| 653 |
+
# 4. Final linear projection
|
| 654 |
+
out = self.out_proj(y)
|
| 655 |
+
|
| 656 |
+
return out, new_conv_state, hssm_state
|
| 657 |
+
|
| 658 |
+
def forward(
|
| 659 |
+
self,
|
| 660 |
+
hidden_states: torch.Tensor,
|
| 661 |
+
attention_mask: torch.Tensor | None = None,
|
| 662 |
+
past_key_values: Cache | None = None,
|
| 663 |
+
use_cache: bool | None = False,
|
| 664 |
+
output_attentions: bool | None = False,
|
| 665 |
+
**kwargs,
|
| 666 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 667 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 668 |
+
|
| 669 |
+
if "cuda" in self.in_proj.weight.device.type:
|
| 670 |
+
output, conv_state, hssm_state = self.cuda_kernels_forward(
|
| 671 |
+
hidden_states, last_state, use_cache, attention_mask
|
| 672 |
+
)
|
| 673 |
+
else:
|
| 674 |
+
raise NotImplementedError
|
| 675 |
+
|
| 676 |
+
update_layer_cache(
|
| 677 |
+
self,
|
| 678 |
+
past_key_values,
|
| 679 |
+
recurrent_state=hssm_state,
|
| 680 |
+
conv_state=conv_state,
|
| 681 |
+
offset=hidden_states.shape[1],
|
| 682 |
+
)
|
| 683 |
+
|
| 684 |
+
return output, None, past_key_values
|
fla/layers/mamba.py
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from transformers.utils import logging
|
| 11 |
+
|
| 12 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 13 |
+
from fla.modules.activations import ACT2FN
|
| 14 |
+
|
| 15 |
+
with warnings.catch_warnings():
|
| 16 |
+
warnings.simplefilter('ignore')
|
| 17 |
+
try:
|
| 18 |
+
from mamba_ssm.ops.selective_scan_interface import mamba_inner_fn, selective_scan_fn
|
| 19 |
+
from mamba_ssm.ops.triton.selective_state_update import selective_state_update
|
| 20 |
+
except ImportError:
|
| 21 |
+
selective_state_update, selective_scan_fn, mamba_inner_fn = None, None, None
|
| 22 |
+
|
| 23 |
+
try:
|
| 24 |
+
from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
|
| 25 |
+
except ImportError:
|
| 26 |
+
causal_conv1d_update, causal_conv1d_fn = None, None
|
| 27 |
+
is_fast_path_available = all((
|
| 28 |
+
selective_state_update,
|
| 29 |
+
selective_scan_fn,
|
| 30 |
+
mamba_inner_fn,
|
| 31 |
+
))
|
| 32 |
+
if TYPE_CHECKING:
|
| 33 |
+
from transformers.processing_utils import Unpack
|
| 34 |
+
|
| 35 |
+
from fla.models.utils import Cache
|
| 36 |
+
|
| 37 |
+
logger = logging.get_logger(__name__)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class Mamba(nn.Module):
|
| 41 |
+
"""
|
| 42 |
+
Compute ∆, A, B, C, and D the state space parameters and compute the `contextualized_states`.
|
| 43 |
+
A, D are input independent (see Mamba paper [1] Section 3.5.2 "Interpretation of A" for why A isn't selective)
|
| 44 |
+
∆, B, C are input-dependent (this is a key difference between Mamba and the linear time invariant S4,
|
| 45 |
+
and is why Mamba is called **selective** state spaces)
|
| 46 |
+
"""
|
| 47 |
+
|
| 48 |
+
def __init__(
|
| 49 |
+
self,
|
| 50 |
+
hidden_size: int = 2048,
|
| 51 |
+
state_size: int = 16,
|
| 52 |
+
conv_kernel: int = 4,
|
| 53 |
+
use_conv_bias: bool = True,
|
| 54 |
+
intermediate_size: int = 2048,
|
| 55 |
+
time_step_rank: int = 256,
|
| 56 |
+
use_bias: bool = True,
|
| 57 |
+
hidden_act: str = "silu",
|
| 58 |
+
layer_idx: int = None,
|
| 59 |
+
backend: str = "cuda",
|
| 60 |
+
):
|
| 61 |
+
super().__init__()
|
| 62 |
+
|
| 63 |
+
self.hidden_size = hidden_size
|
| 64 |
+
self.ssm_state_size = state_size
|
| 65 |
+
self.conv_kernel_size = conv_kernel
|
| 66 |
+
self.use_conv_bias = use_conv_bias
|
| 67 |
+
self.intermediate_size = intermediate_size
|
| 68 |
+
self.time_step_rank = time_step_rank
|
| 69 |
+
self.use_bias = use_bias
|
| 70 |
+
|
| 71 |
+
self.conv1d = nn.Conv1d(
|
| 72 |
+
in_channels=self.intermediate_size,
|
| 73 |
+
out_channels=self.intermediate_size,
|
| 74 |
+
bias=use_conv_bias,
|
| 75 |
+
kernel_size=conv_kernel,
|
| 76 |
+
groups=self.intermediate_size,
|
| 77 |
+
padding=conv_kernel - 1,
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
self.activation = hidden_act
|
| 81 |
+
self.act = ACT2FN[hidden_act]
|
| 82 |
+
|
| 83 |
+
self.layer_idx = layer_idx
|
| 84 |
+
|
| 85 |
+
# projection of the input hidden states
|
| 86 |
+
self.in_proj = nn.Linear(self.hidden_size, self.intermediate_size * 2, bias=use_bias)
|
| 87 |
+
# selective projection used to make dt, B and C input dependant
|
| 88 |
+
self.x_proj = nn.Linear(self.intermediate_size, self.time_step_rank + self.ssm_state_size * 2, bias=False)
|
| 89 |
+
# time step projection (discretization)
|
| 90 |
+
self.dt_proj = nn.Linear(self.time_step_rank, self.intermediate_size, bias=True)
|
| 91 |
+
|
| 92 |
+
# S4D real initialization. These are not discretized!
|
| 93 |
+
# The core is to load them, compute the discrete states, then write the updated state. Keeps the memory bounded
|
| 94 |
+
A = torch.arange(1, self.ssm_state_size + 1, dtype=torch.float32)[None, :]
|
| 95 |
+
A = A.expand(self.intermediate_size, -1).contiguous()
|
| 96 |
+
|
| 97 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 98 |
+
self.D = nn.Parameter(torch.ones(self.intermediate_size))
|
| 99 |
+
self.out_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=use_bias)
|
| 100 |
+
|
| 101 |
+
if not is_fast_path_available:
|
| 102 |
+
logger.warning_once(
|
| 103 |
+
"The fast path is not available because on of "
|
| 104 |
+
"`(selective_state_update, selective_scan_fn, causal_conv1d_fn, causal_conv1d_update, mamba_inner_fn)`"
|
| 105 |
+
" is None. Falling back to the naive implementation. "
|
| 106 |
+
"To install follow https://github.com/state-spaces/mamba/#installation and"
|
| 107 |
+
" https://github.com/Dao-AILab/causal-conv1d",
|
| 108 |
+
)
|
| 109 |
+
import os
|
| 110 |
+
backend = os.environ.get('FLA_CONV_BACKEND', backend)
|
| 111 |
+
assert backend in ['cuda', 'triton'], f"Unsupported backend: {backend}"
|
| 112 |
+
if backend == 'cuda' and causal_conv1d_fn is None:
|
| 113 |
+
logger.warning_once(
|
| 114 |
+
"The CUDA backend is not available because `causal_conv1d` is None. "
|
| 115 |
+
"Falling back to the Triton backend. "
|
| 116 |
+
"To install follow https://github.com/Dao-AILab/causal-conv1d",
|
| 117 |
+
)
|
| 118 |
+
backend = 'triton'
|
| 119 |
+
if backend == 'triton':
|
| 120 |
+
from fla.modules.convolution import causal_conv1d as causal_conv1d_triton
|
| 121 |
+
from fla.modules.convolution import causal_conv1d_update as causal_conv1d_update_triton
|
| 122 |
+
self.causal_conv1d_fn = causal_conv1d_triton
|
| 123 |
+
self.causal_conv1d_update = causal_conv1d_update_triton
|
| 124 |
+
else:
|
| 125 |
+
self.causal_conv1d_fn = causal_conv1d_fn
|
| 126 |
+
self.causal_conv1d_update = causal_conv1d_update
|
| 127 |
+
self.backend = backend
|
| 128 |
+
|
| 129 |
+
def _to_causal_conv_layout(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 130 |
+
return hidden_states.transpose(1, 2).contiguous()
|
| 131 |
+
|
| 132 |
+
def _from_causal_conv_layout(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 133 |
+
return hidden_states.transpose(1, 2).contiguous()
|
| 134 |
+
|
| 135 |
+
def _build_conv_state(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 136 |
+
seq_len = hidden_states.shape[-1]
|
| 137 |
+
if seq_len >= self.conv_kernel_size:
|
| 138 |
+
return hidden_states[..., -self.conv_kernel_size:].contiguous()
|
| 139 |
+
return nn.functional.pad(hidden_states, (self.conv_kernel_size - seq_len, 0)).contiguous()
|
| 140 |
+
|
| 141 |
+
def cuda_kernels_forward(
|
| 142 |
+
self,
|
| 143 |
+
hidden_states: torch.Tensor,
|
| 144 |
+
last_state: dict | None = None,
|
| 145 |
+
use_cache: bool | None = False,
|
| 146 |
+
attention_mask: torch.LongTensor | None = None,
|
| 147 |
+
**kwargs: Unpack[dict],
|
| 148 |
+
):
|
| 149 |
+
if last_state is not None and hidden_states.shape[1] != 1:
|
| 150 |
+
raise ValueError("Mamba cached decoding only supports a single new token per step.")
|
| 151 |
+
|
| 152 |
+
# 1. Gated MLP's linear projection
|
| 153 |
+
projected_states = self.in_proj(hidden_states).transpose(1, 2)
|
| 154 |
+
|
| 155 |
+
if self.training and not use_cache:
|
| 156 |
+
contextualized_states = mamba_inner_fn(
|
| 157 |
+
projected_states,
|
| 158 |
+
self.conv1d.weight,
|
| 159 |
+
self.conv1d.bias if self.use_conv_bias else None,
|
| 160 |
+
self.x_proj.weight,
|
| 161 |
+
self.dt_proj.weight,
|
| 162 |
+
self.out_proj.weight,
|
| 163 |
+
self.out_proj.bias.float() if self.use_bias else None,
|
| 164 |
+
-torch.exp(self.A_log.float()),
|
| 165 |
+
None, # input-dependent B
|
| 166 |
+
None, # input-dependent C
|
| 167 |
+
self.D.float(),
|
| 168 |
+
delta_bias=self.dt_proj.bias.float(),
|
| 169 |
+
delta_softplus=True,
|
| 170 |
+
)
|
| 171 |
+
return contextualized_states, None, None
|
| 172 |
+
|
| 173 |
+
hidden_states, gate = projected_states.chunk(2, dim=1)
|
| 174 |
+
|
| 175 |
+
if attention_mask is not None and last_state is None:
|
| 176 |
+
# Mask before the depthwise conv so cached/prefill conv inputs do not keep pad tokens.
|
| 177 |
+
hidden_states = hidden_states * attention_mask.unsqueeze(1)
|
| 178 |
+
|
| 179 |
+
# 2. Convolution sequence transformation
|
| 180 |
+
conv_inputs = hidden_states
|
| 181 |
+
conv_weights = self.conv1d.weight.view(self.conv1d.weight.size(0), self.conv1d.weight.size(2))
|
| 182 |
+
if last_state is not None:
|
| 183 |
+
conv_state = last_state['conv_state']
|
| 184 |
+
ssm_state = last_state['recurrent_state']
|
| 185 |
+
|
| 186 |
+
if self.backend == 'triton':
|
| 187 |
+
hidden_states, conv_state = self.causal_conv1d_update(
|
| 188 |
+
x=self._to_causal_conv_layout(conv_inputs),
|
| 189 |
+
cache=conv_state,
|
| 190 |
+
weight=conv_weights,
|
| 191 |
+
bias=self.conv1d.bias,
|
| 192 |
+
activation=self.activation,
|
| 193 |
+
)
|
| 194 |
+
hidden_states = self._from_causal_conv_layout(hidden_states)
|
| 195 |
+
else:
|
| 196 |
+
hidden_states = self.causal_conv1d_update(
|
| 197 |
+
conv_inputs.squeeze(-1),
|
| 198 |
+
conv_state,
|
| 199 |
+
conv_weights,
|
| 200 |
+
self.conv1d.bias,
|
| 201 |
+
self.activation,
|
| 202 |
+
)
|
| 203 |
+
hidden_states = hidden_states.unsqueeze(-1)
|
| 204 |
+
else:
|
| 205 |
+
conv_state = None
|
| 206 |
+
ssm_state = None
|
| 207 |
+
if self.backend == 'triton':
|
| 208 |
+
hidden_states, conv_state = self.causal_conv1d_fn(
|
| 209 |
+
x=self._to_causal_conv_layout(conv_inputs),
|
| 210 |
+
weight=conv_weights,
|
| 211 |
+
bias=self.conv1d.bias,
|
| 212 |
+
activation=self.activation,
|
| 213 |
+
output_final_state=bool(use_cache),
|
| 214 |
+
)
|
| 215 |
+
hidden_states = self._from_causal_conv_layout(hidden_states)
|
| 216 |
+
else:
|
| 217 |
+
if use_cache:
|
| 218 |
+
conv_state = self._build_conv_state(conv_inputs)
|
| 219 |
+
hidden_states = self.causal_conv1d_fn(
|
| 220 |
+
conv_inputs, conv_weights, self.conv1d.bias, activation=self.activation,
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
if attention_mask is not None and last_state is None:
|
| 224 |
+
# Re-mask after the conv: causal kernels can regenerate non-zero values at masked positions,
|
| 225 |
+
# and those values would otherwise leak into x_proj and the SSM recurrence.
|
| 226 |
+
hidden_states = hidden_states * attention_mask.unsqueeze(1)
|
| 227 |
+
|
| 228 |
+
# 3. State Space Model sequence transformation
|
| 229 |
+
# 3.a. input varying initialization of time_step, B and C
|
| 230 |
+
ssm_parameters = self.x_proj(hidden_states.transpose(1, 2))
|
| 231 |
+
time_step, B, C = torch.split(
|
| 232 |
+
ssm_parameters, [self.time_step_rank, self.ssm_state_size, self.ssm_state_size], dim=-1,
|
| 233 |
+
)
|
| 234 |
+
discrete_time_step = self.dt_proj.weight @ time_step.transpose(1, 2)
|
| 235 |
+
|
| 236 |
+
A = -torch.exp(self.A_log.float())
|
| 237 |
+
# 3.c perform the recurrence y ← SSM(A, B, C)(x)
|
| 238 |
+
time_proj_bias = self.dt_proj.bias.float() if hasattr(self.dt_proj, "bias") else None
|
| 239 |
+
if last_state is not None:
|
| 240 |
+
scan_outputs = selective_state_update(
|
| 241 |
+
ssm_state,
|
| 242 |
+
hidden_states[..., 0],
|
| 243 |
+
discrete_time_step[..., 0],
|
| 244 |
+
A,
|
| 245 |
+
B[:, 0],
|
| 246 |
+
C[:, 0],
|
| 247 |
+
self.D,
|
| 248 |
+
gate[..., 0],
|
| 249 |
+
time_proj_bias,
|
| 250 |
+
dt_softplus=True,
|
| 251 |
+
).unsqueeze(-1)
|
| 252 |
+
else:
|
| 253 |
+
scan_outputs, ssm_state = selective_scan_fn(
|
| 254 |
+
hidden_states,
|
| 255 |
+
discrete_time_step,
|
| 256 |
+
A,
|
| 257 |
+
B.transpose(1, 2),
|
| 258 |
+
C.transpose(1, 2),
|
| 259 |
+
self.D.float(),
|
| 260 |
+
gate,
|
| 261 |
+
time_proj_bias,
|
| 262 |
+
delta_softplus=True,
|
| 263 |
+
return_last_state=True,
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
# 4. Final linear projection
|
| 267 |
+
contextualized_states = self.out_proj(scan_outputs.transpose(1, 2))
|
| 268 |
+
return contextualized_states, conv_state, ssm_state
|
| 269 |
+
|
| 270 |
+
def slow_forward(
|
| 271 |
+
self,
|
| 272 |
+
input_states,
|
| 273 |
+
last_state: dict | None = None,
|
| 274 |
+
use_cache: bool | None = False,
|
| 275 |
+
attention_mask: torch.LongTensor | None = None,
|
| 276 |
+
**kwargs: Unpack[dict],
|
| 277 |
+
):
|
| 278 |
+
if last_state is not None and input_states.shape[1] != 1:
|
| 279 |
+
raise ValueError("Mamba cached decoding only supports a single new token per step.")
|
| 280 |
+
|
| 281 |
+
batch_size, seq_len, _ = input_states.shape
|
| 282 |
+
dtype = input_states.dtype
|
| 283 |
+
# 1. Gated MLP's linear projection
|
| 284 |
+
# [batch, 2 * intermediate_size, seq_len]
|
| 285 |
+
projected_states = self.in_proj(input_states).transpose(1, 2)
|
| 286 |
+
hidden_states, gate = projected_states.chunk(2, dim=1)
|
| 287 |
+
|
| 288 |
+
if attention_mask is not None and last_state is None:
|
| 289 |
+
# Mask before the depthwise conv so cached/prefill conv inputs do not keep pad tokens.
|
| 290 |
+
hidden_states = hidden_states * attention_mask.unsqueeze(1)
|
| 291 |
+
|
| 292 |
+
# 2. Convolution sequence transformation
|
| 293 |
+
if last_state is not None:
|
| 294 |
+
conv_state = last_state['conv_state']
|
| 295 |
+
ssm_state = last_state['recurrent_state'].clone().to(hidden_states.device)
|
| 296 |
+
|
| 297 |
+
# decode path: single token
|
| 298 |
+
conv_state = conv_state.roll(shifts=-1, dims=-1)
|
| 299 |
+
conv_state[:, :, -1] = hidden_states[:, :, 0].to(conv_state.device)
|
| 300 |
+
hidden_states = torch.sum(conv_state * self.conv1d.weight[:, 0, :], dim=-1)
|
| 301 |
+
if self.use_conv_bias:
|
| 302 |
+
hidden_states += self.conv1d.bias
|
| 303 |
+
# [batch, intermediate_size, 1] : decoding
|
| 304 |
+
hidden_states = self.act(hidden_states).to(dtype).unsqueeze(-1)
|
| 305 |
+
elif use_cache:
|
| 306 |
+
ssm_state = torch.zeros(
|
| 307 |
+
(batch_size, self.intermediate_size, self.ssm_state_size),
|
| 308 |
+
device=hidden_states.device, dtype=dtype,
|
| 309 |
+
)
|
| 310 |
+
conv_state = self._build_conv_state(hidden_states)
|
| 311 |
+
# [batch, intermediate_size, seq_len]
|
| 312 |
+
hidden_states = self.act(self.conv1d(hidden_states)[..., :seq_len])
|
| 313 |
+
else:
|
| 314 |
+
ssm_state = torch.zeros(
|
| 315 |
+
(batch_size, self.intermediate_size, self.ssm_state_size),
|
| 316 |
+
device=hidden_states.device, dtype=dtype,
|
| 317 |
+
)
|
| 318 |
+
conv_state = None
|
| 319 |
+
# [batch, intermediate_size, seq_len]
|
| 320 |
+
hidden_states = self.act(self.conv1d(hidden_states)[..., :seq_len])
|
| 321 |
+
|
| 322 |
+
if attention_mask is not None and last_state is None:
|
| 323 |
+
# Re-mask after the conv: causal kernels can regenerate non-zero values at masked positions,
|
| 324 |
+
# and those values would otherwise leak into x_proj and the SSM recurrence.
|
| 325 |
+
hidden_states = hidden_states * attention_mask.unsqueeze(1)
|
| 326 |
+
|
| 327 |
+
# 3. State Space Model sequence transformation
|
| 328 |
+
# 3.a. Selection: [batch, seq_len, self.time_step_rank + self.ssm_state_size * 2]
|
| 329 |
+
ssm_parameters = self.x_proj(hidden_states.transpose(1, 2))
|
| 330 |
+
time_step, B, C = torch.split(
|
| 331 |
+
ssm_parameters, [self.time_step_rank, self.ssm_state_size, self.ssm_state_size], dim=-1,
|
| 332 |
+
)
|
| 333 |
+
# [batch, seq_len, intermediate_size]
|
| 334 |
+
discrete_time_step = self.dt_proj(time_step)
|
| 335 |
+
# [batch, intermediate_size, seq_len]
|
| 336 |
+
discrete_time_step = nn.functional.softplus(discrete_time_step).transpose(1, 2)
|
| 337 |
+
|
| 338 |
+
# 3.b. Discretization: B and C to [batch, seq_len, intermediate_size, ssm_state_size] (SRAM)
|
| 339 |
+
# [intermediate_size, ssm_state_size]
|
| 340 |
+
A = -torch.exp(self.A_log.float())
|
| 341 |
+
# [batch, intermediate_size, seq_len, ssm_state_size]
|
| 342 |
+
discrete_A = torch.exp(A[None, :, None, :] * discrete_time_step[:, :, :, None])
|
| 343 |
+
# [batch, intermediate_size, seq_len, ssm_state_size]
|
| 344 |
+
discrete_B = discrete_time_step[:, :, :, None] * B[:, None, :, :].float()
|
| 345 |
+
deltaB_u = discrete_B * hidden_states[:, :, :, None].float()
|
| 346 |
+
|
| 347 |
+
# 3.c perform the recurrence y ← SSM(A, B, C)(x)
|
| 348 |
+
scan_outputs = []
|
| 349 |
+
for i in range(hidden_states.shape[-1]):
|
| 350 |
+
# [batch, intermediade_size, ssm_state]
|
| 351 |
+
ssm_state = discrete_A[:, :, i, :] * ssm_state + deltaB_u[:, :, i, :]
|
| 352 |
+
# [batch, intermediade_size, 1]
|
| 353 |
+
scan_output = torch.matmul(ssm_state.to(dtype), C[:, i, :].unsqueeze(-1))
|
| 354 |
+
scan_outputs.append(scan_output[:, :, 0])
|
| 355 |
+
# [batch, seq_len, intermediade_size]
|
| 356 |
+
scan_output = torch.stack(scan_outputs, dim=-1)
|
| 357 |
+
scan_output = scan_output + (hidden_states * self.D[None, :, None])
|
| 358 |
+
scan_output = (scan_output * self.act(gate))
|
| 359 |
+
|
| 360 |
+
# 4. Final linear projection
|
| 361 |
+
# [batch, seq_len, hidden_size]
|
| 362 |
+
contextualized_states = self.out_proj(scan_output.transpose(1, 2))
|
| 363 |
+
return contextualized_states, conv_state, ssm_state
|
| 364 |
+
# fmt: on
|
| 365 |
+
|
| 366 |
+
def forward(
|
| 367 |
+
self,
|
| 368 |
+
hidden_states: torch.Tensor,
|
| 369 |
+
attention_mask: torch.LongTensor | None = None,
|
| 370 |
+
past_key_values: Cache | None = None,
|
| 371 |
+
use_cache: bool | None = False,
|
| 372 |
+
output_attentions: bool | None = False,
|
| 373 |
+
**kwargs: Unpack[dict],
|
| 374 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 375 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 376 |
+
|
| 377 |
+
if is_fast_path_available and "cuda" in self.x_proj.weight.device.type:
|
| 378 |
+
output, conv_state, ssm_state = self.cuda_kernels_forward(
|
| 379 |
+
hidden_states, last_state, use_cache, attention_mask, **kwargs
|
| 380 |
+
)
|
| 381 |
+
else:
|
| 382 |
+
output, conv_state, ssm_state = self.slow_forward(
|
| 383 |
+
hidden_states, last_state, use_cache, attention_mask, **kwargs
|
| 384 |
+
)
|
| 385 |
+
|
| 386 |
+
if use_cache and past_key_values is not None:
|
| 387 |
+
update_layer_cache(
|
| 388 |
+
self,
|
| 389 |
+
past_key_values,
|
| 390 |
+
recurrent_state=ssm_state,
|
| 391 |
+
conv_state=conv_state,
|
| 392 |
+
offset=hidden_states.shape[1],
|
| 393 |
+
)
|
| 394 |
+
|
| 395 |
+
return output, None, past_key_values
|
fla/layers/mamba2.py
ADDED
|
@@ -0,0 +1,649 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import math
|
| 6 |
+
import warnings
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
from transformers.utils import logging
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 14 |
+
from fla.modules.activations import ACT2FN
|
| 15 |
+
from fla.modules.layernorm_gated import RMSNormGated
|
| 16 |
+
|
| 17 |
+
with warnings.catch_warnings():
|
| 18 |
+
warnings.simplefilter('ignore')
|
| 19 |
+
try:
|
| 20 |
+
from mamba_ssm.ops.triton.selective_state_update import selective_state_update
|
| 21 |
+
from mamba_ssm.ops.triton.ssd_combined import mamba_chunk_scan_combined, mamba_split_conv1d_scan_combined
|
| 22 |
+
except ImportError:
|
| 23 |
+
selective_state_update, mamba_chunk_scan_combined, mamba_split_conv1d_scan_combined = None, None, None
|
| 24 |
+
try:
|
| 25 |
+
from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
|
| 26 |
+
except ImportError:
|
| 27 |
+
causal_conv1d_update, causal_conv1d_fn = None, None
|
| 28 |
+
is_fast_path_available = selective_state_update is not None
|
| 29 |
+
|
| 30 |
+
if TYPE_CHECKING:
|
| 31 |
+
from fla.models.utils import Cache
|
| 32 |
+
|
| 33 |
+
logger = logging.get_logger(__name__)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def apply_mask_to_padding_states(hidden_states, attention_mask):
|
| 37 |
+
"""
|
| 38 |
+
Tunes out the hidden states for padding tokens, see https://github.com/state-spaces/mamba/issues/66
|
| 39 |
+
"""
|
| 40 |
+
if attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1:
|
| 41 |
+
dtype = hidden_states.dtype
|
| 42 |
+
hidden_states = (hidden_states * attention_mask[:, :, None]).to(dtype)
|
| 43 |
+
|
| 44 |
+
return hidden_states
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def pad_tensor_by_size(input_tensor: torch.Tensor, pad_size: int):
|
| 48 |
+
"""
|
| 49 |
+
Padding x tensor with `pad_size` on the seq_len dim (dim=1)
|
| 50 |
+
|
| 51 |
+
Assumes that we only have tensors of either size 4 or 3
|
| 52 |
+
"""
|
| 53 |
+
pad_shape = (0, 0, 0, 0, 0, pad_size, 0, 0) if len(input_tensor.shape) == 4 else (0, 0, 0, pad_size, 0, 0)
|
| 54 |
+
|
| 55 |
+
return torch.nn.functional.pad(input_tensor, pad_shape, mode="constant", value=0)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def reshape_into_chunks(input_tensor, pad_size, chunk_size):
|
| 59 |
+
"""
|
| 60 |
+
Padding input_tensor with `pad_size` on the seq_len dim (dim=1) and
|
| 61 |
+
simultaneously splitting it into chunk sequences.
|
| 62 |
+
|
| 63 |
+
Assumes that we only have tensors of either size 4 or 3
|
| 64 |
+
"""
|
| 65 |
+
# [bsz, seq_len, ...] -> [bsz, seq_len multiple of chunk_size, ...]
|
| 66 |
+
input_tensor = pad_tensor_by_size(input_tensor, pad_size)
|
| 67 |
+
|
| 68 |
+
if len(input_tensor.shape) == 3:
|
| 69 |
+
# [bsz, seq_len multiple of chunk_size, num_heads] -> [bsz, -1, chunk_size, num_heads]
|
| 70 |
+
return input_tensor.reshape(input_tensor.shape[0], -1, chunk_size, input_tensor.shape[2])
|
| 71 |
+
else:
|
| 72 |
+
# [bsz, seq_len multiple of chunk_size, num_heads, head_dim or state_size] ->
|
| 73 |
+
# [bsz, -1, chunk_size, num_heads, head_dim or state_size]
|
| 74 |
+
return input_tensor.reshape(
|
| 75 |
+
input_tensor.shape[0], -1, chunk_size, input_tensor.shape[2], input_tensor.shape[3],
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def segment_sum(input_tensor):
|
| 80 |
+
"""
|
| 81 |
+
More stable segment sum calculation. Uses cumulative sums and masking instead of direct subtractions.
|
| 82 |
+
"""
|
| 83 |
+
chunk_size = input_tensor.size(-1)
|
| 84 |
+
# 1. expand input tensor to have an additional dimension and repeat along that dimension
|
| 85 |
+
# [..., chunk_size] -> [..., chunk_size, chunk_size]
|
| 86 |
+
input_tensor = input_tensor[..., None].expand(*input_tensor.size(), chunk_size)
|
| 87 |
+
# 2. create a lower triangular mask with the diagonal set to 0 to 0 out elements above diag
|
| 88 |
+
mask = torch.tril(torch.ones(chunk_size, chunk_size, device=input_tensor.device, dtype=torch.bool), diagonal=-1)
|
| 89 |
+
input_tensor = input_tensor.masked_fill(~mask, 0)
|
| 90 |
+
# 3. compute actual cumsum
|
| 91 |
+
tensor_segsum = torch.cumsum(input_tensor, dim=-2)
|
| 92 |
+
|
| 93 |
+
# 4. apply mask to keep only the lower triangular part of the cumulative sum result (incl diagonal this time)
|
| 94 |
+
mask = torch.tril(torch.ones(chunk_size, chunk_size, device=input_tensor.device, dtype=torch.bool), diagonal=0)
|
| 95 |
+
tensor_segsum = tensor_segsum.masked_fill(~mask, -torch.inf)
|
| 96 |
+
return tensor_segsum
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
class Mamba2(nn.Module):
|
| 100 |
+
"""
|
| 101 |
+
Compute ∆, A, B, C, and D the state space parameters and compute the `contextualized_states`.
|
| 102 |
+
A, D are input independent (see Mamba paper [1] Section 3.5.2 "Interpretation of A" for why A isn't selective)
|
| 103 |
+
∆, B, C are input-dependent (this is a key difference between Mamba and the linear time invariant S4,
|
| 104 |
+
and is why Mamba is called **selective** state spaces)
|
| 105 |
+
"""
|
| 106 |
+
|
| 107 |
+
def __init__(
|
| 108 |
+
self,
|
| 109 |
+
num_heads: int,
|
| 110 |
+
head_dim: int = 64,
|
| 111 |
+
hidden_size: int = 2048,
|
| 112 |
+
state_size: int = 128,
|
| 113 |
+
expand: int = 2,
|
| 114 |
+
n_groups: int = 1,
|
| 115 |
+
conv_kernel: int = 4,
|
| 116 |
+
use_conv_bias: bool = False,
|
| 117 |
+
hidden_act: str = "silu",
|
| 118 |
+
rms_norm: bool = True,
|
| 119 |
+
chunk_size: int = 256,
|
| 120 |
+
time_step_rank: float = 256,
|
| 121 |
+
time_step_limit: tuple[float, float] = (0.0, float("inf")),
|
| 122 |
+
time_step_min: float = 0.001,
|
| 123 |
+
time_step_max: float = 0.1,
|
| 124 |
+
use_bias: bool = True,
|
| 125 |
+
norm_eps: float = 1e-5,
|
| 126 |
+
layer_idx: int = None,
|
| 127 |
+
backend: str = "cuda",
|
| 128 |
+
) -> Mamba2:
|
| 129 |
+
super().__init__()
|
| 130 |
+
|
| 131 |
+
self.num_heads = num_heads
|
| 132 |
+
self.head_dim = head_dim
|
| 133 |
+
self.hidden_size = hidden_size
|
| 134 |
+
self.ssm_state_size = state_size
|
| 135 |
+
self.expand = expand
|
| 136 |
+
self.intermediate_size = int(expand * hidden_size)
|
| 137 |
+
self.n_groups = n_groups
|
| 138 |
+
|
| 139 |
+
self.conv_kernel_size = conv_kernel
|
| 140 |
+
self.use_conv_bias = use_conv_bias
|
| 141 |
+
self.activation = hidden_act
|
| 142 |
+
self.act = ACT2FN[hidden_act]
|
| 143 |
+
|
| 144 |
+
self.rms_norm = rms_norm
|
| 145 |
+
self.norm_eps = norm_eps
|
| 146 |
+
|
| 147 |
+
self.chunk_size = chunk_size
|
| 148 |
+
|
| 149 |
+
self.time_step_rank = int(time_step_rank)
|
| 150 |
+
self.time_step_limit = time_step_limit
|
| 151 |
+
self.time_step_min = time_step_min
|
| 152 |
+
self.time_step_max = time_step_max
|
| 153 |
+
|
| 154 |
+
self.conv_dim = self.intermediate_size + 2 * self.n_groups * self.ssm_state_size
|
| 155 |
+
self.conv1d = nn.Conv1d(
|
| 156 |
+
in_channels=self.conv_dim,
|
| 157 |
+
out_channels=self.conv_dim,
|
| 158 |
+
bias=use_conv_bias,
|
| 159 |
+
kernel_size=conv_kernel,
|
| 160 |
+
groups=self.conv_dim,
|
| 161 |
+
padding=conv_kernel - 1,
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
# projection of the input hidden states
|
| 165 |
+
projection_size = self.intermediate_size + self.conv_dim + self.num_heads
|
| 166 |
+
self.in_proj = nn.Linear(
|
| 167 |
+
self.hidden_size,
|
| 168 |
+
projection_size,
|
| 169 |
+
bias=use_bias,
|
| 170 |
+
)
|
| 171 |
+
# selective projection used to make dt, B and C input dependant
|
| 172 |
+
|
| 173 |
+
# time step projection (discretization)
|
| 174 |
+
# instantiate once and copy inv_dt in init_weights of PretrainedModel
|
| 175 |
+
# hard coded for now
|
| 176 |
+
dt_init_floor = 1e-4
|
| 177 |
+
dt = torch.exp(
|
| 178 |
+
torch.rand(self.num_heads) * (
|
| 179 |
+
math.log(self.time_step_max) - math.log(self.time_step_min)
|
| 180 |
+
) + math.log(self.time_step_min)
|
| 181 |
+
)
|
| 182 |
+
dt = torch.clamp(dt, min=dt_init_floor)
|
| 183 |
+
# Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
|
| 184 |
+
inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| 185 |
+
self.dt_bias = nn.Parameter(inv_dt)
|
| 186 |
+
|
| 187 |
+
# S4D real initialization. These are not discretized!
|
| 188 |
+
# The core is to load them, compute the discrete states, then write the updated state. Keeps the memory bounded
|
| 189 |
+
A = torch.empty(self.num_heads, dtype=torch.float32).uniform_(0, 16)
|
| 190 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 191 |
+
self.A_log._no_weight_decay = True
|
| 192 |
+
self.norm = RMSNormGated(
|
| 193 |
+
self.intermediate_size, eps=self.norm_eps, norm_before_gate=False,
|
| 194 |
+
)
|
| 195 |
+
self.D = nn.Parameter(torch.ones(self.num_heads))
|
| 196 |
+
self.D._no_weight_decay = True
|
| 197 |
+
|
| 198 |
+
self.out_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=use_bias)
|
| 199 |
+
self.use_bias = use_bias
|
| 200 |
+
|
| 201 |
+
self.layer_idx = layer_idx
|
| 202 |
+
|
| 203 |
+
if not is_fast_path_available:
|
| 204 |
+
logger.warning_once(
|
| 205 |
+
"The fast path is not available because one of "
|
| 206 |
+
"`(selective_state_update)` is None. "
|
| 207 |
+
"Falling back to the naive implementation. "
|
| 208 |
+
"To install follow https://github.com/state-spaces/mamba/#installation",
|
| 209 |
+
)
|
| 210 |
+
import os
|
| 211 |
+
backend = os.environ.get('FLA_CONV_BACKEND', backend)
|
| 212 |
+
assert backend in ['cuda', 'triton'], f"Unsupported backend: {backend}"
|
| 213 |
+
if backend == 'cuda' and causal_conv1d_fn is None:
|
| 214 |
+
logger.warning_once(
|
| 215 |
+
"The CUDA backend is not available because `causal_conv1d` is None. "
|
| 216 |
+
"Falling back to the Triton backend. "
|
| 217 |
+
"To install follow https://github.com/Dao-AILab/causal-conv1d",
|
| 218 |
+
)
|
| 219 |
+
backend = 'triton'
|
| 220 |
+
if backend == 'triton':
|
| 221 |
+
from fla.modules.convolution import causal_conv1d as causal_conv1d_triton
|
| 222 |
+
from fla.modules.convolution import causal_conv1d_update as causal_conv1d_update_triton
|
| 223 |
+
self.causal_conv1d_fn = causal_conv1d_triton
|
| 224 |
+
self.causal_conv1d_update = causal_conv1d_update_triton
|
| 225 |
+
logger.warning(
|
| 226 |
+
"Mamba2 does not recommend using Triton's conv1d backend, "
|
| 227 |
+
"as it is untested and may contain bugs.",
|
| 228 |
+
)
|
| 229 |
+
else:
|
| 230 |
+
self.causal_conv1d_fn = causal_conv1d_fn
|
| 231 |
+
self.causal_conv1d_update = causal_conv1d_update
|
| 232 |
+
self.backend = backend
|
| 233 |
+
|
| 234 |
+
def cuda_kernels_forward(
|
| 235 |
+
self,
|
| 236 |
+
hidden_states: torch.Tensor,
|
| 237 |
+
last_state: dict | None = None,
|
| 238 |
+
use_cache: bool = False,
|
| 239 |
+
attention_mask: torch.Tensor | None = None,
|
| 240 |
+
):
|
| 241 |
+
# 1. Gated MLP's linear projection
|
| 242 |
+
projected_states = self.in_proj(hidden_states)
|
| 243 |
+
|
| 244 |
+
# Set up dimensions for reshapes later
|
| 245 |
+
batch_size, seq_len, _ = hidden_states.shape
|
| 246 |
+
groups_time_state_size = self.n_groups * self.ssm_state_size
|
| 247 |
+
d_mlp = (
|
| 248 |
+
projected_states.shape[-1]
|
| 249 |
+
- 2 * self.intermediate_size
|
| 250 |
+
- 2 * self.n_groups * self.ssm_state_size
|
| 251 |
+
- self.num_heads
|
| 252 |
+
) // 2
|
| 253 |
+
|
| 254 |
+
# Single step calculations via cache (decode)
|
| 255 |
+
if last_state is not None:
|
| 256 |
+
if hidden_states.shape[1] != 1:
|
| 257 |
+
raise ValueError("Mamba2 cached decoding only supports a single new token per step.")
|
| 258 |
+
conv_state = last_state['conv_state']
|
| 259 |
+
ssm_state = last_state['recurrent_state']
|
| 260 |
+
|
| 261 |
+
_, _, gate, hidden_states_B_C, dt = projected_states.squeeze(1).split(
|
| 262 |
+
[d_mlp, d_mlp, self.intermediate_size, self.conv_dim, self.num_heads], dim=-1,
|
| 263 |
+
)
|
| 264 |
+
|
| 265 |
+
# 2. Convolution sequence transformation
|
| 266 |
+
hidden_states_B_C = self.causal_conv1d_update(
|
| 267 |
+
hidden_states_B_C.contiguous(),
|
| 268 |
+
conv_state,
|
| 269 |
+
self.conv1d.weight.squeeze(1),
|
| 270 |
+
self.conv1d.bias,
|
| 271 |
+
self.activation,
|
| 272 |
+
)
|
| 273 |
+
|
| 274 |
+
hidden_states, B, C = torch.split(
|
| 275 |
+
hidden_states_B_C,
|
| 276 |
+
[
|
| 277 |
+
self.intermediate_size,
|
| 278 |
+
groups_time_state_size,
|
| 279 |
+
groups_time_state_size,
|
| 280 |
+
],
|
| 281 |
+
dim=-1,
|
| 282 |
+
)
|
| 283 |
+
|
| 284 |
+
# 3. SSM transformation
|
| 285 |
+
A = -torch.exp(self.A_log.float()) # (nheads,)
|
| 286 |
+
A = A[:, None, ...][:, :, None].expand(-1, self.head_dim, self.ssm_state_size).to(dtype=torch.float32)
|
| 287 |
+
dt = dt[:, :, None].expand(-1, -1, self.head_dim)
|
| 288 |
+
dt_bias = self.dt_bias[:, None, ...].expand(-1, self.head_dim)
|
| 289 |
+
D = self.D[:, None, ...].expand(-1, self.head_dim)
|
| 290 |
+
B = B.view(batch_size, self.n_groups, B.shape[1] // self.n_groups)
|
| 291 |
+
C = C.view(batch_size, self.n_groups, C.shape[1] // self.n_groups)
|
| 292 |
+
hidden_states_reshaped = hidden_states.view(batch_size, self.num_heads, self.head_dim)
|
| 293 |
+
|
| 294 |
+
hidden_states = selective_state_update(
|
| 295 |
+
ssm_state,
|
| 296 |
+
hidden_states_reshaped,
|
| 297 |
+
dt,
|
| 298 |
+
A,
|
| 299 |
+
B,
|
| 300 |
+
C,
|
| 301 |
+
D,
|
| 302 |
+
z=None,
|
| 303 |
+
dt_bias=dt_bias,
|
| 304 |
+
dt_softplus=True,
|
| 305 |
+
)
|
| 306 |
+
hidden_states = hidden_states.view(batch_size, self.num_heads * self.head_dim)
|
| 307 |
+
hidden_states = self.norm(hidden_states, gate)
|
| 308 |
+
|
| 309 |
+
# 4. Final linear projection
|
| 310 |
+
out = self.out_proj(hidden_states)[:, None, ...]
|
| 311 |
+
|
| 312 |
+
# conv_state is updated in-place by causal_conv1d_update
|
| 313 |
+
# ssm_state is updated in-place by selective_state_update
|
| 314 |
+
return out, conv_state, ssm_state
|
| 315 |
+
|
| 316 |
+
# Fused calculations or step by step if no initialized cache is found (prefill)
|
| 317 |
+
else:
|
| 318 |
+
A = -torch.exp(self.A_log.float()) # (num_heads) or (intermediate_size, state_size)
|
| 319 |
+
dt_limit_kwargs = {} if self.time_step_limit == (0.0, float("inf")) else {"dt_limit": self.time_step_limit}
|
| 320 |
+
|
| 321 |
+
# 2-4. Fused kernel for conv1d, SSM, and the final projection
|
| 322 |
+
if self.training and not use_cache:
|
| 323 |
+
out = mamba_split_conv1d_scan_combined(
|
| 324 |
+
projected_states,
|
| 325 |
+
self.conv1d.weight.squeeze(1),
|
| 326 |
+
self.conv1d.bias,
|
| 327 |
+
self.dt_bias,
|
| 328 |
+
A,
|
| 329 |
+
D=self.D,
|
| 330 |
+
chunk_size=self.chunk_size,
|
| 331 |
+
seq_idx=None, # was seq_idx
|
| 332 |
+
activation=self.activation,
|
| 333 |
+
rmsnorm_weight=self.norm.weight,
|
| 334 |
+
rmsnorm_eps=self.norm.eps,
|
| 335 |
+
outproj_weight=self.out_proj.weight,
|
| 336 |
+
outproj_bias=self.out_proj.bias,
|
| 337 |
+
headdim=self.head_dim,
|
| 338 |
+
ngroups=self.n_groups,
|
| 339 |
+
norm_before_gate=False,
|
| 340 |
+
return_final_states=False,
|
| 341 |
+
**dt_limit_kwargs,
|
| 342 |
+
)
|
| 343 |
+
return out, None, None
|
| 344 |
+
|
| 345 |
+
else:
|
| 346 |
+
_, _, gate, hidden_states_B_C, dt = projected_states.split(
|
| 347 |
+
[d_mlp, d_mlp, self.intermediate_size, self.conv_dim, self.num_heads], dim=-1,
|
| 348 |
+
)
|
| 349 |
+
|
| 350 |
+
# 2. Convolution sequence transformation
|
| 351 |
+
hidden_states_B_C = apply_mask_to_padding_states(hidden_states_B_C, attention_mask)
|
| 352 |
+
# Compute conv_state for cache
|
| 353 |
+
new_conv_state = None
|
| 354 |
+
if use_cache:
|
| 355 |
+
hidden_states_B_C_transposed = hidden_states_B_C.transpose(1, 2)
|
| 356 |
+
new_conv_state = nn.functional.pad(
|
| 357 |
+
hidden_states_B_C_transposed,
|
| 358 |
+
(self.conv_kernel_size - hidden_states_B_C_transposed.shape[-1], 0),
|
| 359 |
+
)
|
| 360 |
+
|
| 361 |
+
if self.activation not in ["silu", "swish"]:
|
| 362 |
+
hidden_states_B_C = self.act(
|
| 363 |
+
self.conv1d(hidden_states_B_C.transpose(1, 2))[..., :seq_len].transpose(1, 2),
|
| 364 |
+
)
|
| 365 |
+
else:
|
| 366 |
+
_conv1d_output = self.causal_conv1d_fn(
|
| 367 |
+
x=hidden_states_B_C.transpose(1, 2).contiguous(),
|
| 368 |
+
weight=self.conv1d.weight.squeeze(1),
|
| 369 |
+
bias=self.conv1d.bias,
|
| 370 |
+
activation=self.activation,
|
| 371 |
+
)
|
| 372 |
+
if self.backend == 'cuda':
|
| 373 |
+
hidden_states_B_C = _conv1d_output
|
| 374 |
+
hidden_states_B_C = hidden_states_B_C.transpose(1, 2)
|
| 375 |
+
elif self.backend == 'triton':
|
| 376 |
+
hidden_states_B_C, _ = _conv1d_output
|
| 377 |
+
hidden_states_B_C = hidden_states_B_C.transpose(1, 2).contiguous()
|
| 378 |
+
else:
|
| 379 |
+
raise ValueError(f"Unsupported backend: {self.backend}")
|
| 380 |
+
|
| 381 |
+
hidden_states_B_C = (hidden_states_B_C * attention_mask[:, :, None]).to(hidden_states_B_C.dtype) \
|
| 382 |
+
if attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1 \
|
| 383 |
+
else hidden_states_B_C
|
| 384 |
+
hidden_states, B, C = torch.split(
|
| 385 |
+
hidden_states_B_C,
|
| 386 |
+
[self.intermediate_size, groups_time_state_size, groups_time_state_size],
|
| 387 |
+
dim=-1,
|
| 388 |
+
)
|
| 389 |
+
|
| 390 |
+
# 3. SSM transformation
|
| 391 |
+
scan_output, ssm_state = mamba_chunk_scan_combined(
|
| 392 |
+
hidden_states.view(batch_size, seq_len, -1, self.head_dim),
|
| 393 |
+
dt,
|
| 394 |
+
A,
|
| 395 |
+
B.view(batch_size, seq_len, self.n_groups, -1),
|
| 396 |
+
C.view(batch_size, seq_len, self.n_groups, -1),
|
| 397 |
+
chunk_size=self.chunk_size,
|
| 398 |
+
D=self.D,
|
| 399 |
+
z=None,
|
| 400 |
+
seq_idx=None,
|
| 401 |
+
return_final_states=True,
|
| 402 |
+
dt_bias=self.dt_bias,
|
| 403 |
+
dt_softplus=True,
|
| 404 |
+
**dt_limit_kwargs,
|
| 405 |
+
)
|
| 406 |
+
|
| 407 |
+
scan_output = scan_output.view(batch_size, seq_len, -1)
|
| 408 |
+
# Multiply "gate" branch and apply extra normalization layer
|
| 409 |
+
scan_output = self.norm(scan_output, gate)
|
| 410 |
+
|
| 411 |
+
# 4. Final linear projection
|
| 412 |
+
out = self.out_proj(scan_output)
|
| 413 |
+
|
| 414 |
+
return out, new_conv_state, ssm_state
|
| 415 |
+
|
| 416 |
+
# fmt: off
|
| 417 |
+
def torch_forward(
|
| 418 |
+
self,
|
| 419 |
+
input_states,
|
| 420 |
+
last_state: dict | None = None,
|
| 421 |
+
use_cache: bool = False,
|
| 422 |
+
attention_mask: torch.Tensor | None = None,
|
| 423 |
+
):
|
| 424 |
+
batch_size, seq_len, _ = input_states.shape
|
| 425 |
+
dtype = input_states.dtype
|
| 426 |
+
|
| 427 |
+
# 1. Gated MLP's linear projection
|
| 428 |
+
projected_states = self.in_proj(input_states)
|
| 429 |
+
d_mlp = (projected_states.shape[-1] - 2 * self.intermediate_size -
|
| 430 |
+
2 * self.n_groups * self.ssm_state_size - self.num_heads) // 2
|
| 431 |
+
_, _, gate, hidden_states_B_C, dt = projected_states.split(
|
| 432 |
+
[d_mlp, d_mlp, self.intermediate_size, self.conv_dim, self.num_heads], dim=-1,
|
| 433 |
+
)
|
| 434 |
+
|
| 435 |
+
# 2. Convolution sequence transformation
|
| 436 |
+
if last_state is not None:
|
| 437 |
+
if input_states.shape[1] != 1:
|
| 438 |
+
raise ValueError("Mamba2 cached decoding only supports a single new token per step.")
|
| 439 |
+
# Decode path: single-step update
|
| 440 |
+
conv_state = last_state['conv_state']
|
| 441 |
+
ssm_state = last_state['recurrent_state']
|
| 442 |
+
|
| 443 |
+
conv_state = conv_state.roll(shifts=-1, dims=-1)
|
| 444 |
+
conv_state[:, :, -1] = hidden_states_B_C[:, 0, :].to(conv_state.device)
|
| 445 |
+
|
| 446 |
+
# We need to guarantee that anything regarding the cache is on the same device
|
| 447 |
+
conv_states_for_compute = conv_state.to(device=self.conv1d.weight.device)
|
| 448 |
+
|
| 449 |
+
hidden_states_B_C = torch.sum(
|
| 450 |
+
conv_states_for_compute * self.conv1d.weight.squeeze(1), dim=-1,
|
| 451 |
+
)
|
| 452 |
+
if self.use_conv_bias:
|
| 453 |
+
hidden_states_B_C = hidden_states_B_C + self.conv1d.bias
|
| 454 |
+
hidden_states_B_C = self.act(hidden_states_B_C)
|
| 455 |
+
else:
|
| 456 |
+
# Prefill path
|
| 457 |
+
hidden_states_B_C = apply_mask_to_padding_states(hidden_states_B_C, attention_mask)
|
| 458 |
+
new_conv_state = None
|
| 459 |
+
if use_cache:
|
| 460 |
+
hidden_states_B_C_transposed = hidden_states_B_C.transpose(1, 2)
|
| 461 |
+
new_conv_state = nn.functional.pad(
|
| 462 |
+
hidden_states_B_C_transposed, (self.conv_kernel_size - hidden_states_B_C_transposed.shape[-1], 0),
|
| 463 |
+
)
|
| 464 |
+
|
| 465 |
+
hidden_states_B_C = self.act(self.conv1d(hidden_states_B_C.transpose(1, 2))[..., :seq_len].transpose(1, 2))
|
| 466 |
+
|
| 467 |
+
if last_state is None:
|
| 468 |
+
hidden_states_B_C = (hidden_states_B_C * attention_mask[:, :, None]).to(hidden_states_B_C.dtype) \
|
| 469 |
+
if attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1 \
|
| 470 |
+
else hidden_states_B_C
|
| 471 |
+
hidden_states, B, C = torch.split(
|
| 472 |
+
hidden_states_B_C,
|
| 473 |
+
[self.intermediate_size, self.n_groups * self.ssm_state_size, self.n_groups * self.ssm_state_size],
|
| 474 |
+
dim=-1,
|
| 475 |
+
)
|
| 476 |
+
|
| 477 |
+
# 3. SSM transformation
|
| 478 |
+
A = -torch.exp(self.A_log.float()) # [num_heads]
|
| 479 |
+
if last_state is not None:
|
| 480 |
+
# Decode path
|
| 481 |
+
cache_device = ssm_state.device
|
| 482 |
+
|
| 483 |
+
# Note: there is no need to pad parameter matrices here, as there is just one new token
|
| 484 |
+
# for batched generation
|
| 485 |
+
dt = dt[:, 0, :][:, None, ...]
|
| 486 |
+
dt = dt.transpose(1, 2).expand(batch_size, dt.shape[-1], self.head_dim)
|
| 487 |
+
# [num_heads] -> [num_heads, head_dim]
|
| 488 |
+
dt_bias = self.dt_bias[..., None].expand(self.dt_bias.shape[0], self.head_dim)
|
| 489 |
+
|
| 490 |
+
dt = torch.nn.functional.softplus(dt + dt_bias.to(dt.dtype))
|
| 491 |
+
dt = torch.clamp(dt, self.time_step_limit[0], self.time_step_limit[1])
|
| 492 |
+
A = A[..., None, None].expand(self.num_heads, self.head_dim, self.ssm_state_size).to(dtype=torch.float32)
|
| 493 |
+
# [bsz, num_heads, head_dim, state_size]
|
| 494 |
+
dA = (torch.exp(dt[..., None] * A)).to(device=cache_device)
|
| 495 |
+
|
| 496 |
+
# Discretize B
|
| 497 |
+
# [bsz, n_groups * state_size] -> [bsz, n_groups, 1, state_size] ->
|
| 498 |
+
# -> [bsz, n_groups, group to head repetition factor, state_size] -> [bsz, num_heads, state_size]
|
| 499 |
+
B = B.reshape(batch_size, self.n_groups, -1)[..., None, :]
|
| 500 |
+
B = B.expand(batch_size, self.n_groups, self.num_heads // self.n_groups, B.shape[-1]).contiguous()
|
| 501 |
+
B = B.reshape(batch_size, -1, B.shape[-1])
|
| 502 |
+
# [bsz, num_heads, head_dim, state_size]
|
| 503 |
+
dB = dt[..., None] * B[..., None, :]
|
| 504 |
+
|
| 505 |
+
# Discretize x into dB
|
| 506 |
+
# [bsz, intermediate_size] -> [bsz, num_heads, head_dim]
|
| 507 |
+
hidden_states = hidden_states.reshape(batch_size, -1, self.head_dim)
|
| 508 |
+
dBx = (dB * hidden_states[..., None]).to(device=cache_device)
|
| 509 |
+
|
| 510 |
+
# State calculation
|
| 511 |
+
ssm_state = ssm_state * dA + dBx
|
| 512 |
+
|
| 513 |
+
# Subsequent output
|
| 514 |
+
# [bsz, n_groups * state_size] -> [bsz, num_heads, state_size]
|
| 515 |
+
C = C.reshape(batch_size, self.n_groups, -1)[..., None, :]
|
| 516 |
+
C = C.expand(batch_size, self.n_groups, self.num_heads // self.n_groups, C.shape[-1]).contiguous()
|
| 517 |
+
C = C.reshape(batch_size, -1, C.shape[-1])
|
| 518 |
+
# [bsz, num_heads, head_dim]
|
| 519 |
+
|
| 520 |
+
ssm_states_for_compute = ssm_state.to(device=C.device, dtype=C.dtype) # Shape: [b, h, d, n]
|
| 521 |
+
# Reshape ssm_states to merge the first two dimensions
|
| 522 |
+
# Shape: [b*h, d, n]
|
| 523 |
+
ssm_states_reshaped = ssm_states_for_compute.view(batch_size * self.num_heads, self.head_dim, self.ssm_state_size)
|
| 524 |
+
C_reshaped = C.view(batch_size * self.num_heads, self.ssm_state_size, 1) # Shape: [b*h, n, 1]
|
| 525 |
+
y = torch.bmm(ssm_states_reshaped, C_reshaped)
|
| 526 |
+
y = y.view(batch_size, self.num_heads, self.head_dim)
|
| 527 |
+
|
| 528 |
+
# D skip connection
|
| 529 |
+
# [num_heads] -> [num_heads, head_dim]
|
| 530 |
+
D = self.D[..., None].expand(self.D.shape[0], self.head_dim)
|
| 531 |
+
y = (y + hidden_states * D).to(y.dtype)
|
| 532 |
+
|
| 533 |
+
# [bsz, num_heads, head_dim] -> [bsz, 1, intermediate_size]
|
| 534 |
+
y = y.reshape(batch_size, -1)[:, None, ...]
|
| 535 |
+
|
| 536 |
+
scan_output = self.norm(y, gate)
|
| 537 |
+
contextualized_states = self.out_proj(scan_output.to(dtype))
|
| 538 |
+
return contextualized_states, conv_state, ssm_state
|
| 539 |
+
else:
|
| 540 |
+
# Prefill path
|
| 541 |
+
# begin ssd naive implementation without einsums
|
| 542 |
+
dt = nn.functional.softplus(dt + self.dt_bias)
|
| 543 |
+
dt = torch.clamp(dt, self.time_step_limit[0], self.time_step_limit[1])
|
| 544 |
+
hidden_states = hidden_states.reshape(batch_size, seq_len, -1, self.head_dim).float()
|
| 545 |
+
B = B.reshape(batch_size, seq_len, -1, self.ssm_state_size).float()
|
| 546 |
+
C = C.reshape(batch_size, seq_len, -1, self.ssm_state_size).float()
|
| 547 |
+
B = B.repeat(1, 1, self.num_heads // self.n_groups, 1)
|
| 548 |
+
C = C.repeat(1, 1, self.num_heads // self.n_groups, 1)
|
| 549 |
+
pad_size = (self.chunk_size - seq_len % self.chunk_size) % self.chunk_size
|
| 550 |
+
|
| 551 |
+
D_residual = self.D[..., None] * pad_tensor_by_size(hidden_states, pad_size)
|
| 552 |
+
|
| 553 |
+
# Discretize x and A
|
| 554 |
+
hidden_states = hidden_states * dt[..., None]
|
| 555 |
+
A = A.to(hidden_states.dtype) * dt
|
| 556 |
+
|
| 557 |
+
# Rearrange into blocks/chunks
|
| 558 |
+
hidden_states, A, B, C = [reshape_into_chunks(t, pad_size, self.chunk_size) for t in (hidden_states, A, B, C)]
|
| 559 |
+
|
| 560 |
+
# [bsz, -1, chunk_size, num_heads] -> [bsz, num_heads, -1, chunk_size]
|
| 561 |
+
A = A.permute(0, 3, 1, 2)
|
| 562 |
+
A_cumsum = torch.cumsum(A, dim=-1)
|
| 563 |
+
|
| 564 |
+
# 1. Compute the output for each intra-chunk (diagonal blocks)
|
| 565 |
+
# This is the analog of a causal mask
|
| 566 |
+
L = torch.exp(segment_sum(A))
|
| 567 |
+
|
| 568 |
+
# Contraction of C and B to get G (attention-weights like)
|
| 569 |
+
# shape: (b, c, l, s, h, n)
|
| 570 |
+
G_intermediate = C[:, :, :, None, :, :] * B[:, :, None, :, :, :]
|
| 571 |
+
G = G_intermediate.sum(dim=-1) # shape: (b, c, l, s, h)
|
| 572 |
+
|
| 573 |
+
# Compute M, equivalent to applying attention mask to weights
|
| 574 |
+
M_intermediate = G[..., None] * L.permute(0, 2, 3, 4, 1)[..., None]
|
| 575 |
+
M = M_intermediate.sum(dim=-1)
|
| 576 |
+
|
| 577 |
+
# Compute Y_diag (apply to values)
|
| 578 |
+
Y_diag = (M[..., None] * hidden_states[:, :, None]).sum(dim=3)
|
| 579 |
+
|
| 580 |
+
# 2. Compute the state for each intra-chunk
|
| 581 |
+
# (right term of low-rank factorization of off-diagonal blocks; B terms)
|
| 582 |
+
decay_states = torch.exp(A_cumsum[:, :, :, -1:] - A_cumsum)
|
| 583 |
+
B_decay = B * decay_states.permute(0, -2, -1, 1)[..., None]
|
| 584 |
+
states = (B_decay[..., None, :] * hidden_states[..., None]).sum(dim=2)
|
| 585 |
+
|
| 586 |
+
# 3. Compute the inter-chunk SSM recurrence; produces correct SSM states at chunk boundaries
|
| 587 |
+
# (middle term of factorization of off-diag blocks; A terms)
|
| 588 |
+
previous_states = torch.zeros_like(states[:, :1])
|
| 589 |
+
states = torch.cat([previous_states, states], dim=1)
|
| 590 |
+
decay_chunk = torch.exp(segment_sum(nn.functional.pad(A_cumsum[:, :, :, -1], (1, 0))))
|
| 591 |
+
decay_chunk = decay_chunk.transpose(1, 3)
|
| 592 |
+
new_states = (decay_chunk[..., None, None] * states[:, :, None, ...]).sum(dim=1)
|
| 593 |
+
states, ssm_state = new_states[:, :-1], new_states[:, -1]
|
| 594 |
+
|
| 595 |
+
# 4. Compute state -> output conversion per chunk
|
| 596 |
+
# (left term of low-rank factorization of off-diagonal blocks; C terms)
|
| 597 |
+
state_decay_out = torch.exp(A_cumsum)
|
| 598 |
+
C_times_states = (C[..., None, :] * states[:, :, None, ...])
|
| 599 |
+
state_decay_out_permuted = state_decay_out.permute(0, 2, 3, 1)
|
| 600 |
+
Y_off = (C_times_states.sum(-1) * state_decay_out_permuted[..., None])
|
| 601 |
+
|
| 602 |
+
# Add output of intra-chunk and inter-chunk terms (diagonal and off-diagonal blocks)
|
| 603 |
+
y = Y_diag + Y_off
|
| 604 |
+
# [bsz, -1, self.chunk_size, num_heads, head_dim] -> [bsz, (padded) seq_len, num_heads, head_dim]
|
| 605 |
+
y = y.reshape(batch_size, -1, self.num_heads, self.head_dim)
|
| 606 |
+
|
| 607 |
+
y = y + D_residual
|
| 608 |
+
# Cutting off padded chunks
|
| 609 |
+
if pad_size > 0:
|
| 610 |
+
y = y[:, :seq_len, :, :]
|
| 611 |
+
y = y.reshape(batch_size, seq_len, -1)
|
| 612 |
+
|
| 613 |
+
scan_output = self.norm(y, gate)
|
| 614 |
+
|
| 615 |
+
# end ssd naive
|
| 616 |
+
|
| 617 |
+
# 4. Final linear projection
|
| 618 |
+
contextualized_states = self.out_proj(scan_output.to(dtype)) # [batch, seq_len, hidden_size]
|
| 619 |
+
return contextualized_states, new_conv_state if use_cache else None, ssm_state
|
| 620 |
+
# fmt: on
|
| 621 |
+
|
| 622 |
+
def forward(
|
| 623 |
+
self,
|
| 624 |
+
hidden_states: torch.Tensor,
|
| 625 |
+
attention_mask: torch.Tensor | None = None,
|
| 626 |
+
past_key_values: Cache | None = None,
|
| 627 |
+
use_cache: bool | None = False,
|
| 628 |
+
output_attentions: bool | None = False,
|
| 629 |
+
**kwargs,
|
| 630 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 631 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 632 |
+
|
| 633 |
+
if is_fast_path_available and "cuda" in self.in_proj.weight.device.type:
|
| 634 |
+
output, conv_state, ssm_state = self.cuda_kernels_forward(hidden_states, last_state, use_cache, attention_mask)
|
| 635 |
+
else:
|
| 636 |
+
dtype = hidden_states.dtype
|
| 637 |
+
if last_state is None and attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1:
|
| 638 |
+
hidden_states = (hidden_states * attention_mask[:, :, None]).to(dtype)
|
| 639 |
+
output, conv_state, ssm_state = self.torch_forward(hidden_states, last_state, use_cache, attention_mask)
|
| 640 |
+
|
| 641 |
+
update_layer_cache(
|
| 642 |
+
self,
|
| 643 |
+
past_key_values,
|
| 644 |
+
recurrent_state=ssm_state,
|
| 645 |
+
conv_state=conv_state,
|
| 646 |
+
offset=hidden_states.shape[1],
|
| 647 |
+
)
|
| 648 |
+
|
| 649 |
+
return output, None, past_key_values
|
fla/layers/mesa_net.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from einops import rearrange
|
| 10 |
+
from torch.nn import functional as F
|
| 11 |
+
|
| 12 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 13 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 14 |
+
from fla.modules.l2norm import l2_norm
|
| 15 |
+
from fla.ops.mesa_net import chunk_mesa_net, mesa_net_decoding_one_step
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from transformers.processing_utils import Unpack
|
| 19 |
+
|
| 20 |
+
from fla.models.utils import Cache
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class MesaNet(nn.Module):
|
| 24 |
+
"""
|
| 25 |
+
The layer implementaion for [MesaNet: Sequence Modeling by Locally Optimal Test-Time Training]. # noqa
|
| 26 |
+
|
| 27 |
+
Args:
|
| 28 |
+
hidden_size (int, Optional):
|
| 29 |
+
The hidden size of the input. Default: 2048.
|
| 30 |
+
expand_v (float, Optional):
|
| 31 |
+
The expansion ratio for the value dim. Default: 1.
|
| 32 |
+
num_heads (int, Optional):
|
| 33 |
+
The number of heads. Default: 16.
|
| 34 |
+
mode (str, Optional):
|
| 35 |
+
Which MesaNet kernel to use.
|
| 36 |
+
Currently available: `chunk`.
|
| 37 |
+
Default: `chunk`.
|
| 38 |
+
use_output_gate (bool, Optional):
|
| 39 |
+
Whether to use output gate. Default: `False`.
|
| 40 |
+
conv_size (int):
|
| 41 |
+
The kernel size of the short convolution. Default: 4.
|
| 42 |
+
layer_idx (int, Optional):
|
| 43 |
+
The index of the layer. Default: None.
|
| 44 |
+
norm_eps (float, Optional):
|
| 45 |
+
The epsilon value for the normalization layer. Default: 1e-5.
|
| 46 |
+
lambda_lower_bound (float):
|
| 47 |
+
The lower bound for the lambda parameter. Default: 0.25.
|
| 48 |
+
max_cg_step_training (int):
|
| 49 |
+
The maximum number of CG steps for training. Default: 30.
|
| 50 |
+
max_cg_step_decoding (int):
|
| 51 |
+
The maximum number of CG steps for decoding. Default: 30.
|
| 52 |
+
"""
|
| 53 |
+
|
| 54 |
+
def __init__(
|
| 55 |
+
self,
|
| 56 |
+
hidden_size: int = 2048,
|
| 57 |
+
num_heads: int = 16,
|
| 58 |
+
head_dim: int = 128,
|
| 59 |
+
mode: str = 'chunk',
|
| 60 |
+
use_output_gate: bool = False,
|
| 61 |
+
use_short_conv: bool = True,
|
| 62 |
+
conv_size: int = 4,
|
| 63 |
+
conv_bias: bool = False,
|
| 64 |
+
layer_idx: int = None,
|
| 65 |
+
norm_eps: float = 1e-5,
|
| 66 |
+
lambda_lower_bound: float = 0.25,
|
| 67 |
+
max_cg_step_training: int = 30,
|
| 68 |
+
max_cg_step_decoding: int = 30,
|
| 69 |
+
**kwargs,
|
| 70 |
+
) -> MesaNet:
|
| 71 |
+
super().__init__()
|
| 72 |
+
|
| 73 |
+
self.mode = mode
|
| 74 |
+
self.hidden_size = hidden_size
|
| 75 |
+
self.use_output_gate = use_output_gate
|
| 76 |
+
self.use_short_conv = use_short_conv
|
| 77 |
+
self.conv_size = conv_size
|
| 78 |
+
self.conv_bias = conv_bias
|
| 79 |
+
self.num_heads = num_heads
|
| 80 |
+
self.head_dim = head_dim
|
| 81 |
+
self.key_dim = self.num_heads * self.head_dim
|
| 82 |
+
self.value_dim = self.key_dim
|
| 83 |
+
self.head_k_dim = self.head_dim
|
| 84 |
+
self.head_v_dim = self.head_dim
|
| 85 |
+
self.layer_idx = layer_idx
|
| 86 |
+
self.lambda_lower_bound = lambda_lower_bound
|
| 87 |
+
self.max_cg_step_training = max_cg_step_training
|
| 88 |
+
self.max_cg_step_decoding = max_cg_step_decoding
|
| 89 |
+
|
| 90 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 91 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 92 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 93 |
+
self.a_proj = nn.Linear(hidden_size, self.num_heads, bias=True)
|
| 94 |
+
self.b_proj = nn.Linear(hidden_size, self.num_heads, bias=True)
|
| 95 |
+
|
| 96 |
+
lambda_initial_value = 1.0
|
| 97 |
+
init_lamb_value = torch.log(torch.exp(torch.tensor(lambda_initial_value - lambda_lower_bound)) - 1.0)
|
| 98 |
+
init_lamb_params = torch.empty(self.key_dim, dtype=torch.float32).fill_(init_lamb_value)
|
| 99 |
+
|
| 100 |
+
self.lambda_params = nn.Parameter(init_lamb_params)
|
| 101 |
+
self.lambda_params._no_weight_decay = True
|
| 102 |
+
|
| 103 |
+
self.conv_size = conv_size
|
| 104 |
+
self.q_conv1d = ShortConvolution(
|
| 105 |
+
hidden_size=self.key_dim,
|
| 106 |
+
kernel_size=conv_size,
|
| 107 |
+
bias=self.conv_bias,
|
| 108 |
+
activation='silu',
|
| 109 |
+
)
|
| 110 |
+
self.k_conv1d = ShortConvolution(
|
| 111 |
+
hidden_size=self.key_dim,
|
| 112 |
+
kernel_size=conv_size,
|
| 113 |
+
bias=self.conv_bias,
|
| 114 |
+
activation='silu',
|
| 115 |
+
)
|
| 116 |
+
if use_output_gate:
|
| 117 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 118 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
|
| 119 |
+
else:
|
| 120 |
+
self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
|
| 121 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 122 |
+
|
| 123 |
+
def forward(
|
| 124 |
+
self,
|
| 125 |
+
hidden_states: torch.Tensor,
|
| 126 |
+
attention_mask: torch.Tensor | None = None,
|
| 127 |
+
past_key_values: Cache | None = None,
|
| 128 |
+
use_cache: bool | None = False,
|
| 129 |
+
output_attentions: bool | None = False,
|
| 130 |
+
**kwargs: Unpack[dict],
|
| 131 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 132 |
+
if attention_mask is not None:
|
| 133 |
+
assert len(attention_mask.shape) == 2, (
|
| 134 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 135 |
+
"for padding purposes (0 indicating padding). "
|
| 136 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 140 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 141 |
+
|
| 142 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 143 |
+
if attention_mask is not None:
|
| 144 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 145 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 146 |
+
|
| 147 |
+
conv_state_q, conv_state_k = None, None
|
| 148 |
+
if last_state is not None:
|
| 149 |
+
conv_state_q, conv_state_k = last_state['conv_state']
|
| 150 |
+
q, conv_state_q = self.q_conv1d(
|
| 151 |
+
x=self.q_proj(hidden_states),
|
| 152 |
+
cache=conv_state_q,
|
| 153 |
+
output_final_state=use_cache,
|
| 154 |
+
cu_seqlens=cu_seqlens,
|
| 155 |
+
)
|
| 156 |
+
k, conv_state_k = self.k_conv1d(
|
| 157 |
+
x=self.k_proj(hidden_states),
|
| 158 |
+
cache=conv_state_k,
|
| 159 |
+
output_final_state=use_cache,
|
| 160 |
+
cu_seqlens=cu_seqlens,
|
| 161 |
+
)
|
| 162 |
+
v = self.v_proj(hidden_states)
|
| 163 |
+
|
| 164 |
+
q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
|
| 165 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 166 |
+
beta = self.b_proj(hidden_states).float().sigmoid()
|
| 167 |
+
g = F.logsigmoid(self.a_proj(hidden_states).float())
|
| 168 |
+
lamb = F.softplus(self.lambda_params.float()) + self.lambda_lower_bound
|
| 169 |
+
lamb = lamb.reshape(self.num_heads, -1)
|
| 170 |
+
|
| 171 |
+
last_h_kk, last_h_kv = last_state['recurrent_state'] if last_state is not None else (None, None)
|
| 172 |
+
|
| 173 |
+
# prefilling or training
|
| 174 |
+
# Note that QK will be normalized inside the kernel to avoid saving the activations, thereby reducing the memory usage.
|
| 175 |
+
if last_state is None:
|
| 176 |
+
o, h_kk, h_kv = chunk_mesa_net(
|
| 177 |
+
q=q,
|
| 178 |
+
k=k,
|
| 179 |
+
v=v,
|
| 180 |
+
g=g,
|
| 181 |
+
beta=beta,
|
| 182 |
+
lamb=lamb,
|
| 183 |
+
output_final_state=use_cache,
|
| 184 |
+
max_CG_iteration=self.max_cg_step_training,
|
| 185 |
+
use_qk_l2norm_in_kernel=True,
|
| 186 |
+
cu_seqlens=cu_seqlens,
|
| 187 |
+
)
|
| 188 |
+
# decoding
|
| 189 |
+
else:
|
| 190 |
+
q = l2_norm(q)
|
| 191 |
+
k = l2_norm(k)
|
| 192 |
+
o, h_kk, h_kv = mesa_net_decoding_one_step(
|
| 193 |
+
q=q.squeeze(0),
|
| 194 |
+
k=k.squeeze(0),
|
| 195 |
+
v=v.squeeze(0),
|
| 196 |
+
g=g.squeeze(0),
|
| 197 |
+
beta=beta.squeeze(0),
|
| 198 |
+
lamb=lamb,
|
| 199 |
+
prev_h_kk=last_h_kk,
|
| 200 |
+
prev_h_kv=last_h_kv,
|
| 201 |
+
max_CG_iteration=self.max_cg_step_decoding,
|
| 202 |
+
)
|
| 203 |
+
o = o.unsqueeze(0).to(q)
|
| 204 |
+
|
| 205 |
+
update_layer_cache(
|
| 206 |
+
self,
|
| 207 |
+
past_key_values,
|
| 208 |
+
recurrent_state=(h_kk, h_kv),
|
| 209 |
+
conv_state=(conv_state_q, conv_state_k),
|
| 210 |
+
offset=q_len,
|
| 211 |
+
)
|
| 212 |
+
if self.use_output_gate:
|
| 213 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 214 |
+
o = self.o_norm(o, g)
|
| 215 |
+
else:
|
| 216 |
+
o = self.o_norm(o)
|
| 217 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 218 |
+
o = self.o_proj(o)
|
| 219 |
+
if attention_mask is not None:
|
| 220 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 221 |
+
return o, None, past_key_values
|
fla/layers/mla.py
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
""" Implementing the Deepseek Multi Latent Attention (MLA) module. Reference:
|
| 4 |
+
|
| 5 |
+
https://github.com/huggingface/transformers/blob/main/src/transformers/models/deepseek_v3/modeling_deepseek_v3.py#L328
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import math
|
| 11 |
+
import warnings
|
| 12 |
+
from typing import TYPE_CHECKING
|
| 13 |
+
|
| 14 |
+
import torch
|
| 15 |
+
import torch.nn as nn
|
| 16 |
+
import torch.nn.functional as F
|
| 17 |
+
from einops import rearrange, repeat
|
| 18 |
+
from transformers.utils import logging
|
| 19 |
+
|
| 20 |
+
from fla.layers.utils import pad_input, unpad_input
|
| 21 |
+
from fla.modules import RMSNorm, RotaryEmbedding
|
| 22 |
+
from fla.ops.utils.index import prepare_lens_from_mask
|
| 23 |
+
|
| 24 |
+
if TYPE_CHECKING:
|
| 25 |
+
from fla.models.utils import Cache
|
| 26 |
+
|
| 27 |
+
try:
|
| 28 |
+
from flash_attn import flash_attn_func, flash_attn_varlen_func
|
| 29 |
+
except ImportError:
|
| 30 |
+
warnings.warn(
|
| 31 |
+
"Flash Attention is not installed. Please install it via `pip install flash-attn --no-build-isolation`",
|
| 32 |
+
category=ImportWarning,
|
| 33 |
+
)
|
| 34 |
+
flash_attn_func = None
|
| 35 |
+
|
| 36 |
+
logger = logging.get_logger(__name__)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def yarn_get_mscale(scale=1, mscale=1):
|
| 40 |
+
if scale <= 1:
|
| 41 |
+
return 1.0
|
| 42 |
+
return 0.1 * mscale * math.log(scale) + 1.0
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class MultiheadLatentAttention(nn.Module):
|
| 46 |
+
r"""
|
| 47 |
+
Multi-headed attention from [Deepseek V2](https://arxiv.org/abs/2405.04434)
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
def __init__(
|
| 51 |
+
self,
|
| 52 |
+
hidden_size: int = 2048,
|
| 53 |
+
num_heads: int = 16,
|
| 54 |
+
q_lora_rank: int | None = 1536, # q lora rank is optional, None indicates no q lora
|
| 55 |
+
qk_rope_head_dim: int = 64,
|
| 56 |
+
kv_lora_rank: int = 512, # following the original Deepseek paper
|
| 57 |
+
v_head_dim: int = 128,
|
| 58 |
+
qk_nope_head_dim: int = 128,
|
| 59 |
+
qk_head_dim: int | None = 192, # qk_nope_head_dim + qk_rope_head_dim
|
| 60 |
+
window_size: int | None = None,
|
| 61 |
+
rope_theta: float = 10000.,
|
| 62 |
+
max_position_embeddings: int | None = None,
|
| 63 |
+
rope_scaling: dict | None = None,
|
| 64 |
+
layer_idx: int = None,
|
| 65 |
+
) -> MultiheadLatentAttention:
|
| 66 |
+
super().__init__()
|
| 67 |
+
|
| 68 |
+
# sanity check
|
| 69 |
+
if qk_head_dim is not None:
|
| 70 |
+
assert qk_head_dim == qk_nope_head_dim + qk_rope_head_dim, \
|
| 71 |
+
f"qk_head_dim {qk_head_dim} != qk_nope_head_dim {qk_nope_head_dim} + qk_rope_head_dim {qk_rope_head_dim}"
|
| 72 |
+
else:
|
| 73 |
+
qk_head_dim = qk_nope_head_dim + qk_rope_head_dim
|
| 74 |
+
|
| 75 |
+
# attention params info
|
| 76 |
+
self.hidden_size = hidden_size
|
| 77 |
+
self.num_heads = num_heads
|
| 78 |
+
self.q_lora_rank = q_lora_rank
|
| 79 |
+
self.qk_rope_head_dim = qk_rope_head_dim
|
| 80 |
+
self.kv_lora_rank = kv_lora_rank
|
| 81 |
+
self.v_head_dim = v_head_dim
|
| 82 |
+
self.qk_nope_head_dim = qk_nope_head_dim
|
| 83 |
+
self.qk_head_dim = qk_head_dim
|
| 84 |
+
|
| 85 |
+
self.window_size = window_size
|
| 86 |
+
self.rope_theta = rope_theta
|
| 87 |
+
self.max_position_embeddings = max_position_embeddings
|
| 88 |
+
self.layer_idx = layer_idx
|
| 89 |
+
|
| 90 |
+
if flash_attn_func is None:
|
| 91 |
+
raise ImportError("Please install Flash Attention via `pip install flash-attn --no-build-isolation` first")
|
| 92 |
+
|
| 93 |
+
if q_lora_rank is not None:
|
| 94 |
+
self.q_proj = nn.Sequential(
|
| 95 |
+
nn.Linear(hidden_size, q_lora_rank, bias=False),
|
| 96 |
+
RMSNorm(q_lora_rank, dtype=torch.float32),
|
| 97 |
+
nn.Linear(q_lora_rank, self.num_heads * self.qk_head_dim, bias=False),
|
| 98 |
+
)
|
| 99 |
+
else:
|
| 100 |
+
self.q_proj = nn.Linear(hidden_size, self.num_heads * self.qk_head_dim, bias=False)
|
| 101 |
+
|
| 102 |
+
self.k_rope = nn.Linear(hidden_size, self.qk_rope_head_dim, bias=False)
|
| 103 |
+
self.kv_proj = nn.Sequential(
|
| 104 |
+
nn.Linear(hidden_size, self.kv_lora_rank, bias=False),
|
| 105 |
+
RMSNorm(self.kv_lora_rank, dtype=torch.float32),
|
| 106 |
+
nn.Linear(self.kv_lora_rank, self.num_heads * (self.qk_nope_head_dim + self.v_head_dim), bias=False),
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
self.o_proj = nn.Linear(self.num_heads * self.v_head_dim, hidden_size, bias=False)
|
| 110 |
+
|
| 111 |
+
self.scaling = self.qk_head_dim ** (-0.5)
|
| 112 |
+
if rope_scaling is not None and rope_scaling.get("rope_type", "default") != "default":
|
| 113 |
+
mscale_all_dim = rope_scaling.get("mscale_all_dim", 0)
|
| 114 |
+
scaling_factor = rope_scaling["factor"]
|
| 115 |
+
if mscale_all_dim:
|
| 116 |
+
mscale = yarn_get_mscale(scaling_factor, mscale_all_dim)
|
| 117 |
+
self.scaling = self.scaling * mscale * mscale
|
| 118 |
+
|
| 119 |
+
self.rotary = RotaryEmbedding(dim=self.qk_rope_head_dim, base=self.rope_theta)
|
| 120 |
+
|
| 121 |
+
def forward(
|
| 122 |
+
self,
|
| 123 |
+
hidden_states: torch.Tensor,
|
| 124 |
+
attention_mask: torch.Tensor | None,
|
| 125 |
+
past_key_values: Cache | None = None,
|
| 126 |
+
output_attentions: bool = False,
|
| 127 |
+
use_cache: bool = False,
|
| 128 |
+
**kwargs,
|
| 129 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 130 |
+
# if attention_mask is not None, this is doing inference
|
| 131 |
+
if attention_mask is not None:
|
| 132 |
+
assert len(attention_mask.shape) == 2, (
|
| 133 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 134 |
+
"for padding purposes (0 indicating padding). "
|
| 135 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
# prepare q, k, v
|
| 139 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 140 |
+
|
| 141 |
+
q_states = self.q_proj(hidden_states)
|
| 142 |
+
q_states = rearrange(q_states, '... (h d) -> ... h d', d=self.qk_head_dim)
|
| 143 |
+
q_pass, q_rot = torch.split(q_states, [self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)
|
| 144 |
+
k_pass, k_rot = self.kv_proj(hidden_states), self.k_rope(hidden_states)
|
| 145 |
+
|
| 146 |
+
k_rot = rearrange(k_rot, 'b t d -> b t 1 d')
|
| 147 |
+
k_pass = rearrange(k_pass, '... (h d) -> ... h d', d=self.qk_nope_head_dim + self.v_head_dim)
|
| 148 |
+
k_pass, v = torch.split(k_pass, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
|
| 149 |
+
|
| 150 |
+
# apply rotary position embedding
|
| 151 |
+
seqlen_offset, max_seqlen = 0, q_len
|
| 152 |
+
if past_key_values is not None:
|
| 153 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 154 |
+
max_seqlen = q_len + seqlen_offset
|
| 155 |
+
|
| 156 |
+
if attention_mask is not None:
|
| 157 |
+
seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
|
| 158 |
+
max_seqlen = q_len + max(seqlen_offset)
|
| 159 |
+
|
| 160 |
+
if self.max_position_embeddings is not None:
|
| 161 |
+
max_seqlen = max(max_seqlen, self.max_position_embeddings)
|
| 162 |
+
cu_seqlens = kwargs.get("cu_seqlens")
|
| 163 |
+
q_rot, k_rot = self.rotary(
|
| 164 |
+
q_rot, k_rot, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens,
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
k_rot = repeat(k_rot, 'b t 1 d -> b t h d', h=self.num_heads)
|
| 168 |
+
q = torch.cat((q_pass, q_rot), dim=-1)
|
| 169 |
+
k = torch.cat((k_pass, k_rot), dim=-1)
|
| 170 |
+
|
| 171 |
+
# TODO: instead of caching the full k, v, we can actually only cache the compressed_kv and k_rot
|
| 172 |
+
# and recover the full k, v from compressed_kv and k_rot
|
| 173 |
+
if past_key_values is not None:
|
| 174 |
+
cache_has_content = past_key_values.get_seq_length(self.layer_idx) > 0
|
| 175 |
+
k_cached, v_cached = past_key_values.update(
|
| 176 |
+
attn_state=(k, v),
|
| 177 |
+
layer_idx=self.layer_idx,
|
| 178 |
+
offset=q_len,
|
| 179 |
+
)['attn_state']
|
| 180 |
+
if cache_has_content:
|
| 181 |
+
k, v = k_cached, v_cached
|
| 182 |
+
|
| 183 |
+
# Head dim match to use flash-attn
|
| 184 |
+
if self.qk_head_dim != self.v_head_dim:
|
| 185 |
+
v = F.pad(v, [0, self.qk_head_dim - self.v_head_dim])
|
| 186 |
+
|
| 187 |
+
# Contains at least one padding token in the sequence
|
| 188 |
+
if attention_mask is not None:
|
| 189 |
+
if q.shape[1] == 1 and self.window_size is not None:
|
| 190 |
+
attention_mask = attention_mask[:, -self.window_size:]
|
| 191 |
+
q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(q, (k, v), attention_mask, q_len)
|
| 192 |
+
cu_seqlens_q, cu_seqlens_k = cu_seqlens
|
| 193 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 194 |
+
o = flash_attn_varlen_func(
|
| 195 |
+
q, k, v,
|
| 196 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 197 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 198 |
+
max_seqlen_q=max_seqlen_q,
|
| 199 |
+
max_seqlen_k=max_seqlen_k,
|
| 200 |
+
causal=True,
|
| 201 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 202 |
+
)
|
| 203 |
+
o = pad_input(o, indices_q, batch_size, q_len)
|
| 204 |
+
elif cu_seqlens is not None:
|
| 205 |
+
o = flash_attn_varlen_func(
|
| 206 |
+
q.squeeze(0), k.squeeze(0), v.squeeze(0),
|
| 207 |
+
cu_seqlens_q=cu_seqlens,
|
| 208 |
+
cu_seqlens_k=cu_seqlens,
|
| 209 |
+
max_seqlen_q=max_seqlen,
|
| 210 |
+
max_seqlen_k=max_seqlen,
|
| 211 |
+
causal=True,
|
| 212 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 213 |
+
).unsqueeze(0)
|
| 214 |
+
else:
|
| 215 |
+
o = flash_attn_func(
|
| 216 |
+
q, k, v,
|
| 217 |
+
causal=True,
|
| 218 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 219 |
+
)
|
| 220 |
+
|
| 221 |
+
if self.qk_head_dim != self.v_head_dim:
|
| 222 |
+
o = o[:, :, :, :self.v_head_dim]
|
| 223 |
+
o = o.reshape(batch_size, q_len, -1)
|
| 224 |
+
o = self.o_proj(o)
|
| 225 |
+
return o, None, past_key_values
|
fla/layers/mom.py
ADDED
|
@@ -0,0 +1,831 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
import math
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from einops import rearrange
|
| 10 |
+
from torch.nn import functional as F
|
| 11 |
+
|
| 12 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 13 |
+
from fla.ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule
|
| 14 |
+
|
| 15 |
+
if TYPE_CHECKING:
|
| 16 |
+
from transformers.processing_utils import Unpack
|
| 17 |
+
|
| 18 |
+
from fla.models.utils import Cache
|
| 19 |
+
|
| 20 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, unpad_input, update_layer_cache
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def _upad_input(
|
| 24 |
+
query_layer: torch.Tensor,
|
| 25 |
+
key_layer: torch.Tensor,
|
| 26 |
+
value_layer: torch.Tensor,
|
| 27 |
+
gate_layer: torch.Tensor,
|
| 28 |
+
beta_layer: torch.Tensor,
|
| 29 |
+
attention_mask: torch.Tensor,
|
| 30 |
+
):
|
| 31 |
+
"""
|
| 32 |
+
Unpads query, key, and values tensors, using a single dimension for all tokens even though they belong to
|
| 33 |
+
different batches.
|
| 34 |
+
|
| 35 |
+
This function is used instead of `flash_attn.bert_padding.unpad_input` in order to avoid the recomputation
|
| 36 |
+
of the same intermediary
|
| 37 |
+
tensors for query, key, value tensors.
|
| 38 |
+
|
| 39 |
+
Arguments:
|
| 40 |
+
query_layer (`torch.Tensor`):
|
| 41 |
+
Query state with padding. Shape: (batch_size, query_length, num_heads, head_dim).
|
| 42 |
+
key_layer (`torch.Tensor`):
|
| 43 |
+
Key state with padding. Shape: (batch_size, kv_seq_len, num_key_value_heads, head_dim).
|
| 44 |
+
value_layer (`torch.Tensor`):
|
| 45 |
+
Value state with padding. Shape: (batch_size, kv_seq_len, num_key_value_heads, head_dim).
|
| 46 |
+
attention_mask (`torch.Tensor`):
|
| 47 |
+
Boolean or int tensor of shape (batch_size, sequence_length), 1 means valid and 0 means not valid.
|
| 48 |
+
query_length (`int`):
|
| 49 |
+
Target length.
|
| 50 |
+
|
| 51 |
+
Return:
|
| 52 |
+
query_layer (`torch.Tensor`):
|
| 53 |
+
Query state without padding. Shape: (total_target_length, num_heads, head_dim).
|
| 54 |
+
key_layer (`torch.Tensor`):
|
| 55 |
+
Key state with padding. Shape: (total_source_length, num_key_value_heads, head_dim).
|
| 56 |
+
value_layer (`torch.Tensor`):
|
| 57 |
+
Value state with padding. Shape: (total_source_length, num_key_value_heads, head_dim).
|
| 58 |
+
indices_q (`torch.Tensor`):
|
| 59 |
+
The indices of non-masked tokens from the flattened input target sequence.
|
| 60 |
+
(cu_seqlens_q, cu_seqlens_k) (`Tuple[int]`):
|
| 61 |
+
The cumulative sequence lengths for the target (query) and source (key, value), used to index
|
| 62 |
+
into ragged (unpadded) tensors. `cu_seqlens` shape is (batch_size + 1,).
|
| 63 |
+
(max_seqlen_in_batch_q, max_seqlen_in_batch_k) (`Tuple[int]`):
|
| 64 |
+
Maximum sequence length in batch (`max_seqlen_in_batch_q` for the target sequence i.e. query,
|
| 65 |
+
`max_seqlen_in_batch_k` for the source sequence i.e. key/value).
|
| 66 |
+
"""
|
| 67 |
+
query_length = query_layer.shape[1]
|
| 68 |
+
indices_k, cu_seqlens_k, max_seqlen_in_batch_k = get_unpad_data(attention_mask)
|
| 69 |
+
batch_size, kv_seq_len, dim = key_layer.shape
|
| 70 |
+
v_dim = value_layer.shape[-1]
|
| 71 |
+
|
| 72 |
+
key_layer = index_first_axis(key_layer.reshape(batch_size * kv_seq_len, dim), indices_k)
|
| 73 |
+
value_layer = index_first_axis(
|
| 74 |
+
value_layer.reshape(batch_size * kv_seq_len, v_dim), indices_k,
|
| 75 |
+
)
|
| 76 |
+
gate_layer = index_first_axis(gate_layer.reshape(batch_size * kv_seq_len, -1), indices_k)
|
| 77 |
+
beta_layer = index_first_axis(beta_layer.reshape(batch_size * kv_seq_len, -1), indices_k)
|
| 78 |
+
if query_length == kv_seq_len:
|
| 79 |
+
query_layer = index_first_axis(query_layer.reshape(batch_size * kv_seq_len, dim), indices_k)
|
| 80 |
+
cu_seqlens_q = cu_seqlens_k
|
| 81 |
+
max_seqlen_in_batch_q = max_seqlen_in_batch_k
|
| 82 |
+
indices_q = indices_k
|
| 83 |
+
elif query_length == 1:
|
| 84 |
+
max_seqlen_in_batch_q = 1
|
| 85 |
+
cu_seqlens_q = torch.arange(
|
| 86 |
+
batch_size + 1, dtype=torch.int32, device=query_layer.device,
|
| 87 |
+
) # There is a memcpy here, that is very bad.
|
| 88 |
+
indices_q = cu_seqlens_q[:-1]
|
| 89 |
+
query_layer = query_layer.squeeze(1)
|
| 90 |
+
else:
|
| 91 |
+
# The -q_len: slice assumes left padding.
|
| 92 |
+
attention_mask = attention_mask[:, -query_length:]
|
| 93 |
+
query_layer, indices_q, cu_seqlens_q, max_seqlen_in_batch_q = unpad_input(query_layer, attention_mask)
|
| 94 |
+
|
| 95 |
+
return (
|
| 96 |
+
query_layer,
|
| 97 |
+
key_layer,
|
| 98 |
+
value_layer,
|
| 99 |
+
gate_layer,
|
| 100 |
+
beta_layer,
|
| 101 |
+
indices_q,
|
| 102 |
+
(cu_seqlens_q, cu_seqlens_k),
|
| 103 |
+
(max_seqlen_in_batch_q, max_seqlen_in_batch_k),
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def transform(
|
| 108 |
+
x: torch.Tensor,
|
| 109 |
+
routing_mask: torch.Tensor,
|
| 110 |
+
num_memories: int,
|
| 111 |
+
selected_memories: torch.Tensor,
|
| 112 |
+
attention_mask: torch.Tensor,
|
| 113 |
+
):
|
| 114 |
+
"""
|
| 115 |
+
Reorganize token embeddings into memory-aligned chunks.
|
| 116 |
+
|
| 117 |
+
Steps:
|
| 118 |
+
- Expand for top-k routing if needed.
|
| 119 |
+
- Mask out padded tokens via `attention_mask`.
|
| 120 |
+
- Sort tokens by (batch, memory).
|
| 121 |
+
- Gather and pad tokens per memory slot.
|
| 122 |
+
|
| 123 |
+
Args:
|
| 124 |
+
x: (batch, seq, hidden) input embeddings.
|
| 125 |
+
routing_mask: (batch, seq, num_memories) binary routing mask.
|
| 126 |
+
num_memories: number of memory slots.
|
| 127 |
+
selected_memories: memory indices per token,
|
| 128 |
+
(batch, seq) if k=1 else (batch, seq, topk).
|
| 129 |
+
attention_mask: (batch, seq) valid-token mask.
|
| 130 |
+
|
| 131 |
+
Returns:
|
| 132 |
+
transformed_x: (num_memories, batch, max_len, hidden) reorganized tokens.
|
| 133 |
+
truncation_indices: (batch*num_memories, max_len) gather indices.
|
| 134 |
+
sorted_indices: (batch*seq*topk,) global sort order.
|
| 135 |
+
max_len: int, max tokens per memory.
|
| 136 |
+
mask: (batch*num_memories, max_len) validity mask.
|
| 137 |
+
mask_2: (num_memories, batch, max_len) validity mask reshaped.
|
| 138 |
+
"""
|
| 139 |
+
if selected_memories.dim() == 3:
|
| 140 |
+
# (batch, seq, topk)
|
| 141 |
+
topk = selected_memories.shape[2]
|
| 142 |
+
# x (batch, seq, hidden)
|
| 143 |
+
x = x.repeat_interleave(topk, dim=1)
|
| 144 |
+
# x (batch, seq * topk, hidden)
|
| 145 |
+
# (batch, seq, topk)
|
| 146 |
+
selected_memories = selected_memories.reshape(selected_memories.shape[0], -1)
|
| 147 |
+
# (batch, seq * topk)
|
| 148 |
+
|
| 149 |
+
if attention_mask is not None:
|
| 150 |
+
attention_mask = attention_mask[:, -routing_mask.shape[1]:]
|
| 151 |
+
# mask out the masked tokens
|
| 152 |
+
routing_mask[attention_mask.bitwise_not().unsqueeze(-1).expand(-1, -1, num_memories)] = 0
|
| 153 |
+
|
| 154 |
+
b, s, d = x.shape
|
| 155 |
+
x_flat = x.reshape(b * s, d) # [b*s, d]
|
| 156 |
+
|
| 157 |
+
with torch.no_grad():
|
| 158 |
+
batch_indices = torch.arange(b, device=x.device).unsqueeze(-1)
|
| 159 |
+
batch_indices = batch_indices.repeat(1, s).reshape(-1)
|
| 160 |
+
if attention_mask is not None:
|
| 161 |
+
# sort the masked tokens to the end
|
| 162 |
+
batch_indices[attention_mask.repeat_interleave(topk, dim=1).bitwise_not().flatten()] = b
|
| 163 |
+
# (b * s)
|
| 164 |
+
memories_flat = selected_memories.reshape(-1) # [b*s]
|
| 165 |
+
|
| 166 |
+
combined = batch_indices * (memories_flat.max() + 1) + memories_flat
|
| 167 |
+
sorted_indices = combined.argsort()
|
| 168 |
+
|
| 169 |
+
x_sorted = x_flat[sorted_indices] # [b*s, d]
|
| 170 |
+
# (b*s, hidden) -> (b, s, hidd)
|
| 171 |
+
with torch.no_grad():
|
| 172 |
+
# routing_mask (b, s, num_memories)
|
| 173 |
+
batch_memory_tokens = routing_mask.sum(dim=1)
|
| 174 |
+
# (b, num_memories)
|
| 175 |
+
flatten_offset = batch_memory_tokens.flatten().cumsum(dim=0)
|
| 176 |
+
max_len = batch_memory_tokens.max()
|
| 177 |
+
indices = (
|
| 178 |
+
torch.arange(max_len, device=flatten_offset.device).unsqueeze(0).expand(b * num_memories, -1)
|
| 179 |
+
+ torch.cat([torch.tensor([0], device=flatten_offset.device), flatten_offset[:-1]], dim=0).unsqueeze(1)
|
| 180 |
+
)
|
| 181 |
+
mask = indices < flatten_offset.unsqueeze(-1)
|
| 182 |
+
truncation_indices = torch.where(mask, indices, torch.zeros_like(indices))
|
| 183 |
+
|
| 184 |
+
gathered_x = torch.gather(x_sorted, 0, truncation_indices.reshape(-1).unsqueeze(-1).expand(-1, d))
|
| 185 |
+
transformed_x = gathered_x.reshape(b * num_memories, -1, d).reshape((b, num_memories, max_len, d)).transpose(0, 1)
|
| 186 |
+
# transformed_x = transformed_x * mask.unsqueeze(-1).expand_as(transformed_x)
|
| 187 |
+
# pad_x = torch.zeros((b * num_memories, capacity_len-max_len, d), dtype=transformed_x.dtype, device=transformed_x.device)
|
| 188 |
+
# pad_mask = torch.zeros((b * num_memories, capacity_len-max_len), dtype=transformed_x.dtype, device=transformed_x.device)
|
| 189 |
+
# left pad
|
| 190 |
+
# transformed_x = torch.cat((pad_x, transformed_x), dim=1).reshape((b, num_memories, capacity_len, d)).transpose(0, 1)
|
| 191 |
+
mask_2 = mask.reshape((b, num_memories, max_len)).transpose(0, 1)
|
| 192 |
+
# truncation_indices += capacity_len-max_len
|
| 193 |
+
# if attention_mask is not None:
|
| 194 |
+
# mask_2
|
| 195 |
+
|
| 196 |
+
return transformed_x, truncation_indices, sorted_indices, max_len, mask, mask_2
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def reconstruct(
|
| 200 |
+
transformed_x,
|
| 201 |
+
indices: torch.Tensor,
|
| 202 |
+
sorted_indices: torch.Tensor,
|
| 203 |
+
batch_size: int,
|
| 204 |
+
seq_len: int,
|
| 205 |
+
topk: int,
|
| 206 |
+
routing_weights: torch.Tensor,
|
| 207 |
+
mask: torch.Tensor,
|
| 208 |
+
):
|
| 209 |
+
'''
|
| 210 |
+
Reconstruct and mix transformed outputs back into the original input sequence shape.
|
| 211 |
+
|
| 212 |
+
Key operations:
|
| 213 |
+
1. Reshapes and transposes `transformed_x` to prepare for scattering.
|
| 214 |
+
2. Applies the `mask` to zero out invalid positions.
|
| 215 |
+
3. Uses `torch.scatter_add_` to scatter and sum the transformed outputs back to their original positions
|
| 216 |
+
based on `indices`.
|
| 217 |
+
4. Rearranges the scattered outputs using `sorted_indices` to ensure correct ordering.
|
| 218 |
+
5. Applies the `routing_weights` to weight the outputs.
|
| 219 |
+
6. Sums over the `topk` dimension to produce the final reconstructed output.
|
| 220 |
+
|
| 221 |
+
Args:
|
| 222 |
+
transformed_x (torch.Tensor):
|
| 223 |
+
The transformed output tensor from memory units or experts.
|
| 224 |
+
Shape: (num_memories, batch_size, capacity_len, hidden_size)
|
| 225 |
+
indices (torch.Tensor):
|
| 226 |
+
Indices used for scattering the transformed outputs back to their corresponding positions.
|
| 227 |
+
Shape: (batch*num_memories, max_len)
|
| 228 |
+
sorted_indices (torch.Tensor):
|
| 229 |
+
Sorting indices used to rearrange the scattered outputs back into the original sequence order.
|
| 230 |
+
Shape: (batch_size*seq_len*topk)
|
| 231 |
+
batch_size (int):
|
| 232 |
+
The size of the batch.
|
| 233 |
+
seq_len (int):
|
| 234 |
+
The length of the input sequence.
|
| 235 |
+
topk (int):
|
| 236 |
+
The number of top elements selected (`topk`) per token during the selection process.
|
| 237 |
+
routing_weights (torch.Tensor):
|
| 238 |
+
Routing weights assigned to the top-k selected outputs when reconstructing the final output.
|
| 239 |
+
Shape: (batch_size, seq_len, topk)
|
| 240 |
+
mask (torch.Tensor):
|
| 241 |
+
Boolean mask indicating valid positions in the sequence.
|
| 242 |
+
Shape: (batch*num_memories, max_len)
|
| 243 |
+
|
| 244 |
+
Returns:
|
| 245 |
+
restored_x (torch.Tensor):
|
| 246 |
+
The reconstructed output tensor in the original input sequence shape.
|
| 247 |
+
Shape: (batch_size, seq_len, hidden_size)
|
| 248 |
+
'''
|
| 249 |
+
transformed_x = transformed_x.transpose(0, 1).reshape(
|
| 250 |
+
(-1, transformed_x.shape[2], transformed_x.shape[3]))
|
| 251 |
+
b, s, k, d = batch_size, seq_len, topk, transformed_x.shape[2]
|
| 252 |
+
gathered_x = transformed_x.reshape(
|
| 253 |
+
(transformed_x.shape[0] * transformed_x.shape[1], transformed_x.shape[2]))
|
| 254 |
+
mask_expanded = mask.reshape(-1).unsqueeze(-1).expand_as(gathered_x)
|
| 255 |
+
gathered_x = gathered_x * mask_expanded
|
| 256 |
+
|
| 257 |
+
assert (indices >= 0).all(), "Indices should be non-negative"
|
| 258 |
+
|
| 259 |
+
resortd_x = torch.zeros((b * s * k, d), device=gathered_x.device, dtype=gathered_x.dtype).scatter_add_(
|
| 260 |
+
0,
|
| 261 |
+
indices.reshape(-1).unsqueeze(-1).expand(-1, d),
|
| 262 |
+
gathered_x,
|
| 263 |
+
)
|
| 264 |
+
assert (indices < resortd_x.size(0)).all(), "Indices should be less than resortd_x size"
|
| 265 |
+
|
| 266 |
+
inverse_indices = sorted_indices.argsort()
|
| 267 |
+
rearranged_x_flat = resortd_x[inverse_indices]
|
| 268 |
+
restored_x = rearranged_x_flat.reshape((b, s * k, d))
|
| 269 |
+
restored_x = restored_x.reshape(b, s, k, d) * routing_weights.reshape(b, s, k).unsqueeze(-1)
|
| 270 |
+
restored_x = restored_x.sum(dim=2)
|
| 271 |
+
return restored_x
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
class MomAttention(nn.Module):
|
| 275 |
+
"""
|
| 276 |
+
The layer implementaion for [MoM: Linear Sequence Modeling with Mixture-of-Memories](https://arxiv.org/abs/2502.13685).
|
| 277 |
+
"""
|
| 278 |
+
|
| 279 |
+
def __init__(
|
| 280 |
+
self,
|
| 281 |
+
hidden_size: int = 2048,
|
| 282 |
+
head_dim: int = 256,
|
| 283 |
+
num_heads: int = 4,
|
| 284 |
+
expand_v: float = 2,
|
| 285 |
+
mode: str = 'chunk',
|
| 286 |
+
use_output_gate: bool = True,
|
| 287 |
+
use_short_conv: bool = True,
|
| 288 |
+
conv_size: int = 4,
|
| 289 |
+
conv_bias: bool = False,
|
| 290 |
+
layer_idx: int = None,
|
| 291 |
+
norm_eps: float = 1e-5,
|
| 292 |
+
num_memories: int = 8,
|
| 293 |
+
topk: int = 2,
|
| 294 |
+
capacity: float = 1.0,
|
| 295 |
+
shared_mem: bool = False,
|
| 296 |
+
single_kv_proj: bool = False,
|
| 297 |
+
**kwargs,
|
| 298 |
+
) -> MomAttention:
|
| 299 |
+
super().__init__()
|
| 300 |
+
self.num_memories = num_memories
|
| 301 |
+
self.topk = topk
|
| 302 |
+
self.capacity = capacity
|
| 303 |
+
self.shared_mem = shared_mem
|
| 304 |
+
self.single_kv_proj = single_kv_proj
|
| 305 |
+
|
| 306 |
+
self.mode = mode
|
| 307 |
+
|
| 308 |
+
self.hidden_size = hidden_size
|
| 309 |
+
self.expand_v = expand_v
|
| 310 |
+
|
| 311 |
+
self.use_output_gate = use_output_gate
|
| 312 |
+
self.use_short_conv = use_short_conv
|
| 313 |
+
self.conv_size = conv_size
|
| 314 |
+
self.conv_bias = conv_bias
|
| 315 |
+
|
| 316 |
+
self.head_dim = head_dim
|
| 317 |
+
self.num_heads = num_heads
|
| 318 |
+
|
| 319 |
+
self.key_dim = int(self.num_heads * self.head_dim)
|
| 320 |
+
self.value_dim = int(self.key_dim * self.expand_v)
|
| 321 |
+
self.head_qk_dim = head_dim
|
| 322 |
+
self.head_v_dim = int(head_dim * self.expand_v)
|
| 323 |
+
self.layer_idx = layer_idx
|
| 324 |
+
self.silu = nn.SiLU()
|
| 325 |
+
|
| 326 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not suppoerted mode `{mode}`."
|
| 327 |
+
|
| 328 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 329 |
+
self.gate = nn.Linear(self.hidden_size, self.num_memories, bias=False)
|
| 330 |
+
if self.single_kv_proj:
|
| 331 |
+
self.shared_k = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 332 |
+
self.shared_v = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 333 |
+
self.shared_b = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 334 |
+
self.shared_a = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 335 |
+
else:
|
| 336 |
+
self.k_proj = nn.ModuleList([
|
| 337 |
+
nn.Linear(self.hidden_size, self.key_dim, bias=False)
|
| 338 |
+
for _ in range(self.num_memories)
|
| 339 |
+
])
|
| 340 |
+
self.v_proj = nn.ModuleList([
|
| 341 |
+
nn.Linear(self.hidden_size, self.value_dim, bias=False)
|
| 342 |
+
for _ in range(self.num_memories)
|
| 343 |
+
])
|
| 344 |
+
self.b_proj = nn.ModuleList([
|
| 345 |
+
nn.Linear(self.hidden_size, self.num_heads, bias=False)
|
| 346 |
+
for _ in range(self.num_memories)
|
| 347 |
+
])
|
| 348 |
+
self.a_proj = nn.ModuleList([
|
| 349 |
+
nn.Linear(self.hidden_size, self.num_heads, bias=False)
|
| 350 |
+
for _ in range(self.num_memories)
|
| 351 |
+
])
|
| 352 |
+
if self.shared_mem:
|
| 353 |
+
self.shared_k = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 354 |
+
self.shared_v = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 355 |
+
self.shared_b = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 356 |
+
self.shared_a = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 357 |
+
|
| 358 |
+
A = torch.empty(self.num_heads, dtype=torch.float32).uniform_(0, 16)
|
| 359 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 360 |
+
self.A_log._no_weight_decay = True
|
| 361 |
+
# hard coded for now
|
| 362 |
+
dt_min = 0.001
|
| 363 |
+
dt_max = 0.1
|
| 364 |
+
dt_init_floor = 1e-4
|
| 365 |
+
dt = torch.exp(
|
| 366 |
+
torch.rand(self.num_heads) * (math.log(dt_max) - math.log(dt_min))
|
| 367 |
+
+ math.log(dt_min),
|
| 368 |
+
)
|
| 369 |
+
dt = torch.clamp(dt, min=dt_init_floor)
|
| 370 |
+
# Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
|
| 371 |
+
inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| 372 |
+
self.dt_bias = nn.Parameter(inv_dt)
|
| 373 |
+
# Just to be explicit. Without this we already don't put wd on dt_bias because of the check
|
| 374 |
+
# name.endswith("bias") in param_grouping.py
|
| 375 |
+
self.dt_bias._no_weight_decay = True
|
| 376 |
+
|
| 377 |
+
if use_short_conv:
|
| 378 |
+
self.conv_size = conv_size
|
| 379 |
+
self.q_conv1d = ShortConvolution(
|
| 380 |
+
hidden_size=self.key_dim,
|
| 381 |
+
kernel_size=conv_size,
|
| 382 |
+
bias=conv_bias,
|
| 383 |
+
activation='silu',
|
| 384 |
+
)
|
| 385 |
+
self.k_conv1d = ShortConvolution(
|
| 386 |
+
hidden_size=self.key_dim,
|
| 387 |
+
kernel_size=conv_size,
|
| 388 |
+
bias=conv_bias,
|
| 389 |
+
activation='silu',
|
| 390 |
+
)
|
| 391 |
+
self.v_conv1d = ShortConvolution(
|
| 392 |
+
hidden_size=self.value_dim,
|
| 393 |
+
kernel_size=conv_size,
|
| 394 |
+
bias=conv_bias,
|
| 395 |
+
activation='silu',
|
| 396 |
+
)
|
| 397 |
+
else:
|
| 398 |
+
raise UserWarning(
|
| 399 |
+
"ShortConvolution is crucial to the performance. "
|
| 400 |
+
"Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
|
| 401 |
+
)
|
| 402 |
+
if use_output_gate:
|
| 403 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 404 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
|
| 405 |
+
else:
|
| 406 |
+
self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
|
| 407 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 408 |
+
self.apply(self._initialize_weights)
|
| 409 |
+
|
| 410 |
+
def _initialize_weights(self, module: nn.Module):
|
| 411 |
+
if getattr(module, "_is_hf_initialized", False):
|
| 412 |
+
return
|
| 413 |
+
if isinstance(module, nn.Linear):
|
| 414 |
+
nn.init.xavier_uniform_(module.weight, gain=2 ** -2.5)
|
| 415 |
+
if module.bias is not None:
|
| 416 |
+
nn.init.zeros_(module.bias)
|
| 417 |
+
module._is_hf_initialized = True
|
| 418 |
+
|
| 419 |
+
def forward(
|
| 420 |
+
self,
|
| 421 |
+
hidden_states: torch.Tensor,
|
| 422 |
+
attention_mask: torch.Tensor | None = None,
|
| 423 |
+
past_key_values: Cache | None = None,
|
| 424 |
+
use_cache: bool | None = False,
|
| 425 |
+
output_attentions: bool | None = False,
|
| 426 |
+
**kwargs: Unpack[dict],
|
| 427 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 428 |
+
if attention_mask is not None:
|
| 429 |
+
attention_mask = (attention_mask == 1)
|
| 430 |
+
assert len(attention_mask.shape) == 2, (
|
| 431 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 432 |
+
"for padding purposes (0 indicating padding). "
|
| 433 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 434 |
+
)
|
| 435 |
+
|
| 436 |
+
origin_cu_seqlens = kwargs.get('cu_seqlens')
|
| 437 |
+
if origin_cu_seqlens is not None:
|
| 438 |
+
hidden_states, attention_mask = self.cu2pad(hidden_states, origin_cu_seqlens)
|
| 439 |
+
|
| 440 |
+
mode = 'fused_recurrent' if (hidden_states.shape[1] <= 64 and not self.training) else self.mode
|
| 441 |
+
if self.training:
|
| 442 |
+
assert mode == 'chunk', "Only chunk mode is supported in training."
|
| 443 |
+
|
| 444 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 445 |
+
# _, q_len = hidden_states.shape[0], hidden_states.shape[1]
|
| 446 |
+
|
| 447 |
+
# 🔍 topk gating
|
| 448 |
+
router_logits = self.gate(hidden_states) # (bsz, q_len, num_memories)
|
| 449 |
+
scores = F.softmax(router_logits, dim=2, dtype=torch.float)
|
| 450 |
+
routing_weights, selected_memories = torch.topk(scores, self.topk, dim=-1) # (bsz, seq, topk)
|
| 451 |
+
routing_weights /= routing_weights.sum(dim=-1, keepdim=True)
|
| 452 |
+
routing_weights = routing_weights.to(hidden_states.dtype) # we cast back to the input dtype
|
| 453 |
+
routing_weights_full = torch.zeros(
|
| 454 |
+
routing_weights.shape[0],
|
| 455 |
+
routing_weights.shape[1],
|
| 456 |
+
self.num_memories,
|
| 457 |
+
dtype=routing_weights.dtype,
|
| 458 |
+
device=routing_weights.device,
|
| 459 |
+
).scatter(-1, selected_memories, routing_weights)
|
| 460 |
+
routing_mask = routing_weights_full.bool().int()
|
| 461 |
+
|
| 462 |
+
# if self.use_output_gate:
|
| 463 |
+
# o_g = self.g_proj(hidden_states)
|
| 464 |
+
|
| 465 |
+
batch_size, seq_len = hidden_states.shape[0], hidden_states.shape[1]
|
| 466 |
+
|
| 467 |
+
shared_hidden_states = hidden_states
|
| 468 |
+
hidden_states, indices, sorted_indices, max_len, mask, mask_2 = transform(
|
| 469 |
+
hidden_states, routing_mask, self.num_memories, selected_memories, attention_mask)
|
| 470 |
+
|
| 471 |
+
q = self.q_proj(hidden_states)
|
| 472 |
+
if self.single_kv_proj:
|
| 473 |
+
k = self.shared_k(hidden_states)
|
| 474 |
+
v = self.shared_v(hidden_states)
|
| 475 |
+
beta = self.shared_b(hidden_states).sigmoid()
|
| 476 |
+
g = -self.A_log.float().exp() * F.softplus(self.shared_a(hidden_states).float() + self.dt_bias)
|
| 477 |
+
else:
|
| 478 |
+
k = torch.stack([k_expert(hidden_states[i]) for i, k_expert in enumerate(self.k_proj)], dim=0)
|
| 479 |
+
v = torch.stack([v_expert(hidden_states[i]) for i, v_expert in enumerate(self.v_proj)], dim=0)
|
| 480 |
+
beta = torch.stack([b_expert(hidden_states[i]).sigmoid() for i, b_expert in enumerate(self.b_proj)], dim=0)
|
| 481 |
+
g = torch.stack([-self.A_log.float().exp() * F.softplus(a_expert(hidden_states[i]).float() + self.dt_bias)
|
| 482 |
+
for i, a_expert in enumerate(self.a_proj)], dim=0)
|
| 483 |
+
|
| 484 |
+
q, k, v, g, beta, mask_2 = (rearrange(x, 'e b l ... -> (e b) l ...') for x in (q, k, v, g, beta, mask_2))
|
| 485 |
+
cu_q, cu_k, cu_v, cu_g, cu_beta, indices_q, cu_seqlen_all, max_seq_lens = _upad_input(q, k, v, g, beta, mask_2)
|
| 486 |
+
cu_seqlens, reverse_indices = cu_seqlen_all[0].to(torch.long).unique(return_inverse=True)
|
| 487 |
+
cu_q, cu_k, cu_v, cu_g, cu_beta = (x.unsqueeze(0).contiguous() for x in (cu_q, cu_k, cu_v, cu_g, cu_beta))
|
| 488 |
+
|
| 489 |
+
if self.use_short_conv:
|
| 490 |
+
conv_state_q, conv_state_k, conv_state_v = [None, None], [None, None], [None, None]
|
| 491 |
+
if last_state is not None:
|
| 492 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 493 |
+
|
| 494 |
+
conv_cu_seqlens = cu_seqlens
|
| 495 |
+
padded = False
|
| 496 |
+
if self.training:
|
| 497 |
+
conv_cu_seqlens = None
|
| 498 |
+
elif seq_len != 1 and (cu_seqlens[1:] - cu_seqlens[:-1]).min().item() < self.conv_size:
|
| 499 |
+
padded = True
|
| 500 |
+
conv_cu_seqlens, cu_q, cu_k, cu_v, pad_lengths = self.pad_for_conv(cu_seqlens, cu_q, cu_k, cu_v)
|
| 501 |
+
|
| 502 |
+
conv_q = self.prepare_recurrent_state(
|
| 503 |
+
conv_state_q[0],
|
| 504 |
+
conv_cu_seqlens,
|
| 505 |
+
cu_seqlen_all[0],
|
| 506 |
+
reverse_indices,
|
| 507 |
+
batch_size,
|
| 508 |
+
)
|
| 509 |
+
cu_q, conv_q_new = self.q_conv1d(
|
| 510 |
+
x=cu_q,
|
| 511 |
+
cache=conv_q,
|
| 512 |
+
output_final_state=use_cache,
|
| 513 |
+
cu_seqlens=conv_cu_seqlens,
|
| 514 |
+
)
|
| 515 |
+
conv_state_q[0] = self.handle_recurrent_state(
|
| 516 |
+
conv_state_q[0],
|
| 517 |
+
conv_q_new,
|
| 518 |
+
conv_cu_seqlens,
|
| 519 |
+
cu_seqlen_all[0],
|
| 520 |
+
reverse_indices,
|
| 521 |
+
)
|
| 522 |
+
conv_k = self.prepare_recurrent_state(
|
| 523 |
+
conv_state_k[0],
|
| 524 |
+
conv_cu_seqlens,
|
| 525 |
+
cu_seqlen_all[0],
|
| 526 |
+
reverse_indices,
|
| 527 |
+
batch_size,
|
| 528 |
+
)
|
| 529 |
+
cu_k, conv_k_new = self.k_conv1d(
|
| 530 |
+
x=cu_k,
|
| 531 |
+
cache=conv_k,
|
| 532 |
+
output_final_state=use_cache,
|
| 533 |
+
cu_seqlens=conv_cu_seqlens,
|
| 534 |
+
)
|
| 535 |
+
conv_state_k[0] = self.handle_recurrent_state(
|
| 536 |
+
conv_state_k[0],
|
| 537 |
+
conv_k_new,
|
| 538 |
+
conv_cu_seqlens,
|
| 539 |
+
cu_seqlen_all[0],
|
| 540 |
+
reverse_indices,
|
| 541 |
+
)
|
| 542 |
+
conv_v = self.prepare_recurrent_state(
|
| 543 |
+
conv_state_v[0],
|
| 544 |
+
conv_cu_seqlens,
|
| 545 |
+
cu_seqlen_all[0],
|
| 546 |
+
reverse_indices,
|
| 547 |
+
batch_size,
|
| 548 |
+
)
|
| 549 |
+
cu_v, conv_v_new = self.v_conv1d(
|
| 550 |
+
x=cu_v,
|
| 551 |
+
cache=conv_v,
|
| 552 |
+
output_final_state=use_cache,
|
| 553 |
+
cu_seqlens=conv_cu_seqlens,
|
| 554 |
+
)
|
| 555 |
+
conv_state_v[0] = self.handle_recurrent_state(
|
| 556 |
+
conv_state_v[0],
|
| 557 |
+
conv_v_new, conv_cu_seqlens,
|
| 558 |
+
cu_seqlen_all[0],
|
| 559 |
+
reverse_indices,
|
| 560 |
+
)
|
| 561 |
+
|
| 562 |
+
if padded:
|
| 563 |
+
cu_q, cu_k, cu_v = self.unpad_after_conv(conv_cu_seqlens, cu_seqlens, cu_q, cu_k, cu_v, pad_lengths)
|
| 564 |
+
|
| 565 |
+
else:
|
| 566 |
+
q, k, v = self.silu(q), self.silu(k), self.silu(v)
|
| 567 |
+
|
| 568 |
+
cu_q, cu_k, cu_v = map(lambda x: rearrange(x, 'b t (h d) -> b t h d', h=self.num_heads), (cu_q, cu_k, cu_v))
|
| 569 |
+
|
| 570 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else [
|
| 571 |
+
None for _ in range(1 + self.shared_mem)]
|
| 572 |
+
if mode == 'chunk':
|
| 573 |
+
o, recurrent_state_ = chunk_gated_delta_rule(
|
| 574 |
+
q=cu_q,
|
| 575 |
+
k=cu_k,
|
| 576 |
+
v=cu_v,
|
| 577 |
+
g=cu_g,
|
| 578 |
+
beta=cu_beta,
|
| 579 |
+
initial_state=recurrent_state[0],
|
| 580 |
+
output_final_state=use_cache,
|
| 581 |
+
use_qk_l2norm_in_kernel=True,
|
| 582 |
+
cu_seqlens=cu_seqlens,
|
| 583 |
+
)
|
| 584 |
+
recurrent_state[0] = self.handle_recurrent_state(
|
| 585 |
+
recurrent_state[0],
|
| 586 |
+
recurrent_state_,
|
| 587 |
+
cu_seqlens,
|
| 588 |
+
cu_seqlen_all[0],
|
| 589 |
+
reverse_indices,
|
| 590 |
+
)
|
| 591 |
+
|
| 592 |
+
elif mode == 'fused_recurrent':
|
| 593 |
+
memories = self.prepare_recurrent_state(
|
| 594 |
+
recurrent_state[0],
|
| 595 |
+
cu_seqlens, cu_seqlen_all[0],
|
| 596 |
+
reverse_indices, batch_size,
|
| 597 |
+
)
|
| 598 |
+
o, recurrent_state_ = fused_recurrent_gated_delta_rule(
|
| 599 |
+
q=cu_q,
|
| 600 |
+
k=cu_k,
|
| 601 |
+
v=cu_v,
|
| 602 |
+
g=cu_g,
|
| 603 |
+
beta=cu_beta,
|
| 604 |
+
initial_state=memories,
|
| 605 |
+
output_final_state=use_cache,
|
| 606 |
+
use_qk_l2norm_in_kernel=True,
|
| 607 |
+
cu_seqlens=cu_seqlens,
|
| 608 |
+
)
|
| 609 |
+
recurrent_state[0] = self.handle_recurrent_state(
|
| 610 |
+
recurrent_state[0],
|
| 611 |
+
recurrent_state_,
|
| 612 |
+
cu_seqlens,
|
| 613 |
+
cu_seqlen_all[0],
|
| 614 |
+
reverse_indices,
|
| 615 |
+
)
|
| 616 |
+
|
| 617 |
+
o = o.squeeze(0).contiguous()
|
| 618 |
+
o = pad_input(o, indices_q, batch_size*self.num_memories, max_len)
|
| 619 |
+
o = rearrange(o, '(e b) l h d -> e b l (h d)', b=batch_size)
|
| 620 |
+
o = reconstruct(o, indices=indices, sorted_indices=sorted_indices, batch_size=batch_size,
|
| 621 |
+
seq_len=seq_len, topk=self.topk, routing_weights=routing_weights, mask=mask)
|
| 622 |
+
o = rearrange(o, 'b l (h d) -> b l h d', h=self.num_heads)
|
| 623 |
+
|
| 624 |
+
if self.shared_mem:
|
| 625 |
+
shared_o = self.shared_o(shared_hidden_states, attention_mask, recurrent_state,
|
| 626 |
+
use_cache, conv_state_q, conv_state_k, conv_state_v)
|
| 627 |
+
o += shared_o
|
| 628 |
+
|
| 629 |
+
update_layer_cache(
|
| 630 |
+
self,
|
| 631 |
+
past_key_values,
|
| 632 |
+
recurrent_state=recurrent_state,
|
| 633 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 634 |
+
offset=q.shape[2],
|
| 635 |
+
)
|
| 636 |
+
|
| 637 |
+
if self.use_output_gate:
|
| 638 |
+
g = rearrange(self.g_proj(shared_hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 639 |
+
o = self.o_norm(o, g)
|
| 640 |
+
else:
|
| 641 |
+
o = self.o_norm(o)
|
| 642 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 643 |
+
o = self.o_proj(o)
|
| 644 |
+
|
| 645 |
+
if origin_cu_seqlens is not None:
|
| 646 |
+
indices, _, _ = get_unpad_data(attention_mask[:, -seq_len:])
|
| 647 |
+
o = index_first_axis(rearrange(o, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 648 |
+
|
| 649 |
+
return o, None, past_key_values, router_logits.view(-1, self.num_memories)
|
| 650 |
+
|
| 651 |
+
def shared_o(
|
| 652 |
+
self,
|
| 653 |
+
hidden_states: torch.Tensor,
|
| 654 |
+
attention_mask: torch.Tensor | None = None,
|
| 655 |
+
recurrent_state=None,
|
| 656 |
+
use_cache: bool | None = False,
|
| 657 |
+
conv_state_q=[None, None],
|
| 658 |
+
conv_state_k=[None, None],
|
| 659 |
+
conv_state_v=[None, None],
|
| 660 |
+
**kwargs,
|
| 661 |
+
) -> torch.Tensor:
|
| 662 |
+
if attention_mask is not None:
|
| 663 |
+
assert len(attention_mask.shape) == 2, (
|
| 664 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 665 |
+
"for padding purposes (0 indicating padding). "
|
| 666 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 667 |
+
)
|
| 668 |
+
|
| 669 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 670 |
+
if self.training:
|
| 671 |
+
assert mode == 'chunk', "Only chunk mode is supported in training."
|
| 672 |
+
|
| 673 |
+
cu_seqlens = None
|
| 674 |
+
if attention_mask is not None:
|
| 675 |
+
batch_size, q_len = hidden_states.shape[0], hidden_states.shape[1]
|
| 676 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 677 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 678 |
+
|
| 679 |
+
if self.use_short_conv:
|
| 680 |
+
q, conv_state_q[1] = self.q_conv1d(
|
| 681 |
+
x=self.q_proj(hidden_states),
|
| 682 |
+
cache=conv_state_q[1],
|
| 683 |
+
output_final_state=use_cache,
|
| 684 |
+
cu_seqlens=cu_seqlens,
|
| 685 |
+
)
|
| 686 |
+
k, conv_state_k[1] = self.k_conv1d(
|
| 687 |
+
x=self.shared_k(hidden_states),
|
| 688 |
+
cache=conv_state_k[1],
|
| 689 |
+
output_final_state=use_cache,
|
| 690 |
+
cu_seqlens=cu_seqlens,
|
| 691 |
+
)
|
| 692 |
+
v, conv_state_v[1] = self.v_conv1d(
|
| 693 |
+
x=self.shared_v(hidden_states),
|
| 694 |
+
cache=conv_state_v[1],
|
| 695 |
+
output_final_state=use_cache,
|
| 696 |
+
cu_seqlens=cu_seqlens,
|
| 697 |
+
)
|
| 698 |
+
else:
|
| 699 |
+
q = self.silu(self.q_proj(hidden_states))
|
| 700 |
+
k = self.silu(self.shared_k(hidden_states))
|
| 701 |
+
v = self.silu(self.shared_v(hidden_states))
|
| 702 |
+
|
| 703 |
+
q, k, v = map(lambda x: rearrange(x, 'b t (h d) -> b t h d', h=self.num_heads), (q, k, v))
|
| 704 |
+
beta = self.shared_b(hidden_states).sigmoid()
|
| 705 |
+
g = -self.A_log.float().exp() * F.softplus(self.shared_a(hidden_states).float() + self.dt_bias)
|
| 706 |
+
|
| 707 |
+
if mode == 'chunk':
|
| 708 |
+
o, recurrent_state[-1] = chunk_gated_delta_rule(
|
| 709 |
+
q=q,
|
| 710 |
+
k=k,
|
| 711 |
+
v=v,
|
| 712 |
+
g=g,
|
| 713 |
+
beta=beta,
|
| 714 |
+
initial_state=recurrent_state[-1],
|
| 715 |
+
output_final_state=use_cache,
|
| 716 |
+
cu_seqlens=cu_seqlens,
|
| 717 |
+
use_qk_l2norm_in_kernel=True,
|
| 718 |
+
)
|
| 719 |
+
elif mode == 'fused_recurrent':
|
| 720 |
+
o, recurrent_state[-1] = fused_recurrent_gated_delta_rule(
|
| 721 |
+
q=q,
|
| 722 |
+
k=k,
|
| 723 |
+
v=v,
|
| 724 |
+
g=g,
|
| 725 |
+
beta=beta,
|
| 726 |
+
initial_state=recurrent_state[-1],
|
| 727 |
+
output_final_state=use_cache,
|
| 728 |
+
cu_seqlens=cu_seqlens,
|
| 729 |
+
use_qk_l2norm_in_kernel=True,
|
| 730 |
+
)
|
| 731 |
+
else:
|
| 732 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 733 |
+
|
| 734 |
+
if attention_mask is not None:
|
| 735 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 736 |
+
return o
|
| 737 |
+
|
| 738 |
+
def cu2pad(self, x, cu_seqlens):
|
| 739 |
+
batch_size = cu_seqlens.shape[0] - 1
|
| 740 |
+
max_len = (cu_seqlens[1:] - cu_seqlens[:-1]).max().item()
|
| 741 |
+
indices = torch.tensor([], dtype=torch.long, device=x.device)
|
| 742 |
+
attention_mask = torch.ones((batch_size, max_len), dtype=torch.bool, device=x.device)
|
| 743 |
+
for i in range(batch_size):
|
| 744 |
+
seq_len = cu_seqlens[i+1] - cu_seqlens[i]
|
| 745 |
+
pad_len = max_len - seq_len
|
| 746 |
+
batch_indices = torch.arange(pad_len, max_len, device=x.device)
|
| 747 |
+
batch_indices = batch_indices + i * max_len
|
| 748 |
+
indices = torch.cat([indices, batch_indices])
|
| 749 |
+
attention_mask[i, :pad_len] = False
|
| 750 |
+
x = pad_input(x.squeeze(0), indices, batch_size, max_len)
|
| 751 |
+
return x, attention_mask
|
| 752 |
+
|
| 753 |
+
def pad_for_conv(self, cu_seqlens, cu_q, cu_k, cu_v):
|
| 754 |
+
lengths = cu_seqlens[1:] - cu_seqlens[:-1]
|
| 755 |
+
pad_lengths = torch.clamp(self.conv_size - lengths, min=0)
|
| 756 |
+
new_lengths = lengths + pad_lengths
|
| 757 |
+
new_cu_seqlens = torch.cat([
|
| 758 |
+
torch.tensor([0], device=cu_seqlens.device, dtype=cu_seqlens.dtype),
|
| 759 |
+
torch.cumsum(new_lengths, dim=0),
|
| 760 |
+
])
|
| 761 |
+
final_total_len = new_cu_seqlens[-1].item()
|
| 762 |
+
new_q = torch.zeros((1, final_total_len, cu_q.shape[-1]), dtype=cu_q.dtype, device=cu_q.device)
|
| 763 |
+
new_k = torch.zeros((1, final_total_len, cu_k.shape[-1]), dtype=cu_k.dtype, device=cu_k.device)
|
| 764 |
+
new_v = torch.zeros((1, final_total_len, cu_v.shape[-1]), dtype=cu_v.dtype, device=cu_v.device)
|
| 765 |
+
num_sequences = len(lengths)
|
| 766 |
+
for i in range(num_sequences):
|
| 767 |
+
src_start = cu_seqlens[i]
|
| 768 |
+
src_end = cu_seqlens[i+1]
|
| 769 |
+
dest_start = new_cu_seqlens[i] + pad_lengths[i]
|
| 770 |
+
dest_end = new_cu_seqlens[i+1]
|
| 771 |
+
new_q[:, dest_start:dest_end, ...] = cu_q[:, src_start:src_end, ...]
|
| 772 |
+
new_k[:, dest_start:dest_end, ...] = cu_k[:, src_start:src_end, ...]
|
| 773 |
+
new_v[:, dest_start:dest_end, ...] = cu_v[:, src_start:src_end, ...]
|
| 774 |
+
|
| 775 |
+
return new_cu_seqlens, new_q, new_k, new_v, pad_lengths
|
| 776 |
+
|
| 777 |
+
def unpad_after_conv(self, conv_cu_seqlens, cu_seqlens, cu_q, cu_k, cu_v, pad_lengths):
|
| 778 |
+
original_total_len = cu_seqlens[-1].item()
|
| 779 |
+
orig_q = torch.empty((1, original_total_len, cu_q.shape[-1]), dtype=cu_q.dtype, device=cu_q.device)
|
| 780 |
+
orig_k = torch.empty((1, original_total_len, cu_k.shape[-1]), dtype=cu_k.dtype, device=cu_k.device)
|
| 781 |
+
orig_v = torch.empty((1, original_total_len, cu_v.shape[-1]), dtype=cu_v.dtype, device=cu_v.device)
|
| 782 |
+
|
| 783 |
+
num_sequences = len(pad_lengths)
|
| 784 |
+
for i in range(num_sequences):
|
| 785 |
+
dest_start = cu_seqlens[i]
|
| 786 |
+
dest_end = cu_seqlens[i+1]
|
| 787 |
+
src_start = conv_cu_seqlens[i] + pad_lengths[i]
|
| 788 |
+
src_end = conv_cu_seqlens[i+1]
|
| 789 |
+
|
| 790 |
+
orig_q[:, dest_start:dest_end, ...] = cu_q[:, src_start:src_end, ...]
|
| 791 |
+
orig_k[:, dest_start:dest_end, ...] = cu_k[:, src_start:src_end, ...]
|
| 792 |
+
orig_v[:, dest_start:dest_end, ...] = cu_v[:, src_start:src_end, ...]
|
| 793 |
+
return orig_q, orig_k, orig_v
|
| 794 |
+
|
| 795 |
+
def prepare_recurrent_state(self, recurrent_state, cu_seqlens, cu_seqlen_all, reverse_indices, batch_size):
|
| 796 |
+
if recurrent_state is None:
|
| 797 |
+
return None
|
| 798 |
+
|
| 799 |
+
if cu_seqlens is None:
|
| 800 |
+
return recurrent_state
|
| 801 |
+
|
| 802 |
+
total_len = len(cu_seqlen_all)
|
| 803 |
+
if len(cu_seqlens) != total_len:
|
| 804 |
+
# select memories that are activated
|
| 805 |
+
memories = torch.zeros_like(recurrent_state[:self.topk*batch_size])
|
| 806 |
+
mem_id = 0
|
| 807 |
+
for i in range(total_len-1):
|
| 808 |
+
if cu_seqlen_all[i] != cu_seqlen_all[i+1]:
|
| 809 |
+
memories[mem_id] = recurrent_state[i]
|
| 810 |
+
mem_id += 1
|
| 811 |
+
assert mem_id == self.topk * batch_size, f"The number of memories {mem_id} is not correct."
|
| 812 |
+
else:
|
| 813 |
+
memories = recurrent_state
|
| 814 |
+
|
| 815 |
+
return memories
|
| 816 |
+
|
| 817 |
+
def handle_recurrent_state(self, recurrent_state, recurrent_state_new, cu_seqlens, cu_seqlen_all, reverse_indices):
|
| 818 |
+
if recurrent_state_new is None:
|
| 819 |
+
return None
|
| 820 |
+
if cu_seqlens is None:
|
| 821 |
+
return recurrent_state_new
|
| 822 |
+
if recurrent_state is None:
|
| 823 |
+
recurrent_state = torch.zeros_like(recurrent_state_new[reverse_indices[1:]-1])
|
| 824 |
+
total_len = len(cu_seqlen_all)
|
| 825 |
+
if len(cu_seqlens) != total_len:
|
| 826 |
+
for i in range(total_len-1):
|
| 827 |
+
if cu_seqlen_all[i] != cu_seqlen_all[i+1]:
|
| 828 |
+
recurrent_state[i] = recurrent_state_new[reverse_indices[i+1]-1]
|
| 829 |
+
else:
|
| 830 |
+
recurrent_state = recurrent_state_new
|
| 831 |
+
return recurrent_state
|
fla/layers/multiscale_retention.py
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from einops import rearrange, repeat
|
| 10 |
+
from transformers.activations import ACT2FN
|
| 11 |
+
|
| 12 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 13 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 14 |
+
from fla.modules.rotary import RotaryEmbedding
|
| 15 |
+
from fla.ops.retention import chunk_retention, fused_chunk_retention, fused_recurrent_retention, parallel_retention
|
| 16 |
+
from fla.ops.utils.index import prepare_lens_from_mask
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from transformers.processing_utils import Unpack
|
| 20 |
+
|
| 21 |
+
from fla.models.utils import Cache
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class MultiScaleRetention(nn.Module):
|
| 25 |
+
r"""
|
| 26 |
+
The layer implementaion for [Retentive Network: A Successor to Transformer for Large Language Models](https://arxiv.org/pdf/2307.08621.pdf). # noqa
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
mode (str, Optional):
|
| 30 |
+
Which Retention kernel to use.
|
| 31 |
+
Currently available: `chunk`, `fused_recurrent`, `parallel`, and `fused_chunk`.
|
| 32 |
+
Default: `chunk`.
|
| 33 |
+
hidden_size (int, Optional):
|
| 34 |
+
The hidden size of the input. Default: 1024.
|
| 35 |
+
expand_k (float, Optional):
|
| 36 |
+
The expansion ratio for the key dim. Default: 1.0.
|
| 37 |
+
expand_v (float, Optional):
|
| 38 |
+
The expansion ratio for the value dim. Default: 2.0.
|
| 39 |
+
num_heads (int, Optional):
|
| 40 |
+
The number of heads. Default: 8.
|
| 41 |
+
num_kv_heads (int, Optional):
|
| 42 |
+
The number of key/value heads, used for MQA. Default: None.
|
| 43 |
+
feature_map (str, Optional):
|
| 44 |
+
Feature map function applied to queries/keys. Default: None.
|
| 45 |
+
use_short_conv (bool, Optional):
|
| 46 |
+
Whether to use short convolutions. Default: `False`.
|
| 47 |
+
conv_size (int, Optional):
|
| 48 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 49 |
+
conv_bias (bool, Optional):
|
| 50 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 51 |
+
use_output_gate (bool, Optional):
|
| 52 |
+
Whether to use output gate. Default: `True`.
|
| 53 |
+
gate_fn (str, Optional):
|
| 54 |
+
The activation function for the output gate. Default: `swish`.
|
| 55 |
+
elementwise_affine (bool, Optional):
|
| 56 |
+
If `True`, applies elementwise affine to LayerNorm with learnable parameters. Default: `True`.
|
| 57 |
+
norm_eps (float, Optional):
|
| 58 |
+
The epsilon value for the layernorm/rmsnorm layer. Default: 1e-5.
|
| 59 |
+
fuse_norm (bool, Optional):
|
| 60 |
+
Whether to fuse the norm and the output gate for better memory footprint. Default: `True`.
|
| 61 |
+
layer_idx (int, Optional):
|
| 62 |
+
The index of the layer. Default: None.
|
| 63 |
+
"""
|
| 64 |
+
|
| 65 |
+
def __init__(
|
| 66 |
+
self,
|
| 67 |
+
mode: str = 'chunk',
|
| 68 |
+
hidden_size: int = 1024,
|
| 69 |
+
expand_k: float = 1.0,
|
| 70 |
+
expand_v: float = 2.0,
|
| 71 |
+
num_heads: int = 8,
|
| 72 |
+
num_kv_heads: int | None = None,
|
| 73 |
+
feature_map: str | None = None,
|
| 74 |
+
use_short_conv: bool = False,
|
| 75 |
+
conv_size: int = 4,
|
| 76 |
+
conv_bias: bool = False,
|
| 77 |
+
use_output_gate: bool = True,
|
| 78 |
+
gate_fn: str = 'swish',
|
| 79 |
+
elementwise_affine: bool | None = True,
|
| 80 |
+
norm_eps: float = 1e-5,
|
| 81 |
+
fuse_norm: bool = True,
|
| 82 |
+
layer_idx: int = None,
|
| 83 |
+
**kwargs,
|
| 84 |
+
) -> MultiScaleRetention:
|
| 85 |
+
super().__init__()
|
| 86 |
+
|
| 87 |
+
self.mode = mode
|
| 88 |
+
self.hidden_size = hidden_size
|
| 89 |
+
self.expand_k = expand_k
|
| 90 |
+
self.expand_v = expand_v
|
| 91 |
+
self.num_heads = num_heads
|
| 92 |
+
self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
|
| 93 |
+
self.num_kv_groups = self.num_heads // self.num_kv_heads
|
| 94 |
+
self.feature_map_fn = ACT2FN[feature_map] if feature_map is not None else None
|
| 95 |
+
|
| 96 |
+
self.use_short_conv = use_short_conv
|
| 97 |
+
self.conv_size = conv_size
|
| 98 |
+
self.conv_bias = conv_bias
|
| 99 |
+
self.use_output_gate = use_output_gate
|
| 100 |
+
|
| 101 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 102 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 103 |
+
self.key_dim_per_group = self.key_dim // self.num_kv_groups
|
| 104 |
+
self.value_dim_per_group = self.value_dim // self.num_kv_groups
|
| 105 |
+
self.layer_idx = layer_idx
|
| 106 |
+
|
| 107 |
+
assert mode in ['chunk', 'fused_chunk', 'parallel', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 108 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 109 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 110 |
+
|
| 111 |
+
self.head_k_dim = self.key_dim // num_heads
|
| 112 |
+
self.head_v_dim = self.value_dim // num_heads
|
| 113 |
+
|
| 114 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 115 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim_per_group, bias=False)
|
| 116 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim_per_group, bias=False)
|
| 117 |
+
if self.use_output_gate:
|
| 118 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 119 |
+
|
| 120 |
+
if use_short_conv:
|
| 121 |
+
self.conv_size = conv_size
|
| 122 |
+
self.q_conv1d = ShortConvolution(
|
| 123 |
+
hidden_size=self.key_dim,
|
| 124 |
+
kernel_size=conv_size,
|
| 125 |
+
bias=conv_bias,
|
| 126 |
+
activation='silu',
|
| 127 |
+
)
|
| 128 |
+
self.k_conv1d = ShortConvolution(
|
| 129 |
+
hidden_size=self.key_dim_per_group,
|
| 130 |
+
kernel_size=conv_size,
|
| 131 |
+
bias=conv_bias,
|
| 132 |
+
activation='silu',
|
| 133 |
+
)
|
| 134 |
+
self.v_conv1d = ShortConvolution(
|
| 135 |
+
hidden_size=self.value_dim_per_group,
|
| 136 |
+
kernel_size=conv_size,
|
| 137 |
+
bias=conv_bias,
|
| 138 |
+
activation='silu',
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 142 |
+
|
| 143 |
+
if gate_fn == 'swish' and fuse_norm and use_output_gate:
|
| 144 |
+
self.g_norm_swish_gate = FusedRMSNormGated(
|
| 145 |
+
hidden_size=self.head_v_dim,
|
| 146 |
+
elementwise_affine=elementwise_affine,
|
| 147 |
+
eps=norm_eps,
|
| 148 |
+
)
|
| 149 |
+
self.fuse_norm_and_gate = True
|
| 150 |
+
else:
|
| 151 |
+
self.fuse_norm_and_gate = False
|
| 152 |
+
self.g_norm = RMSNorm(
|
| 153 |
+
hidden_size=self.head_v_dim,
|
| 154 |
+
elementwise_affine=elementwise_affine,
|
| 155 |
+
eps=norm_eps,
|
| 156 |
+
dtype=torch.float32
|
| 157 |
+
)
|
| 158 |
+
self.gate_fn = ACT2FN[gate_fn]
|
| 159 |
+
|
| 160 |
+
# TODO: fix this issue
|
| 161 |
+
# https://github.com/Dao-AILab/flash-attention/blob/main/flash_attn/ops/triton/rotary.py#L180
|
| 162 |
+
# Ideally, we would want to support arbitrary d_head_qk
|
| 163 |
+
assert self.head_k_dim <= 256, "head_k_dim must be less than or equal to 256"
|
| 164 |
+
self.rotary = RotaryEmbedding(dim=self.head_k_dim)
|
| 165 |
+
|
| 166 |
+
def forward(
|
| 167 |
+
self,
|
| 168 |
+
hidden_states: torch.Tensor,
|
| 169 |
+
attention_mask: torch.Tensor | None = None,
|
| 170 |
+
past_key_values: Cache | None = None,
|
| 171 |
+
use_cache: bool | None = False,
|
| 172 |
+
output_attentions: bool | None = False,
|
| 173 |
+
**kwargs: Unpack[dict],
|
| 174 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 175 |
+
if attention_mask is not None:
|
| 176 |
+
assert len(attention_mask.shape) == 2, (
|
| 177 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 178 |
+
"for padding purposes (0 indicating padding). "
|
| 179 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 183 |
+
mode = 'fused_recurrent' if q_len <= 64 else self.mode
|
| 184 |
+
|
| 185 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 186 |
+
|
| 187 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 188 |
+
if attention_mask is not None:
|
| 189 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 190 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 191 |
+
|
| 192 |
+
if self.use_short_conv:
|
| 193 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 194 |
+
if last_state is not None:
|
| 195 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 196 |
+
q, conv_state_q = self.q_conv1d(
|
| 197 |
+
x=self.q_proj(hidden_states),
|
| 198 |
+
cache=conv_state_q,
|
| 199 |
+
output_final_state=use_cache,
|
| 200 |
+
cu_seqlens=cu_seqlens,
|
| 201 |
+
)
|
| 202 |
+
k, conv_state_k = self.k_conv1d(
|
| 203 |
+
x=self.k_proj(hidden_states),
|
| 204 |
+
cache=conv_state_k,
|
| 205 |
+
output_final_state=use_cache,
|
| 206 |
+
cu_seqlens=cu_seqlens,
|
| 207 |
+
)
|
| 208 |
+
v, conv_state_v = self.v_conv1d(
|
| 209 |
+
x=self.v_proj(hidden_states),
|
| 210 |
+
cache=conv_state_v,
|
| 211 |
+
output_final_state=use_cache,
|
| 212 |
+
cu_seqlens=cu_seqlens,
|
| 213 |
+
)
|
| 214 |
+
else:
|
| 215 |
+
q = self.q_proj(hidden_states)
|
| 216 |
+
k = self.k_proj(hidden_states)
|
| 217 |
+
v = self.v_proj(hidden_states)
|
| 218 |
+
|
| 219 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 220 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 221 |
+
if self.feature_map_fn is not None:
|
| 222 |
+
q, k = map(self.feature_map_fn, (q, k))
|
| 223 |
+
|
| 224 |
+
seqlen_offset, max_seqlen = 0, q.shape[1]
|
| 225 |
+
if past_key_values is not None:
|
| 226 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 227 |
+
max_seqlen = q.shape[1] + seqlen_offset
|
| 228 |
+
|
| 229 |
+
if attention_mask is not None and seqlen_offset > 0:
|
| 230 |
+
# to deliminate the offsets of padding tokens
|
| 231 |
+
seqlen_offset = prepare_lens_from_mask(attention_mask) - q_len
|
| 232 |
+
max_seqlen = q.shape[1] + seqlen_offset.max().item()
|
| 233 |
+
|
| 234 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
|
| 235 |
+
|
| 236 |
+
if self.num_kv_groups > 1:
|
| 237 |
+
k = repeat(k, '... h d -> ... (h g) d', g=self.num_kv_groups)
|
| 238 |
+
v = repeat(v, '... (h d) -> ... (h g) d', d=self.head_v_dim, g=self.num_kv_groups)
|
| 239 |
+
else:
|
| 240 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 241 |
+
|
| 242 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 243 |
+
if mode == 'chunk':
|
| 244 |
+
o, recurrent_state = chunk_retention(
|
| 245 |
+
q=q,
|
| 246 |
+
k=k,
|
| 247 |
+
v=v,
|
| 248 |
+
initial_state=recurrent_state,
|
| 249 |
+
output_final_state=use_cache,
|
| 250 |
+
cu_seqlens=cu_seqlens,
|
| 251 |
+
)
|
| 252 |
+
elif mode == 'fused_chunk':
|
| 253 |
+
o, recurrent_state = fused_chunk_retention(
|
| 254 |
+
q=q,
|
| 255 |
+
k=k,
|
| 256 |
+
v=v,
|
| 257 |
+
initial_state=recurrent_state,
|
| 258 |
+
output_final_state=use_cache,
|
| 259 |
+
cu_seqlens=cu_seqlens,
|
| 260 |
+
)
|
| 261 |
+
elif mode == 'parallel':
|
| 262 |
+
o, recurrent_state = parallel_retention(
|
| 263 |
+
q=q,
|
| 264 |
+
k=k,
|
| 265 |
+
v=v,
|
| 266 |
+
cu_seqlens=cu_seqlens,
|
| 267 |
+
)
|
| 268 |
+
elif mode == 'fused_recurrent':
|
| 269 |
+
o, recurrent_state = fused_recurrent_retention(
|
| 270 |
+
q=q,
|
| 271 |
+
k=k,
|
| 272 |
+
v=v,
|
| 273 |
+
initial_state=recurrent_state,
|
| 274 |
+
output_final_state=use_cache,
|
| 275 |
+
cu_seqlens=cu_seqlens,
|
| 276 |
+
)
|
| 277 |
+
else:
|
| 278 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 279 |
+
|
| 280 |
+
update_layer_cache(
|
| 281 |
+
self,
|
| 282 |
+
past_key_values,
|
| 283 |
+
recurrent_state=recurrent_state,
|
| 284 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 285 |
+
offset=q_len,
|
| 286 |
+
)
|
| 287 |
+
|
| 288 |
+
if self.use_output_gate:
|
| 289 |
+
g = self.g_proj(hidden_states)
|
| 290 |
+
if self.fuse_norm_and_gate:
|
| 291 |
+
g = rearrange(g, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 292 |
+
o = self.g_norm_swish_gate(o, g)
|
| 293 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 294 |
+
else:
|
| 295 |
+
o = rearrange(self.g_norm(o), '... h d -> ... (h d)')
|
| 296 |
+
o = o * self.gate_fn(g)
|
| 297 |
+
else:
|
| 298 |
+
o = rearrange(self.g_norm(o), '... h d -> ... (h d)')
|
| 299 |
+
o = self.o_proj(o)
|
| 300 |
+
if attention_mask is not None:
|
| 301 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 302 |
+
|
| 303 |
+
return o, None, past_key_values
|
fla/layers/nsa.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from einops import rearrange
|
| 10 |
+
from transformers.utils import logging
|
| 11 |
+
|
| 12 |
+
from fla.modules import RotaryEmbedding
|
| 13 |
+
from fla.ops.nsa.parallel import parallel_nsa
|
| 14 |
+
from fla.ops.utils.index import prepare_lens_from_mask
|
| 15 |
+
|
| 16 |
+
if TYPE_CHECKING:
|
| 17 |
+
from fla.models.utils import Cache
|
| 18 |
+
|
| 19 |
+
logger = logging.get_logger(__name__)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class NativeSparseAttention(nn.Module):
|
| 23 |
+
|
| 24 |
+
def __init__(
|
| 25 |
+
self,
|
| 26 |
+
hidden_size: int = 2048,
|
| 27 |
+
num_heads: int = 64,
|
| 28 |
+
num_kv_heads: int | None = 4,
|
| 29 |
+
head_dim: int = 64,
|
| 30 |
+
qkv_bias: bool = False,
|
| 31 |
+
block_size: int | None = 64,
|
| 32 |
+
block_counts: torch.LongTensor | int | None = 16,
|
| 33 |
+
window_size: int | None = 512,
|
| 34 |
+
rope_theta: float | None = 10000.,
|
| 35 |
+
max_position_embeddings: int | None = None,
|
| 36 |
+
layer_idx: int = None,
|
| 37 |
+
):
|
| 38 |
+
super().__init__()
|
| 39 |
+
|
| 40 |
+
self.hidden_size = hidden_size
|
| 41 |
+
self.num_heads = num_heads
|
| 42 |
+
if num_kv_heads is None:
|
| 43 |
+
self.num_kv_heads = self.num_heads
|
| 44 |
+
else:
|
| 45 |
+
self.num_kv_heads = num_kv_heads
|
| 46 |
+
self.num_kv_groups = num_heads // self.num_kv_heads
|
| 47 |
+
self.head_dim = head_dim
|
| 48 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 49 |
+
self.qkv_bias = qkv_bias
|
| 50 |
+
|
| 51 |
+
self.block_size = block_size
|
| 52 |
+
self.block_counts = block_counts
|
| 53 |
+
self.window_size = window_size
|
| 54 |
+
self.rope_theta = rope_theta
|
| 55 |
+
self.max_position_embeddings = max_position_embeddings
|
| 56 |
+
self.layer_idx = layer_idx
|
| 57 |
+
|
| 58 |
+
self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=self.qkv_bias)
|
| 59 |
+
self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 60 |
+
self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 61 |
+
self.g_proj = nn.Linear(self.hidden_size, self.num_heads * 3, bias=False)
|
| 62 |
+
self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
|
| 63 |
+
|
| 64 |
+
self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
|
| 65 |
+
|
| 66 |
+
def forward(
|
| 67 |
+
self,
|
| 68 |
+
hidden_states: torch.Tensor,
|
| 69 |
+
attention_mask: torch.LongTensor | None = None,
|
| 70 |
+
past_key_values: Cache | None = None,
|
| 71 |
+
output_attentions: bool = False,
|
| 72 |
+
use_cache: bool = False,
|
| 73 |
+
**kwargs,
|
| 74 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 75 |
+
if attention_mask is not None:
|
| 76 |
+
assert len(attention_mask.shape) == 2, (
|
| 77 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 78 |
+
"for padding purposes (0 indicating padding). "
|
| 79 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
batch_size, seq_len, _ = hidden_states.size()
|
| 83 |
+
|
| 84 |
+
q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 85 |
+
k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 86 |
+
v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 87 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=3)
|
| 88 |
+
g_cmp, g_slc, g_swa = g.sigmoid().unbind(-1)
|
| 89 |
+
|
| 90 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 91 |
+
|
| 92 |
+
seqlen_offset, max_seqlen = 0, seq_len
|
| 93 |
+
if past_key_values is not None:
|
| 94 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 95 |
+
max_seqlen = q.shape[1] + seqlen_offset
|
| 96 |
+
|
| 97 |
+
if attention_mask is not None:
|
| 98 |
+
# to deliminate the offsets of padding tokens
|
| 99 |
+
seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
|
| 100 |
+
max_seqlen = q.shape[1] + max(seqlen_offset)
|
| 101 |
+
|
| 102 |
+
if self.max_position_embeddings is not None:
|
| 103 |
+
max_seqlen = max(max_seqlen, self.max_position_embeddings)
|
| 104 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
|
| 105 |
+
|
| 106 |
+
if past_key_values is not None:
|
| 107 |
+
cache_has_content = past_key_values.get_seq_length(self.layer_idx) > 0
|
| 108 |
+
k_cached, v_cached = past_key_values.update(
|
| 109 |
+
attn_state=(k.flatten(-2, -1), v.flatten(-2, -1)),
|
| 110 |
+
layer_idx=self.layer_idx,
|
| 111 |
+
offset=seq_len,
|
| 112 |
+
cache_kwargs=dict(window_size=self.window_size),
|
| 113 |
+
)['attn_state']
|
| 114 |
+
if cache_has_content:
|
| 115 |
+
k, v = k_cached, v_cached
|
| 116 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 117 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 118 |
+
|
| 119 |
+
o = parallel_nsa(
|
| 120 |
+
q=q,
|
| 121 |
+
k=k,
|
| 122 |
+
v=v,
|
| 123 |
+
g_cmp=g_cmp,
|
| 124 |
+
g_slc=g_slc,
|
| 125 |
+
g_swa=g_swa,
|
| 126 |
+
block_size=self.block_size,
|
| 127 |
+
block_counts=self.block_counts,
|
| 128 |
+
window_size=self.window_size,
|
| 129 |
+
cu_seqlens=cu_seqlens,
|
| 130 |
+
)
|
| 131 |
+
o = o.reshape(batch_size, seq_len, -1)
|
| 132 |
+
o = self.o_proj(o)
|
| 133 |
+
|
| 134 |
+
if not output_attentions:
|
| 135 |
+
attentions = None
|
| 136 |
+
|
| 137 |
+
return o, attentions, past_key_values
|
fla/layers/path_attn.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
from transformers.utils import logging
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import pad_input, unpad_input
|
| 14 |
+
from fla.modules import RMSNorm, ShortConvolution
|
| 15 |
+
from fla.modules.l2norm import l2_norm
|
| 16 |
+
from fla.ops.attn.decoding import attn_decoding_one_step
|
| 17 |
+
from fla.ops.path_attn.parallel import parallel_path_attn
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
from fla.models.utils import Cache
|
| 21 |
+
|
| 22 |
+
logger = logging.get_logger(__name__)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class PaTHAttention(nn.Module):
|
| 26 |
+
def __init__(
|
| 27 |
+
self,
|
| 28 |
+
hidden_size: int = 2048,
|
| 29 |
+
num_heads: int = 32,
|
| 30 |
+
num_kv_heads: int | None = None,
|
| 31 |
+
use_forget_gate: bool = False,
|
| 32 |
+
use_qk_norm: bool = False,
|
| 33 |
+
layer_idx: int = None,
|
| 34 |
+
use_low_rank_w: bool = True,
|
| 35 |
+
use_w_shortconv: bool = True,
|
| 36 |
+
conv_size: int = 3,
|
| 37 |
+
conv_bias: bool = False,
|
| 38 |
+
):
|
| 39 |
+
super().__init__()
|
| 40 |
+
|
| 41 |
+
self.hidden_size = hidden_size
|
| 42 |
+
self.num_heads = num_heads
|
| 43 |
+
if num_kv_heads is None:
|
| 44 |
+
self.num_kv_heads = self.num_heads
|
| 45 |
+
else:
|
| 46 |
+
self.num_kv_heads = num_kv_heads
|
| 47 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 48 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 49 |
+
|
| 50 |
+
self.layer_idx = layer_idx
|
| 51 |
+
|
| 52 |
+
self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 53 |
+
self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=False)
|
| 54 |
+
self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=False)
|
| 55 |
+
|
| 56 |
+
# We use low-rank parameterization for the w_proj to reduce parameters in MHA settings.
|
| 57 |
+
if use_low_rank_w:
|
| 58 |
+
self.w_proj = nn.Sequential(
|
| 59 |
+
nn.Linear(self.hidden_size, 32, bias=False),
|
| 60 |
+
nn.Linear(32, self.kv_dim, bias=False),
|
| 61 |
+
)
|
| 62 |
+
# In MQA/GQA settings, key/value heads are shared, so we use a standard linear projection
|
| 63 |
+
# which doesn't introduce too many parameters
|
| 64 |
+
else:
|
| 65 |
+
self.w_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=False)
|
| 66 |
+
|
| 67 |
+
# per head norm
|
| 68 |
+
if use_qk_norm:
|
| 69 |
+
self.maybe_q_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 70 |
+
self.maybe_k_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 71 |
+
else:
|
| 72 |
+
self.maybe_q_norm = nn.Identity()
|
| 73 |
+
self.maybe_k_norm = nn.Identity()
|
| 74 |
+
|
| 75 |
+
if use_w_shortconv:
|
| 76 |
+
self.w_conv1d = ShortConvolution(hidden_size=self.kv_dim, kernel_size=conv_size, bias=conv_bias, activation='silu')
|
| 77 |
+
self.use_w_shortconv = use_w_shortconv
|
| 78 |
+
self.bt_proj = nn.Linear(self.hidden_size, self.num_kv_heads, bias=True)
|
| 79 |
+
self.use_forget_gate = use_forget_gate
|
| 80 |
+
if use_forget_gate:
|
| 81 |
+
self.g_proj = nn.Linear(self.hidden_size, self.num_heads, bias=True)
|
| 82 |
+
self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 83 |
+
|
| 84 |
+
def forward(
|
| 85 |
+
self,
|
| 86 |
+
hidden_states: torch.Tensor,
|
| 87 |
+
attention_mask: torch.LongTensor | None = None,
|
| 88 |
+
past_key_values: Cache | None = None,
|
| 89 |
+
output_attentions: bool = False,
|
| 90 |
+
use_cache: bool = False,
|
| 91 |
+
**kwargs,
|
| 92 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 93 |
+
if use_cache:
|
| 94 |
+
assert past_key_values is not None, "past_key_values must be provided when use_cache is True"
|
| 95 |
+
if attention_mask is not None:
|
| 96 |
+
assert len(attention_mask.shape) == 2, (
|
| 97 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 98 |
+
"for padding purposes (0 indicating padding). "
|
| 99 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 100 |
+
)
|
| 101 |
+
batch_size, q_len, _ = hidden_states.size()
|
| 102 |
+
q = self.q_proj(hidden_states)
|
| 103 |
+
k = self.k_proj(hidden_states)
|
| 104 |
+
v = self.v_proj(hidden_states)
|
| 105 |
+
w = self.w_proj(hidden_states)
|
| 106 |
+
beta = self.bt_proj(hidden_states).float().sigmoid() * 2 # allowing negative eigenvalues
|
| 107 |
+
g = F.logsigmoid(self.g_proj(hidden_states).float()) if self.use_forget_gate else None
|
| 108 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 109 |
+
assert not (cu_seqlens is not None and attention_mask is not None), (
|
| 110 |
+
"cu_seqlens should not be provided when attention_mask is not None"
|
| 111 |
+
)
|
| 112 |
+
# Training
|
| 113 |
+
if attention_mask is None:
|
| 114 |
+
assert use_cache is False, "use_cache should be False in training"
|
| 115 |
+
if self.use_w_shortconv:
|
| 116 |
+
w, _ = self.w_conv1d(w, cache=None, output_final_state=False, cu_seqlens=cu_seqlens)
|
| 117 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_dim)
|
| 118 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 119 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 120 |
+
w = rearrange(w, '... (h d) -> ... h d', d=self.head_dim)
|
| 121 |
+
q, k = self.maybe_q_norm(q), self.maybe_k_norm(k)
|
| 122 |
+
w = l2_norm(w, output_dtype=torch.float32)
|
| 123 |
+
o, _ = parallel_path_attn(q=q, k=k, v=v, w=w, beta=beta, g=g, cu_seqlens=cu_seqlens)
|
| 124 |
+
|
| 125 |
+
# Prefilling or decoding
|
| 126 |
+
else:
|
| 127 |
+
assert self.training is False, "attention mask is not supported in training. Please use variable length input."
|
| 128 |
+
try:
|
| 129 |
+
last_state = past_key_values[self.layer_idx]
|
| 130 |
+
except KeyError:
|
| 131 |
+
last_state = None
|
| 132 |
+
# Decoding
|
| 133 |
+
if last_state is not None:
|
| 134 |
+
if g is not None:
|
| 135 |
+
past_k, past_v, past_g = last_state['attn_state']
|
| 136 |
+
else:
|
| 137 |
+
past_k, past_v = last_state['attn_state']
|
| 138 |
+
past_g = None
|
| 139 |
+
w_conv_state = last_state['conv_state']
|
| 140 |
+
past_k = rearrange(past_k, '... (h d) -> ... h d', d=self.head_dim)
|
| 141 |
+
if self.use_w_shortconv:
|
| 142 |
+
w, w_conv_state = self.w_conv1d(w, cache=w_conv_state, output_final_state=use_cache, cu_seqlens=cu_seqlens)
|
| 143 |
+
w = rearrange(w, '... (h d) -> ... h d', d=self.head_dim)
|
| 144 |
+
w = l2_norm(w, output_dtype=torch.float32)
|
| 145 |
+
|
| 146 |
+
@torch.compile
|
| 147 |
+
def rank_one_update(k, w, beta):
|
| 148 |
+
original_dtype = k.dtype
|
| 149 |
+
k = k.float()
|
| 150 |
+
w = w.float()
|
| 151 |
+
beta = beta.float()
|
| 152 |
+
k = k - beta[..., None].float() * (k * w).sum(-1, keepdim=True) * w
|
| 153 |
+
return k.to(original_dtype)
|
| 154 |
+
|
| 155 |
+
past_k = rank_one_update(past_k, w, beta)
|
| 156 |
+
past_k = rearrange(past_k, '... h d -> ... (h d)')
|
| 157 |
+
k = torch.cat([past_k, k], dim=1)
|
| 158 |
+
v = torch.cat([past_v, v], dim=1)
|
| 159 |
+
g = torch.cat([past_g, g], dim=1) if g is not None else None
|
| 160 |
+
past_key_values[self.layer_idx]['attn_state'] = (k, v, g) if g is not None else (k, v)
|
| 161 |
+
past_key_values.update(
|
| 162 |
+
conv_state=w_conv_state,
|
| 163 |
+
layer_idx=self.layer_idx,
|
| 164 |
+
offset=q_len,
|
| 165 |
+
)
|
| 166 |
+
if g is not None:
|
| 167 |
+
q, (k, v, g), indices_q, cu_seqlens, max_seq_lens = unpad_input(
|
| 168 |
+
q, (k, v, g), attention_mask, q_len, keepdim=True)
|
| 169 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 170 |
+
else:
|
| 171 |
+
q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(
|
| 172 |
+
q, (k, v), attention_mask, q_len, keepdim=True)
|
| 173 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 174 |
+
_, cu_seqlens = cu_seqlens
|
| 175 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_dim)
|
| 176 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 177 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 178 |
+
assert max_seqlen_q == 1, "only support q_len == 1 for decoding"
|
| 179 |
+
o = attn_decoding_one_step(q, k, v, g, cu_seqlens=cu_seqlens, do_gate_scale=True) # reduced to fox's decoding
|
| 180 |
+
# Prefilling
|
| 181 |
+
else:
|
| 182 |
+
v_cache = v.clone()
|
| 183 |
+
g_cache = g.clone() if g is not None else None
|
| 184 |
+
if g is None:
|
| 185 |
+
q, (k, v, w, beta), indices_q, cu_seqlens, max_seq_lens = unpad_input(
|
| 186 |
+
q, (k, v, w, beta), attention_mask, q_len, keepdim=True)
|
| 187 |
+
else:
|
| 188 |
+
q, (k, v, w, beta, g), indices_q, cu_seqlens, max_seq_lens = unpad_input(
|
| 189 |
+
q, (k, v, w, beta, g), attention_mask, q_len, keepdim=True)
|
| 190 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 191 |
+
assert max_seqlen_q == max_seqlen_k, "max_seqlen_q should be equal to max_seqlen_k in prefilling"
|
| 192 |
+
_, cu_seqlens = cu_seqlens
|
| 193 |
+
if self.use_w_shortconv:
|
| 194 |
+
w, w_conv_state = self.w_conv1d(w, cache=None, output_final_state=use_cache, cu_seqlens=cu_seqlens)
|
| 195 |
+
else:
|
| 196 |
+
w_conv_state = None
|
| 197 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_dim)
|
| 198 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 199 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 200 |
+
w = rearrange(w, '... (h d) -> ... h d', d=self.head_dim)
|
| 201 |
+
w = l2_norm(w, output_dtype=torch.float32)
|
| 202 |
+
o, k_cache = parallel_path_attn(q=q, k=k, v=v, w=w, beta=beta, g=g,
|
| 203 |
+
cu_seqlens=cu_seqlens, use_cache=use_cache)
|
| 204 |
+
if use_cache:
|
| 205 |
+
k_cache = pad_input(k_cache.squeeze(0), indices_q, batch_size, q_len)
|
| 206 |
+
k_cache = rearrange(k_cache, '... h d -> ... (h d)')
|
| 207 |
+
past_key_values.update(
|
| 208 |
+
attn_state=(k_cache, v_cache, g_cache) if g_cache is not None else (k_cache, v_cache),
|
| 209 |
+
conv_state=w_conv_state,
|
| 210 |
+
layer_idx=self.layer_idx,
|
| 211 |
+
offset=q_len,
|
| 212 |
+
)
|
| 213 |
+
o = pad_input(o.squeeze(0), indices_q, batch_size, q_len)
|
| 214 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 215 |
+
o = self.o_proj(o)
|
| 216 |
+
return o, None, past_key_values
|
fla/layers/quasar.py
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
# Modified for QuasarAttention
|
| 3 |
+
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
+
import contextlib
|
| 7 |
+
import math
|
| 8 |
+
import os
|
| 9 |
+
from typing import TYPE_CHECKING
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import torch.nn as nn
|
| 13 |
+
from einops import rearrange, repeat
|
| 14 |
+
from torch.nn import functional as F
|
| 15 |
+
|
| 16 |
+
from fla.layers.utils import get_unpad_data, index_first_axis, pad_input
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _quasar_debug_tensor(name: str, tensor: torch.Tensor, layer_idx: int | None) -> None:
|
| 20 |
+
if os.environ.get("QUASAR_DEBUG_FINITE", "0") != "1":
|
| 21 |
+
return
|
| 22 |
+
if tensor is None or torch.isfinite(tensor).all():
|
| 23 |
+
return
|
| 24 |
+
with torch.no_grad():
|
| 25 |
+
t = torch.nan_to_num(tensor.detach().float(), nan=0.0, posinf=0.0, neginf=0.0)
|
| 26 |
+
nonfinite = int((~torch.isfinite(tensor)).sum().item())
|
| 27 |
+
print(
|
| 28 |
+
f"[QUASAR DEBUG] layer={layer_idx} stage={name} nonfinite={nonfinite} "
|
| 29 |
+
f"min={float(t.min())} max={float(t.max())} mean={float(t.mean())}",
|
| 30 |
+
flush=True,
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class _TorchRMSNormGated(nn.Module):
|
| 35 |
+
def __init__(self, hidden_size: int, activation: str = "sigmoid", eps: float = 1e-5):
|
| 36 |
+
super().__init__()
|
| 37 |
+
self.weight = nn.Parameter(torch.ones(hidden_size))
|
| 38 |
+
self.activation = activation
|
| 39 |
+
self.eps = eps
|
| 40 |
+
|
| 41 |
+
def reset_parameters(self) -> None:
|
| 42 |
+
self.weight.data.fill_(1.0)
|
| 43 |
+
|
| 44 |
+
def forward(self, x: torch.Tensor, gate: torch.Tensor) -> torch.Tensor:
|
| 45 |
+
dtype = x.dtype
|
| 46 |
+
y = torch.nan_to_num(
|
| 47 |
+
x.float(),
|
| 48 |
+
nan=0.0,
|
| 49 |
+
posinf=1e4,
|
| 50 |
+
neginf=-1e4,
|
| 51 |
+
).clamp_(min=-1e4, max=1e4)
|
| 52 |
+
y = y * torch.rsqrt(y.square().mean(dim=-1, keepdim=True) + self.eps)
|
| 53 |
+
weight = torch.nan_to_num(
|
| 54 |
+
self.weight.float(),
|
| 55 |
+
nan=1.0,
|
| 56 |
+
posinf=1.0,
|
| 57 |
+
neginf=1.0,
|
| 58 |
+
).clamp_(min=0.0, max=4.0)
|
| 59 |
+
y = y.to(dtype) * weight.to(dtype=dtype, device=x.device)
|
| 60 |
+
gate = torch.nan_to_num(
|
| 61 |
+
gate.float(),
|
| 62 |
+
nan=0.0,
|
| 63 |
+
posinf=30.0,
|
| 64 |
+
neginf=-30.0,
|
| 65 |
+
).clamp_(min=-30.0, max=30.0)
|
| 66 |
+
if self.activation in {"swish", "silu"}:
|
| 67 |
+
gate = gate * torch.sigmoid(gate)
|
| 68 |
+
elif self.activation == "sigmoid":
|
| 69 |
+
gate = torch.sigmoid(gate)
|
| 70 |
+
return y * gate.to(dtype=dtype, device=x.device)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def rotate_half(x):
|
| 74 |
+
"""Rotates half the hidden dims of the input."""
|
| 75 |
+
x1 = x[..., : x.shape[-1] // 2]
|
| 76 |
+
x2 = x[..., x.shape[-1] // 2 :]
|
| 77 |
+
return torch.cat((-x2, x1), dim=-1)
|
| 78 |
+
|
| 79 |
+
def apply_rotary_pos_emb(q, k, cos, sin, position_ids=None):
|
| 80 |
+
"""Applies Rotary Position Embedding to the query and key tensors."""
|
| 81 |
+
# cos, sin: [1, 1, seq_len, rotary_dim]
|
| 82 |
+
# q, k: [batch_size, seq_len, n_heads, head_dim]
|
| 83 |
+
rotary_dim = cos.shape[-1]
|
| 84 |
+
q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
|
| 85 |
+
k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]
|
| 86 |
+
cos = cos.transpose(1, 2) # [1, seq_len, 1, rotary_dim]
|
| 87 |
+
sin = sin.transpose(1, 2) # [1, seq_len, 1, rotary_dim]
|
| 88 |
+
q_embed = (q_rot * cos) + (rotate_half(q_rot) * sin)
|
| 89 |
+
k_embed = (k_rot * cos) + (rotate_half(k_rot) * sin)
|
| 90 |
+
return torch.cat([q_embed, q_pass], dim=-1), torch.cat([k_embed, k_pass], dim=-1)
|
| 91 |
+
|
| 92 |
+
if TYPE_CHECKING:
|
| 93 |
+
from transformers.processing_utils import Unpack
|
| 94 |
+
|
| 95 |
+
from fla.models.utils import Cache
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
class QuasarAttention(nn.Module):
|
| 99 |
+
"""
|
| 100 |
+
QuasarAttention layer implementation.
|
| 101 |
+
|
| 102 |
+
Args:
|
| 103 |
+
hidden_size (int, Optional):
|
| 104 |
+
The hidden size of the input. Default: 2048.
|
| 105 |
+
head_dim (int, Optional):
|
| 106 |
+
The dimension of each head. Default: 128.
|
| 107 |
+
num_heads (int, Optional):
|
| 108 |
+
The number of heads. Default: 16.
|
| 109 |
+
mode (str, Optional):
|
| 110 |
+
Which QuasarAttention kernel to use.
|
| 111 |
+
Currently available: `chunk` and `fused_recurrent`.
|
| 112 |
+
Default: `chunk`.
|
| 113 |
+
use_short_conv (bool, Optional):
|
| 114 |
+
Whether to use short convolutions. Default: `True`.
|
| 115 |
+
conv_size (int, Optional):
|
| 116 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 117 |
+
conv_bias (bool, Optional):
|
| 118 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 119 |
+
layer_idx (int, Optional):
|
| 120 |
+
The index of the layer. Default: None.
|
| 121 |
+
norm_eps (float, Optional):
|
| 122 |
+
The epsilon value for the normalization layer. Default: 1e-5.
|
| 123 |
+
"""
|
| 124 |
+
|
| 125 |
+
def __init__(
|
| 126 |
+
self,
|
| 127 |
+
hidden_size: int = 2048,
|
| 128 |
+
head_dim: int = 128,
|
| 129 |
+
num_heads: int = 16,
|
| 130 |
+
mode: str = "chunk",
|
| 131 |
+
use_short_conv: bool = True,
|
| 132 |
+
conv_size: int = 4,
|
| 133 |
+
conv_bias: bool = False,
|
| 134 |
+
layer_idx: int = None,
|
| 135 |
+
norm_eps: float = 1e-5,
|
| 136 |
+
**kwargs,
|
| 137 |
+
) -> QuasarAttention:
|
| 138 |
+
super().__init__()
|
| 139 |
+
|
| 140 |
+
self.mode = mode
|
| 141 |
+
self.hidden_size = hidden_size
|
| 142 |
+
|
| 143 |
+
self.use_short_conv = use_short_conv
|
| 144 |
+
self.conv_size = conv_size
|
| 145 |
+
self.conv_bias = conv_bias
|
| 146 |
+
|
| 147 |
+
self.head_dim = head_dim
|
| 148 |
+
self.num_heads = num_heads
|
| 149 |
+
self.key_dim = int(self.num_heads * self.head_dim)
|
| 150 |
+
self.value_dim = int(self.num_heads * self.head_dim)
|
| 151 |
+
self.layer_idx = layer_idx
|
| 152 |
+
|
| 153 |
+
assert mode in ["chunk", "fused_recurrent"], f"Not supported mode `{mode}`."
|
| 154 |
+
|
| 155 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 156 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 157 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 158 |
+
|
| 159 |
+
# KDA matching: Use SiLU on q, k, v for better learning if not using short conv
|
| 160 |
+
# (Short conv already has its own activation)
|
| 161 |
+
self.q_act = nn.SiLU()
|
| 162 |
+
self.k_act = nn.SiLU()
|
| 163 |
+
self.v_act = nn.SiLU()
|
| 164 |
+
|
| 165 |
+
if use_short_conv:
|
| 166 |
+
from fla.modules.convolution import ShortConvolution
|
| 167 |
+
|
| 168 |
+
self.q_conv1d = ShortConvolution(
|
| 169 |
+
hidden_size=self.key_dim,
|
| 170 |
+
kernel_size=conv_size,
|
| 171 |
+
bias=conv_bias,
|
| 172 |
+
activation="silu",
|
| 173 |
+
)
|
| 174 |
+
self.k_conv1d = ShortConvolution(
|
| 175 |
+
hidden_size=self.key_dim,
|
| 176 |
+
kernel_size=conv_size,
|
| 177 |
+
bias=conv_bias,
|
| 178 |
+
activation="silu",
|
| 179 |
+
)
|
| 180 |
+
self.v_conv1d = ShortConvolution(
|
| 181 |
+
hidden_size=self.value_dim,
|
| 182 |
+
kernel_size=conv_size,
|
| 183 |
+
bias=conv_bias,
|
| 184 |
+
activation="silu",
|
| 185 |
+
)
|
| 186 |
+
|
| 187 |
+
# Data-dependent Beta (Adaptive Decay)
|
| 188 |
+
# Instead of a static per-head parameter, we use a linear projection
|
| 189 |
+
# to allow the model to learn contextual importance (read/write sharpness).
|
| 190 |
+
self.b_proj = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 191 |
+
|
| 192 |
+
# Learnable state decay (like KDA/Mamba A matrix)
|
| 193 |
+
self.A_log = nn.Parameter(torch.log(torch.empty(self.num_heads, dtype=torch.float32).uniform_(1, 16)))
|
| 194 |
+
self.A_log._no_weight_decay = True
|
| 195 |
+
self.dt_bias = nn.Parameter(torch.zeros(self.key_dim, dtype=torch.float32))
|
| 196 |
+
self.dt_bias._no_weight_decay = True
|
| 197 |
+
|
| 198 |
+
# KIMI matches: separate f_proj for kernel and g_proj for final output gating
|
| 199 |
+
self.f_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 200 |
+
self.g_proj = nn.Sequential(
|
| 201 |
+
nn.Linear(hidden_size, self.head_dim, bias=False),
|
| 202 |
+
nn.Linear(self.head_dim, self.value_dim, bias=True),
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
self.o_norm = _TorchRMSNormGated(self.head_dim, activation="sigmoid", eps=norm_eps)
|
| 206 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 207 |
+
|
| 208 |
+
def reset_parameters(self) -> None:
|
| 209 |
+
for module in self.children():
|
| 210 |
+
reset = getattr(module, "reset_parameters", None)
|
| 211 |
+
if callable(reset):
|
| 212 |
+
reset()
|
| 213 |
+
|
| 214 |
+
def forward(
|
| 215 |
+
self,
|
| 216 |
+
hidden_states: torch.Tensor,
|
| 217 |
+
attention_mask: torch.Tensor | None = None,
|
| 218 |
+
past_key_values: Cache | None = None,
|
| 219 |
+
use_cache: bool | None = False,
|
| 220 |
+
output_attentions: bool | None = False,
|
| 221 |
+
**kwargs: Unpack[dict],
|
| 222 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 223 |
+
if attention_mask is not None:
|
| 224 |
+
assert len(attention_mask.shape) == 2, (
|
| 225 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 226 |
+
"for padding purposes (0 indicating padding). "
|
| 227 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 231 |
+
mode = self.mode
|
| 232 |
+
if self.training and mode == "fused_recurrent":
|
| 233 |
+
# The fused recurrent Quasar path is forward-only in this tree.
|
| 234 |
+
# Training must use the chunk kernel until its backward exists.
|
| 235 |
+
mode = "chunk"
|
| 236 |
+
|
| 237 |
+
# Bailing hidden states can be very large after MoE/FSDP checkpoint
|
| 238 |
+
# restore. Quasar's delta-rule triangular solve is much more sensitive
|
| 239 |
+
# to projection scale than GQA/GLA, so sanitize and RMS-normalize only
|
| 240 |
+
# the Quasar branch input. The residual model path remains untouched.
|
| 241 |
+
input_dtype = hidden_states.dtype
|
| 242 |
+
hidden_states = torch.nan_to_num(
|
| 243 |
+
hidden_states.float(),
|
| 244 |
+
nan=0.0,
|
| 245 |
+
posinf=60.0,
|
| 246 |
+
neginf=-60.0,
|
| 247 |
+
).clamp_(min=-60.0, max=60.0)
|
| 248 |
+
hidden_states = hidden_states * torch.rsqrt(
|
| 249 |
+
hidden_states.square().mean(dim=-1, keepdim=True) + 1e-6
|
| 250 |
+
)
|
| 251 |
+
hidden_states = hidden_states.to(dtype=input_dtype)
|
| 252 |
+
_quasar_debug_tensor("input_normed", hidden_states, self.layer_idx)
|
| 253 |
+
|
| 254 |
+
last_state = None
|
| 255 |
+
recurrent_state = None
|
| 256 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 257 |
+
|
| 258 |
+
if past_key_values is not None and self.layer_idx is not None:
|
| 259 |
+
if hasattr(past_key_values, "recurrent_states") and self.layer_idx in past_key_values.recurrent_states:
|
| 260 |
+
recurrent_state = past_key_values.recurrent_states[self.layer_idx]
|
| 261 |
+
if hasattr(past_key_values, "conv_states") and self.layer_idx in past_key_values.conv_states:
|
| 262 |
+
conv_state_q, conv_state_k, conv_state_v = past_key_values.conv_states[self.layer_idx]
|
| 263 |
+
else:
|
| 264 |
+
try:
|
| 265 |
+
# Standard list/tuple cache (FLA style fallback)
|
| 266 |
+
if len(past_key_values) > self.layer_idx:
|
| 267 |
+
last_state = past_key_values[self.layer_idx]
|
| 268 |
+
if isinstance(last_state, dict):
|
| 269 |
+
recurrent_state = last_state.get("recurrent_state", None)
|
| 270 |
+
convs = last_state.get("conv_state", None)
|
| 271 |
+
if convs is not None:
|
| 272 |
+
conv_state_q, conv_state_k, conv_state_v = convs
|
| 273 |
+
except TypeError:
|
| 274 |
+
pass
|
| 275 |
+
|
| 276 |
+
cu_seqlens = kwargs.get("cu_seqlens")
|
| 277 |
+
if attention_mask is not None:
|
| 278 |
+
# Optimization: Skip unpadding if all tokens are valid (common in packed distillation)
|
| 279 |
+
if attention_mask.all():
|
| 280 |
+
indices, cu_seqlens = None, None
|
| 281 |
+
else:
|
| 282 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 283 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 284 |
+
else:
|
| 285 |
+
indices = None
|
| 286 |
+
|
| 287 |
+
if self.use_short_conv:
|
| 288 |
+
q, conv_state_q = self.q_conv1d(
|
| 289 |
+
x=self.q_proj(hidden_states),
|
| 290 |
+
cache=conv_state_q,
|
| 291 |
+
output_final_state=use_cache,
|
| 292 |
+
cu_seqlens=cu_seqlens,
|
| 293 |
+
)
|
| 294 |
+
k, conv_state_k = self.k_conv1d(
|
| 295 |
+
x=self.k_proj(hidden_states),
|
| 296 |
+
cache=conv_state_k,
|
| 297 |
+
output_final_state=use_cache,
|
| 298 |
+
cu_seqlens=cu_seqlens,
|
| 299 |
+
)
|
| 300 |
+
v, conv_state_v = self.v_conv1d(
|
| 301 |
+
x=self.v_proj(hidden_states),
|
| 302 |
+
cache=conv_state_v,
|
| 303 |
+
output_final_state=use_cache,
|
| 304 |
+
cu_seqlens=cu_seqlens,
|
| 305 |
+
)
|
| 306 |
+
else:
|
| 307 |
+
q = self.q_act(self.q_proj(hidden_states))
|
| 308 |
+
k = self.k_act(self.k_proj(hidden_states))
|
| 309 |
+
v = self.v_act(self.v_proj(hidden_states))
|
| 310 |
+
_quasar_debug_tensor("q_proj", q, self.layer_idx)
|
| 311 |
+
_quasar_debug_tensor("k_proj", k, self.layer_idx)
|
| 312 |
+
_quasar_debug_tensor("v_proj", v, self.layer_idx)
|
| 313 |
+
|
| 314 |
+
q = rearrange(q, "... (h d) -> ... h d", d=self.head_dim)
|
| 315 |
+
k = rearrange(k, "... (h d) -> ... h d", d=self.head_dim)
|
| 316 |
+
v = rearrange(v, "... (h d) -> ... h d", d=self.head_dim)
|
| 317 |
+
|
| 318 |
+
# Apply RoPE if provided
|
| 319 |
+
cos = kwargs.get("cos")
|
| 320 |
+
sin = kwargs.get("sin")
|
| 321 |
+
if cos is not None and sin is not None:
|
| 322 |
+
if attention_mask is not None:
|
| 323 |
+
# Unpad cos/sin using the same indices
|
| 324 |
+
# cos/sin shape is [1, 1, seq_len, head_dim] or [batch_size, seq_len, head_dim]
|
| 325 |
+
if cos.shape[0] == 1 and cos.shape[1] == 1:
|
| 326 |
+
# Broadcastable/Shared RoPE [1, 1, seq_len, head_dim]
|
| 327 |
+
# We need to expand to [batch_size, seq_len, head_dim] before unpadding
|
| 328 |
+
cos_expanded = cos.squeeze(1).expand(batch_size, -1, -1)
|
| 329 |
+
sin_expanded = sin.squeeze(1).expand(batch_size, -1, -1)
|
| 330 |
+
cos = index_first_axis(rearrange(cos_expanded, "b s d -> (b s) d"), indices).unsqueeze(0).unsqueeze(1)
|
| 331 |
+
sin = index_first_axis(rearrange(sin_expanded, "b s d -> (b s) d"), indices).unsqueeze(0).unsqueeze(1)
|
| 332 |
+
else:
|
| 333 |
+
# Already [batch_size, 1, seq_len, head_dim] or [batch_size, seq_len, head_dim]
|
| 334 |
+
if cos.dim() == 4:
|
| 335 |
+
cos = cos.squeeze(1)
|
| 336 |
+
sin = sin.squeeze(1)
|
| 337 |
+
cos = index_first_axis(rearrange(cos, "b s d -> (b s) d"), indices).unsqueeze(0).unsqueeze(1)
|
| 338 |
+
sin = index_first_axis(rearrange(sin, "b s d -> (b s) d"), indices).unsqueeze(0).unsqueeze(1)
|
| 339 |
+
|
| 340 |
+
q, k = apply_rotary_pos_emb(q, k, cos, sin)
|
| 341 |
+
|
| 342 |
+
# QK Normalization AFTER RoPE — ensures kernel receives unit-norm vectors
|
| 343 |
+
# regardless of any precision drift introduced by the rotation
|
| 344 |
+
q = F.normalize(q, p=2, dim=-1)
|
| 345 |
+
k = F.normalize(k, p=2, dim=-1)
|
| 346 |
+
_quasar_debug_tensor("q_norm", q, self.layer_idx)
|
| 347 |
+
_quasar_debug_tensor("k_norm", k, self.layer_idx)
|
| 348 |
+
|
| 349 |
+
# Adaptive Beta: Sigmoid(b_proj(x)) is bounded to (0, 1) to prevent explosions.
|
| 350 |
+
beta = self.b_proj(hidden_states).sigmoid()
|
| 351 |
+
_quasar_debug_tensor("beta", beta, self.layer_idx)
|
| 352 |
+
|
| 353 |
+
if mode == "chunk":
|
| 354 |
+
from fla.ops.quasar.chunk import chunk_quasar
|
| 355 |
+
|
| 356 |
+
o, recurrent_state = chunk_quasar(
|
| 357 |
+
q=q,
|
| 358 |
+
k=k,
|
| 359 |
+
v=v,
|
| 360 |
+
beta=beta,
|
| 361 |
+
A_log=self.A_log,
|
| 362 |
+
dt_bias=self.dt_bias,
|
| 363 |
+
initial_state=recurrent_state,
|
| 364 |
+
output_final_state=use_cache,
|
| 365 |
+
cu_seqlens=cu_seqlens,
|
| 366 |
+
use_qk_l2norm_in_kernel=True,
|
| 367 |
+
)
|
| 368 |
+
_quasar_debug_tensor("chunk_kernel_o", o, self.layer_idx)
|
| 369 |
+
elif mode == "fused_recurrent":
|
| 370 |
+
from fla.ops.quasar.fused_recurrent import fused_recurrent_quasar
|
| 371 |
+
|
| 372 |
+
# Use f_proj for kernel gate in fused mode
|
| 373 |
+
f_gate = self.f_proj(hidden_states)
|
| 374 |
+
f_gate = rearrange(f_gate, "... (h d) -> ... h d", d=self.head_dim)
|
| 375 |
+
o, recurrent_state = fused_recurrent_quasar(
|
| 376 |
+
q=q,
|
| 377 |
+
k=k,
|
| 378 |
+
v=v,
|
| 379 |
+
g=f_gate,
|
| 380 |
+
beta=beta,
|
| 381 |
+
A_log=self.A_log,
|
| 382 |
+
dt_bias=self.dt_bias,
|
| 383 |
+
initial_state=recurrent_state,
|
| 384 |
+
output_final_state=use_cache,
|
| 385 |
+
use_qk_l2norm_in_kernel=True,
|
| 386 |
+
)
|
| 387 |
+
_quasar_debug_tensor("fused_kernel_o", o, self.layer_idx)
|
| 388 |
+
else:
|
| 389 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 390 |
+
|
| 391 |
+
o = torch.nan_to_num(
|
| 392 |
+
o.float(),
|
| 393 |
+
nan=0.0,
|
| 394 |
+
posinf=1e4,
|
| 395 |
+
neginf=-1e4,
|
| 396 |
+
).clamp_(min=-1e4, max=1e4).to(dtype=v.dtype)
|
| 397 |
+
_quasar_debug_tensor("kernel_o_clamped", o, self.layer_idx)
|
| 398 |
+
|
| 399 |
+
if past_key_values is not None:
|
| 400 |
+
if hasattr(past_key_values, "update_quasar_state"):
|
| 401 |
+
past_key_values.update_quasar_state(
|
| 402 |
+
self.layer_idx,
|
| 403 |
+
recurrent_state,
|
| 404 |
+
(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None
|
| 405 |
+
)
|
| 406 |
+
else:
|
| 407 |
+
with contextlib.suppress(TypeError):
|
| 408 |
+
past_key_values.update(
|
| 409 |
+
recurrent_state=recurrent_state,
|
| 410 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 411 |
+
layer_idx=self.layer_idx,
|
| 412 |
+
offset=q_len,
|
| 413 |
+
)
|
| 414 |
+
|
| 415 |
+
# Final output gating using g_proj
|
| 416 |
+
# Handle flattened inputs (unpadded) from FSDP/Flash-Linear-Attention
|
| 417 |
+
if hidden_states.dim() == 2:
|
| 418 |
+
# (N, D) -> (N, H, D/H)
|
| 419 |
+
g = self.g_proj(hidden_states)
|
| 420 |
+
g = rearrange(g, "n (h d) -> n h d", d=self.head_dim)
|
| 421 |
+
_quasar_debug_tensor("output_gate", g, self.layer_idx)
|
| 422 |
+
o = self.o_norm(o, g)
|
| 423 |
+
o = rearrange(o, "n h d -> n (h d)")
|
| 424 |
+
else:
|
| 425 |
+
# (B, S, D) -> (B, S, H, D/H)
|
| 426 |
+
g = self.g_proj(hidden_states)
|
| 427 |
+
g = rearrange(g, "b s (h d) -> b s h d", d=self.head_dim)
|
| 428 |
+
_quasar_debug_tensor("output_gate", g, self.layer_idx)
|
| 429 |
+
o = self.o_norm(o, g)
|
| 430 |
+
o = rearrange(o, "b s h d -> b s (h d)")
|
| 431 |
+
_quasar_debug_tensor("post_norm_gate", o, self.layer_idx)
|
| 432 |
+
|
| 433 |
+
o = self.o_proj(o)
|
| 434 |
+
_quasar_debug_tensor("o_proj", o, self.layer_idx)
|
| 435 |
+
if attention_mask is not None:
|
| 436 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 437 |
+
|
| 438 |
+
# LFM2 expects 2 return values (hidden_states, _)
|
| 439 |
+
return o, None
|
fla/layers/rebased.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
https://github.com/corl-team/rebased/blob/main/flash_linear_attention/fla/layers/rebased_fast.py
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
from einops import rearrange
|
| 12 |
+
|
| 13 |
+
from fla.modules.feature_map import RebasedFeatureMap
|
| 14 |
+
from fla.ops.linear_attn import chunk_linear_attn, fused_chunk_linear_attn
|
| 15 |
+
from fla.ops.rebased import parallel_rebased
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class ReBasedLinearAttention(nn.Module):
|
| 19 |
+
|
| 20 |
+
def __init__(
|
| 21 |
+
self,
|
| 22 |
+
hidden_size: int,
|
| 23 |
+
l_max: int = 2048,
|
| 24 |
+
feature_dim: int = 16,
|
| 25 |
+
num_key_value_heads: int = 16,
|
| 26 |
+
num_heads: int = 16,
|
| 27 |
+
use_gamma: bool | None = True,
|
| 28 |
+
use_beta: bool | None = True,
|
| 29 |
+
normalize: bool | None = True,
|
| 30 |
+
causal: bool = True,
|
| 31 |
+
eps: float = 1e-5,
|
| 32 |
+
mode: str = "parallel",
|
| 33 |
+
layer_idx: int | None = None,
|
| 34 |
+
**kwargs,
|
| 35 |
+
) -> ReBasedLinearAttention:
|
| 36 |
+
super().__init__()
|
| 37 |
+
self.hidden_size = hidden_size
|
| 38 |
+
self.l_max = l_max
|
| 39 |
+
self.mode = mode
|
| 40 |
+
assert self.mode in ["fused_chunk", "parallel", 'chunk']
|
| 41 |
+
|
| 42 |
+
self.feature_dim = feature_dim
|
| 43 |
+
self.num_key_value_heads = num_key_value_heads
|
| 44 |
+
self.num_heads = num_heads
|
| 45 |
+
self.head_dim = self.hidden_size // self.num_key_value_heads
|
| 46 |
+
self.use_gamma = use_gamma
|
| 47 |
+
self.use_beta = use_beta
|
| 48 |
+
self.normalize = normalize
|
| 49 |
+
self.causal = causal
|
| 50 |
+
self.eps = eps
|
| 51 |
+
self.mode = mode
|
| 52 |
+
self.layer_idx = layer_idx
|
| 53 |
+
|
| 54 |
+
self.feature_map = RebasedFeatureMap(self.feature_dim, use_gamma, use_beta, normalize)
|
| 55 |
+
self.q_proj = nn.Linear(self.hidden_size, self.feature_dim * self.num_heads, bias=False)
|
| 56 |
+
self.k_proj = nn.Linear(self.hidden_size, self.feature_dim * self.num_heads, bias=False)
|
| 57 |
+
self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
|
| 58 |
+
self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
|
| 59 |
+
self.dropout = nn.Identity()
|
| 60 |
+
|
| 61 |
+
def forward(self, hidden_states: torch.Tensor, **kwargs):
|
| 62 |
+
mode = self.mode
|
| 63 |
+
q = rearrange(
|
| 64 |
+
self.q_proj(hidden_states),
|
| 65 |
+
"... (h d) -> ... h d",
|
| 66 |
+
h=self.num_heads,
|
| 67 |
+
d=self.feature_dim,
|
| 68 |
+
)
|
| 69 |
+
k = rearrange(
|
| 70 |
+
self.k_proj(hidden_states),
|
| 71 |
+
"... (h d) -> ... h d",
|
| 72 |
+
h=self.num_heads,
|
| 73 |
+
d=self.feature_dim,
|
| 74 |
+
)
|
| 75 |
+
v = rearrange(
|
| 76 |
+
self.v_proj(hidden_states),
|
| 77 |
+
"... (h d) -> ... h d",
|
| 78 |
+
h=self.num_key_value_heads,
|
| 79 |
+
d=self.head_dim,
|
| 80 |
+
)
|
| 81 |
+
q, k = self.feature_map(q, flatten=(mode != 'parallel')), self.feature_map(k, flatten=(mode != 'parallel'))
|
| 82 |
+
if mode == "fused_chunk":
|
| 83 |
+
o = fused_chunk_linear_attn(
|
| 84 |
+
q=q,
|
| 85 |
+
k=k,
|
| 86 |
+
v=v,
|
| 87 |
+
normalize=True,
|
| 88 |
+
scale=1,
|
| 89 |
+
)
|
| 90 |
+
elif mode == 'chunk':
|
| 91 |
+
o = chunk_linear_attn(
|
| 92 |
+
q=q,
|
| 93 |
+
k=k,
|
| 94 |
+
v=v,
|
| 95 |
+
normalize=True,
|
| 96 |
+
scale=1,
|
| 97 |
+
)
|
| 98 |
+
elif mode == 'parallel':
|
| 99 |
+
assert q.shape[-1] <= 128
|
| 100 |
+
o = parallel_rebased(
|
| 101 |
+
q=q,
|
| 102 |
+
k=k,
|
| 103 |
+
v=v,
|
| 104 |
+
eps=self.eps,
|
| 105 |
+
use_scale=True,
|
| 106 |
+
use_normalize=True,
|
| 107 |
+
)
|
| 108 |
+
o = rearrange(o, "... h d -> ... (h d)")
|
| 109 |
+
o = self.o_proj(o)
|
| 110 |
+
o = self.dropout(o)
|
| 111 |
+
return o
|
| 112 |
+
|
| 113 |
+
# https://github.com/HazyResearch/zoology/blob/main/zoology/mixers/based.py#L119
|
| 114 |
+
def forward_reference(
|
| 115 |
+
self,
|
| 116 |
+
hidden_states: torch.Tensor,
|
| 117 |
+
filters: torch.Tensor = None,
|
| 118 |
+
*args,
|
| 119 |
+
**kwargs,
|
| 120 |
+
):
|
| 121 |
+
"""
|
| 122 |
+
x (torch.Tensor): tensor of shape (b, d, t)
|
| 123 |
+
y (torch.Tensor): tensor of shape (b, d, t)
|
| 124 |
+
"""
|
| 125 |
+
b, t, _ = hidden_states.size()
|
| 126 |
+
q, k, v = self.q_proj(hidden_states), self.k_proj(hidden_states), self.v_proj(hidden_states)
|
| 127 |
+
|
| 128 |
+
q = q.view(b, t, -1, self.feature_dim).transpose(1, 2)
|
| 129 |
+
k = k.view(b, t, -1, self.feature_dim).transpose(1, 2)
|
| 130 |
+
v = v.view(b, t, -1, self.head_dim).transpose(1, 2)
|
| 131 |
+
|
| 132 |
+
# Linear attention
|
| 133 |
+
q, k = self.feature_map(q), self.feature_map(k)
|
| 134 |
+
q, k, v = q.unsqueeze(-2), k.unsqueeze(-2), v.unsqueeze(-1)
|
| 135 |
+
|
| 136 |
+
# Compute attention
|
| 137 |
+
if self.causal:
|
| 138 |
+
y = ((q * (k * v).cumsum(2)).sum(-1) / ((q * k.cumsum(2)).sum(-1) + self.eps))
|
| 139 |
+
else:
|
| 140 |
+
y = ((q * (k * v).sum(2, True)).sum(-1) / ((q * k.sum(2, True)).sum(-1) + self.eps))
|
| 141 |
+
y = rearrange(y, 'b h t d -> b t (h d)')
|
| 142 |
+
y = self.o_proj(y.to(hidden_states.dtype))
|
| 143 |
+
y = self.dropout(y)
|
| 144 |
+
return y.to(hidden_states.dtype)
|
fla/layers/rodimus.py
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
import torch.utils.checkpoint
|
| 12 |
+
from einops import rearrange, repeat
|
| 13 |
+
from transformers.utils import logging
|
| 14 |
+
|
| 15 |
+
from fla.layers.utils import (
|
| 16 |
+
get_layer_cache,
|
| 17 |
+
get_unpad_data,
|
| 18 |
+
index_first_axis,
|
| 19 |
+
pad_input,
|
| 20 |
+
require_cache_layer_idx,
|
| 21 |
+
unpad_input,
|
| 22 |
+
update_layer_cache,
|
| 23 |
+
)
|
| 24 |
+
from fla.modules import RMSNorm, RotaryEmbedding, ShortConvolution
|
| 25 |
+
from fla.modules.layernorm_gated import RMSNormGated
|
| 26 |
+
from fla.ops.gla import chunk_gla, fused_chunk_gla, fused_recurrent_gla
|
| 27 |
+
|
| 28 |
+
if TYPE_CHECKING:
|
| 29 |
+
from transformers.processing_utils import Unpack
|
| 30 |
+
|
| 31 |
+
from fla.models.utils import Cache
|
| 32 |
+
|
| 33 |
+
try:
|
| 34 |
+
from flash_attn import flash_attn_func, flash_attn_varlen_func
|
| 35 |
+
except ImportError:
|
| 36 |
+
warnings.warn(
|
| 37 |
+
"Flash Attention is not installed. Please install it via `pip install flash-attn --no-build-isolation`",
|
| 38 |
+
category=ImportWarning,
|
| 39 |
+
)
|
| 40 |
+
flash_attn_func = None
|
| 41 |
+
|
| 42 |
+
logger = logging.get_logger(__name__)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def align_multiple(value, multiple_size=8):
|
| 46 |
+
if value % multiple_size != 0:
|
| 47 |
+
value += multiple_size - (value % multiple_size)
|
| 48 |
+
return value
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def autocast_to_fp16(x):
|
| 52 |
+
if x.dtype not in {torch.float16, torch.bfloat16}:
|
| 53 |
+
return x.to(dtype=torch.bfloat16)
|
| 54 |
+
else:
|
| 55 |
+
return x
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
class RodimusAttention(nn.Module):
|
| 59 |
+
def __init__(
|
| 60 |
+
self,
|
| 61 |
+
block_type: str = 'rodimus',
|
| 62 |
+
mode: str = 'chunk',
|
| 63 |
+
hidden_size: int = 1024,
|
| 64 |
+
input_gate_low_rank: float | str | None = 'auto',
|
| 65 |
+
expand_ratio: int = 64,
|
| 66 |
+
use_short_conv: bool = True,
|
| 67 |
+
conv_size: int = 4,
|
| 68 |
+
conv_bias: bool = True,
|
| 69 |
+
norm_eps: float = 1e-5,
|
| 70 |
+
k_norm_eps: float | None = None,
|
| 71 |
+
residual_in_fp32: bool = True,
|
| 72 |
+
layer_idx: int = None,
|
| 73 |
+
):
|
| 74 |
+
super().__init__()
|
| 75 |
+
|
| 76 |
+
self.block_type = block_type
|
| 77 |
+
self.mode = mode
|
| 78 |
+
self.hidden_size = hidden_size
|
| 79 |
+
self.d_inner = align_multiple(int(self.hidden_size * 2), 8)
|
| 80 |
+
|
| 81 |
+
self.expand_ratio = expand_ratio
|
| 82 |
+
self.input_gate_low_rank = max(self.hidden_size // 64, 16) if input_gate_low_rank == "auto" else input_gate_low_rank
|
| 83 |
+
|
| 84 |
+
self.use_short_conv = use_short_conv
|
| 85 |
+
self.conv_size = conv_size
|
| 86 |
+
self.conv_bias = conv_bias
|
| 87 |
+
|
| 88 |
+
self.norm_eps = norm_eps
|
| 89 |
+
self.k_norm_eps = k_norm_eps if k_norm_eps is not None else 1e-12
|
| 90 |
+
self.mem_size = expand_ratio
|
| 91 |
+
|
| 92 |
+
self.residual_in_fp32 = residual_in_fp32
|
| 93 |
+
self.layer_idx = layer_idx
|
| 94 |
+
|
| 95 |
+
assert mode in ['chunk', 'fused_recurrent', 'fused_chunk'], f"Not supported mode `{mode}`."
|
| 96 |
+
|
| 97 |
+
self.gate_proj = nn.Linear(self.hidden_size, self.d_inner, bias=False)
|
| 98 |
+
self.up_proj = nn.Linear(self.hidden_size, self.d_inner, bias=False)
|
| 99 |
+
self.activation_norm = RMSNormGated(hidden_size=self.d_inner, eps=norm_eps, norm_before_gate=False)
|
| 100 |
+
self.down_proj = nn.Linear(self.d_inner, self.hidden_size, bias=False)
|
| 101 |
+
|
| 102 |
+
if use_short_conv:
|
| 103 |
+
self.short_conv = ShortConvolution(
|
| 104 |
+
hidden_size=self.d_inner,
|
| 105 |
+
kernel_size=conv_size,
|
| 106 |
+
bias=conv_bias,
|
| 107 |
+
activation='silu',
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
self.residual_weight = nn.Parameter(torch.ones(
|
| 111 |
+
(self.d_inner, ), dtype=torch.float32 if self.residual_in_fp32 else None), requires_grad=True)
|
| 112 |
+
|
| 113 |
+
self.k_proj = nn.Linear(self.d_inner, self.mem_size, bias=False)
|
| 114 |
+
self.q_proj = nn.Linear(self.d_inner, self.mem_size, bias=False)
|
| 115 |
+
|
| 116 |
+
self.g_gate_proj = nn.Linear(self.d_inner, self.mem_size, bias=True)
|
| 117 |
+
self.tau_gate_proj = nn.Linear(self.d_inner, self.mem_size, bias=True)
|
| 118 |
+
self.i_gate_proj = nn.Sequential(
|
| 119 |
+
nn.Linear(self.d_inner, self.input_gate_low_rank, bias=False),
|
| 120 |
+
nn.Linear(self.input_gate_low_rank, self.d_inner, bias=True),
|
| 121 |
+
nn.Sigmoid(),
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
def forward(
|
| 125 |
+
self,
|
| 126 |
+
hidden_states: torch.Tensor,
|
| 127 |
+
attention_mask: torch.Tensor | None = None,
|
| 128 |
+
past_key_values: Cache | None = None,
|
| 129 |
+
use_cache: bool | None = False,
|
| 130 |
+
output_attentions: bool | None = False,
|
| 131 |
+
**kwargs: Unpack[dict],
|
| 132 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 133 |
+
if attention_mask is not None:
|
| 134 |
+
assert len(attention_mask.shape) == 2, (
|
| 135 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 136 |
+
"for padding purposes (0 indicating padding). "
|
| 137 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 141 |
+
# mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 142 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] == 1 else self.mode
|
| 143 |
+
|
| 144 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 145 |
+
|
| 146 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 147 |
+
if attention_mask is not None:
|
| 148 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 149 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 150 |
+
|
| 151 |
+
hidden_states, final_gate = self.up_proj(hidden_states), self.gate_proj(hidden_states)
|
| 152 |
+
|
| 153 |
+
if self.use_short_conv:
|
| 154 |
+
conv_state = None
|
| 155 |
+
if last_state is not None:
|
| 156 |
+
conv_state = last_state['conv_state']
|
| 157 |
+
shift_hidden_states, conv_state = self.short_conv(
|
| 158 |
+
x=hidden_states,
|
| 159 |
+
cache=conv_state,
|
| 160 |
+
output_final_state=use_cache,
|
| 161 |
+
cu_seqlens=cu_seqlens,
|
| 162 |
+
)
|
| 163 |
+
else:
|
| 164 |
+
shift_hidden_states = hidden_states
|
| 165 |
+
|
| 166 |
+
q = self.q_proj(shift_hidden_states)
|
| 167 |
+
k = self.k_proj(shift_hidden_states)
|
| 168 |
+
v = self.i_gate_proj(hidden_states) * hidden_states
|
| 169 |
+
|
| 170 |
+
g_gate = F.linear(shift_hidden_states, self.g_gate_proj.weight) + self.g_gate_proj.bias.float()
|
| 171 |
+
tau_gate = F.linear(shift_hidden_states, self.tau_gate_proj.weight) + self.tau_gate_proj.bias.float()
|
| 172 |
+
|
| 173 |
+
g_gate = F.softplus(g_gate)
|
| 174 |
+
it_gate = g_gate
|
| 175 |
+
rt_gate_log = -g_gate
|
| 176 |
+
|
| 177 |
+
tau_gate = F.sigmoid(tau_gate)
|
| 178 |
+
it_gate = it_gate ** tau_gate
|
| 179 |
+
rt_gate_log = rt_gate_log * tau_gate
|
| 180 |
+
|
| 181 |
+
k = F.normalize(k.float(), dim=-1, eps=self.k_norm_eps) * it_gate
|
| 182 |
+
q, k, v, rt_gate_log = map(lambda x: x.unsqueeze(1).transpose(1, 2), (q, k, v, rt_gate_log))
|
| 183 |
+
|
| 184 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 185 |
+
if mode == 'fused_recurrent':
|
| 186 |
+
o, recurrent_state = fused_recurrent_gla(
|
| 187 |
+
q=q,
|
| 188 |
+
k=k,
|
| 189 |
+
v=v,
|
| 190 |
+
gk=rt_gate_log,
|
| 191 |
+
initial_state=recurrent_state,
|
| 192 |
+
output_final_state=use_cache,
|
| 193 |
+
cu_seqlens=cu_seqlens,
|
| 194 |
+
head_first=False,
|
| 195 |
+
)
|
| 196 |
+
elif mode == 'fused_chunk':
|
| 197 |
+
o, recurrent_state = fused_chunk_gla(
|
| 198 |
+
q=q,
|
| 199 |
+
k=k,
|
| 200 |
+
v=v,
|
| 201 |
+
g=rt_gate_log,
|
| 202 |
+
initial_state=recurrent_state,
|
| 203 |
+
output_final_state=use_cache,
|
| 204 |
+
head_first=False,
|
| 205 |
+
)
|
| 206 |
+
elif mode == 'chunk':
|
| 207 |
+
q, k, rt_gate_log = map(lambda x: x.to(v.dtype), (q, k, rt_gate_log))
|
| 208 |
+
o, recurrent_state = chunk_gla(
|
| 209 |
+
q=q,
|
| 210 |
+
k=k,
|
| 211 |
+
v=v,
|
| 212 |
+
g=rt_gate_log,
|
| 213 |
+
initial_state=recurrent_state,
|
| 214 |
+
output_final_state=use_cache,
|
| 215 |
+
cu_seqlens=cu_seqlens,
|
| 216 |
+
head_first=False,
|
| 217 |
+
)
|
| 218 |
+
else:
|
| 219 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 220 |
+
|
| 221 |
+
rodimus_caches = None
|
| 222 |
+
if past_key_values is not None:
|
| 223 |
+
if self.block_type == 'rodimus':
|
| 224 |
+
update_layer_cache(
|
| 225 |
+
self,
|
| 226 |
+
past_key_values,
|
| 227 |
+
recurrent_state=recurrent_state,
|
| 228 |
+
conv_state=conv_state if self.use_short_conv else None,
|
| 229 |
+
offset=q_len,
|
| 230 |
+
)
|
| 231 |
+
else:
|
| 232 |
+
rodimus_caches = (recurrent_state, conv_state if self.use_short_conv else None)
|
| 233 |
+
|
| 234 |
+
o = (o.transpose(1, 2).squeeze(1) + (shift_hidden_states.float()
|
| 235 |
+
if self.residual_in_fp32 else shift_hidden_states) * self.residual_weight).to(o.dtype)
|
| 236 |
+
|
| 237 |
+
o = self.activation_norm(o, final_gate)
|
| 238 |
+
o = self.down_proj(o)
|
| 239 |
+
|
| 240 |
+
if attention_mask is not None:
|
| 241 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 242 |
+
|
| 243 |
+
if self.block_type == 'rodimus':
|
| 244 |
+
return o, None, past_key_values
|
| 245 |
+
else:
|
| 246 |
+
return o, None, (past_key_values, rodimus_caches)
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
class SlidingWindowSharedKeyAttention(nn.Module):
|
| 250 |
+
def __init__(
|
| 251 |
+
self,
|
| 252 |
+
hidden_size: int = 2048,
|
| 253 |
+
num_heads: int = 32,
|
| 254 |
+
qkv_bias: bool = False,
|
| 255 |
+
qk_norm: bool = False,
|
| 256 |
+
window_size: int = 2048,
|
| 257 |
+
rope_theta: float | None = 10000.,
|
| 258 |
+
max_position_embeddings: int | None = None,
|
| 259 |
+
layer_idx: int = None,
|
| 260 |
+
):
|
| 261 |
+
super().__init__()
|
| 262 |
+
|
| 263 |
+
self.hidden_size = hidden_size
|
| 264 |
+
self.num_heads = num_heads
|
| 265 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 266 |
+
self.qkv_bias = qkv_bias
|
| 267 |
+
self.qk_norm = qk_norm
|
| 268 |
+
|
| 269 |
+
self.window_size = window_size
|
| 270 |
+
self.rope_theta = rope_theta
|
| 271 |
+
self.max_position_embeddings = max_position_embeddings
|
| 272 |
+
self.layer_idx = layer_idx
|
| 273 |
+
|
| 274 |
+
self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
|
| 275 |
+
self.k_proj = nn.Linear(self.hidden_size, self.head_dim, bias=self.qkv_bias)
|
| 276 |
+
self.v_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
|
| 277 |
+
self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 278 |
+
|
| 279 |
+
if qk_norm:
|
| 280 |
+
self.q_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 281 |
+
self.k_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 282 |
+
|
| 283 |
+
self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
|
| 284 |
+
|
| 285 |
+
def forward(
|
| 286 |
+
self,
|
| 287 |
+
hidden_states: torch.Tensor,
|
| 288 |
+
attention_mask: torch.LongTensor | None = None,
|
| 289 |
+
past_key_values: Cache | None = None,
|
| 290 |
+
output_attentions: bool = False,
|
| 291 |
+
use_cache: bool = False,
|
| 292 |
+
**kwargs,
|
| 293 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 294 |
+
rodimus_caches = kwargs.get('rodimus_caches')
|
| 295 |
+
|
| 296 |
+
if attention_mask is not None:
|
| 297 |
+
assert len(attention_mask.shape) == 2, (
|
| 298 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 299 |
+
"for padding purposes (0 indicating padding). "
|
| 300 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 301 |
+
)
|
| 302 |
+
|
| 303 |
+
batch_size, q_len, _ = hidden_states.size()
|
| 304 |
+
|
| 305 |
+
q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 306 |
+
k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 307 |
+
v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 308 |
+
|
| 309 |
+
if self.qk_norm:
|
| 310 |
+
q, k = self.q_norm(q), self.k_norm(k)
|
| 311 |
+
|
| 312 |
+
# equivalent to cu_seqlens in `flash_attn`
|
| 313 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 314 |
+
|
| 315 |
+
layer_idx = require_cache_layer_idx(self, past_key_values)
|
| 316 |
+
seqlen_offset, max_seqlen = 0, q.shape[1]
|
| 317 |
+
if past_key_values is not None:
|
| 318 |
+
seqlen_offset = past_key_values.get_seq_length(layer_idx)
|
| 319 |
+
max_seqlen = q.shape[1] + seqlen_offset
|
| 320 |
+
|
| 321 |
+
if attention_mask is not None:
|
| 322 |
+
# to deliminate the offsets of padding tokens
|
| 323 |
+
seqlen_offset = seqlen_offset + attention_mask.sum(-1) - attention_mask.shape[-1]
|
| 324 |
+
max_seqlen = q.shape[1] + max(seqlen_offset)
|
| 325 |
+
|
| 326 |
+
if self.max_position_embeddings is not None:
|
| 327 |
+
max_seqlen = max(max_seqlen, self.max_position_embeddings)
|
| 328 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
|
| 329 |
+
|
| 330 |
+
if past_key_values is not None:
|
| 331 |
+
if rodimus_caches is not None:
|
| 332 |
+
recurrent_state, conv_state = rodimus_caches
|
| 333 |
+
else:
|
| 334 |
+
recurrent_state, conv_state = None, None
|
| 335 |
+
|
| 336 |
+
cache_has_content = past_key_values.get_seq_length(layer_idx) > 0
|
| 337 |
+
k_cached, v_cached = past_key_values.update(
|
| 338 |
+
recurrent_state=recurrent_state,
|
| 339 |
+
conv_state=conv_state,
|
| 340 |
+
attn_state=[k.flatten(-2, -1), v.flatten(-2, -1)],
|
| 341 |
+
layer_idx=layer_idx,
|
| 342 |
+
offset=q_len,
|
| 343 |
+
cache_kwargs=dict(window_size=self.window_size),
|
| 344 |
+
)['attn_state']
|
| 345 |
+
if cache_has_content:
|
| 346 |
+
k, v = k_cached, v_cached
|
| 347 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 348 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 349 |
+
|
| 350 |
+
if flash_attn_func is None:
|
| 351 |
+
raise ImportError("Please install Flash Attention via `pip install flash-attn --no-build-isolation` first")
|
| 352 |
+
|
| 353 |
+
q, k, v = map(autocast_to_fp16, (q, k, v))
|
| 354 |
+
k = repeat(k, "... h d -> ... (n h) d", n=self.num_heads)
|
| 355 |
+
# Contains at least one padding token in the sequence
|
| 356 |
+
if attention_mask is not None:
|
| 357 |
+
q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(
|
| 358 |
+
q=q,
|
| 359 |
+
states=(k, v),
|
| 360 |
+
attention_mask=attention_mask[:, -max(self.window_size, q_len):],
|
| 361 |
+
q_len=q_len,
|
| 362 |
+
)
|
| 363 |
+
cu_seqlens_q, cu_seqlens_k = cu_seqlens
|
| 364 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 365 |
+
o = flash_attn_varlen_func(
|
| 366 |
+
q, k, v,
|
| 367 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 368 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 369 |
+
max_seqlen_q=max_seqlen_q,
|
| 370 |
+
max_seqlen_k=max_seqlen_k,
|
| 371 |
+
causal=True,
|
| 372 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 373 |
+
)
|
| 374 |
+
o = pad_input(o, indices_q, batch_size, q_len)
|
| 375 |
+
elif cu_seqlens is not None:
|
| 376 |
+
o = flash_attn_varlen_func(
|
| 377 |
+
q.squeeze(0), k.squeeze(0), v.squeeze(0),
|
| 378 |
+
cu_seqlens_q=cu_seqlens,
|
| 379 |
+
cu_seqlens_k=cu_seqlens,
|
| 380 |
+
max_seqlen_q=max_seqlen,
|
| 381 |
+
max_seqlen_k=max_seqlen,
|
| 382 |
+
causal=True,
|
| 383 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 384 |
+
).unsqueeze(0)
|
| 385 |
+
else:
|
| 386 |
+
o = flash_attn_func(
|
| 387 |
+
q, k, v,
|
| 388 |
+
causal=True,
|
| 389 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 390 |
+
)
|
| 391 |
+
o = o.reshape(batch_size, q_len, -1)
|
| 392 |
+
o = self.o_proj(o.to(dtype=self.o_proj.weight.dtype))
|
| 393 |
+
|
| 394 |
+
if not output_attentions:
|
| 395 |
+
attentions = None
|
| 396 |
+
|
| 397 |
+
return o, attentions, past_key_values
|
fla/layers/rwkv6.py
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
# "Eagle and Finch: RWKV with Matrix-Valued States and Dynamic Recurrence"[https://arxiv.org/abs/2404.05892]
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import math
|
| 8 |
+
import warnings
|
| 9 |
+
from typing import TYPE_CHECKING
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import torch.nn as nn
|
| 13 |
+
from einops import rearrange
|
| 14 |
+
|
| 15 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 16 |
+
from fla.modules import GroupNorm
|
| 17 |
+
from fla.modules.activations import ACT2FN
|
| 18 |
+
from fla.modules.token_shift import token_shift
|
| 19 |
+
from fla.ops.rwkv6 import chunk_rwkv6, fused_recurrent_rwkv6
|
| 20 |
+
|
| 21 |
+
if TYPE_CHECKING:
|
| 22 |
+
from fla.models.utils import Cache
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class RWKV6Attention(nn.Module):
|
| 26 |
+
|
| 27 |
+
def __init__(
|
| 28 |
+
self,
|
| 29 |
+
mode: str = 'chunk',
|
| 30 |
+
hidden_size: int = 1024,
|
| 31 |
+
expand_k: float = 0.5,
|
| 32 |
+
expand_v: float = 1.0,
|
| 33 |
+
num_heads: int = 4,
|
| 34 |
+
gate_fn: str = 'swish',
|
| 35 |
+
proj_low_rank_dim: int = 32,
|
| 36 |
+
gate_low_rank_dim: int = 64,
|
| 37 |
+
fuse_norm: bool = True,
|
| 38 |
+
elementwise_affine: bool | None = True,
|
| 39 |
+
norm_eps: float = 1e-5,
|
| 40 |
+
layer_idx: int = None,
|
| 41 |
+
**kwargs,
|
| 42 |
+
) -> RWKV6Attention:
|
| 43 |
+
super().__init__()
|
| 44 |
+
|
| 45 |
+
self.mode = mode
|
| 46 |
+
self.hidden_size = hidden_size
|
| 47 |
+
self.expand_k = expand_k
|
| 48 |
+
self.expand_v = expand_v
|
| 49 |
+
self.num_heads = num_heads
|
| 50 |
+
self.proj_low_rank_dim = proj_low_rank_dim
|
| 51 |
+
self.gate_low_rank_dim = gate_low_rank_dim
|
| 52 |
+
|
| 53 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 54 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 55 |
+
self.layer_idx = layer_idx
|
| 56 |
+
|
| 57 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 58 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 59 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 60 |
+
|
| 61 |
+
self.head_k_dim = self.key_dim // num_heads
|
| 62 |
+
self.head_v_dim = self.value_dim // num_heads
|
| 63 |
+
|
| 64 |
+
self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
|
| 65 |
+
self.x_proj = nn.Sequential(
|
| 66 |
+
LerpLinear(hidden_size, proj_low_rank_dim * 5),
|
| 67 |
+
nn.Tanh(),
|
| 68 |
+
nn.Linear(proj_low_rank_dim * 5, hidden_size, bias=False),
|
| 69 |
+
)
|
| 70 |
+
self.x_bias = nn.Parameter(torch.zeros(5, hidden_size))
|
| 71 |
+
|
| 72 |
+
self.r_proj = DDLerpLinear(hidden_size, self.key_dim)
|
| 73 |
+
self.w_proj = DDLerpLinear(hidden_size, self.key_dim, low_rank_dim=gate_low_rank_dim)
|
| 74 |
+
self.k_proj = DDLerpLinear(hidden_size, self.key_dim)
|
| 75 |
+
self.v_proj = DDLerpLinear(hidden_size, self.value_dim)
|
| 76 |
+
self.g_proj = DDLerpLinear(hidden_size, self.value_dim)
|
| 77 |
+
self.bonus = nn.Parameter(torch.zeros(num_heads, self.head_k_dim))
|
| 78 |
+
|
| 79 |
+
# TODO: fuse GroupNorm and output gate
|
| 80 |
+
self.g_norm = GroupNorm(self.num_heads, self.value_dim, elementwise_affine=elementwise_affine, bias=True, eps=norm_eps)
|
| 81 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 82 |
+
self.gate_fn = ACT2FN[gate_fn]
|
| 83 |
+
|
| 84 |
+
try:
|
| 85 |
+
from transformers.modeling_utils import _init_weights
|
| 86 |
+
except ImportError:
|
| 87 |
+
_init_weights = True
|
| 88 |
+
if _init_weights:
|
| 89 |
+
self.apply(self._initialize_weights)
|
| 90 |
+
|
| 91 |
+
warnings.warn(
|
| 92 |
+
"According to Bo, you are using a potentially buggy FLA implementation of RWKV. "
|
| 93 |
+
"If you plan to report any numbers based on this implementation, we strongly recommend "
|
| 94 |
+
"cross-checking with the official repo: https://github.com/BlinkDL/RWKV-LM. "
|
| 95 |
+
"Bo may disagree with results reported from this version.",
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
def _initialize_weights(self, module: nn.Module):
|
| 99 |
+
if getattr(module, "_is_hf_initialized", False):
|
| 100 |
+
return
|
| 101 |
+
if isinstance(module, nn.Linear):
|
| 102 |
+
nn.init.xavier_uniform_(module.weight, gain=2 ** -2.5)
|
| 103 |
+
if module.bias is not None:
|
| 104 |
+
nn.init.zeros_(module.bias)
|
| 105 |
+
if isinstance(module, nn.Parameter):
|
| 106 |
+
nn.init.xavier_uniform_(module, gain=2 ** -2.5)
|
| 107 |
+
module._is_hf_initialized = True
|
| 108 |
+
|
| 109 |
+
def forward(
|
| 110 |
+
self,
|
| 111 |
+
hidden_states: torch.Tensor,
|
| 112 |
+
attention_mask: torch.Tensor | None = None,
|
| 113 |
+
past_key_values: Cache | None = None,
|
| 114 |
+
use_cache: bool | None = False,
|
| 115 |
+
output_attentions: bool | None = False,
|
| 116 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 117 |
+
**kwargs,
|
| 118 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 119 |
+
if attention_mask is not None:
|
| 120 |
+
assert len(attention_mask.shape) == 2, (
|
| 121 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 122 |
+
"for padding purposes (0 indicating padding). "
|
| 123 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
batch_size, seq_len, hidden_size = hidden_states.shape
|
| 127 |
+
# launching the triton kernel for just one token will actually be slower
|
| 128 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 129 |
+
|
| 130 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 131 |
+
|
| 132 |
+
if attention_mask is not None:
|
| 133 |
+
hidden_states = hidden_states.mul(attention_mask[:, -hidden_states.shape[-2]:, None])
|
| 134 |
+
|
| 135 |
+
if hidden_states.shape[1] == 1 and last_state is not None:
|
| 136 |
+
shifted = last_state['conv_state'].unsqueeze(1)
|
| 137 |
+
delta = shifted - hidden_states
|
| 138 |
+
elif last_state is None:
|
| 139 |
+
delta = token_shift(hidden_states, cu_seqlens)
|
| 140 |
+
else:
|
| 141 |
+
shifted = self.time_shift(hidden_states)
|
| 142 |
+
shifted[:, 0] = last_state['conv_state']
|
| 143 |
+
delta = shifted - hidden_states
|
| 144 |
+
|
| 145 |
+
x = self.x_proj[0](hidden_states, delta, cu_seqlens).view(batch_size, seq_len, -1, self.proj_low_rank_dim)
|
| 146 |
+
x = torch.einsum('b t n r, h n r-> b t n h', self.x_proj[1](x), self.x_proj[2].weight.view(hidden_size, 5, -1))
|
| 147 |
+
|
| 148 |
+
r, w, k, v, g = x.add_(self.x_bias).unbind(-2)
|
| 149 |
+
r = self.r_proj(hidden_states, r, delta, cu_seqlens)
|
| 150 |
+
w = self.w_proj(hidden_states, w, delta, cu_seqlens)
|
| 151 |
+
k = self.k_proj(hidden_states, k, delta, cu_seqlens)
|
| 152 |
+
v = self.v_proj(hidden_states, v, delta, cu_seqlens)
|
| 153 |
+
g = self.g_proj(hidden_states, g, delta, cu_seqlens)
|
| 154 |
+
|
| 155 |
+
# dealing with left-padding
|
| 156 |
+
if attention_mask is not None:
|
| 157 |
+
v = v.mul(attention_mask[:, -v.shape[-2]:, None])
|
| 158 |
+
r, w, k = map(lambda x: rearrange(x, 'b t (h d) -> b t h d', d=self.head_k_dim), (r, w, k))
|
| 159 |
+
v = rearrange(v, 'b t (h d) -> b t h d', d=self.head_v_dim)
|
| 160 |
+
w = -torch.exp(w)
|
| 161 |
+
u = self.bonus
|
| 162 |
+
|
| 163 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 164 |
+
|
| 165 |
+
if mode == 'fused_recurrent':
|
| 166 |
+
o, recurrent_state = fused_recurrent_rwkv6(
|
| 167 |
+
r=r,
|
| 168 |
+
k=k,
|
| 169 |
+
v=v,
|
| 170 |
+
w=w,
|
| 171 |
+
u=u,
|
| 172 |
+
scale=1.,
|
| 173 |
+
initial_state=recurrent_state,
|
| 174 |
+
output_final_state=use_cache,
|
| 175 |
+
cu_seqlens=cu_seqlens,
|
| 176 |
+
)
|
| 177 |
+
elif mode == 'chunk':
|
| 178 |
+
o, recurrent_state = chunk_rwkv6(
|
| 179 |
+
r=r,
|
| 180 |
+
k=k,
|
| 181 |
+
v=v,
|
| 182 |
+
w=w,
|
| 183 |
+
u=u,
|
| 184 |
+
scale=1.,
|
| 185 |
+
initial_state=recurrent_state,
|
| 186 |
+
output_final_state=use_cache,
|
| 187 |
+
cu_seqlens=cu_seqlens,
|
| 188 |
+
)
|
| 189 |
+
else:
|
| 190 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 191 |
+
|
| 192 |
+
update_layer_cache(
|
| 193 |
+
self,
|
| 194 |
+
past_key_values,
|
| 195 |
+
recurrent_state=recurrent_state,
|
| 196 |
+
conv_state=hidden_states[:, -1],
|
| 197 |
+
offset=seq_len,
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
o = self.g_norm(rearrange(o, '... h d -> ... (h d)')) * self.gate_fn(g)
|
| 201 |
+
o = self.o_proj(o)
|
| 202 |
+
|
| 203 |
+
return o, None, past_key_values
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
class LoRA(nn.Module):
|
| 207 |
+
|
| 208 |
+
def __init__(
|
| 209 |
+
self,
|
| 210 |
+
input_dim: int,
|
| 211 |
+
output_dim: int,
|
| 212 |
+
low_rank_dim: int,
|
| 213 |
+
bias: bool | None = True,
|
| 214 |
+
activation: str | None = 'tanh',
|
| 215 |
+
):
|
| 216 |
+
super().__init__()
|
| 217 |
+
|
| 218 |
+
self.input_dim = input_dim
|
| 219 |
+
self.output_dim = output_dim
|
| 220 |
+
self.low_rank_dim = low_rank_dim
|
| 221 |
+
self.bias = bias
|
| 222 |
+
|
| 223 |
+
if activation is None:
|
| 224 |
+
self.activation = nn.Identity()
|
| 225 |
+
elif activation == 'sigmoid':
|
| 226 |
+
self.activation = nn.Sigmoid()
|
| 227 |
+
elif activation == 'tanh':
|
| 228 |
+
self.activation = nn.Tanh()
|
| 229 |
+
elif activation == 'relu':
|
| 230 |
+
self.activation = nn.ReLU()
|
| 231 |
+
else:
|
| 232 |
+
raise ValueError(f"Not supported activation `{activation}`.")
|
| 233 |
+
|
| 234 |
+
self.lora = nn.Sequential(
|
| 235 |
+
nn.Linear(input_dim, low_rank_dim, bias=False),
|
| 236 |
+
self.activation,
|
| 237 |
+
nn.Linear(low_rank_dim, output_dim, bias=bias),
|
| 238 |
+
)
|
| 239 |
+
try:
|
| 240 |
+
from transformers.modeling_utils import _init_weights
|
| 241 |
+
except ImportError:
|
| 242 |
+
_init_weights = True
|
| 243 |
+
if _init_weights:
|
| 244 |
+
self.apply(self._initialize_weights)
|
| 245 |
+
|
| 246 |
+
def __repr__(self) -> str:
|
| 247 |
+
s = f"{self.__class__.__name__}("
|
| 248 |
+
s += f"input_dim={self.input_dim}, low_rank_dim={self.low_rank_dim}, output_dim={self.output_dim}"
|
| 249 |
+
if not self.bias:
|
| 250 |
+
s += f", bias={self.bias}"
|
| 251 |
+
s += ")"
|
| 252 |
+
return s
|
| 253 |
+
|
| 254 |
+
def _initialize_weights(self, module: nn.Module):
|
| 255 |
+
if getattr(module, "_is_hf_initialized", False):
|
| 256 |
+
return
|
| 257 |
+
|
| 258 |
+
# Initialize weights to zero as in original code
|
| 259 |
+
nn.init.zeros_(self.lora[0].weight)
|
| 260 |
+
original_dtype = self.lora[2].weight.dtype
|
| 261 |
+
shape = self.lora[2].weight.shape
|
| 262 |
+
# Convert to float32 for numerical stability in orthogonal init
|
| 263 |
+
weight_fp32 = self.lora[2].weight.float()
|
| 264 |
+
|
| 265 |
+
# Calculate gain based on dimensions
|
| 266 |
+
gain = math.sqrt(shape[1] / shape[0]) if shape[1] > shape[0] else 1
|
| 267 |
+
|
| 268 |
+
# Apply orthogonal initialization with scaling factor 0.1
|
| 269 |
+
nn.init.orthogonal_(weight_fp32, gain=gain * 0.1)
|
| 270 |
+
|
| 271 |
+
# Convert back to original dtype
|
| 272 |
+
self.lora[2].weight.data.copy_(weight_fp32.to(original_dtype))
|
| 273 |
+
# Set Lora[2] bias to zero
|
| 274 |
+
if self.lora[2].bias is not None:
|
| 275 |
+
nn.init.zeros_(self.lora[2].bias)
|
| 276 |
+
|
| 277 |
+
module._is_hf_initialized = True
|
| 278 |
+
|
| 279 |
+
def set_bias_value(self, value):
|
| 280 |
+
"""Set bias to a specific value (for v0, w0 etc.)"""
|
| 281 |
+
if self.bias and self.lora[2].bias is not None:
|
| 282 |
+
if isinstance(value, torch.Tensor):
|
| 283 |
+
# Handle tensor values
|
| 284 |
+
self.lora[2].bias.data.copy_(value.to(self.lora[2].bias.dtype))
|
| 285 |
+
else:
|
| 286 |
+
# Handle scalar values
|
| 287 |
+
nn.init.constant_(self.lora[2].bias, value)
|
| 288 |
+
|
| 289 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 290 |
+
return self.lora(x)
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
class LerpLinear(nn.Module):
|
| 294 |
+
|
| 295 |
+
def __init__(
|
| 296 |
+
self,
|
| 297 |
+
input_dim: int,
|
| 298 |
+
output_dim: int,
|
| 299 |
+
low_rank_dim: int | None = None,
|
| 300 |
+
):
|
| 301 |
+
super().__init__()
|
| 302 |
+
|
| 303 |
+
self.input_dim = input_dim
|
| 304 |
+
self.output_dim = output_dim
|
| 305 |
+
self.low_rank_dim = low_rank_dim
|
| 306 |
+
|
| 307 |
+
self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
|
| 308 |
+
if low_rank_dim is None:
|
| 309 |
+
self.linear = nn.Linear(input_dim, output_dim, bias=False)
|
| 310 |
+
else:
|
| 311 |
+
self.linear = LoRA(input_dim, output_dim, low_rank_dim)
|
| 312 |
+
self.mu = nn.Parameter(torch.zeros(input_dim))
|
| 313 |
+
|
| 314 |
+
def __repr__(self) -> str:
|
| 315 |
+
s = f"{self.__class__.__name__}({self.input_dim}, {self.output_dim}"
|
| 316 |
+
if self.low_rank_dim is not None:
|
| 317 |
+
s += f", low_rank_dim={self.low_rank_dim}"
|
| 318 |
+
s += ")"
|
| 319 |
+
return s
|
| 320 |
+
|
| 321 |
+
def forward(self, x: torch.Tensor, delta: torch.Tensor | None = None,
|
| 322 |
+
cu_seqlens: torch.LongTensor | None = None) -> torch.Tensor:
|
| 323 |
+
if delta is None:
|
| 324 |
+
delta = token_shift(x, cu_seqlens)
|
| 325 |
+
return self.linear(x + delta * self.mu)
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
class DDLerpLinear(nn.Module):
|
| 329 |
+
|
| 330 |
+
def __init__(
|
| 331 |
+
self,
|
| 332 |
+
input_dim: int,
|
| 333 |
+
output_dim: int,
|
| 334 |
+
low_rank_dim: int | None = None,
|
| 335 |
+
):
|
| 336 |
+
super().__init__()
|
| 337 |
+
|
| 338 |
+
self.input_dim = input_dim
|
| 339 |
+
self.output_dim = output_dim
|
| 340 |
+
self.low_rank_dim = low_rank_dim
|
| 341 |
+
|
| 342 |
+
self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
|
| 343 |
+
if low_rank_dim is None:
|
| 344 |
+
self.linear = nn.Linear(input_dim, output_dim, bias=False)
|
| 345 |
+
else:
|
| 346 |
+
self.linear = LoRA(input_dim, output_dim, low_rank_dim)
|
| 347 |
+
|
| 348 |
+
def __repr__(self) -> str:
|
| 349 |
+
s = f"{self.__class__.__name__}({self.input_dim}, {self.output_dim}"
|
| 350 |
+
if self.low_rank_dim is not None:
|
| 351 |
+
s += f", low_rank_dim={self.low_rank_dim}"
|
| 352 |
+
s += ")"
|
| 353 |
+
return s
|
| 354 |
+
|
| 355 |
+
def forward(self, x: torch.Tensor, mu: torch.Tensor,
|
| 356 |
+
delta: torch.Tensor | None = None,
|
| 357 |
+
cu_seqlens: torch.LongTensor | None = None) -> torch.Tensor:
|
| 358 |
+
if delta is None:
|
| 359 |
+
delta = token_shift(x, cu_seqlens)
|
| 360 |
+
return self.linear(x + delta * mu)
|
fla/layers/rwkv7.py
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
from torch.nn import functional as F
|
| 12 |
+
|
| 13 |
+
from fla.layers.rwkv6 import LoRA
|
| 14 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 15 |
+
from fla.modules import GroupNorm
|
| 16 |
+
from fla.modules.l2norm import l2_norm
|
| 17 |
+
from fla.modules.token_shift import token_shift
|
| 18 |
+
from fla.ops.rwkv7 import chunk_rwkv7, fused_mul_recurrent_rwkv7
|
| 19 |
+
from fla.ops.rwkv7.fused_addcmul import fused_addcmul_rwkv7
|
| 20 |
+
from fla.ops.rwkv7.fused_k_update import fused_k_rwkv7
|
| 21 |
+
from fla.ops.rwkv7.gate_output_correction import gate_output_correction
|
| 22 |
+
|
| 23 |
+
if TYPE_CHECKING:
|
| 24 |
+
from fla.models.utils import Cache
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class RWKV7Attention(nn.Module):
|
| 28 |
+
|
| 29 |
+
def __init__(
|
| 30 |
+
self,
|
| 31 |
+
mode: str = 'chunk',
|
| 32 |
+
hidden_size: int = 1024,
|
| 33 |
+
head_dim: int | None = 64,
|
| 34 |
+
num_heads: int | None = None,
|
| 35 |
+
decay_low_rank_dim: int | None = None,
|
| 36 |
+
gate_low_rank_dim: int | None = None,
|
| 37 |
+
a_low_rank_dim: int | None = None,
|
| 38 |
+
v_low_rank_dim: int | None = None,
|
| 39 |
+
elementwise_affine: bool | None = True,
|
| 40 |
+
norm_eps: float = 1e-5,
|
| 41 |
+
layer_idx: int = None,
|
| 42 |
+
fuse_norm: bool = False,
|
| 43 |
+
value_dim: int = None,
|
| 44 |
+
num_hidden_layers: int = None,
|
| 45 |
+
**kwargs,
|
| 46 |
+
) -> RWKV7Attention:
|
| 47 |
+
super().__init__()
|
| 48 |
+
|
| 49 |
+
self.mode = mode
|
| 50 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 51 |
+
self.hidden_size = hidden_size
|
| 52 |
+
|
| 53 |
+
self.key_dim = hidden_size
|
| 54 |
+
self.value_dim = value_dim if value_dim is not None else hidden_size
|
| 55 |
+
if head_dim is None and num_heads is None:
|
| 56 |
+
raise ValueError("Either `head_dim` or `num_heads` must be specified.")
|
| 57 |
+
elif head_dim is not None:
|
| 58 |
+
self.head_dim = head_dim
|
| 59 |
+
self.num_heads = int(hidden_size // head_dim)
|
| 60 |
+
elif num_heads is not None:
|
| 61 |
+
self.head_dim = int(hidden_size // num_heads)
|
| 62 |
+
self.num_heads = num_heads
|
| 63 |
+
self.head_v_dim = int(self.value_dim // self.num_heads)
|
| 64 |
+
|
| 65 |
+
# Increase lora dimension for headdim>64
|
| 66 |
+
factor = self.head_dim / 64
|
| 67 |
+
if decay_low_rank_dim is None:
|
| 68 |
+
decay_low_rank_dim = max(32, int(round((2.5 * (hidden_size**0.5)) * factor / 32) * 32))
|
| 69 |
+
self.decay_low_rank_dim = decay_low_rank_dim
|
| 70 |
+
else:
|
| 71 |
+
self.decay_low_rank_dim = decay_low_rank_dim
|
| 72 |
+
|
| 73 |
+
if gate_low_rank_dim is None:
|
| 74 |
+
gate_low_rank_dim = max(32, int(round((5 * (hidden_size**0.5)) / 32) * 32))
|
| 75 |
+
self.gate_low_rank_dim = gate_low_rank_dim
|
| 76 |
+
else:
|
| 77 |
+
self.gate_low_rank_dim = gate_low_rank_dim
|
| 78 |
+
|
| 79 |
+
if a_low_rank_dim is None:
|
| 80 |
+
a_low_rank_dim = max(32, int(round((2.5 * (hidden_size**0.5)) * factor / 32) * 32))
|
| 81 |
+
self.a_low_rank_dim = a_low_rank_dim
|
| 82 |
+
else:
|
| 83 |
+
self.a_low_rank_dim = a_low_rank_dim
|
| 84 |
+
|
| 85 |
+
if v_low_rank_dim is None:
|
| 86 |
+
v_low_rank_dim = max(32, int(round((1.7 * (hidden_size**0.5)) * factor / 32) * 32))
|
| 87 |
+
self.v_low_rank_dim = v_low_rank_dim
|
| 88 |
+
else:
|
| 89 |
+
self.v_low_rank_dim = v_low_rank_dim
|
| 90 |
+
|
| 91 |
+
self.layer_idx = layer_idx
|
| 92 |
+
self.num_hidden_layers = num_hidden_layers
|
| 93 |
+
self.fuse_norm = fuse_norm
|
| 94 |
+
|
| 95 |
+
self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
|
| 96 |
+
self.x_r = nn.Parameter(torch.zeros(1, 1, hidden_size))
|
| 97 |
+
self.x_w = nn.Parameter(torch.zeros(1, 1, hidden_size))
|
| 98 |
+
self.x_k = nn.Parameter(torch.zeros(1, 1, hidden_size))
|
| 99 |
+
self.x_v = nn.Parameter(torch.zeros(1, 1, hidden_size))
|
| 100 |
+
self.x_a = nn.Parameter(torch.zeros(1, 1, hidden_size))
|
| 101 |
+
self.x_g = nn.Parameter(torch.zeros(1, 1, hidden_size))
|
| 102 |
+
|
| 103 |
+
self.k_k = nn.Parameter(torch.zeros(self.key_dim))
|
| 104 |
+
self.k_a = nn.Parameter(torch.zeros(self.key_dim))
|
| 105 |
+
self.r_k = nn.Parameter(torch.zeros(self.num_heads, self.head_dim))
|
| 106 |
+
|
| 107 |
+
self.r_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 108 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 109 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 110 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 111 |
+
|
| 112 |
+
self.w_lora = LoRA(hidden_size, self.key_dim, low_rank_dim=decay_low_rank_dim, activation='tanh')
|
| 113 |
+
if self.layer_idx != 0:
|
| 114 |
+
self.v_lora = LoRA(hidden_size, self.value_dim, low_rank_dim=v_low_rank_dim, activation=None)
|
| 115 |
+
self.a_lora = LoRA(hidden_size, self.key_dim, low_rank_dim=a_low_rank_dim, activation=None)
|
| 116 |
+
self.g_lora = LoRA(hidden_size, self.value_dim, low_rank_dim=gate_low_rank_dim, activation='sigmoid', bias=False)
|
| 117 |
+
|
| 118 |
+
if self.fuse_norm:
|
| 119 |
+
self.g_norm = GroupNorm(
|
| 120 |
+
num_groups=self.num_heads,
|
| 121 |
+
hidden_size=self.value_dim,
|
| 122 |
+
elementwise_affine=elementwise_affine,
|
| 123 |
+
eps=self.head_dim*norm_eps,
|
| 124 |
+
bias=True,
|
| 125 |
+
)
|
| 126 |
+
else:
|
| 127 |
+
self.g_norm = nn.GroupNorm(
|
| 128 |
+
num_groups=self.num_heads,
|
| 129 |
+
num_channels=self.value_dim,
|
| 130 |
+
eps=self.head_dim*norm_eps,
|
| 131 |
+
affine=elementwise_affine,
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
try:
|
| 135 |
+
from transformers.modeling_utils import _init_weights
|
| 136 |
+
except ImportError:
|
| 137 |
+
_init_weights = True
|
| 138 |
+
if _init_weights:
|
| 139 |
+
self.apply(self._initialize_weights)
|
| 140 |
+
for name, module in self.named_modules():
|
| 141 |
+
module._in_rwkv_module = True
|
| 142 |
+
|
| 143 |
+
warnings.warn(
|
| 144 |
+
"According to Bo, you are using a potentially buggy FLA implementation of RWKV. "
|
| 145 |
+
"If you plan to report any numbers based on this implementation, we strongly recommend "
|
| 146 |
+
"cross-checking with the official repo: https://github.com/BlinkDL/RWKV-LM. "
|
| 147 |
+
"Bo may disagree with results reported from this version.",
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
@torch.no_grad()
|
| 151 |
+
@torch.compiler.disable
|
| 152 |
+
def _initialize_weights(self, module: nn.Module):
|
| 153 |
+
if getattr(module, "_is_hf_initialized", False):
|
| 154 |
+
return
|
| 155 |
+
|
| 156 |
+
# Initialize only when we're processing the RWKV7Attention module itself
|
| 157 |
+
if isinstance(module, RWKV7Attention) and self.layer_idx is not None:
|
| 158 |
+
ratio_0_to_1 = self.layer_idx / (self.num_hidden_layers - 1) # 0 to 1
|
| 159 |
+
ratio_1_to_almost0 = 1.0 - (self.layer_idx / self.num_hidden_layers) # 1 to ~0
|
| 160 |
+
|
| 161 |
+
# Create position-based initialization tensor
|
| 162 |
+
ddd = torch.ones(1, 1, self.hidden_size, device=self.x_r.device)
|
| 163 |
+
www = torch.zeros(self.hidden_size, device=self.x_r.device)
|
| 164 |
+
zigzag = torch.zeros(self.hidden_size, device=self.x_r.device)
|
| 165 |
+
linear = torch.zeros(self.hidden_size, device=self.x_r.device)
|
| 166 |
+
for n in range(self.hidden_size):
|
| 167 |
+
linear[n] = n / (self.hidden_size-1) - 0.5
|
| 168 |
+
zigzag[n] = ((n % self.head_dim) - ((self.head_dim-1) / 2)) / ((self.head_dim-1) / 2)
|
| 169 |
+
zigzag[n] = zigzag[n] * abs(zigzag[n])
|
| 170 |
+
www[n] = -6 + 6 * (n / (self.hidden_size - 1)) ** (1 + 1 * ratio_0_to_1 ** 0.3)
|
| 171 |
+
ddd[0, 0, n] = n / self.hidden_size
|
| 172 |
+
|
| 173 |
+
# Initialize x_* parameters directly
|
| 174 |
+
self.x_r.data = (1.0 - torch.pow(ddd, 0.2 * ratio_1_to_almost0)).to(self.x_r.dtype)
|
| 175 |
+
self.x_w.data = (1.0 - torch.pow(ddd, 0.9 * ratio_1_to_almost0)).to(self.x_w.dtype)
|
| 176 |
+
self.x_k.data = (1.0 - torch.pow(ddd, 0.7 * ratio_1_to_almost0)).to(self.x_k.dtype)
|
| 177 |
+
self.x_v.data = (1.0 - torch.pow(ddd, 0.7 * ratio_1_to_almost0)).to(self.x_v.dtype)
|
| 178 |
+
self.x_a.data = (1.0 - torch.pow(ddd, 0.9 * ratio_1_to_almost0)).to(self.x_a.dtype)
|
| 179 |
+
self.x_g.data = (1.0 - torch.pow(ddd, 0.2 * ratio_1_to_almost0)).to(self.x_g.dtype)
|
| 180 |
+
|
| 181 |
+
# Initialize k_k, k_a, r_k
|
| 182 |
+
nn.init.constant_(self.k_a, 1.02)
|
| 183 |
+
nn.init.constant_(self.r_k, -0.04)
|
| 184 |
+
self.k_k.data.copy_((torch.zeros(self.hidden_size, device=self.k_k.device) +
|
| 185 |
+
0.71 - linear*0.1).to(self.k_k.dtype))
|
| 186 |
+
# Set specific bias values for LoRA modules
|
| 187 |
+
# 0.5 comes from F.softplus
|
| 188 |
+
self.w_lora.set_bias_value(www + 0.5 + zigzag*2.5)
|
| 189 |
+
self.a_lora.set_bias_value(-0.19 + zigzag*0.3 + linear*0.4)
|
| 190 |
+
|
| 191 |
+
# v0 initialization - ones (for non-first layers)
|
| 192 |
+
if self.layer_idx != 0:
|
| 193 |
+
self.v_lora._initialize_weights(self.v_lora)
|
| 194 |
+
self.v_lora.set_bias_value(0.73 - linear*0.4)
|
| 195 |
+
|
| 196 |
+
# Initialize GroupNorm
|
| 197 |
+
self.g_norm.weight.data[:] = ((self.layer_idx + 1) / self.num_hidden_layers) ** 0.7
|
| 198 |
+
|
| 199 |
+
# Initialize Linear projections
|
| 200 |
+
self._orthogonal_init(self.r_proj.weight)
|
| 201 |
+
self._orthogonal_init(self.k_proj.weight, gain=0.1)
|
| 202 |
+
self._orthogonal_init(self.v_proj.weight)
|
| 203 |
+
self.o_proj.weight.data.zero_()
|
| 204 |
+
|
| 205 |
+
# Clean up temporary tensors to free memory
|
| 206 |
+
del ddd, www, zigzag, linear
|
| 207 |
+
|
| 208 |
+
module._is_hf_initialized = True
|
| 209 |
+
|
| 210 |
+
@staticmethod
|
| 211 |
+
def _orthogonal_init(weight, gain=1.0):
|
| 212 |
+
oringinal_dtype = weight.dtype
|
| 213 |
+
weight = weight.float()
|
| 214 |
+
nn.init.orthogonal_(weight, gain=gain)
|
| 215 |
+
weight = weight.to(oringinal_dtype)
|
| 216 |
+
|
| 217 |
+
def forward(
|
| 218 |
+
self,
|
| 219 |
+
hidden_states: torch.Tensor,
|
| 220 |
+
attention_mask: torch.Tensor | None = None,
|
| 221 |
+
past_key_values: Cache | None = None,
|
| 222 |
+
use_cache: bool | None = False,
|
| 223 |
+
output_attentions: bool | None = False,
|
| 224 |
+
v_first: torch.Tensor = None,
|
| 225 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 226 |
+
**kwargs,
|
| 227 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 228 |
+
batch_size, seq_len, _ = hidden_states.shape
|
| 229 |
+
if attention_mask is not None:
|
| 230 |
+
assert len(attention_mask.shape) == 2, (
|
| 231 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 232 |
+
"for padding purposes (0 indicating padding). "
|
| 233 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 234 |
+
)
|
| 235 |
+
am = attention_mask.narrow(1, attention_mask.size(1) - seq_len, seq_len).unsqueeze(-1)
|
| 236 |
+
|
| 237 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 238 |
+
|
| 239 |
+
if attention_mask is not None:
|
| 240 |
+
hidden_states = hidden_states.mul(am)
|
| 241 |
+
|
| 242 |
+
# delta [batch_size, seq_len, hidden_size]
|
| 243 |
+
# conv_cache [N, D]
|
| 244 |
+
if last_state is None:
|
| 245 |
+
conv_cache = None
|
| 246 |
+
recurrent_state = None
|
| 247 |
+
else:
|
| 248 |
+
conv_cache = last_state['conv_state']
|
| 249 |
+
recurrent_state = last_state['recurrent_state']
|
| 250 |
+
|
| 251 |
+
delta, conv_state = token_shift(
|
| 252 |
+
hidden_states, cu_seqlens, output_cache=True, cache=conv_cache,
|
| 253 |
+
)
|
| 254 |
+
xr, xw, xk, xv, xa, xg = fused_addcmul_rwkv7(hidden_states, delta, self.x_r, self.x_w,
|
| 255 |
+
self.x_k, self.x_v, self.x_a, self.x_g)
|
| 256 |
+
|
| 257 |
+
r = self.r_proj(xr)
|
| 258 |
+
# Using bf16 for LoRA computation is numerically safe here because:
|
| 259 |
+
# 1. After sigmoid activation:
|
| 260 |
+
# - Max absolute error (vs float32): 0.003
|
| 261 |
+
# - Mean absolute error: 0.0004
|
| 262 |
+
# 2. Subsequent scaling by -0.6065 will further reduce relative error
|
| 263 |
+
# (error scales linearly with constant multiplication)
|
| 264 |
+
# 3. Final compounded error remains within acceptable bounds for bf16 precision
|
| 265 |
+
# Empirical observation confirms bf16 introduces no practical degradation
|
| 266 |
+
w = -0.6065306597126334 * self.w_lora(xw).sigmoid()
|
| 267 |
+
|
| 268 |
+
k = self.k_proj(xk)
|
| 269 |
+
v = self.v_proj(xv)
|
| 270 |
+
|
| 271 |
+
if self.layer_idx == 0:
|
| 272 |
+
v_first = v
|
| 273 |
+
else:
|
| 274 |
+
v = torch.lerp(v, v_first, self.v_lora(xv).sigmoid())
|
| 275 |
+
a = self.a_lora(xa).sigmoid()
|
| 276 |
+
g = self.g_lora(xg)
|
| 277 |
+
|
| 278 |
+
if self.fuse_norm:
|
| 279 |
+
kk = l2_norm(rearrange(k * self.k_k, 'b t (h d) -> b t h d', d=self.head_dim))
|
| 280 |
+
else:
|
| 281 |
+
kk = F.normalize(rearrange(k * self.k_k, 'b t (h d) -> b t h d', d=self.head_dim), dim=-1, p=2.0)
|
| 282 |
+
|
| 283 |
+
# Prefer addcmul over expanded form for numerical stability in bf16:
|
| 284 |
+
# 1. Fused Multiply-Add (FMA) in addcmul reduces intermediate rounding:
|
| 285 |
+
# - Single op vs original 3 ops (mul, sub, mul)
|
| 286 |
+
# - 1 less intermediate value storage (bf16 write->read overhead)
|
| 287 |
+
# 2. Mathematically equivalent to k*(1 + (a-1)*self.k_a)
|
| 288 |
+
# but with better precision preservation
|
| 289 |
+
# 3. Particularly crucial for bf16 where intermediate values easily lose precision
|
| 290 |
+
# 4. Pytorch method: k = k.addcmul(k * (a - 1), self.k_a)
|
| 291 |
+
k = fused_k_rwkv7(k, a, self.k_a)
|
| 292 |
+
|
| 293 |
+
# dealing with left-padding
|
| 294 |
+
if attention_mask is not None:
|
| 295 |
+
v = v * am
|
| 296 |
+
|
| 297 |
+
r, w, k, a = map(lambda x: rearrange(x, 'b t (h d) -> b t h d', d=self.head_dim), (r, w, k, a))
|
| 298 |
+
v = rearrange(v, 'b t (h d) -> b t h d', d=self.head_v_dim)
|
| 299 |
+
|
| 300 |
+
if self.training or seq_len >= 64:
|
| 301 |
+
# if training, use chunk mode no matter how short the sequence is
|
| 302 |
+
# launching the triton kernel for just one token will actually be slower
|
| 303 |
+
o, recurrent_state = chunk_rwkv7(
|
| 304 |
+
r=r,
|
| 305 |
+
w=w,
|
| 306 |
+
k=k,
|
| 307 |
+
v=v,
|
| 308 |
+
a=-kk,
|
| 309 |
+
b=kk * a,
|
| 310 |
+
scale=1.,
|
| 311 |
+
initial_state=recurrent_state,
|
| 312 |
+
output_final_state=use_cache,
|
| 313 |
+
cu_seqlens=cu_seqlens,
|
| 314 |
+
safe_gate=True,
|
| 315 |
+
chunk_size=64,
|
| 316 |
+
)
|
| 317 |
+
else:
|
| 318 |
+
o, recurrent_state = fused_mul_recurrent_rwkv7(
|
| 319 |
+
r=r,
|
| 320 |
+
w=w,
|
| 321 |
+
k=k,
|
| 322 |
+
v=v,
|
| 323 |
+
kk=kk,
|
| 324 |
+
a=a,
|
| 325 |
+
scale=1.,
|
| 326 |
+
initial_state=recurrent_state,
|
| 327 |
+
output_final_state=use_cache,
|
| 328 |
+
cu_seqlens=cu_seqlens,
|
| 329 |
+
)
|
| 330 |
+
|
| 331 |
+
update_layer_cache(
|
| 332 |
+
self,
|
| 333 |
+
past_key_values,
|
| 334 |
+
recurrent_state=recurrent_state,
|
| 335 |
+
conv_state=conv_state,
|
| 336 |
+
offset=r.shape[1],
|
| 337 |
+
)
|
| 338 |
+
|
| 339 |
+
if self.fuse_norm:
|
| 340 |
+
o = self.g_norm(rearrange(o, '... h d -> ... (h d)'))
|
| 341 |
+
else:
|
| 342 |
+
o = self.g_norm(rearrange(o, 'b t h d -> (b t) (h d)')).view(batch_size, seq_len, -1)
|
| 343 |
+
|
| 344 |
+
o = gate_output_correction(o, r, k, self.r_k, v, g)
|
| 345 |
+
o = self.o_proj(o)
|
| 346 |
+
|
| 347 |
+
return o, None, past_key_values, v_first
|
fla/layers/simple_gla.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
from einops import rearrange, repeat
|
| 11 |
+
|
| 12 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 13 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 14 |
+
from fla.modules.activations import ACT2FN
|
| 15 |
+
from fla.ops.simple_gla import chunk_simple_gla, fused_recurrent_simple_gla
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from fla.models.utils import Cache
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class SimpleGatedLinearAttention(nn.Module):
|
| 22 |
+
r"""
|
| 23 |
+
The layer implementaion for [Gated Linear Attention Transformers with Hardware-Efficient Training](https://arxiv.org/abs/2312.06635). # noqa
|
| 24 |
+
This layer calls the simplified GLA kernel in which the gating is head-wise instead of elementwise.
|
| 25 |
+
|
| 26 |
+
Args:
|
| 27 |
+
mode (str, Optional):
|
| 28 |
+
Which GLA kernel to use.
|
| 29 |
+
Currently available: `chunk`.
|
| 30 |
+
Default: `chunk`.
|
| 31 |
+
hidden_size (int, Optional):
|
| 32 |
+
The hidden size of the input. Default: 1024.
|
| 33 |
+
expand_k (float, Optional):
|
| 34 |
+
The expansion ratio for the key dim. Default: 1.0.
|
| 35 |
+
expand_v (float, Optional):
|
| 36 |
+
The expansion ratio for the value dim. Default: 1.0.
|
| 37 |
+
num_heads (int, Optional):
|
| 38 |
+
The number of heads. Default: 4.
|
| 39 |
+
num_kv_heads (int, Optional):
|
| 40 |
+
The number of key/value heads, used for MQA. Default: None.
|
| 41 |
+
feature_map (str, Optional):
|
| 42 |
+
Feature map function applied to queries/keys. Default: None.
|
| 43 |
+
use_short_conv (bool, Optional):
|
| 44 |
+
Whether to use short convolutions. Default: `False`.
|
| 45 |
+
conv_size (int, Optional):
|
| 46 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 47 |
+
conv_bias (bool, Optional):
|
| 48 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 49 |
+
gate_fn (str, Optional):
|
| 50 |
+
The activation function for the output gate. Default: `swish`.
|
| 51 |
+
elementwise_affine (bool, Optional):
|
| 52 |
+
If `True`, applies elementwise affine to LayerNorm with learnable parameters. Default: `True`.
|
| 53 |
+
norm_eps (float, Optional):
|
| 54 |
+
The epsilon value for the layernorm/rmsnorm layer. Default: 1e-5.
|
| 55 |
+
gate_logit_normalizer (int, Optional):
|
| 56 |
+
The normalizer for the gate logits, appied after `logsigmoid`. Default: 16.
|
| 57 |
+
fuse_norm (bool, Optional):
|
| 58 |
+
Whether to fuse the norm and the output gate for better memory footprint. Default: `True`.
|
| 59 |
+
layer_idx (int, Optional):
|
| 60 |
+
The index of the layer. Default: None.
|
| 61 |
+
"""
|
| 62 |
+
|
| 63 |
+
def __init__(
|
| 64 |
+
self,
|
| 65 |
+
mode: str = 'chunk',
|
| 66 |
+
hidden_size: int = 1024,
|
| 67 |
+
expand_k: float = 1.,
|
| 68 |
+
expand_v: float = 1.,
|
| 69 |
+
num_heads: int = 4,
|
| 70 |
+
num_kv_heads: int | None = None,
|
| 71 |
+
feature_map: str | None = None,
|
| 72 |
+
use_short_conv: bool = True,
|
| 73 |
+
conv_size: int = 4,
|
| 74 |
+
conv_bias: bool = False,
|
| 75 |
+
gate_fn: str = 'swish',
|
| 76 |
+
elementwise_affine: bool | None = True,
|
| 77 |
+
norm_eps: float = 1e-5,
|
| 78 |
+
gate_logit_normalizer: int = 16,
|
| 79 |
+
fuse_norm: bool = True,
|
| 80 |
+
layer_idx: int = None,
|
| 81 |
+
) -> SimpleGatedLinearAttention:
|
| 82 |
+
super().__init__()
|
| 83 |
+
|
| 84 |
+
self.mode = mode
|
| 85 |
+
self.hidden_size = hidden_size
|
| 86 |
+
self.expand_k = expand_k
|
| 87 |
+
self.expand_v = expand_v
|
| 88 |
+
self.num_heads = num_heads
|
| 89 |
+
self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
|
| 90 |
+
self.num_kv_groups = self.num_heads // self.num_kv_heads
|
| 91 |
+
self.feature_map_fn = ACT2FN[feature_map] if feature_map is not None else None
|
| 92 |
+
|
| 93 |
+
self.use_short_conv = use_short_conv
|
| 94 |
+
self.conv_size = conv_size
|
| 95 |
+
self.conv_bias = conv_bias
|
| 96 |
+
|
| 97 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 98 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 99 |
+
self.key_dim_per_group = self.key_dim // self.num_kv_groups
|
| 100 |
+
self.value_dim_per_group = self.value_dim // self.num_kv_groups
|
| 101 |
+
self.layer_idx = layer_idx
|
| 102 |
+
|
| 103 |
+
assert mode in ['chunk', "fused_recurrent"], f"Not supported mode `{mode}`."
|
| 104 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 105 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 106 |
+
|
| 107 |
+
self.head_k_dim = self.key_dim // num_heads
|
| 108 |
+
self.head_v_dim = self.value_dim // num_heads
|
| 109 |
+
|
| 110 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 111 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim_per_group, bias=False)
|
| 112 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim_per_group, bias=False)
|
| 113 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 114 |
+
|
| 115 |
+
if use_short_conv:
|
| 116 |
+
self.conv_size = conv_size
|
| 117 |
+
self.q_conv1d = ShortConvolution(
|
| 118 |
+
hidden_size=self.key_dim,
|
| 119 |
+
kernel_size=conv_size,
|
| 120 |
+
bias=conv_bias,
|
| 121 |
+
activation='silu',
|
| 122 |
+
)
|
| 123 |
+
self.k_conv1d = ShortConvolution(
|
| 124 |
+
hidden_size=self.key_dim_per_group,
|
| 125 |
+
kernel_size=conv_size,
|
| 126 |
+
bias=conv_bias,
|
| 127 |
+
activation='silu',
|
| 128 |
+
)
|
| 129 |
+
self.v_conv1d = ShortConvolution(
|
| 130 |
+
hidden_size=self.value_dim_per_group,
|
| 131 |
+
kernel_size=conv_size,
|
| 132 |
+
bias=conv_bias,
|
| 133 |
+
activation='silu',
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
self.gk_proj = nn.Linear(hidden_size, self.num_heads)
|
| 137 |
+
|
| 138 |
+
if gate_fn == 'swish' and fuse_norm:
|
| 139 |
+
self.g_norm_swish_gate = FusedRMSNormGated(
|
| 140 |
+
hidden_size=self.head_v_dim,
|
| 141 |
+
elementwise_affine=elementwise_affine,
|
| 142 |
+
eps=norm_eps,
|
| 143 |
+
)
|
| 144 |
+
self.fuse_norm_and_gate = True
|
| 145 |
+
else:
|
| 146 |
+
self.fuse_norm_and_gate = False
|
| 147 |
+
self.g_norm = RMSNorm(
|
| 148 |
+
hidden_size=self.head_v_dim,
|
| 149 |
+
elementwise_affine=elementwise_affine,
|
| 150 |
+
eps=norm_eps,
|
| 151 |
+
dtype=torch.float32
|
| 152 |
+
)
|
| 153 |
+
self.gate_fn = ACT2FN[gate_fn]
|
| 154 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 155 |
+
|
| 156 |
+
self.gate_logit_normalizer = gate_logit_normalizer
|
| 157 |
+
|
| 158 |
+
def forward(
|
| 159 |
+
self,
|
| 160 |
+
hidden_states: torch.Tensor,
|
| 161 |
+
attention_mask: torch.Tensor | None = None,
|
| 162 |
+
past_key_values: Cache | None = None,
|
| 163 |
+
use_cache: bool | None = False,
|
| 164 |
+
output_attentions: bool | None = False,
|
| 165 |
+
**kwargs,
|
| 166 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 167 |
+
if attention_mask is not None:
|
| 168 |
+
assert len(attention_mask.shape) == 2, (
|
| 169 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 170 |
+
"for padding purposes (0 indicating padding). "
|
| 171 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
# launching the triton kernel for just one token will actually be slower
|
| 175 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 176 |
+
|
| 177 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 178 |
+
|
| 179 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 180 |
+
if self.use_short_conv:
|
| 181 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 182 |
+
if last_state is not None:
|
| 183 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 184 |
+
conv_mask = attention_mask[:, -hidden_states.shape[1]:] if attention_mask is not None else None
|
| 185 |
+
q, conv_state_q = self.q_conv1d(
|
| 186 |
+
x=self.q_proj(hidden_states),
|
| 187 |
+
mask=conv_mask,
|
| 188 |
+
cache=conv_state_q,
|
| 189 |
+
output_final_state=use_cache,
|
| 190 |
+
cu_seqlens=cu_seqlens,
|
| 191 |
+
)
|
| 192 |
+
k, conv_state_k = self.k_conv1d(
|
| 193 |
+
x=self.k_proj(hidden_states),
|
| 194 |
+
mask=conv_mask,
|
| 195 |
+
cache=conv_state_k,
|
| 196 |
+
output_final_state=use_cache,
|
| 197 |
+
cu_seqlens=cu_seqlens,
|
| 198 |
+
)
|
| 199 |
+
v, conv_state_v = self.v_conv1d(
|
| 200 |
+
x=self.v_proj(hidden_states),
|
| 201 |
+
mask=conv_mask,
|
| 202 |
+
cache=conv_state_v,
|
| 203 |
+
output_final_state=use_cache,
|
| 204 |
+
cu_seqlens=cu_seqlens,
|
| 205 |
+
)
|
| 206 |
+
else:
|
| 207 |
+
q = self.q_proj(hidden_states)
|
| 208 |
+
k = self.k_proj(hidden_states)
|
| 209 |
+
v = self.v_proj(hidden_states)
|
| 210 |
+
gk = self.gk_proj(hidden_states)
|
| 211 |
+
|
| 212 |
+
if self.feature_map_fn is not None:
|
| 213 |
+
q, k = map(self.feature_map_fn, (q, k))
|
| 214 |
+
# dealing with left-padding
|
| 215 |
+
if attention_mask is not None:
|
| 216 |
+
v = v.mul_(attention_mask[:, -v.shape[-2]:, None])
|
| 217 |
+
q = rearrange(q, '... (h d) -> ... h d', h=self.num_heads)
|
| 218 |
+
if self.num_kv_groups > 1:
|
| 219 |
+
k, v = (repeat(x, '... (h d) -> ... (h g) d', h=self.num_kv_heads, g=self.num_kv_groups) for x in (k, v))
|
| 220 |
+
else:
|
| 221 |
+
k, v = (rearrange(x, '... (h d) -> ... h d', h=self.num_kv_heads) for x in (k, v))
|
| 222 |
+
gk = F.logsigmoid(gk) / self.gate_logit_normalizer
|
| 223 |
+
|
| 224 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 225 |
+
if mode == 'chunk':
|
| 226 |
+
o, recurrent_state = chunk_simple_gla(
|
| 227 |
+
q=q,
|
| 228 |
+
k=k,
|
| 229 |
+
v=v,
|
| 230 |
+
g=gk,
|
| 231 |
+
initial_state=recurrent_state,
|
| 232 |
+
output_final_state=use_cache,
|
| 233 |
+
cu_seqlens=cu_seqlens,
|
| 234 |
+
)
|
| 235 |
+
elif mode == 'fused_recurrent':
|
| 236 |
+
o, recurrent_state = fused_recurrent_simple_gla(
|
| 237 |
+
q=q,
|
| 238 |
+
k=k,
|
| 239 |
+
v=v,
|
| 240 |
+
g=gk,
|
| 241 |
+
initial_state=recurrent_state,
|
| 242 |
+
output_final_state=use_cache,
|
| 243 |
+
cu_seqlens=cu_seqlens,
|
| 244 |
+
)
|
| 245 |
+
else:
|
| 246 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 247 |
+
|
| 248 |
+
update_layer_cache(
|
| 249 |
+
self,
|
| 250 |
+
past_key_values,
|
| 251 |
+
recurrent_state=recurrent_state,
|
| 252 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 253 |
+
offset=q.shape[1],
|
| 254 |
+
)
|
| 255 |
+
|
| 256 |
+
g = self.g_proj(hidden_states)
|
| 257 |
+
if self.fuse_norm_and_gate:
|
| 258 |
+
g = rearrange(g, 'b t (h d) -> b t h d', h=self.num_heads)
|
| 259 |
+
o = self.g_norm_swish_gate(o, g)
|
| 260 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 261 |
+
else:
|
| 262 |
+
o = rearrange(self.g_norm(o), 'b t h d -> b t (h d)')
|
| 263 |
+
o = o * self.gate_fn(g)
|
| 264 |
+
o = self.o_proj(o)
|
| 265 |
+
|
| 266 |
+
return o, None, past_key_values
|
| 267 |
+
|
| 268 |
+
def state_size(self, **kwargs) -> int:
|
| 269 |
+
state_size = self.key_dim * self.head_v_dim
|
| 270 |
+
for module in self.children():
|
| 271 |
+
if isinstance(module, ShortConvolution):
|
| 272 |
+
state_size += module.state_size
|
| 273 |
+
return state_size
|
fla/layers/utils.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
# Code is adapted from flash-attn.bert_padding.py
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
from einops import rearrange, repeat
|
| 8 |
+
|
| 9 |
+
from fla.ops.utils.index import prepare_cu_seqlens_from_mask, prepare_lens_from_mask
|
| 10 |
+
from fla.utils import tensor_cache
|
| 11 |
+
|
| 12 |
+
_LAYER_IDX_REQUIRED_MSG = "{cls} requires `layer_idx` when `past_key_values` is provided."
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class IndexFirstAxis(torch.autograd.Function):
|
| 16 |
+
|
| 17 |
+
@staticmethod
|
| 18 |
+
def forward(ctx, x, indices):
|
| 19 |
+
ctx.save_for_backward(indices)
|
| 20 |
+
assert x.ndim >= 2
|
| 21 |
+
ctx.first_axis_dim, other_shape = x.shape[0], x.shape[1:]
|
| 22 |
+
second_dim = other_shape.numel()
|
| 23 |
+
# TD [2022-03-04] For some reason torch.gather is a bit faster than indexing.
|
| 24 |
+
# return x[indices]
|
| 25 |
+
return torch.gather(
|
| 26 |
+
rearrange(x, "b ... -> b (...)"), 0, repeat(indices, "z -> z d", d=second_dim),
|
| 27 |
+
).reshape(-1, *other_shape)
|
| 28 |
+
|
| 29 |
+
@staticmethod
|
| 30 |
+
def backward(ctx, do):
|
| 31 |
+
(indices,) = ctx.saved_tensors
|
| 32 |
+
assert do.ndim >= 2
|
| 33 |
+
other_shape = do.shape[1:]
|
| 34 |
+
do = rearrange(do, "b ... -> b (...)")
|
| 35 |
+
dx = torch.zeros(
|
| 36 |
+
[ctx.first_axis_dim, do.shape[1]],
|
| 37 |
+
device=do.device,
|
| 38 |
+
dtype=do.dtype,
|
| 39 |
+
)
|
| 40 |
+
# TD [2022-03-04] For some reason torch.scatter is a bit faster than indexing.
|
| 41 |
+
# dx[indices] = do
|
| 42 |
+
dx.scatter_(0, repeat(indices, "z -> z d", d=do.shape[1]), do)
|
| 43 |
+
return dx.reshape(ctx.first_axis_dim, *other_shape), None
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
index_first_axis = IndexFirstAxis.apply
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class IndexPutFirstAxis(torch.autograd.Function):
|
| 50 |
+
|
| 51 |
+
@staticmethod
|
| 52 |
+
def forward(ctx, x, indices, first_axis_dim):
|
| 53 |
+
ctx.save_for_backward(indices)
|
| 54 |
+
assert indices.ndim == 1
|
| 55 |
+
assert x.ndim >= 2
|
| 56 |
+
y = torch.zeros(first_axis_dim, *x.shape[1:], device=x.device, dtype=x.dtype)
|
| 57 |
+
# TODO [2022-03-04] For some reason torch.scatter is a bit faster than indexing.
|
| 58 |
+
y[indices] = x
|
| 59 |
+
# y.scatter_(0, repeat(indices, 'z -> z d', d=x.shape[1]), x)
|
| 60 |
+
return y
|
| 61 |
+
|
| 62 |
+
@staticmethod
|
| 63 |
+
def backward(ctx, do):
|
| 64 |
+
(indices,) = ctx.saved_tensors
|
| 65 |
+
# TODO [2022-03-04] For some reason torch.gather is a bit faster than indexing.
|
| 66 |
+
dx = do[indices]
|
| 67 |
+
# dx = torch.gather(do, 0, repeat(indices, 'z -> z d', d=do.shape[1]))
|
| 68 |
+
return dx, None, None
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
index_put_first_axis = IndexPutFirstAxis.apply
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
@tensor_cache
|
| 75 |
+
def get_unpad_data(
|
| 76 |
+
attention_mask: torch.Tensor,
|
| 77 |
+
) -> tuple[torch.Tensor, torch.Tensor, int]:
|
| 78 |
+
"""
|
| 79 |
+
Retrieves indexing data required to repad unpadded (ragged) tensors.
|
| 80 |
+
|
| 81 |
+
Args:
|
| 82 |
+
attention_mask (`torch.Tensor`):
|
| 83 |
+
Boolean or int tensor of shape (batch_size, sequence_length), 1 means valid and 0 means not valid.
|
| 84 |
+
|
| 85 |
+
Return:
|
| 86 |
+
indices (`torch.Tensor`):
|
| 87 |
+
The indices of non-masked tokens from the flattened input sequence.
|
| 88 |
+
cu_seqlens (`torch.Tensor`):
|
| 89 |
+
The cumulative sequence lengths, used to index into ragged (unpadded) tensors.
|
| 90 |
+
`cu_seqlens` shape is [batch_size + 1].
|
| 91 |
+
max_seqlen_in_batch (`int`):
|
| 92 |
+
Maximum sequence length in batch.
|
| 93 |
+
"""
|
| 94 |
+
lens = prepare_lens_from_mask(attention_mask)
|
| 95 |
+
indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten()
|
| 96 |
+
max_seqlen_in_batch = lens.max().item()
|
| 97 |
+
cu_seqlens = prepare_cu_seqlens_from_mask(attention_mask)
|
| 98 |
+
return indices, cu_seqlens, max_seqlen_in_batch
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def unpad_input(
|
| 102 |
+
q: torch.Tensor,
|
| 103 |
+
states: tuple[torch.Tensor],
|
| 104 |
+
attention_mask: torch.Tensor,
|
| 105 |
+
q_len: int,
|
| 106 |
+
keepdim: bool = False,
|
| 107 |
+
):
|
| 108 |
+
"""
|
| 109 |
+
Unpads query, key, and values tensors, using a single dimension for all tokens
|
| 110 |
+
even though they belong to different batches.
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
Arguments:
|
| 114 |
+
q (`torch.Tensor`):
|
| 115 |
+
Query state with padding. Shape: [batch_size, q_len, ...].
|
| 116 |
+
states (`Tuple[torch.Tensor]`):
|
| 117 |
+
Attention state with padding. Shape: [batch_size, seq_len, ...].
|
| 118 |
+
attention_mask (`torch.Tensor`):
|
| 119 |
+
Boolean or int tensor of shape [batch_size, sequence_length], 1 means valid and 0 means not valid.
|
| 120 |
+
q_len (`int`):
|
| 121 |
+
Target length.
|
| 122 |
+
keepdim (`bool`):
|
| 123 |
+
Whether to keep the batch dimension. Default: `False`.
|
| 124 |
+
|
| 125 |
+
Return:
|
| 126 |
+
q (`torch.Tensor`):
|
| 127 |
+
Query state without padding.
|
| 128 |
+
Shape: [1, total_target_length, ...] if `keepdim=True` else [total_target_length, ...].
|
| 129 |
+
states (`Tuple[torch.Tensor]`):
|
| 130 |
+
Attention state without padding.
|
| 131 |
+
Shape: [1, total_source_length, ...] if `keepdim=True` else [total_source_length, ...].
|
| 132 |
+
indices_q (`torch.Tensor`):
|
| 133 |
+
The indices of non-masked tokens from the flattened input target sequence.
|
| 134 |
+
(cu_seqlens_q, cu_seqlens_k) (`Tuple[int]`):
|
| 135 |
+
The cumulative sequence lengths for the target (query) and source (key, value),
|
| 136 |
+
used to index into ragged (unpadded) tensors.
|
| 137 |
+
`cu_seqlens` shape is [batch_size + 1].
|
| 138 |
+
(max_seqlen_in_batch_q, max_seqlen_in_batch_k) (`Tuple[int]`):
|
| 139 |
+
Maximum sequence length in batch (`max_seqlen_in_batch_q` for the target sequence
|
| 140 |
+
i.e. query, `max_seqlen_in_batch_k` for the source sequence i.e. key/value).
|
| 141 |
+
"""
|
| 142 |
+
indices_k, cu_seqlens_k, max_seqlen_in_batch_k = get_unpad_data(attention_mask)
|
| 143 |
+
batch_size, seq_len, *_ = states[0].shape
|
| 144 |
+
|
| 145 |
+
state = tuple(
|
| 146 |
+
index_first_axis(rearrange(s, "b s ... -> (b s) ..."), indices_k)
|
| 147 |
+
for s in states
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
if q_len == seq_len:
|
| 151 |
+
q = index_first_axis(rearrange(q, "b s ... -> (b s) ..."), indices_k)
|
| 152 |
+
cu_seqlens_q = cu_seqlens_k
|
| 153 |
+
max_seqlen_in_batch_q = max_seqlen_in_batch_k
|
| 154 |
+
indices_q = indices_k
|
| 155 |
+
elif q_len == 1:
|
| 156 |
+
max_seqlen_in_batch_q = 1
|
| 157 |
+
cu_seqlens_q = torch.arange(batch_size + 1, dtype=torch.int32, device=q.device)
|
| 158 |
+
indices_q = cu_seqlens_q[:-1]
|
| 159 |
+
q = q.squeeze(1)
|
| 160 |
+
else:
|
| 161 |
+
raise NotImplementedError("We only support either q_len == k_len (prefilling) or q_len == 1 (decoding)")
|
| 162 |
+
|
| 163 |
+
if keepdim:
|
| 164 |
+
q = q.unsqueeze(0)
|
| 165 |
+
state = tuple(s.unsqueeze(0) for s in state)
|
| 166 |
+
|
| 167 |
+
return (
|
| 168 |
+
q,
|
| 169 |
+
state,
|
| 170 |
+
indices_q,
|
| 171 |
+
(cu_seqlens_q, cu_seqlens_k),
|
| 172 |
+
(max_seqlen_in_batch_q, max_seqlen_in_batch_k),
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def pad_input(
|
| 177 |
+
hidden_states: torch.Tensor,
|
| 178 |
+
indices: torch.LongTensor,
|
| 179 |
+
batch_size: int,
|
| 180 |
+
seq_len: int,
|
| 181 |
+
) -> torch.Tensor:
|
| 182 |
+
"""
|
| 183 |
+
Args:
|
| 184 |
+
hidden_states ([total_tokens, ...]):
|
| 185 |
+
where total_tokens denotes the number of tokens in selected in attention_mask.
|
| 186 |
+
indices ([total_tokens]):
|
| 187 |
+
the indices that represent the non-masked tokens of the original padded input sequence.
|
| 188 |
+
batch_size (int):
|
| 189 |
+
batch_size size for the padded sequence.
|
| 190 |
+
seq_len (int):
|
| 191 |
+
maximum sequence length for the padded sequence.
|
| 192 |
+
|
| 193 |
+
Return:
|
| 194 |
+
hidden_states of shape [batch_size, seq_len, ...]
|
| 195 |
+
"""
|
| 196 |
+
output = index_put_first_axis(hidden_states, indices, batch_size * seq_len)
|
| 197 |
+
return rearrange(output, "(b s) ... -> b s ...", b=batch_size)
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def require_cache_layer_idx(module, past_key_values):
|
| 201 |
+
layer_idx = getattr(module, "layer_idx", None)
|
| 202 |
+
if past_key_values is not None and layer_idx is None:
|
| 203 |
+
raise ValueError(_LAYER_IDX_REQUIRED_MSG.format(cls=module.__class__.__name__))
|
| 204 |
+
return layer_idx
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def get_layer_cache(module, past_key_values):
|
| 208 |
+
layer_idx = require_cache_layer_idx(module, past_key_values)
|
| 209 |
+
if past_key_values is not None and len(past_key_values) > layer_idx:
|
| 210 |
+
return past_key_values[layer_idx]
|
| 211 |
+
return None
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
def update_layer_cache(module, past_key_values, **kwargs):
|
| 215 |
+
layer_idx = require_cache_layer_idx(module, past_key_values)
|
| 216 |
+
if past_key_values is not None:
|
| 217 |
+
return past_key_values.update(layer_idx=layer_idx, **kwargs)
|
| 218 |
+
return None
|
fla/models/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Keep model package imports lazy.
|
| 2 |
+
__all__ = []
|
| 3 |
+
|
fla/models/abc/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from transformers import AutoConfig, AutoModel, AutoModelForCausalLM
|
| 3 |
+
|
| 4 |
+
from fla.models.abc.configuration_abc import ABCConfig
|
| 5 |
+
from fla.models.abc.modeling_abc import ABCForCausalLM, ABCModel
|
| 6 |
+
|
| 7 |
+
AutoConfig.register(ABCConfig.model_type, ABCConfig, exist_ok=True)
|
| 8 |
+
AutoModel.register(ABCConfig, ABCModel, exist_ok=True)
|
| 9 |
+
AutoModelForCausalLM.register(ABCConfig, ABCForCausalLM, exist_ok=True)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
__all__ = ['ABCConfig', 'ABCForCausalLM', 'ABCModel']
|
fla/models/abc/configuration_abc.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import warnings
|
| 3 |
+
|
| 4 |
+
from transformers.configuration_utils import PretrainedConfig
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class ABCConfig(PretrainedConfig):
|
| 8 |
+
|
| 9 |
+
model_type = 'abc'
|
| 10 |
+
keys_to_ignore_at_inference = ['past_key_values']
|
| 11 |
+
|
| 12 |
+
def __init__(
|
| 13 |
+
self,
|
| 14 |
+
hidden_size: int = 2048,
|
| 15 |
+
gate_low_rank_dim: int = 16,
|
| 16 |
+
clamp_min: float = -32,
|
| 17 |
+
clamp_max: float = 32,
|
| 18 |
+
hidden_ratio: int | None = 4,
|
| 19 |
+
intermediate_size: int | None = None,
|
| 20 |
+
num_hidden_layers: int = 24,
|
| 21 |
+
num_heads: int = 4,
|
| 22 |
+
num_slots: int | None = 64,
|
| 23 |
+
use_short_conv: bool = False,
|
| 24 |
+
conv_size: int = 4,
|
| 25 |
+
exapnd_k: float = 0.5,
|
| 26 |
+
exapnd_v: float = 1,
|
| 27 |
+
hidden_act: str = "swish",
|
| 28 |
+
max_position_embeddings: int = 2048,
|
| 29 |
+
elementwise_affine: bool | None = True,
|
| 30 |
+
norm_eps: float = 1e-6,
|
| 31 |
+
use_rope: bool = True,
|
| 32 |
+
attn: dict | None = None,
|
| 33 |
+
use_cache: bool = True,
|
| 34 |
+
pad_token_id: int | None = None,
|
| 35 |
+
bos_token_id: int = 1,
|
| 36 |
+
eos_token_id: int = 2,
|
| 37 |
+
tie_word_embeddings: bool = False,
|
| 38 |
+
initializer_range: float = 0.02,
|
| 39 |
+
fuse_norm: bool = True,
|
| 40 |
+
fuse_swiglu: bool = True,
|
| 41 |
+
fuse_cross_entropy: bool = True,
|
| 42 |
+
fuse_linear_cross_entropy: bool = False,
|
| 43 |
+
use_l2warp: bool = False,
|
| 44 |
+
vocab_size: int = 32000,
|
| 45 |
+
**kwargs,
|
| 46 |
+
):
|
| 47 |
+
self.hidden_size = hidden_size
|
| 48 |
+
self.gate_low_rank_dim = gate_low_rank_dim
|
| 49 |
+
self.clamp_min = clamp_min
|
| 50 |
+
self.clamp_max = clamp_max
|
| 51 |
+
self.hidden_ratio = hidden_ratio
|
| 52 |
+
self.intermediate_size = intermediate_size
|
| 53 |
+
self.num_hidden_layers = num_hidden_layers
|
| 54 |
+
self.num_heads = num_heads
|
| 55 |
+
self.num_slots = num_slots
|
| 56 |
+
self.use_short_conv = use_short_conv
|
| 57 |
+
self.conv_size = conv_size
|
| 58 |
+
self.expand_k = exapnd_k
|
| 59 |
+
self.expand_v = exapnd_v
|
| 60 |
+
self.hidden_act = hidden_act
|
| 61 |
+
self.max_position_embeddings = max_position_embeddings
|
| 62 |
+
self.elementwise_affine = elementwise_affine
|
| 63 |
+
self.norm_eps = norm_eps
|
| 64 |
+
self.use_rope = use_rope
|
| 65 |
+
self.attn = attn
|
| 66 |
+
self.use_cache = use_cache
|
| 67 |
+
self.initializer_range = initializer_range
|
| 68 |
+
|
| 69 |
+
self.fuse_norm = fuse_norm
|
| 70 |
+
self.fuse_swiglu = fuse_swiglu
|
| 71 |
+
self.fuse_cross_entropy = fuse_cross_entropy
|
| 72 |
+
self.fuse_linear_cross_entropy = fuse_linear_cross_entropy
|
| 73 |
+
self.use_l2warp = use_l2warp
|
| 74 |
+
self.vocab_size = vocab_size
|
| 75 |
+
|
| 76 |
+
if fuse_cross_entropy and fuse_linear_cross_entropy:
|
| 77 |
+
raise ValueError(
|
| 78 |
+
"`fuse_cross_entropy` and `fuse_linear_cross_entropy` cannot be True at the same time.",
|
| 79 |
+
)
|
| 80 |
+
if fuse_linear_cross_entropy:
|
| 81 |
+
warnings.warn(
|
| 82 |
+
"`fuse_linear_cross_entropy` is enabled, which can improves memory efficiency "
|
| 83 |
+
"at the potential cost of reduced precision. "
|
| 84 |
+
"If you observe issues like loss divergence, consider disabling this setting.",
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
if attn is not None:
|
| 88 |
+
if not isinstance(attn, dict):
|
| 89 |
+
raise ValueError("attn must be a dictionary")
|
| 90 |
+
if 'layers' not in attn:
|
| 91 |
+
raise ValueError("Layer indices must be provided to initialize hybrid attention layers")
|
| 92 |
+
if 'num_heads' not in attn:
|
| 93 |
+
raise ValueError("Number of heads must be provided to initialize hybrid attention layers")
|
| 94 |
+
attn['num_kv_heads'] = attn.get('num_kv_heads', attn['num_heads'])
|
| 95 |
+
attn['qkv_bias'] = attn.get('qkv_bias', False)
|
| 96 |
+
attn['window_size'] = attn.get('window_size', None)
|
| 97 |
+
attn['rope_theta'] = attn.get('rope_theta', 10000.)
|
| 98 |
+
|
| 99 |
+
super().__init__(
|
| 100 |
+
pad_token_id=pad_token_id,
|
| 101 |
+
bos_token_id=bos_token_id,
|
| 102 |
+
eos_token_id=eos_token_id,
|
| 103 |
+
tie_word_embeddings=tie_word_embeddings,
|
| 104 |
+
**kwargs,
|
| 105 |
+
)
|
fla/models/abc/modeling_abc.py
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
import warnings
|
| 5 |
+
from typing import TYPE_CHECKING, Optional
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
|
| 10 |
+
from transformers.modeling_utils import PreTrainedModel
|
| 11 |
+
from transformers.utils import logging
|
| 12 |
+
from transformers.utils.deprecation import deprecate_kwarg
|
| 13 |
+
|
| 14 |
+
from fla.layers.abc import ABCAttention
|
| 15 |
+
from fla.layers.attn import Attention
|
| 16 |
+
from fla.models.abc.configuration_abc import ABCConfig
|
| 17 |
+
from fla.models.utils import Cache, FLAGenerationMixin
|
| 18 |
+
from fla.modules import FusedCrossEntropyLoss, FusedLinearCrossEntropyLoss, RMSNorm
|
| 19 |
+
from fla.modules import GatedMLP as ABCMLP
|
| 20 |
+
from fla.modules.l2warp import l2_warp
|
| 21 |
+
|
| 22 |
+
try:
|
| 23 |
+
from transformers.modeling_layers import GradientCheckpointingLayer
|
| 24 |
+
except ImportError:
|
| 25 |
+
from fla.models.modeling_layers import GradientCheckpointingLayer
|
| 26 |
+
|
| 27 |
+
logger = logging.get_logger(__name__)
|
| 28 |
+
|
| 29 |
+
if TYPE_CHECKING:
|
| 30 |
+
from transformers.processing_utils import Unpack
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class ABCBlock(GradientCheckpointingLayer):
|
| 34 |
+
|
| 35 |
+
def __init__(self, config: ABCConfig, layer_idx: int):
|
| 36 |
+
super().__init__()
|
| 37 |
+
|
| 38 |
+
self.config = config
|
| 39 |
+
self.layer_idx = layer_idx
|
| 40 |
+
|
| 41 |
+
self.attn_norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
|
| 42 |
+
if config.attn is not None and layer_idx in config.attn['layers']:
|
| 43 |
+
self.attn = Attention(
|
| 44 |
+
hidden_size=config.hidden_size,
|
| 45 |
+
num_heads=config.attn['num_heads'],
|
| 46 |
+
num_kv_heads=config.attn['num_kv_heads'],
|
| 47 |
+
qkv_bias=config.attn['qkv_bias'],
|
| 48 |
+
window_size=config.attn['window_size'],
|
| 49 |
+
rope_theta=config.attn['rope_theta'],
|
| 50 |
+
max_position_embeddings=config.max_position_embeddings,
|
| 51 |
+
layer_idx=layer_idx,
|
| 52 |
+
)
|
| 53 |
+
else:
|
| 54 |
+
self.attn = ABCAttention(
|
| 55 |
+
hidden_size=config.hidden_size,
|
| 56 |
+
expand_k=config.expand_k,
|
| 57 |
+
expand_v=config.expand_v,
|
| 58 |
+
num_heads=config.num_heads,
|
| 59 |
+
num_slots=config.num_slots,
|
| 60 |
+
use_short_conv=config.use_short_conv,
|
| 61 |
+
conv_size=config.conv_size,
|
| 62 |
+
gate_fn=config.hidden_act,
|
| 63 |
+
elementwise_affine=config.elementwise_affine,
|
| 64 |
+
norm_eps=config.norm_eps,
|
| 65 |
+
use_rope=config.use_rope,
|
| 66 |
+
clamp_min=config.clamp_min,
|
| 67 |
+
clamp_max=config.clamp_max,
|
| 68 |
+
fuse_norm=config.fuse_norm,
|
| 69 |
+
layer_idx=layer_idx,
|
| 70 |
+
)
|
| 71 |
+
self.mlp_norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
|
| 72 |
+
self.mlp = ABCMLP(
|
| 73 |
+
hidden_size=config.hidden_size,
|
| 74 |
+
hidden_ratio=config.hidden_ratio,
|
| 75 |
+
intermediate_size=config.intermediate_size,
|
| 76 |
+
hidden_act=config.hidden_act,
|
| 77 |
+
fuse_swiglu=config.fuse_swiglu,
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
def forward(
|
| 81 |
+
self,
|
| 82 |
+
hidden_states: torch.Tensor,
|
| 83 |
+
attention_mask: torch.Tensor | None = None,
|
| 84 |
+
past_key_values: Cache | list[torch.FloatTensor] | None = None,
|
| 85 |
+
use_cache: bool | None = False,
|
| 86 |
+
output_attentions: bool | None = False,
|
| 87 |
+
**kwargs: Unpack[dict],
|
| 88 |
+
) -> tuple[torch.FloatTensor, tuple[torch.FloatTensor, torch.FloatTensor] | None]:
|
| 89 |
+
|
| 90 |
+
residual = hidden_states
|
| 91 |
+
|
| 92 |
+
hidden_states = self.attn_norm(hidden_states)
|
| 93 |
+
hidden_states, attentions, past_key_values = self.attn(
|
| 94 |
+
hidden_states=hidden_states,
|
| 95 |
+
attention_mask=attention_mask,
|
| 96 |
+
past_key_values=past_key_values,
|
| 97 |
+
use_cache=use_cache,
|
| 98 |
+
output_attentions=output_attentions,
|
| 99 |
+
**kwargs,
|
| 100 |
+
)
|
| 101 |
+
if self.config.fuse_norm:
|
| 102 |
+
hidden_states, residual = self.mlp_norm(hidden_states, residual, True)
|
| 103 |
+
else:
|
| 104 |
+
hidden_states = residual + hidden_states
|
| 105 |
+
residual = hidden_states
|
| 106 |
+
hidden_states = self.mlp_norm(hidden_states)
|
| 107 |
+
hidden_states = self.mlp(hidden_states)
|
| 108 |
+
hidden_states = residual + hidden_states
|
| 109 |
+
|
| 110 |
+
outputs = (hidden_states, attentions, past_key_values)
|
| 111 |
+
|
| 112 |
+
return outputs
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
class ABCPreTrainedModel(PreTrainedModel):
|
| 116 |
+
|
| 117 |
+
config_class = ABCConfig
|
| 118 |
+
base_model_prefix = 'model'
|
| 119 |
+
supports_gradient_checkpointing = True
|
| 120 |
+
_no_split_modules = ['ABCBlock']
|
| 121 |
+
_supports_cache_class = True
|
| 122 |
+
|
| 123 |
+
def __init__(self, *inputs, **kwargs):
|
| 124 |
+
super().__init__(*inputs, **kwargs)
|
| 125 |
+
|
| 126 |
+
def _init_weights(
|
| 127 |
+
self,
|
| 128 |
+
module: nn.Module,
|
| 129 |
+
prenorm_residual_strategy: str | None = None,
|
| 130 |
+
num_residuals_per_layer: int = 2,
|
| 131 |
+
):
|
| 132 |
+
if isinstance(module, (nn.Linear, nn.Conv1d)):
|
| 133 |
+
# Slightly different from the TF version which uses truncated_normal for initialization
|
| 134 |
+
# cf https://github.com/pytorch/pytorch/pull/5617
|
| 135 |
+
nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
|
| 136 |
+
if module.bias is not None:
|
| 137 |
+
nn.init.zeros_(module.bias)
|
| 138 |
+
elif isinstance(module, nn.Embedding):
|
| 139 |
+
nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
|
| 140 |
+
elif hasattr(module, 'reset_parameters'):
|
| 141 |
+
module.reset_parameters()
|
| 142 |
+
|
| 143 |
+
if prenorm_residual_strategy is not None:
|
| 144 |
+
# Reinitialize selected weights subject to the OpenAI GPT-2 Paper Scheme:
|
| 145 |
+
# > A modified initialization which accounts for the accumulation on the residual path with model depth. Scale
|
| 146 |
+
# > the weights of residual layers at initialization by a factor of 1/√N where N is the # of residual layers.
|
| 147 |
+
# > -- GPT-2 :: https://openai.com/blog/better-language-models/
|
| 148 |
+
#
|
| 149 |
+
# Reference (Megatron-LM): https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/gpt_model.py
|
| 150 |
+
p = None
|
| 151 |
+
if hasattr(module, 'o_proj'):
|
| 152 |
+
p = module.o_proj.weight
|
| 153 |
+
elif hasattr(module, 'down_proj'):
|
| 154 |
+
p = module.down_proj.weight
|
| 155 |
+
if p is not None:
|
| 156 |
+
# Special Scaled Initialization --> There are 2 Layer Norms per Transformer Block
|
| 157 |
+
# Following Pytorch init, except scale by 1/sqrt(2 * n_layer)
|
| 158 |
+
# We need to reinit p since this code could be called multiple times
|
| 159 |
+
# Having just p *= scale would repeatedly scale it down
|
| 160 |
+
if prenorm_residual_strategy == 'rescale':
|
| 161 |
+
nn.init.kaiming_uniform_(p, a=math.sqrt(5))
|
| 162 |
+
with torch.no_grad():
|
| 163 |
+
p /= math.sqrt(num_residuals_per_layer * self.config.num_hidden_layers)
|
| 164 |
+
elif prenorm_residual_strategy == 'zero':
|
| 165 |
+
nn.init.zeros_(p)
|
| 166 |
+
else:
|
| 167 |
+
raise ValueError(f"Invalid prenorm_residual_strategy: {prenorm_residual_strategy}")
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
class ABCModel(ABCPreTrainedModel):
|
| 171 |
+
|
| 172 |
+
def __init__(self, config: ABCConfig):
|
| 173 |
+
super().__init__(config)
|
| 174 |
+
self.padding_idx = config.pad_token_id
|
| 175 |
+
self.vocab_size = config.vocab_size
|
| 176 |
+
|
| 177 |
+
self.embeddings = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
|
| 178 |
+
self.layers = nn.ModuleList([ABCBlock(config, layer_idx) for layer_idx in range(config.num_hidden_layers)])
|
| 179 |
+
self.norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
|
| 180 |
+
|
| 181 |
+
self.gradient_checkpointing = False
|
| 182 |
+
|
| 183 |
+
self.post_init()
|
| 184 |
+
|
| 185 |
+
def get_input_embeddings(self):
|
| 186 |
+
return self.embeddings
|
| 187 |
+
|
| 188 |
+
def set_input_embeddings(self, value):
|
| 189 |
+
self.embeddings = value
|
| 190 |
+
|
| 191 |
+
def forward(
|
| 192 |
+
self,
|
| 193 |
+
input_ids: torch.LongTensor | None = None,
|
| 194 |
+
attention_mask: Optional[torch.Tensor] = None, # noqa
|
| 195 |
+
inputs_embeds: torch.FloatTensor | None = None,
|
| 196 |
+
past_key_values: Cache | list[torch.FloatTensor] | None = None,
|
| 197 |
+
use_cache: bool | None = None,
|
| 198 |
+
output_attentions: bool | None = None,
|
| 199 |
+
output_hidden_states: bool | None = None,
|
| 200 |
+
return_dict: bool | None = None,
|
| 201 |
+
**kwargs: Unpack[dict],
|
| 202 |
+
) -> tuple | BaseModelOutputWithPast:
|
| 203 |
+
if output_attentions:
|
| 204 |
+
warnings.warn("`ABCModel` does not `output_attentions` now, setting it to `False`.")
|
| 205 |
+
output_attentions = False
|
| 206 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 207 |
+
output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 208 |
+
use_cache = use_cache if use_cache is not None else (self.config.use_cache if not self.training else False)
|
| 209 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 210 |
+
|
| 211 |
+
# retrieve input_ids and inputs_embeds
|
| 212 |
+
if input_ids is not None and inputs_embeds is not None:
|
| 213 |
+
raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
|
| 214 |
+
if input_ids is None and inputs_embeds is None:
|
| 215 |
+
raise ValueError("You have to specify either input_ids or inputs_embeds")
|
| 216 |
+
|
| 217 |
+
if inputs_embeds is None:
|
| 218 |
+
inputs_embeds = self.embeddings(input_ids)
|
| 219 |
+
hidden_states = inputs_embeds
|
| 220 |
+
|
| 221 |
+
if use_cache and not isinstance(past_key_values, Cache):
|
| 222 |
+
past_key_values = Cache.from_legacy_cache(past_key_values)
|
| 223 |
+
|
| 224 |
+
all_hidden_states = () if output_hidden_states else None
|
| 225 |
+
all_attns = () if output_attentions else None
|
| 226 |
+
for layer in self.layers:
|
| 227 |
+
if output_hidden_states:
|
| 228 |
+
all_hidden_states += (hidden_states,)
|
| 229 |
+
|
| 230 |
+
hidden_states, attentions, past_key_values = layer(
|
| 231 |
+
hidden_states,
|
| 232 |
+
attention_mask,
|
| 233 |
+
past_key_values=past_key_values,
|
| 234 |
+
use_cache=use_cache,
|
| 235 |
+
output_attentions=output_attentions,
|
| 236 |
+
**kwargs,
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
if output_attentions:
|
| 240 |
+
all_attns += (attentions,)
|
| 241 |
+
|
| 242 |
+
hidden_states = self.norm(hidden_states)
|
| 243 |
+
|
| 244 |
+
# add hidden states from the last decoder layer
|
| 245 |
+
if output_hidden_states:
|
| 246 |
+
all_hidden_states += (hidden_states,)
|
| 247 |
+
|
| 248 |
+
if not return_dict:
|
| 249 |
+
return tuple(i for i in [hidden_states, past_key_values, all_hidden_states, all_attns] if i is not None)
|
| 250 |
+
return BaseModelOutputWithPast(
|
| 251 |
+
last_hidden_state=hidden_states,
|
| 252 |
+
past_key_values=past_key_values,
|
| 253 |
+
hidden_states=all_hidden_states,
|
| 254 |
+
attentions=all_attns,
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
class ABCForCausalLM(ABCPreTrainedModel, FLAGenerationMixin):
|
| 259 |
+
|
| 260 |
+
_tied_weights_keys = ["lm_head.weight"]
|
| 261 |
+
|
| 262 |
+
def __init__(self, config):
|
| 263 |
+
super().__init__(config)
|
| 264 |
+
self.model = ABCModel(config)
|
| 265 |
+
self.vocab_size = config.vocab_size
|
| 266 |
+
self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
|
| 267 |
+
self.criterion = None
|
| 268 |
+
|
| 269 |
+
# Initialize weights and apply final processing
|
| 270 |
+
self.post_init()
|
| 271 |
+
|
| 272 |
+
def get_input_embeddings(self):
|
| 273 |
+
return self.model.embeddings
|
| 274 |
+
|
| 275 |
+
def set_input_embeddings(self, value):
|
| 276 |
+
self.model.embeddings = value
|
| 277 |
+
|
| 278 |
+
def get_output_embeddings(self):
|
| 279 |
+
return self.lm_head
|
| 280 |
+
|
| 281 |
+
def set_output_embeddings(self, new_embeddings):
|
| 282 |
+
self.lm_head = new_embeddings
|
| 283 |
+
|
| 284 |
+
def set_decoder(self, decoder):
|
| 285 |
+
self.model = decoder
|
| 286 |
+
|
| 287 |
+
def get_decoder(self):
|
| 288 |
+
return self.model
|
| 289 |
+
|
| 290 |
+
def generate(self, *args, **kwargs):
|
| 291 |
+
try:
|
| 292 |
+
return super().generate(*args, **kwargs)
|
| 293 |
+
except AttributeError as exception:
|
| 294 |
+
if 'past_key_values' in str(exception):
|
| 295 |
+
raise AttributeError(
|
| 296 |
+
f"You tried to call `generate` with a decoding strategy that manipulates `past_key_values`, "
|
| 297 |
+
f"which is not supported for {self.__class__.__name__}. "
|
| 298 |
+
f"Try another generation strategy instead. "
|
| 299 |
+
f"For the available generation strategies, check this doc: "
|
| 300 |
+
f"https://huggingface.co/docs/transformers/en/generation_strategies#decoding-strategies",
|
| 301 |
+
)
|
| 302 |
+
else:
|
| 303 |
+
raise exception
|
| 304 |
+
|
| 305 |
+
@deprecate_kwarg("num_logits_to_keep", version="4.50", new_name="logits_to_keep")
|
| 306 |
+
def forward(
|
| 307 |
+
self,
|
| 308 |
+
input_ids: torch.LongTensor = None,
|
| 309 |
+
attention_mask: torch.Tensor | None = None,
|
| 310 |
+
inputs_embeds: torch.Tensor | None = None,
|
| 311 |
+
past_key_values: Cache | list[torch.FloatTensor] | None = None,
|
| 312 |
+
labels: torch.LongTensor | None = None,
|
| 313 |
+
use_cache: bool | None = None,
|
| 314 |
+
output_attentions: bool | None = None,
|
| 315 |
+
output_hidden_states: bool | None = None,
|
| 316 |
+
return_dict: bool | None = None,
|
| 317 |
+
logits_to_keep: int | None = 0,
|
| 318 |
+
**kwargs: Unpack[dict],
|
| 319 |
+
) -> tuple | CausalLMOutputWithPast:
|
| 320 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 321 |
+
output_hidden_states = (
|
| 322 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 323 |
+
)
|
| 324 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 325 |
+
|
| 326 |
+
outputs = self.model(
|
| 327 |
+
input_ids=input_ids,
|
| 328 |
+
attention_mask=attention_mask,
|
| 329 |
+
inputs_embeds=inputs_embeds,
|
| 330 |
+
past_key_values=past_key_values,
|
| 331 |
+
use_cache=use_cache,
|
| 332 |
+
output_attentions=output_attentions,
|
| 333 |
+
output_hidden_states=output_hidden_states,
|
| 334 |
+
return_dict=return_dict,
|
| 335 |
+
**kwargs,
|
| 336 |
+
)
|
| 337 |
+
|
| 338 |
+
hidden_states = outputs[0]
|
| 339 |
+
|
| 340 |
+
loss, logits = None, None
|
| 341 |
+
if not self.config.fuse_linear_cross_entropy or labels is None:
|
| 342 |
+
logits = self.lm_head(hidden_states if logits_to_keep is None else hidden_states[:, -logits_to_keep:])
|
| 343 |
+
if labels is not None:
|
| 344 |
+
if getattr(self, 'criterion', None) is None:
|
| 345 |
+
if self.config.fuse_linear_cross_entropy:
|
| 346 |
+
criterion = FusedLinearCrossEntropyLoss(use_l2warp=self.config.use_l2warp)
|
| 347 |
+
elif self.config.fuse_cross_entropy:
|
| 348 |
+
criterion = FusedCrossEntropyLoss(inplace_backward=True)
|
| 349 |
+
else:
|
| 350 |
+
criterion = nn.CrossEntropyLoss()
|
| 351 |
+
else:
|
| 352 |
+
criterion = self.criterion
|
| 353 |
+
labels = labels.to(hidden_states.device)
|
| 354 |
+
labels = torch.cat((labels[..., 1:], torch.full_like(labels[:, :1], criterion.ignore_index)), 1)
|
| 355 |
+
if self.config.fuse_linear_cross_entropy:
|
| 356 |
+
loss = criterion(hidden_states, labels, self.lm_head.weight, self.lm_head.bias)
|
| 357 |
+
else:
|
| 358 |
+
loss = criterion(logits.view(labels.numel(), -1), labels.view(-1))
|
| 359 |
+
loss = l2_warp(loss, logits) if self.config.use_l2warp else loss
|
| 360 |
+
|
| 361 |
+
if not return_dict:
|
| 362 |
+
output = (logits,) + outputs[1:]
|
| 363 |
+
return (loss,) + output if loss is not None else output
|
| 364 |
+
|
| 365 |
+
return CausalLMOutputWithPast(
|
| 366 |
+
loss=loss,
|
| 367 |
+
logits=logits,
|
| 368 |
+
past_key_values=outputs.past_key_values,
|
| 369 |
+
hidden_states=outputs.hidden_states,
|
| 370 |
+
attentions=outputs.attentions,
|
| 371 |
+
)
|
fla/models/bitnet/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from transformers import AutoConfig, AutoModel, AutoModelForCausalLM
|
| 3 |
+
|
| 4 |
+
from fla.models.bitnet.configuration_bitnet import BitNetConfig
|
| 5 |
+
from fla.models.bitnet.modeling_bitnet import BitNetForCausalLM, BitNetModel
|
| 6 |
+
|
| 7 |
+
AutoConfig.register(BitNetConfig.model_type, BitNetConfig, exist_ok=True)
|
| 8 |
+
AutoModel.register(BitNetConfig, BitNetModel, exist_ok=True)
|
| 9 |
+
AutoModelForCausalLM.register(BitNetConfig, BitNetForCausalLM, exist_ok=True)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
__all__ = ['BitNetConfig', 'BitNetForCausalLM', 'BitNetModel']
|
fla/models/bitnet/configuration_bitnet.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import warnings
|
| 3 |
+
|
| 4 |
+
from transformers.configuration_utils import PretrainedConfig
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class BitNetConfig(PretrainedConfig):
|
| 8 |
+
|
| 9 |
+
model_type = 'bitnet'
|
| 10 |
+
keys_to_ignore_at_inference = ['past_key_values']
|
| 11 |
+
|
| 12 |
+
def __init__(
|
| 13 |
+
self,
|
| 14 |
+
hidden_size: int = 2048,
|
| 15 |
+
num_hidden_layers: int = 24,
|
| 16 |
+
num_heads: int = 32,
|
| 17 |
+
num_kv_heads: int | None = None,
|
| 18 |
+
window_size: int | None = None,
|
| 19 |
+
rope_theta: float | None = 10000.,
|
| 20 |
+
max_position_embeddings: int = 2048,
|
| 21 |
+
hidden_ratio: int | None = 4,
|
| 22 |
+
intermediate_size: int | None = None,
|
| 23 |
+
hidden_act: str = "swish",
|
| 24 |
+
initializer_range: float = 0.02,
|
| 25 |
+
elementwise_affine: bool | None = True,
|
| 26 |
+
norm_eps: float = 1e-6,
|
| 27 |
+
use_cache: bool = True,
|
| 28 |
+
pad_token_id: int | None = None,
|
| 29 |
+
bos_token_id: int = 1,
|
| 30 |
+
eos_token_id: int = 2,
|
| 31 |
+
tie_word_embeddings: bool = False,
|
| 32 |
+
fuse_norm: bool = True,
|
| 33 |
+
fuse_swiglu: bool = True,
|
| 34 |
+
fuse_cross_entropy: bool = True,
|
| 35 |
+
fuse_linear_cross_entropy: bool = False,
|
| 36 |
+
use_l2warp: bool = False,
|
| 37 |
+
vocab_size: int = 32000,
|
| 38 |
+
**kwargs,
|
| 39 |
+
):
|
| 40 |
+
self.hidden_size = hidden_size
|
| 41 |
+
self.num_hidden_layers = num_hidden_layers
|
| 42 |
+
self.num_heads = num_heads
|
| 43 |
+
self.num_kv_heads = num_kv_heads
|
| 44 |
+
self.window_size = window_size
|
| 45 |
+
self.rope_theta = rope_theta
|
| 46 |
+
self.max_position_embeddings = max_position_embeddings
|
| 47 |
+
|
| 48 |
+
self.hidden_ratio = hidden_ratio
|
| 49 |
+
self.intermediate_size = intermediate_size
|
| 50 |
+
self.hidden_act = hidden_act
|
| 51 |
+
|
| 52 |
+
self.initializer_range = initializer_range
|
| 53 |
+
self.elementwise_affine = elementwise_affine
|
| 54 |
+
self.norm_eps = norm_eps
|
| 55 |
+
self.use_cache = use_cache
|
| 56 |
+
|
| 57 |
+
self.fuse_norm = fuse_norm
|
| 58 |
+
self.fuse_swiglu = fuse_swiglu
|
| 59 |
+
self.fuse_cross_entropy = fuse_cross_entropy
|
| 60 |
+
self.fuse_linear_cross_entropy = fuse_linear_cross_entropy
|
| 61 |
+
self.use_l2warp = use_l2warp
|
| 62 |
+
self.vocab_size = vocab_size
|
| 63 |
+
|
| 64 |
+
if fuse_cross_entropy and fuse_linear_cross_entropy:
|
| 65 |
+
raise ValueError(
|
| 66 |
+
"`fuse_cross_entropy` and `fuse_linear_cross_entropy` cannot be True at the same time.",
|
| 67 |
+
)
|
| 68 |
+
if fuse_linear_cross_entropy:
|
| 69 |
+
warnings.warn(
|
| 70 |
+
"`fuse_linear_cross_entropy` is enabled, which can improves memory efficiency "
|
| 71 |
+
"at the potential cost of reduced precision. "
|
| 72 |
+
"If you observe issues like loss divergence, consider disabling this setting.",
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
super().__init__(
|
| 76 |
+
pad_token_id=pad_token_id,
|
| 77 |
+
bos_token_id=bos_token_id,
|
| 78 |
+
eos_token_id=eos_token_id,
|
| 79 |
+
tie_word_embeddings=tie_word_embeddings,
|
| 80 |
+
**kwargs,
|
| 81 |
+
)
|
fla/models/bitnet/modeling_bitnet.py
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
import warnings
|
| 5 |
+
from typing import TYPE_CHECKING, Any
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
|
| 10 |
+
from transformers.modeling_utils import PreTrainedModel
|
| 11 |
+
from transformers.utils import logging
|
| 12 |
+
from transformers.utils.deprecation import deprecate_kwarg
|
| 13 |
+
|
| 14 |
+
from fla.layers.bitattn import BitAttention
|
| 15 |
+
from fla.models.bitnet.configuration_bitnet import BitNetConfig
|
| 16 |
+
from fla.models.utils import Cache, FLAGenerationMixin
|
| 17 |
+
from fla.modules import FusedCrossEntropyLoss, FusedLinearCrossEntropyLoss, RMSNorm
|
| 18 |
+
from fla.modules.activations import swiglu
|
| 19 |
+
from fla.modules.fused_bitlinear import FusedBitLinear
|
| 20 |
+
from fla.modules.l2warp import l2_warp
|
| 21 |
+
|
| 22 |
+
if TYPE_CHECKING:
|
| 23 |
+
from transformers.processing_utils import Unpack
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
try:
|
| 27 |
+
from transformers.modeling_layers import GradientCheckpointingLayer
|
| 28 |
+
except ImportError:
|
| 29 |
+
from fla.models.modeling_layers import GradientCheckpointingLayer
|
| 30 |
+
|
| 31 |
+
logger = logging.get_logger(__name__)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class BitNetMLP(nn.Module):
|
| 35 |
+
|
| 36 |
+
def __init__(
|
| 37 |
+
self,
|
| 38 |
+
hidden_size: int,
|
| 39 |
+
hidden_ratio: int | None = None,
|
| 40 |
+
intermediate_size: int | None = None,
|
| 41 |
+
hidden_act: str = 'swish',
|
| 42 |
+
fuse_swiglu: bool = True,
|
| 43 |
+
) -> BitNetMLP:
|
| 44 |
+
super().__init__()
|
| 45 |
+
|
| 46 |
+
self.hidden_size = hidden_size
|
| 47 |
+
# the final number of params is `hidden_ratio * hidden_size^2`
|
| 48 |
+
# `intermediate_size` is chosen to be a multiple of 256 closest to `2/3 * hidden_size * hidden_ratio`
|
| 49 |
+
if hidden_ratio is None:
|
| 50 |
+
hidden_ratio = 4
|
| 51 |
+
if intermediate_size is None:
|
| 52 |
+
intermediate_size = int(hidden_size * hidden_ratio * 2 / 3)
|
| 53 |
+
intermediate_size = 256 * ((intermediate_size + 256 - 1) // 256)
|
| 54 |
+
self.hidden_ratio = hidden_ratio
|
| 55 |
+
self.intermediate_size = intermediate_size
|
| 56 |
+
self.hidden_act = hidden_act
|
| 57 |
+
self.fuse_swiglu = fuse_swiglu
|
| 58 |
+
|
| 59 |
+
if hidden_act != 'swish':
|
| 60 |
+
raise ValueError(f'Unsupported hidden_act: {hidden_act}')
|
| 61 |
+
|
| 62 |
+
self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
|
| 63 |
+
self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
|
| 64 |
+
self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
|
| 65 |
+
|
| 66 |
+
def forward(
|
| 67 |
+
self,
|
| 68 |
+
x: torch.Tensor,
|
| 69 |
+
**kwargs: Unpack[Any],
|
| 70 |
+
) -> torch.Tensor:
|
| 71 |
+
gate, y = self.gate_proj(x), self.up_proj(x)
|
| 72 |
+
return self.down_proj(swiglu(gate, y))
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
class BitNetBlock(GradientCheckpointingLayer):
|
| 76 |
+
|
| 77 |
+
def __init__(self, config: BitNetConfig, layer_idx: int):
|
| 78 |
+
super().__init__()
|
| 79 |
+
|
| 80 |
+
self.config = config
|
| 81 |
+
self.layer_idx = layer_idx
|
| 82 |
+
|
| 83 |
+
self.attn_norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
|
| 84 |
+
self.attn = BitAttention(
|
| 85 |
+
hidden_size=config.hidden_size,
|
| 86 |
+
num_heads=config.num_heads,
|
| 87 |
+
num_kv_heads=config.num_kv_heads,
|
| 88 |
+
window_size=config.window_size,
|
| 89 |
+
rope_theta=config.rope_theta,
|
| 90 |
+
max_position_embeddings=config.max_position_embeddings,
|
| 91 |
+
layer_idx=layer_idx,
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
self.mlp_norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
|
| 95 |
+
self.mlp = BitNetMLP(
|
| 96 |
+
hidden_size=config.hidden_size,
|
| 97 |
+
hidden_ratio=config.hidden_ratio,
|
| 98 |
+
intermediate_size=config.intermediate_size,
|
| 99 |
+
hidden_act=config.hidden_act,
|
| 100 |
+
fuse_swiglu=config.fuse_swiglu,
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
def forward(
|
| 104 |
+
self,
|
| 105 |
+
hidden_states: torch.Tensor,
|
| 106 |
+
attention_mask: torch.Tensor | None = None,
|
| 107 |
+
past_key_values: tuple[torch.Tensor] | None = None,
|
| 108 |
+
output_attentions: bool | None = False,
|
| 109 |
+
use_cache: bool | None = False,
|
| 110 |
+
**kwargs: Unpack[Any],
|
| 111 |
+
) -> tuple[torch.FloatTensor, tuple[torch.FloatTensor, torch.FloatTensor] | None]:
|
| 112 |
+
|
| 113 |
+
residual = hidden_states
|
| 114 |
+
hidden_states = self.attn_norm(hidden_states)
|
| 115 |
+
hidden_states, attentions, past_key_values = self.attn(
|
| 116 |
+
hidden_states=hidden_states,
|
| 117 |
+
attention_mask=attention_mask,
|
| 118 |
+
past_key_values=past_key_values,
|
| 119 |
+
use_cache=use_cache,
|
| 120 |
+
output_attentions=output_attentions,
|
| 121 |
+
**kwargs,
|
| 122 |
+
)
|
| 123 |
+
if self.config.fuse_norm:
|
| 124 |
+
hidden_states, residual = self.mlp_norm(hidden_states, residual, True)
|
| 125 |
+
else:
|
| 126 |
+
hidden_states = residual + hidden_states
|
| 127 |
+
residual = hidden_states
|
| 128 |
+
hidden_states = self.mlp_norm(hidden_states)
|
| 129 |
+
hidden_states = self.mlp(hidden_states, **kwargs)
|
| 130 |
+
hidden_states = residual + hidden_states
|
| 131 |
+
|
| 132 |
+
outputs = (hidden_states,)
|
| 133 |
+
|
| 134 |
+
if output_attentions:
|
| 135 |
+
outputs += (attentions,)
|
| 136 |
+
|
| 137 |
+
if use_cache:
|
| 138 |
+
outputs += (past_key_values,)
|
| 139 |
+
|
| 140 |
+
return outputs
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
class BitNetPreTrainedModel(PreTrainedModel):
|
| 144 |
+
|
| 145 |
+
config_class = BitNetConfig
|
| 146 |
+
base_model_prefix = 'model'
|
| 147 |
+
supports_gradient_checkpointing = True
|
| 148 |
+
_no_split_modules = ['BitNetBlock']
|
| 149 |
+
_supports_cache_class = True
|
| 150 |
+
|
| 151 |
+
def __init__(self, *inputs, **kwargs):
|
| 152 |
+
super().__init__(*inputs, **kwargs)
|
| 153 |
+
|
| 154 |
+
def _init_weights(
|
| 155 |
+
self,
|
| 156 |
+
module: nn.Module,
|
| 157 |
+
rescale_prenorm_residual: bool = False,
|
| 158 |
+
num_residuals_per_layer: int = 2,
|
| 159 |
+
):
|
| 160 |
+
if isinstance(module, (nn.Linear, FusedBitLinear, nn.Conv1d)):
|
| 161 |
+
# Slightly different from the TF version which uses truncated_normal for initialization
|
| 162 |
+
# cf https://github.com/pytorch/pytorch/pull/5617
|
| 163 |
+
nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
|
| 164 |
+
if module.bias is not None:
|
| 165 |
+
nn.init.zeros_(module.bias)
|
| 166 |
+
elif isinstance(module, nn.Embedding):
|
| 167 |
+
nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
|
| 168 |
+
elif hasattr(module, 'reset_parameters'):
|
| 169 |
+
module.reset_parameters()
|
| 170 |
+
|
| 171 |
+
if rescale_prenorm_residual:
|
| 172 |
+
# Reinitialize selected weights subject to the OpenAI GPT-2 Paper Scheme:
|
| 173 |
+
# > A modified initialization which accounts for the accumulation on the residual path with model depth. Scale
|
| 174 |
+
# > the weights of residual layers at initialization by a factor of 1/√N where N is the # of residual layers.
|
| 175 |
+
# > -- GPT-2 :: https://openai.com/blog/better-language-models/
|
| 176 |
+
#
|
| 177 |
+
# Reference (Megatron-LM): https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/gpt_model.py
|
| 178 |
+
p = None
|
| 179 |
+
if hasattr(module, 'o_proj'):
|
| 180 |
+
p = module.o_proj.weight
|
| 181 |
+
elif hasattr(module, 'down_proj'):
|
| 182 |
+
p = module.down_proj.weight
|
| 183 |
+
if p is not None:
|
| 184 |
+
# Special Scaled Initialization --> There are 2 Layer Norms per Transformer Block
|
| 185 |
+
# Following Pytorch init, except scale by 1/sqrt(2 * n_layer)
|
| 186 |
+
# We need to reinit p since this code could be called multiple times
|
| 187 |
+
# Having just p *= scale would repeatedly scale it down
|
| 188 |
+
nn.init.kaiming_uniform_(p, a=math.sqrt(5))
|
| 189 |
+
with torch.no_grad():
|
| 190 |
+
p /= math.sqrt(num_residuals_per_layer * self.config.num_hidden_layers)
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
class BitNetModel(BitNetPreTrainedModel):
|
| 194 |
+
|
| 195 |
+
def __init__(
|
| 196 |
+
self,
|
| 197 |
+
config: BitNetConfig,
|
| 198 |
+
) -> BitNetModel:
|
| 199 |
+
super().__init__(config)
|
| 200 |
+
self.padding_idx = config.pad_token_id
|
| 201 |
+
self.vocab_size = config.vocab_size
|
| 202 |
+
|
| 203 |
+
self.embeddings = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
|
| 204 |
+
self.layers = nn.ModuleList([BitNetBlock(config, layer_idx) for layer_idx in range(config.num_hidden_layers)])
|
| 205 |
+
self.norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
|
| 206 |
+
|
| 207 |
+
self.gradient_checkpointing = False
|
| 208 |
+
|
| 209 |
+
self.post_init()
|
| 210 |
+
|
| 211 |
+
def get_input_embeddings(self):
|
| 212 |
+
return self.embeddings
|
| 213 |
+
|
| 214 |
+
def set_input_embeddings(self, value):
|
| 215 |
+
self.embeddings = value
|
| 216 |
+
|
| 217 |
+
def forward(
|
| 218 |
+
self,
|
| 219 |
+
input_ids: torch.LongTensor | None = None,
|
| 220 |
+
attention_mask: torch.Tensor | None = None,
|
| 221 |
+
past_key_values: list[torch.FloatTensor] | None = None,
|
| 222 |
+
inputs_embeds: torch.FloatTensor | None = None,
|
| 223 |
+
use_cache: bool | None = None,
|
| 224 |
+
output_attentions: bool | None = None,
|
| 225 |
+
output_hidden_states: bool | None = None,
|
| 226 |
+
return_dict: bool | None = None,
|
| 227 |
+
**kwargs: Unpack[Any],
|
| 228 |
+
) -> tuple | CausalLMOutputWithPast:
|
| 229 |
+
if output_attentions:
|
| 230 |
+
warnings.warn(
|
| 231 |
+
"`BitNetModel` does not support output attention weights now, so `output_attentions` is set to `False`.",
|
| 232 |
+
)
|
| 233 |
+
output_attentions = False
|
| 234 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 235 |
+
output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 236 |
+
use_cache = use_cache if use_cache is not None else (self.config.use_cache if not self.training else False)
|
| 237 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 238 |
+
|
| 239 |
+
# retrieve input_ids and inputs_embeds
|
| 240 |
+
if input_ids is not None and inputs_embeds is not None:
|
| 241 |
+
raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
|
| 242 |
+
elif input_ids is None and inputs_embeds is None:
|
| 243 |
+
raise ValueError("You have to specify either input_ids or inputs_embeds")
|
| 244 |
+
|
| 245 |
+
if use_cache and not isinstance(past_key_values, Cache):
|
| 246 |
+
past_key_values = Cache.from_legacy_cache(past_key_values)
|
| 247 |
+
|
| 248 |
+
if inputs_embeds is None:
|
| 249 |
+
inputs_embeds = self.embeddings(input_ids)
|
| 250 |
+
|
| 251 |
+
# embed positions
|
| 252 |
+
hidden_states = inputs_embeds
|
| 253 |
+
|
| 254 |
+
all_hidden_states = () if output_hidden_states else None
|
| 255 |
+
all_attns = () if output_attentions else None
|
| 256 |
+
next_cache = None
|
| 257 |
+
|
| 258 |
+
for layer in self.layers:
|
| 259 |
+
if output_hidden_states:
|
| 260 |
+
all_hidden_states += (hidden_states,)
|
| 261 |
+
|
| 262 |
+
layer_outputs = layer(
|
| 263 |
+
hidden_states,
|
| 264 |
+
attention_mask=attention_mask,
|
| 265 |
+
past_key_values=past_key_values,
|
| 266 |
+
output_attentions=output_attentions,
|
| 267 |
+
use_cache=use_cache,
|
| 268 |
+
**kwargs,
|
| 269 |
+
)
|
| 270 |
+
|
| 271 |
+
hidden_states = layer_outputs[0]
|
| 272 |
+
|
| 273 |
+
if use_cache:
|
| 274 |
+
next_cache = layer_outputs[2 if output_attentions else 1]
|
| 275 |
+
|
| 276 |
+
if output_attentions:
|
| 277 |
+
all_attns += (layer_outputs[1],)
|
| 278 |
+
|
| 279 |
+
hidden_states = self.norm(hidden_states)
|
| 280 |
+
|
| 281 |
+
# add hidden states from the last decoder layer
|
| 282 |
+
if output_hidden_states:
|
| 283 |
+
all_hidden_states += (hidden_states,)
|
| 284 |
+
|
| 285 |
+
if not return_dict:
|
| 286 |
+
return tuple(v for v in [hidden_states, next_cache, all_hidden_states, all_attns] if v is not None)
|
| 287 |
+
|
| 288 |
+
return BaseModelOutputWithPast(
|
| 289 |
+
last_hidden_state=hidden_states,
|
| 290 |
+
past_key_values=next_cache,
|
| 291 |
+
hidden_states=all_hidden_states,
|
| 292 |
+
attentions=all_attns,
|
| 293 |
+
)
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
class BitNetForCausalLM(BitNetPreTrainedModel, FLAGenerationMixin):
|
| 297 |
+
|
| 298 |
+
_tied_weights_keys = ["lm_head.weight"]
|
| 299 |
+
|
| 300 |
+
def __init__(self, config):
|
| 301 |
+
super().__init__(config)
|
| 302 |
+
self.model = BitNetModel(config)
|
| 303 |
+
self.vocab_size = config.vocab_size
|
| 304 |
+
self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
|
| 305 |
+
self.criterion = None
|
| 306 |
+
|
| 307 |
+
# Initialize weights and apply final processing
|
| 308 |
+
self.post_init()
|
| 309 |
+
|
| 310 |
+
def get_input_embeddings(self):
|
| 311 |
+
return self.model.embeddings
|
| 312 |
+
|
| 313 |
+
def set_input_embeddings(self, value):
|
| 314 |
+
self.model.embeddings = value
|
| 315 |
+
|
| 316 |
+
def get_output_embeddings(self):
|
| 317 |
+
return self.lm_head
|
| 318 |
+
|
| 319 |
+
def set_output_embeddings(self, new_embeddings):
|
| 320 |
+
self.lm_head = new_embeddings
|
| 321 |
+
|
| 322 |
+
def set_decoder(self, decoder):
|
| 323 |
+
self.model = decoder
|
| 324 |
+
|
| 325 |
+
def get_decoder(self):
|
| 326 |
+
return self.model
|
| 327 |
+
|
| 328 |
+
@deprecate_kwarg("num_logits_to_keep", version="4.50", new_name="logits_to_keep")
|
| 329 |
+
def forward(
|
| 330 |
+
self,
|
| 331 |
+
input_ids: torch.LongTensor = None,
|
| 332 |
+
attention_mask: torch.Tensor | None = None,
|
| 333 |
+
past_key_values: Cache | list[torch.FloatTensor] | None = None,
|
| 334 |
+
inputs_embeds: torch.FloatTensor | None = None,
|
| 335 |
+
labels: torch.LongTensor | None = None,
|
| 336 |
+
use_cache: bool | None = None,
|
| 337 |
+
output_attentions: bool | None = None,
|
| 338 |
+
output_hidden_states: bool | None = None,
|
| 339 |
+
return_dict: bool | None = None,
|
| 340 |
+
logits_to_keep: int | None = 0,
|
| 341 |
+
**kwargs: Unpack[Any],
|
| 342 |
+
) -> tuple | CausalLMOutputWithPast:
|
| 343 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 344 |
+
output_hidden_states = (
|
| 345 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 346 |
+
)
|
| 347 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 348 |
+
|
| 349 |
+
outputs = self.model(
|
| 350 |
+
input_ids=input_ids,
|
| 351 |
+
attention_mask=attention_mask,
|
| 352 |
+
past_key_values=past_key_values,
|
| 353 |
+
inputs_embeds=inputs_embeds,
|
| 354 |
+
use_cache=use_cache,
|
| 355 |
+
output_attentions=output_attentions,
|
| 356 |
+
output_hidden_states=output_hidden_states,
|
| 357 |
+
return_dict=return_dict,
|
| 358 |
+
**kwargs,
|
| 359 |
+
)
|
| 360 |
+
|
| 361 |
+
hidden_states = outputs[0]
|
| 362 |
+
|
| 363 |
+
logits = None if self.config.fuse_linear_cross_entropy else self.lm_head(hidden_states[:, -logits_to_keep:])
|
| 364 |
+
|
| 365 |
+
loss = None
|
| 366 |
+
if labels is not None:
|
| 367 |
+
if getattr(self, 'criterion', None) is None:
|
| 368 |
+
if self.config.fuse_linear_cross_entropy:
|
| 369 |
+
criterion = FusedLinearCrossEntropyLoss(use_l2warp=self.config.use_l2warp)
|
| 370 |
+
elif self.config.fuse_cross_entropy:
|
| 371 |
+
criterion = FusedCrossEntropyLoss(inplace_backward=True)
|
| 372 |
+
else:
|
| 373 |
+
criterion = nn.CrossEntropyLoss()
|
| 374 |
+
else:
|
| 375 |
+
criterion = self.criterion
|
| 376 |
+
|
| 377 |
+
labels = labels.to(hidden_states.device)
|
| 378 |
+
labels = torch.cat((labels[..., 1:], torch.full_like(labels[:, :1], criterion.ignore_index)), 1)
|
| 379 |
+
if self.config.fuse_linear_cross_entropy:
|
| 380 |
+
loss = criterion(hidden_states, labels, self.lm_head.weight, self.lm_head.bias)
|
| 381 |
+
else:
|
| 382 |
+
loss = criterion(logits.view(labels.numel(), -1), labels.view(-1))
|
| 383 |
+
loss = l2_warp(loss, logits) if self.config.use_l2warp else loss
|
| 384 |
+
|
| 385 |
+
if not return_dict:
|
| 386 |
+
output = (logits,) + outputs[1:]
|
| 387 |
+
return (loss,) + output if loss is not None else output
|
| 388 |
+
|
| 389 |
+
return CausalLMOutputWithPast(
|
| 390 |
+
loss=loss,
|
| 391 |
+
logits=logits,
|
| 392 |
+
past_key_values=outputs.past_key_values,
|
| 393 |
+
hidden_states=outputs.hidden_states,
|
| 394 |
+
attentions=outputs.attentions,
|
| 395 |
+
)
|
fla/models/comba/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from transformers import AutoConfig, AutoModel, AutoModelForCausalLM
|
| 3 |
+
|
| 4 |
+
from fla.models.comba.configuration_comba import CombaConfig
|
| 5 |
+
from fla.models.comba.modeling_comba import CombaForCausalLM, CombaModel
|
| 6 |
+
|
| 7 |
+
AutoConfig.register(CombaConfig.model_type, CombaConfig, exist_ok=True)
|
| 8 |
+
AutoModel.register(CombaConfig, CombaModel, exist_ok=True)
|
| 9 |
+
AutoModelForCausalLM.register(CombaConfig, CombaForCausalLM, exist_ok=True)
|
| 10 |
+
|
| 11 |
+
__all__ = ['CombaConfig', 'CombaForCausalLM', 'CombaModel']
|