Skip to content

Commit c63361f

Browse files
authored
[Speculative Decoding][MTP]Support mtp in epdptp mode (#4614)
* support mtp many features * support mtp reshard in rl mode * fix function * support mtp ep * support mtp in hybird-dp-tp mode * default open scheduler_v1 in mtp
1 parent b401483 commit c63361f

10 files changed

Lines changed: 124 additions & 74 deletions

File tree

fastdeploy/engine/args_utils.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,8 +442,7 @@ def __post_init__(self):
442442
raise NotImplementedError("Only CUDA platform supports logprob.")
443443
if self.speculative_config is not None and self.logprobs_mode.startswith("processed"):
444444
raise NotImplementedError("processed_logprobs not support in speculative.")
445-
if self.speculative_config is not None:
446-
envs.ENABLE_V1_KVCACHE_SCHEDULER = 0
445+
447446
if self.splitwise_role != "mixed" and self.cache_transfer_protocol != "rdma":
448447
envs.ENABLE_V1_KVCACHE_SCHEDULER = 0
449448
if not current_platform.is_cuda() and not current_platform.is_xpu():

fastdeploy/model_executor/layers/attention/append_attn_backend.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ def __init__(
9292
self.rope_3d: bool = getattr(fd_config.model_config, "rope_3d", False) or getattr(
9393
fd_config.model_config, "use_3d_rope", False
9494
)
95+
if fd_config.speculative_config.model_type != "main":
96+
self.rope_3d = False
9597
self.causal: bool = getattr(fd_config.model_config, "causal", True)
9698
self.speculative_method: str = fd_config.speculative_config.method
9799
self.use_speculate: bool = self.speculative_method is not None
@@ -364,7 +366,7 @@ def forward_mixed(
364366
getattr(layer, "cache_v_zp", None),
365367
layer.linear_shift,
366368
layer.linear_smooth,
367-
forward_meta.attn_mask_offsets,
369+
None if self.use_speculate else forward_meta.attn_mask_offsets,
368370
metadata.kv_signal_data_list[layer.layer_id],
369371
getattr(layer, "q_norm_weight", None),
370372
getattr(layer, "k_norm_weight", None),
@@ -383,7 +385,7 @@ def forward_mixed(
383385
metadata.max_partition_size,
384386
metadata.encoder_max_partition_size,
385387
self.speculate_max_draft_token_num + 1,
386-
self.causal,
388+
self.causal or self.use_speculate,
387389
self.speculative_method is not None,
388390
sliding_window,
389391
)

fastdeploy/model_executor/layers/mtp_linear.py

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from paddle import nn
1919
from paddle.distributed import fleet
2020

21-
from fastdeploy.model_executor.utils import set_weight_attrs
21+
from fastdeploy.model_executor.utils import default_weight_loader, set_weight_attrs
2222

2323
from .utils import get_tensor
2424

@@ -53,44 +53,61 @@ def __init__(
5353
self.bias_key = prefix + ".bias"
5454
else:
5555
self.bias_key = None
56-
self.use_ep = fd_config.parallel_config.use_ep
56+
self.fd_config = fd_config
57+
self.tp_group = fd_config.parallel_config.tp_group
5758
self.column_cut = True
59+
self.nranks = fd_config.parallel_config.tensor_parallel_size
5860

5961
ColumnParallelLinear = fleet.meta_parallel.ColumnParallelLinear
6062
RowParallelLinear = fleet.meta_parallel.RowParallelLinear
6163

62-
if self.use_ep:
63-
self.weight = self.create_parameter(
64-
shape=[embedding_dim, num_embeddings],
65-
dtype=paddle.get_default_dtype(),
66-
is_bias=False,
64+
if self.column_cut:
65+
need_gather = True
66+
self.linear = ColumnParallelLinear(
67+
embedding_dim,
68+
num_embeddings,
69+
mp_group=self.tp_group,
70+
weight_attr=None,
71+
has_bias=True if self.bias_key is not None else False,
72+
gather_output=need_gather,
73+
fuse_matmul_bias=False, # False diff更小
6774
)
68-
else:
69-
if self.column_cut:
70-
need_gather = True
71-
self.linear = ColumnParallelLinear(
72-
embedding_dim,
73-
num_embeddings,
74-
mp_group=fleet.get_hybrid_communicate_group().get_model_parallel_group(),
75-
weight_attr=None,
76-
has_bias=True if self.bias_key is not None else False,
77-
gather_output=need_gather,
78-
fuse_matmul_bias=False, # False diff更小
75+
set_weight_attrs(
76+
self.linear.weight,
77+
{
78+
"weight_loader": default_weight_loader(self.fd_config),
79+
"model_format": self.fd_config.model_config.model_format,
80+
},
81+
)
82+
if self.bias_key is not None:
83+
set_weight_attrs(
84+
self.linear.bias,
85+
{"rl_need_attr": {"rl_tp_degree": fd_config.parallel_config.tensor_parallel_size}},
7986
)
87+
if self.nranks > 1:
8088
set_weight_attrs(self.linear.weight, {"output_dim": True})
81-
if self.bias_key is not None:
82-
set_weight_attrs(self.linear.bias, {"output_dim": True})
83-
else:
84-
self.linear = RowParallelLinear(
85-
embedding_dim,
86-
num_embeddings,
87-
mp_group=fleet.get_hybrid_communicate_group().get_model_parallel_group(),
88-
weight_attr=None,
89-
has_bias=True if self.bias_key is not None else False,
90-
input_is_parallel=False,
91-
fuse_matmul_bias=False, # False diff更小
92-
)
93-
set_weight_attrs(self.linear.weight, {"output_dim": False})
89+
else:
90+
self.linear = RowParallelLinear(
91+
embedding_dim,
92+
num_embeddings,
93+
mp_group=self.tp_group,
94+
weight_attr=None,
95+
has_bias=True if self.bias_key is not None else False,
96+
input_is_parallel=False,
97+
fuse_matmul_bias=False, # False diff更小
98+
)
99+
set_weight_attrs(
100+
self.linear.weight,
101+
{
102+
"weight_loader": default_weight_loader(self.fd_config),
103+
"model_format": self.fd_config.model_config.model_format,
104+
},
105+
)
106+
if self.nranks > 1:
107+
set_weight_attrs(self.linear.weight, {"output_dim": True})
108+
set_weight_attrs(
109+
self.linear.weight, {"rl_need_attr": {"rl_tp_degree": fd_config.parallel_config.tensor_parallel_size}}
110+
)
94111

95112
def load_state_dict(self, state_dict):
96113
"""
@@ -100,17 +117,14 @@ def load_state_dict(self, state_dict):
100117
state_dict (dict): A dictionary containing the checkpoint weights and biases.
101118
"""
102119

103-
if self.use_ep:
104-
self.weight.set_value(get_tensor(state_dict.pop(self.weight_key)).astype(paddle.get_default_dtype()))
105-
else:
106-
weight_tensor = get_tensor(state_dict.pop(self.weight_key)).astype(paddle.get_default_dtype())
107-
if self.linear.weight.shape != weight_tensor.shape:
108-
weight_tensor = weight_tensor.transpose([1, 0])
109-
self.linear.weight.set_value(weight_tensor)
120+
weight_tensor = get_tensor(state_dict.pop(self.weight_key)).astype(paddle.get_default_dtype())
121+
if self.linear.weight.shape != weight_tensor.shape:
122+
weight_tensor = weight_tensor.transpose([1, 0])
123+
self.linear.weight.set_value(weight_tensor)
110124

111-
if self.bias_key is not None:
112-
bias = get_tensor(state_dict.pop(self.bias_key)).astype(paddle.get_default_dtype())
113-
self.linear.bias.set_value(bias)
125+
if self.bias_key is not None:
126+
bias = get_tensor(state_dict.pop(self.bias_key)).astype(paddle.get_default_dtype())
127+
self.linear.bias.set_value(bias)
114128

115129
def forward(self, input):
116130
"""
@@ -123,8 +137,5 @@ def forward(self, input):
123137
Tensor: The output tensor after processing through the layer.
124138
"""
125139
logits = input
126-
if self.use_ep:
127-
logits = paddle.matmul(logits, self.weight)
128-
else:
129-
logits = self.linear(logits)
140+
logits = self.linear(logits)
130141
return logits

fastdeploy/model_executor/model_loader/default_loader.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ def load_model(self, fd_config: FDConfig) -> nn.Layer:
7272
# register rl model
7373
import fastdeploy.rl # noqa
7474

75+
if fd_config.speculative_config.model_type != "mtp":
76+
architectures = architectures.replace("Ernie5ForCausalLM", "Ernie5MoeForCausalLM")
77+
else:
78+
architectures = architectures.replace("Ernie5ForCausalLM", "Ernie5MTPForCausalLM")
79+
7580
architectures = architectures + "RL"
7681
context = paddle.LazyGuard()
7782
else:

fastdeploy/model_executor/model_loader/default_loader_v1.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ def load_model(self, fd_config: FDConfig) -> nn.Layer:
6565
# register rl model
6666
import fastdeploy.rl # noqa
6767

68+
if fd_config.speculative_config.model_type != "mtp":
69+
architectures = architectures.replace("Ernie5ForCausalLM", "Ernie5MoeForCausalLM")
70+
else:
71+
architectures = architectures.replace("Ernie5ForCausalLM", "Ernie5MTPForCausalLM")
72+
6873
architectures = architectures + "RL"
6974

7075
enable_cache, _, weight_cache_context = is_weight_cache_enabled(fd_config)

fastdeploy/output/token_processor.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ def _recycle_resources(self, task_id, index, task, result=None, is_prefill=False
502502

503503
def _compute_speculative_status(self):
504504
# TODO(liuzichang): Supplement more statistics
505-
interval = 10
505+
interval = 1
506506
if self.speculative_stats_step % interval == 0:
507507
accept_ratio = 1 - self.total_step * 1.0 / self.number_of_output_tokens
508508
spec_logger.info(
@@ -593,6 +593,9 @@ def _process_batch_output(self):
593593
+ accept_num[i]
594594
].tolist()
595595
if (not recovery_stop) and (len(token_ids) == 0 or token_ids[-1] <= 0):
596+
if envs.ENABLE_V1_KVCACHE_SCHEDULER:
597+
if task_id in self.resource_manager.to_be_rescheduled_request_id_set:
598+
self.resource_manager.reschedule_preempt_task(task_id)
596599
continue
597600
else:
598601
token_id = int(tokens[i, 0])

fastdeploy/rl/dynamic_weight_manager.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,10 @@
1717
import os
1818
import time
1919
from multiprocessing.shared_memory import SharedMemory
20-
from typing import Any, Dict
20+
from typing import Any, Dict, List
2121

2222
import numpy as np
2323
import paddle
24-
from paddle import nn
2524
from paddleformers.utils.log import logger
2625

2726
from fastdeploy.config import FDConfig
@@ -31,7 +30,7 @@
3130
class DynamicWeightManager:
3231
"""Manages model weights loading, updating and shared state across processes."""
3332

34-
def __init__(self, fd_config: FDConfig, model: nn.Layer):
33+
def __init__(self, fd_config: FDConfig, models):
3534
"""Initialize with config and model instances."""
3635
self.fd_config = fd_config
3736
self.load_config = fd_config.load_config
@@ -42,7 +41,10 @@ def __init__(self, fd_config: FDConfig, model: nn.Layer):
4241
self.meta_src_id = self._get_gpu_id()
4342
self.first_load = True
4443
self.ipc_path = f"/shared_ipc_meta/ipc_metas_{self.meta_src_id}"
45-
self.model: nn.Layer = model
44+
if not isinstance(models, List):
45+
self.model_list = [models]
46+
else:
47+
self.model_list = models
4648
self._capture_model_state()
4749
self.update_parameters()
4850
self.finalize_update()
@@ -55,9 +57,10 @@ def __init__(self, fd_config: FDConfig, model: nn.Layer):
5557
@paddle.no_grad()
5658
def _capture_model_state(self):
5759
"""Capture and store initial model parameters state."""
58-
for name, param in self.model.state_dict().items():
59-
logger.debug(f"Model param: {name}, shape={param.shape}, dtype={param.dtype}")
60-
self.state_dict[name] = param
60+
for model in self.model_list:
61+
for name, param in model.state_dict().items():
62+
logger.info(f"Model param: {name}, shape={param.shape}, dtype={param.dtype}")
63+
self.state_dict[name] = param
6164

6265
def update_parameters(self, pid: int = 0) -> None:
6366
"""Core method to update model parameters based on strategy."""
@@ -137,8 +140,9 @@ def clear_parameters(self, pid: int = 0) -> None:
137140

138141
paddle.device.cuda.empty_cache()
139142
# step2: release model weight
140-
for param in self.model.state_dict().values():
141-
param._clear_data()
143+
for model in self.model_list:
144+
for param in model.state_dict().values():
145+
param._clear_data()
142146

143147
self._verify_parameters("clearance")
144148

fastdeploy/spec_decode/base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,20 @@ def __init__(self, fd_config: FDConfig):
3838
Init Speculative proposer
3939
"""
4040
fd_config.parallel_config.tp_group = None
41+
fd_config.parallel_config.ep_group = None
4142
self.fd_config = deepcopy(fd_config)
4243
fd_config.parallel_config.tp_group = dist.get_group(
4344
fd_config.parallel_config.data_parallel_rank + envs.FD_TP_GROUP_GID_OFFSET
4445
)
46+
fd_config.parallel_config.ep_group = dist.get_group(
47+
fd_config.parallel_config.data_parallel_size + envs.FD_TP_GROUP_GID_OFFSET
48+
)
4549
self.fd_config.parallel_config.tp_group = dist.get_group(
4650
fd_config.parallel_config.data_parallel_rank + envs.FD_TP_GROUP_GID_OFFSET
4751
)
52+
self.fd_config.parallel_config.ep_group = dist.get_group(
53+
fd_config.parallel_config.data_parallel_size + envs.FD_TP_GROUP_GID_OFFSET
54+
)
4855
self.parallel_config = self.fd_config.parallel_config
4956
self.model_config = self.fd_config.model_config
5057
self.speculative_config = self.fd_config.speculative_config

fastdeploy/spec_decode/mtp.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ def _update_mtp_config(self, main_model):
9696
"""
9797
Update config for MTP from global config
9898
"""
99-
self.model_config.architectures[0] = "Ernie4_5_MTPForCausalLM"
99+
self.forward_meta: ForwardMeta = None
100+
self.model_config.architectures[0] = self.model_config.architectures[0].replace("Moe", "MTP")
100101
self.speculative_config.sharing_model = main_model
101102
self.model_config.num_hidden_layers = 1
102103
self.model_config.model = self.speculative_config.model
@@ -169,6 +170,9 @@ def initialize_kv_cache(self, main_model_num_blocks, profile: bool = False):
169170
kv_cache_shape = self.attn_backends[0].get_kv_cache_shape(
170171
max_num_blocks=self.num_gpu_blocks, kv_cache_quant_type=kv_cache_quant_type
171172
)
173+
if kv_cache_quant_type == "block_wise_fp8":
174+
kv_cache_scale_shape = [kv_cache_shape[0], kv_cache_shape[1], kv_cache_shape[2]]
175+
local_rank = self.local_rank % self.parallel_config.tensor_parallel_size
172176
if not profile and (
173177
self.cache_config.enable_prefix_caching or self.scheduler_config.splitwise_role != "mixed"
174178
):
@@ -178,8 +182,8 @@ def initialize_kv_cache(self, main_model_num_blocks, profile: bool = False):
178182
self.num_main_model_layers + self.model_config.num_hidden_layers,
179183
):
180184
key_cache = paddle.empty(shape=[], dtype=cache_type)
181-
key_cache_name = f"key_caches_{i}_rank{self.local_rank}.device{self.device_id}"
182-
val_cache_name = f"value_caches_{i}_rank{self.local_rank}.device{self.device_id}"
185+
key_cache_name = f"key_caches_{i}_rank{local_rank}.device{self.device_id}"
186+
val_cache_name = f"value_caches_{i}_rank{local_rank}.device{self.device_id}"
183187
key_cache = share_external_data(key_cache, key_cache_name, kv_cache_shape)
184188
cache_kvs_list.append(key_cache)
185189
value_cache = paddle.empty(shape=[], dtype=cache_type)
@@ -199,6 +203,17 @@ def initialize_kv_cache(self, main_model_num_blocks, profile: bool = False):
199203
fill_value=0,
200204
dtype=cache_type,
201205
)
206+
if kv_cache_quant_type == "block_wise_fp8":
207+
self.cache_kvs[f"key_cache_scales_{i}"] = paddle.full(
208+
shape=kv_cache_scale_shape,
209+
fill_value=0,
210+
dtype=paddle.get_default_dtype(),
211+
)
212+
self.cache_kvs[f"value_cache_scales_{i}"] = paddle.full(
213+
shape=kv_cache_scale_shape,
214+
fill_value=0,
215+
dtype=paddle.get_default_dtype(),
216+
)
202217
self.model_inputs["caches"] = list(self.cache_kvs.values())
203218
for value in self.cache_kvs.values():
204219
del value
@@ -430,11 +445,10 @@ def insert_tasks_v1(self, req_dicts: List[Request], num_running_requests: int):
430445
if "caches" not in self.model_inputs:
431446
self.initialize_kv_cache()
432447
req_len = len(req_dicts)
433-
# has_prefill_task = False
434-
# has_decode_task = False
448+
435449
for i in range(req_len):
436450
request = req_dicts[i]
437-
logger.info(f"{i}th request-{request.request_id}: {request}")
451+
logger.debug(f"{i}th request-{request.request_id}: {request}")
438452
idx = request.idx
439453
if request.task_type.value == RequestType.PREFILL.value: # prefill task
440454
prefill_start_index = request.prefill_start_index
@@ -688,7 +702,7 @@ def _post_process(self, sampled_token_ids):
688702
self.max_model_len,
689703
self.model_inputs["substep"],
690704
)
691-
if self.role == "prefill":
705+
if self.role == "prefill" and self.parallel_config.tensor_parallel_rank == 0:
692706
mtp_save_first_token(
693707
self.model_inputs["base_model_draft_tokens"],
694708
self.model_inputs["not_need_stop"],
@@ -820,11 +834,18 @@ def _propose(self, step_use_cudagraph: bool = False):
820834
)
821835

822836
if self.parallel_config.tensor_parallel_size > 1:
823-
paddle.distributed.broadcast(sampled_token_ids, 0)
837+
paddle.distributed.broadcast(
838+
sampled_token_ids,
839+
self.parallel_config.data_parallel_rank * self.parallel_config.tensor_parallel_size,
840+
group=self.parallel_config.tp_group,
841+
)
824842

825843
self._post_process(sampled_token_ids)
826844
if substep != self.num_model_steps - 1:
827845
self._get_self_hidden_states(hidden_states)
846+
else:
847+
if hasattr(self.model, "empty_input_forward"):
848+
self.model.empty_input_forward()
828849

829850
def _get_self_hidden_states(self, hidden_states):
830851
target_hidden_states = eagle_get_self_hidden_states(

0 commit comments

Comments
 (0)