[feat] update bucketed weights from distributed (#13824)

Co-authored-by: Stefan He <hebiaobuaa@gmail.com>
This commit is contained in:
ShawnY112358
2025-11-26 15:30:45 -08:00
committed by GitHub
co-authored by Stefan He
parent 082b54c689
commit 5155016b56
5 changed files with 124 additions and 15 deletions
+2
View File
@@ -466,6 +466,7 @@ class Engine(EngineBase):
shapes: list[list[int]], shapes: list[list[int]],
group_name: str = "weight_update_group", group_name: str = "weight_update_group",
flush_cache: bool = True, flush_cache: bool = True,
load_format: Optional[str] = None,
): ):
"""Update weights from distributed source.""" """Update weights from distributed source."""
obj = UpdateWeightsFromDistributedReqInput( obj = UpdateWeightsFromDistributedReqInput(
@@ -474,6 +475,7 @@ class Engine(EngineBase):
shapes=shapes, shapes=shapes,
group_name=group_name, group_name=group_name,
flush_cache=flush_cache, flush_cache=flush_cache,
load_format=load_format,
) )
return self.loop.run_until_complete( return self.loop.run_until_complete(
self.tokenizer_manager.update_weights_from_distributed(obj, None) self.tokenizer_manager.update_weights_from_distributed(obj, None)
+2
View File
@@ -1144,6 +1144,8 @@ class UpdateWeightsFromDistributedReqInput(BaseReq):
abort_all_requests: bool = False abort_all_requests: bool = False
# Optional: Update weight version along with weights # Optional: Update weight version along with weights
weight_version: Optional[str] = None weight_version: Optional[str] = None
# Optional format specification for loading
load_format: Optional[str] = None
@dataclass @dataclass
+5 -1
View File
@@ -153,7 +153,11 @@ class BaseTpWorker(ABC):
self, recv_req: UpdateWeightsFromDistributedReqInput self, recv_req: UpdateWeightsFromDistributedReqInput
): ):
success, message = self.model_runner.update_weights_from_distributed( success, message = self.model_runner.update_weights_from_distributed(
recv_req.names, recv_req.dtypes, recv_req.shapes, recv_req.group_name recv_req.names,
recv_req.dtypes,
recv_req.shapes,
recv_req.group_name,
recv_req.load_format,
) )
return success, message return success, message
@@ -1153,7 +1153,14 @@ class ModelRunner:
logger.error(message) logger.error(message)
return False, message return False, message
def update_weights_from_distributed(self, names, dtypes, shapes, group_name): def update_weights_from_distributed(
self,
names,
dtypes,
shapes,
group_name,
load_format: Optional[str] = None,
):
""" """
Update specific parameter in the model weights online Update specific parameter in the model weights online
through `_model_update_group` process group. through `_model_update_group` process group.
@@ -1169,6 +1176,10 @@ class ModelRunner:
"Please call `init_weights_update_group` first." "Please call `init_weights_update_group` first."
) )
if load_format == "flattened_bucket":
return self._update_bucketed_weights_from_distributed(
names, dtypes, shapes, group_name
)
try: try:
weights = [] weights = []
handles = [] handles = []
@@ -1201,6 +1212,37 @@ class ModelRunner:
logger.error(error_msg) logger.error(error_msg)
return False, error_msg return False, error_msg
def _update_bucketed_weights_from_distributed(
self, names, dtypes, shapes, group_name
):
try:
named_tensors = []
for name, dtype, shape in zip(names, dtypes, shapes):
target_dtype = (
dtype if isinstance(dtype, torch.dtype) else getattr(torch, dtype)
)
named_tensors.append(
(name, torch.empty(shape, dtype=target_dtype, device=self.device))
)
bucket = FlattenedTensorBucket(named_tensors=named_tensors)
flattened_tensor = bucket.get_flattened_tensor()
torch.distributed.broadcast(
flattened_tensor,
src=0,
group=self._model_update_group[group_name],
)
reconstructed_tensors = bucket.reconstruct_tensors()
self.model.load_weights(reconstructed_tensors)
return True, f"Succeeded to update parameter online."
except Exception as e:
error_msg = (
f"Failed to update parameter online: {e}. "
f"The full weights of the ModelRunner are partially updated. "
f"Please discard the whole weights."
)
logger.error(error_msg)
return False, error_msg
def update_weights_from_tensor( def update_weights_from_tensor(
self, self,
named_tensors: List[Tuple[str, Union[torch.Tensor, "LocalSerializedTensor"]]], named_tensors: List[Tuple[str, Union[torch.Tensor, "LocalSerializedTensor"]]],
@@ -28,6 +28,7 @@ from transformers import AutoModelForCausalLM
import sglang as sgl import sglang as sgl
from sglang.srt.utils import init_custom_process_group from sglang.srt.utils import init_custom_process_group
from sglang.srt.weight_sync.tensor_bucket import FlattenedTensorBucket
from sglang.test.test_utils import ( from sglang.test.test_utils import (
DEFAULT_MODEL_NAME_FOR_TEST, DEFAULT_MODEL_NAME_FOR_TEST,
DEFAULT_SMALL_MODEL_NAME_FOR_TEST, DEFAULT_SMALL_MODEL_NAME_FOR_TEST,
@@ -69,6 +70,7 @@ def init_process(
backend, backend,
checking_parameters, checking_parameters,
tie_word_embeddings, tie_word_embeddings,
load_format,
barrier, barrier,
pause_generation_mode, pause_generation_mode,
): ):
@@ -84,6 +86,7 @@ def init_process(
checking_parameters, checking_parameters,
tie_word_embeddings, tie_word_embeddings,
state_dict_key_to_shape, state_dict_key_to_shape,
load_format,
barrier, barrier,
) )
elif rank in [1, 2]: elif rank in [1, 2]:
@@ -98,6 +101,7 @@ def init_process(
state_dict_key_to_shape, state_dict_key_to_shape,
backend, backend,
tp_size, tp_size,
load_format,
barrier, barrier,
pause_generation_mode, pause_generation_mode,
) )
@@ -112,6 +116,7 @@ def init_process_hf(
checking_parameters, checking_parameters,
tie_word_embeddings, tie_word_embeddings,
state_dict_key_to_shape, state_dict_key_to_shape,
load_format,
barrier, barrier,
): ):
# These two environment variables are very important # These two environment variables are very important
@@ -179,14 +184,23 @@ def init_process_hf(
if tie_word_embeddings: if tie_word_embeddings:
broadcast_parameters.remove("lm_head.weight") broadcast_parameters.remove("lm_head.weight")
# Broadcast all the weights from the training if load_format == "flattened_bucket":
# engine to other ranks (inference engine). named_tensors = [
for parameter_name in broadcast_parameters: (parameter_name, hf_base_model.get_parameter(parameter_name))
torch.distributed.broadcast( for parameter_name in broadcast_parameters
hf_base_model.get_parameter(parameter_name), ]
src=0, bucket = FlattenedTensorBucket(named_tensors=named_tensors)
group=group, flattened_tensor = bucket.get_flattened_tensor()
) torch.distributed.broadcast(flattened_tensor, src=0, group=group)
else:
# Broadcast all the weights from the training
# engine to other ranks (inference engine).
for parameter_name in broadcast_parameters:
torch.distributed.broadcast(
hf_base_model.get_parameter(parameter_name),
src=0,
group=group,
)
torch.cuda.synchronize() torch.cuda.synchronize()
time_end_broadcast = time.perf_counter() time_end_broadcast = time.perf_counter()
@@ -216,6 +230,7 @@ def init_process_sgl(
state_dict_key_to_shape, state_dict_key_to_shape,
backend, backend,
tp_size, tp_size,
load_format,
barrier, barrier,
pause_generation_mode, pause_generation_mode,
): ):
@@ -341,6 +356,7 @@ def init_process_sgl(
dtypes=dtypes, dtypes=dtypes,
shapes=shapes, shapes=shapes,
group_name="test_parameter_update_group", group_name="test_parameter_update_group",
load_format=load_format,
) )
else: else:
requests.post( requests.post(
@@ -350,6 +366,7 @@ def init_process_sgl(
"dtypes": dtypes, "dtypes": dtypes,
"shapes": shapes, "shapes": shapes,
"group_name": "test_parameter_update_group", "group_name": "test_parameter_update_group",
"load_format": load_format,
"flush_cache": not (pause_generation_mode == "in_place"), "flush_cache": not (pause_generation_mode == "in_place"),
}, },
) )
@@ -431,6 +448,7 @@ def test_update_weights_from_distributed(
state_dict_key_to_shape, state_dict_key_to_shape,
truncate_size, truncate_size,
checking_parameters, checking_parameters,
load_format=None,
pause_generation_mode=None, pause_generation_mode=None,
): ):
tie_word_embeddings = ( tie_word_embeddings = (
@@ -456,6 +474,7 @@ def test_update_weights_from_distributed(
backend, backend,
checking_parameters, checking_parameters,
tie_word_embeddings, tie_word_embeddings,
load_format,
barrier, barrier,
pause_generation_mode, pause_generation_mode,
), ),
@@ -614,31 +633,55 @@ class TestUpdateWeightsFromDistributed(CustomTestCase):
pause_generation_mode = random.choice(["in_place", "retract"]) pause_generation_mode = random.choice(["in_place", "retract"])
else: else:
pause_generation_mode = None pause_generation_mode = None
load_format = random.choice(["flattened_bucket", None])
test_suits = [ test_suits = [
(1, 1, DEFAULT_SMALL_MODEL_NAME_FOR_TEST, mode, pause_generation_mode), (
1,
1,
DEFAULT_SMALL_MODEL_NAME_FOR_TEST,
mode,
pause_generation_mode,
load_format,
),
] ]
else: else:
test_suits = [ test_suits = [
(1, 1, DEFAULT_SMALL_MODEL_NAME_FOR_TEST, "Engine", None), (
1,
1,
DEFAULT_SMALL_MODEL_NAME_FOR_TEST,
"Engine",
None,
random.choice(["flattened_bucket", None]),
),
( (
1, 1,
1, 1,
DEFAULT_MODEL_NAME_FOR_TEST, DEFAULT_MODEL_NAME_FOR_TEST,
"Sever", "Sever",
random.choice(["in_place", "retract"]), random.choice(["in_place", "retract"]),
random.choice(["flattened_bucket", None]),
), ),
] ]
if torch.cuda.device_count() >= 4: if torch.cuda.device_count() >= 4:
test_suits.extend( test_suits.extend(
[ [
(2, 1, DEFAULT_SMALL_MODEL_NAME_FOR_TEST, "Engine", None), (
2,
1,
DEFAULT_SMALL_MODEL_NAME_FOR_TEST,
"Engine",
None,
random.choice(["flattened_bucket", None]),
),
( (
1, 1,
2, 2,
DEFAULT_MODEL_NAME_FOR_TEST, DEFAULT_MODEL_NAME_FOR_TEST,
"Server", "Server",
random.choice(["in_place", "retract"]), random.choice(["in_place", "retract"]),
random.choice(["flattened_bucket", None]),
), ),
] ]
) )
@@ -646,13 +689,21 @@ class TestUpdateWeightsFromDistributed(CustomTestCase):
if torch.cuda.device_count() >= 5: if torch.cuda.device_count() >= 5:
test_suits.extend( test_suits.extend(
[ [
(2, 2, DEFAULT_SMALL_MODEL_NAME_FOR_TEST, "Engine", None), (
2,
2,
DEFAULT_SMALL_MODEL_NAME_FOR_TEST,
"Engine",
None,
random.choice(["flattened_bucket", None]),
),
( (
2, 2,
2, 2,
DEFAULT_MODEL_NAME_FOR_TEST, DEFAULT_MODEL_NAME_FOR_TEST,
"Server", "Server",
random.choice(["in_place", "retract"]), random.choice(["in_place", "retract"]),
random.choice(["flattened_bucket", None]),
), ),
] ]
) )
@@ -689,7 +740,14 @@ class TestUpdateWeightsFromDistributed(CustomTestCase):
"lm_head.weight", "lm_head.weight",
] ]
for tp_size, dp_size, model_name, backend, pause_generation_mode in test_suits: for (
tp_size,
dp_size,
model_name,
backend,
pause_generation_mode,
load_format,
) in test_suits:
test_update_weights_from_distributed( test_update_weights_from_distributed(
tp_size, tp_size,
dp_size, dp_size,
@@ -698,6 +756,7 @@ class TestUpdateWeightsFromDistributed(CustomTestCase):
model_state_dict_shapes[model_name], model_state_dict_shapes[model_name],
truncate_size, truncate_size,
checking_parameters, checking_parameters,
load_format,
pause_generation_mode, pause_generation_mode,
) )