Skip to content

Commit bda3b85

Browse files
authored
[Train] Add DeepSeek Engram (#1107)
**Add deepseek engram**, ref: [DeepSeek Engram Paper](https://github.com/deepseek-ai/Engram/blob/main/Engram_paper.pdf), [DeepSeek Engram Github](https://github.com/deepseek-ai/Engram) - Support tensor parallel, pipeline parallel, sequence parallel, distributed data parallel - Support NgramHash Caching - End to end training support - CI/CD Tests - CKPT Conversion: FlagScale to HuggingFace TODO: - Engram Embedding Offload - Engram Prefetch, attn/mlp computation and memory access overlapping - FlagOS support, based on Megatron-LM-FL and TransformerEngine-FL
1 parent b0b713f commit bda3b85

27 files changed

Lines changed: 3339 additions & 2 deletions

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ exclude: |
55
flagscale/models/megatron/.*|
66
tools/checkpoint/loader.*\.py|
77
tools/checkpoint/saver.*\.py|
8+
tools/checkpoint/qwen3_engram/modeling_hf/.*|
89
examples/aquila/tokenizer*|
910
)$
1011
repos:
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
system:
2+
no_shared_fs: ${experiment.runner.no_shared_fs}
3+
num_workers: 2
4+
tensor_model_parallel_size: 1
5+
pipeline_model_parallel_size: 1
6+
expert_model_parallel_size: 1
7+
context_parallel_size: 1
8+
sequence_parallel: true
9+
use_distributed_optimizer: true
10+
overlap_grad_reduce: true
11+
overlap_param_gather: true
12+
precision:
13+
bf16: true
14+
attention_softmax_in_fp32: true
15+
accumulate_allreduce_grads_in_fp32: true
16+
logging:
17+
log_interval: 1
18+
tensorboard_log_interval: 1
19+
wandb_project: ${experiment.exp_name}
20+
wandb_exp_name: ${experiment.exp_name}
21+
log_timers_to_tensorboard: true
22+
log_validation_ppl_to_tensorboard: true
23+
log_throughput: true
24+
log_params_norm: true
25+
log_num_zeros_in_grad: true
26+
log_memory_to_tensorboard: true
27+
checkpoint:
28+
save_interval: ${experiment.save_steps}
29+
load: ${experiment.load}
30+
ckpt_format: ${experiment.ckpt_format}
31+
32+
model:
33+
transformer_impl: transformer_engine
34+
num_layers: 36
35+
hidden_size: 2560
36+
ffn_hidden_size: 9728
37+
num_attention_heads: 32
38+
kv_channels: 128
39+
group_query_attention: true
40+
num_query_groups: 8
41+
seq_length: 4096
42+
max_position_embeddings: 32768
43+
norm_epsilon: 1e-6
44+
use_rotary_position_embeddings: true
45+
rotary_base: 10000
46+
swiglu: true
47+
normalization: RMSNorm
48+
qk_layernorm: true
49+
init_method_std: 0.02
50+
attention_dropout: 0.0
51+
hidden_dropout: 0.0
52+
untie_embeddings_and_output_weights: true
53+
position_embedding_type: rope
54+
no_rope_fusion: true
55+
56+
# hf: attention_bias: true
57+
disable_bias_linear: true
58+
add_qkv_bias: false
59+
60+
# engram args =================
61+
use_engram: true
62+
engram_tokenizer_name_or_path: xxx
63+
engram_vocab_size: [759680, 759680]
64+
max_ngram_size: 3
65+
n_embed_per_ngram: 512
66+
n_head_per_ngram: 8
67+
engram_layer_ids: [2]
68+
engram_pad_id: 2
69+
engram_seed: 0
70+
engram_kernel_size: 4
71+
engram_hc_mult: 1
72+
# hf: engram_lr_multiplier: 5.0 miss
73+
74+
75+
# training
76+
seed: ${experiment.seed}
77+
finetune: false
78+
# to be update
79+
micro_batch_size: 1
80+
# hf: gbs: 1024
81+
global_batch_size: 128
82+
# hf: max_steps: 24000
83+
train_iters: 102400
84+
eval_iters: 0
85+
86+
87+
optimizer:
88+
clip_grad: 1.0
89+
weight_decay: 0.1
90+
adam_beta1: 0.9
91+
adam_beta2: 0.95
92+
lr_scheduler:
93+
lr: 3.0e-4
94+
min_lr: 3.0e-4
95+
# lr_warmup_fraction: 0.01
96+
lr_warmup_iters: 240
97+
lr_decay_style: WSD
98+
lr_wsd_decay_style: cosine
99+
lr_wsd_decay_iters: 10
100+
101+
data:
102+
reset_position_ids: True
103+
reset_attention_mask: True
104+
data_path: xxx
105+
split: 1
106+
no_mmap_bin_files: true
107+
tokenizer:
108+
legacy_tokenizer: true
109+
tokenizer_type: Qwen2TokenizerFS
110+
tokenizer_path: xxx
111+
vocab_size: 151936
112+
make_vocab_size_divisible_by: 64
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
defaults:
2+
- _self_
3+
- train: engram
4+
5+
experiment:
6+
exp_name: Qwen3-Engram
7+
seed: 42
8+
save_steps: 50
9+
load: None
10+
exp_dir: xxx
11+
ckpt_format: torch
12+
task:
13+
type: train
14+
backend: megatron
15+
entrypoint: flagscale/train/megatron/train_engram.py
16+
runner:
17+
per_node_task: false
18+
no_shared_fs: false
19+
rdzv_backend: static
20+
hostfile: xxx
21+
ssh_port: xxx
22+
cmds:
23+
before_start: ulimit -n 1048576 && source /root/miniconda3/bin/activate flagscale-train
24+
envs:
25+
LOGLEVEL: "INFO"
26+
CUDA_VISIBLE_DEVICES: "0,1,2,3,4,5,6,7"
27+
CUDA_DEVICE_MAX_CONNECTIONS: 1
28+
29+
action: run
30+
31+
hydra:
32+
run:
33+
dir: ${experiment.exp_dir}/hydra
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
## built-in
2+
import copy
3+
import math
4+
5+
## third-party
6+
import torch
7+
import torch.nn as nn
8+
9+
from .engram_config import EngramConfig
10+
from .multi_head_embedding import MultiHeadEmbedding
11+
12+
## engram
13+
from .ngram_hash import get_or_create_hash_mapping
14+
from .short_conv import ShortConv
15+
16+
17+
class Engram(nn.Module):
18+
def __init__(self, engram_cfg: EngramConfig, layer_id):
19+
super().__init__()
20+
assert engram_cfg.engram_hc_mult == 1, (
21+
"Engram do not support hyper-connection now, engram_hc_mult must be 1"
22+
)
23+
self.engram_cfg = engram_cfg
24+
self.backbone_config = copy.deepcopy(engram_cfg)
25+
26+
self.layer_id = layer_id
27+
global_hash_mapping = get_or_create_hash_mapping(
28+
engram_vocab_size=engram_cfg.engram_vocab_size,
29+
max_ngram_size=engram_cfg.max_ngram_size,
30+
n_embed_per_ngram=engram_cfg.n_embed_per_ngram,
31+
n_head_per_ngram=engram_cfg.n_head_per_ngram,
32+
layer_ids=engram_cfg.engram_layer_ids,
33+
tokenizer_name_or_path=engram_cfg.engram_tokenizer_name_or_path,
34+
pad_id=engram_cfg.engram_pad_id,
35+
seed=engram_cfg.engram_seed,
36+
)
37+
self.multi_head_embedding = MultiHeadEmbedding(
38+
engram_cfg,
39+
list_of_N=[
40+
x for y in global_hash_mapping.vocab_size_across_layers[self.layer_id] for x in y
41+
],
42+
D=engram_cfg.n_embed_per_ngram // engram_cfg.n_head_per_ngram,
43+
)
44+
self.short_conv = ShortConv(
45+
hidden_size=self.backbone_config.hidden_size,
46+
kernel_size=engram_cfg.engram_kernel_size,
47+
dilation=engram_cfg.max_ngram_size,
48+
hc_mult=self.backbone_config.engram_hc_mult,
49+
)
50+
engram_hidden_size = (engram_cfg.max_ngram_size - 1) * engram_cfg.n_embed_per_ngram
51+
self.value_proj = nn.Linear(engram_hidden_size, self.backbone_config.hidden_size)
52+
self.key_projs = nn.ModuleList(
53+
[
54+
nn.Linear(engram_hidden_size, self.backbone_config.hidden_size)
55+
for _ in range(self.backbone_config.engram_hc_mult)
56+
]
57+
)
58+
self.norm1 = nn.ModuleList(
59+
[
60+
nn.RMSNorm(self.backbone_config.hidden_size)
61+
for _ in range(self.backbone_config.engram_hc_mult)
62+
]
63+
)
64+
self.norm2 = nn.ModuleList(
65+
[
66+
nn.RMSNorm(self.backbone_config.hidden_size)
67+
for _ in range(self.backbone_config.engram_hc_mult)
68+
]
69+
)
70+
71+
def forward(self, hidden_states, hash_input_ids):
72+
"""
73+
# hidden_states: [L, B, HC_MULT, D]
74+
hidden_states: [L, B, D] # do not support hyper-connection now, hc_mult must be 1
75+
input_ids: [B, L]
76+
77+
# return: [L, B, HC_MULT, D]
78+
return: [L, B, D] # do not support hyper-connection now, hc_mult must be 1
79+
"""
80+
assert hash_input_ids is not None, "Hash input ids can not be None for EngramModel"
81+
# [B, L, N_GRAM * N_HEADS_PER_GRAM]
82+
# fake hyper-connection
83+
hidden_states = hidden_states.unsqueeze(2)
84+
85+
embeddings = self.multi_head_embedding(hash_input_ids).flatten(start_dim=-2)
86+
# [L/tp_size, B, N_GRAM * N_HEADS_PER_GRAM, N_EMBED_PER_GRAM // N_HEADS_PER_GRAM]
87+
# [L/tp_size, B, N_GRAM * N_EMBED_PER_NGRAM]
88+
89+
# Pre-compute scaling factor for efficiency
90+
scale = 1.0 / math.sqrt(self.backbone_config.hidden_size)
91+
gates = []
92+
for hc_idx in range(self.backbone_config.engram_hc_mult):
93+
key = self.key_projs[hc_idx](embeddings)
94+
# [L/tp_size, B, HIDDEN_SIZE]
95+
normed_key = self.norm1[hc_idx](key)
96+
97+
query = hidden_states[:, :, hc_idx, :]
98+
# [L, B, HIDDEN_SIZE]
99+
normed_query = self.norm2[hc_idx](query)
100+
101+
# Compute scaled dot product similarity
102+
gate = torch.sum(normed_key * normed_query, dim=-1, keepdim=True) * scale
103+
# Apply smooth absolute value transformation: sign(x) * sqrt(|x|)
104+
# This is equivalent to: abs().clamp_min(1e-6).sqrt() * sign()
105+
gate = torch.sign(gate) * torch.sqrt(torch.abs(gate).clamp_min(1e-6))
106+
gate = torch.sigmoid(gate)
107+
# [L, B, 1]
108+
109+
gates.append(gate)
110+
gates = torch.stack(gates, dim=2)
111+
# [L, B, HC_MULT, 1]
112+
113+
value = gates * self.value_proj(embeddings).unsqueeze(2)
114+
# [L, B, HC_MULT, HIDDEN_SIZE]
115+
output = value + self.short_conv(value)
116+
# [L, B, HC_MULT, HIDDEN_SIZE]
117+
118+
# re-fake hyper-connection
119+
assert output.shape[2] == 1, "Engram do not support hyper-connection now, hc_mult must be 1"
120+
output = output.squeeze(2)
121+
122+
return output

0 commit comments

Comments
 (0)