225 lines
9.1 KiB
Python
225 lines
9.1 KiB
Python
# Copyright (c) ModelScope Contributors. All rights reserved.
|
|
import concurrent.futures
|
|
import importlib.metadata
|
|
import logging
|
|
import os
|
|
import torch
|
|
import torch.distributed as dist
|
|
from contextlib import contextmanager
|
|
from copy import copy, deepcopy
|
|
from packaging import version
|
|
from tqdm import tqdm
|
|
from transformers.modeling_utils import custom_object_save
|
|
from transformers.utils import is_torch_npu_available
|
|
from transformers.utils.versions import require_version
|
|
|
|
from swift.model import get_model_processor, save_checkpoint
|
|
from swift.utils import (HfConfigFactory, disable_safe_ddp_context_use_barrier, get_logger, get_modules_to_not_convert,
|
|
get_multimodal_target_regex, is_master, split_list)
|
|
|
|
logger = get_logger()
|
|
|
|
|
|
def _patch__batched_p2p_ops():
|
|
from megatron.core.pipeline_parallel import p2p_communication
|
|
|
|
_batched_p2p_ops_origin = p2p_communication._batched_p2p_ops
|
|
|
|
def _batched_p2p_ops(**kwargs):
|
|
kwargs['group'] = None
|
|
return _batched_p2p_ops_origin(**kwargs)
|
|
|
|
p2p_communication._batched_p2p_ops = _batched_p2p_ops
|
|
|
|
|
|
def _patch_torch_FileSystemReader():
|
|
from torch.distributed.checkpoint.filesystem import FileSystemReader
|
|
from torch.futures import Future
|
|
_origin_read_data = FileSystemReader.read_data
|
|
_origin__slice_file = FileSystemReader._slice_file
|
|
READER_MAX_WORKERS = int(os.environ.get('MCORE_READER_MAX_WORKERS', '16'))
|
|
|
|
@contextmanager
|
|
def _patch__slice_file(prog_bar):
|
|
|
|
def _slice_file(self, *args, **kwargs):
|
|
prog_bar.update()
|
|
return _origin__slice_file(self, *args, **kwargs)
|
|
|
|
FileSystemReader._slice_file = _slice_file
|
|
try:
|
|
yield
|
|
finally:
|
|
FileSystemReader._slice_file = _origin__slice_file
|
|
|
|
def read_data(self, plan, planner):
|
|
|
|
def _worker(plan_shard):
|
|
_origin_read_data(self, plan_shard, planner)
|
|
|
|
prog_bar = tqdm(total=len(plan.items), dynamic_ncols=True, desc='Loading: ')
|
|
plan_shards = split_list(plan.items, READER_MAX_WORKERS, contiguous=False)
|
|
with _patch__slice_file(prog_bar):
|
|
with concurrent.futures.ThreadPoolExecutor(max_workers=READER_MAX_WORKERS) as pool:
|
|
futures = []
|
|
for i in range(READER_MAX_WORKERS):
|
|
plan_shard = copy(plan)
|
|
plan_shard.items = plan_shards[i]
|
|
futures.append(pool.submit(_worker, plan_shard))
|
|
concurrent.futures.wait(futures)
|
|
prog_bar.close()
|
|
fut: Future = Future()
|
|
fut.set_result(None)
|
|
return fut
|
|
|
|
FileSystemReader.read_data = read_data
|
|
|
|
|
|
def _patch_validate_non_overlapping_shards_metadata():
|
|
# too slow
|
|
from torch.distributed._shard.sharded_tensor import api
|
|
from torch.distributed._shard.sharding_spec import api as api2
|
|
from torch.distributed.checkpoint import default_planner
|
|
|
|
def validate_non_overlapping_shards_metadata(*args, **kwargs):
|
|
pass
|
|
|
|
api.validate_non_overlapping_shards_metadata = validate_non_overlapping_shards_metadata
|
|
api2.validate_non_overlapping_shards_metadata = validate_non_overlapping_shards_metadata
|
|
|
|
def _validate_global_plan(*args, **kwargs):
|
|
return True
|
|
|
|
default_planner._validate_global_plan = _validate_global_plan
|
|
|
|
|
|
def _patch_unified_memory():
|
|
if is_torch_npu_available():
|
|
return
|
|
|
|
from torch.utils import cpp_extension
|
|
load_inline = cpp_extension.load_inline
|
|
|
|
def _new_load_inline(*args, **kwargs):
|
|
name = kwargs.get('name')
|
|
if name == 'managed_alloc_runtime':
|
|
raise RuntimeError
|
|
return load_inline(*args, **kwargs)
|
|
|
|
# not create unified memory mempool
|
|
cpp_extension.load_inline = _new_load_inline
|
|
try:
|
|
from megatron.core.inference import unified_memory
|
|
except Exception:
|
|
pass
|
|
finally:
|
|
cpp_extension.load_inline = load_inline
|
|
|
|
|
|
def _patch_mcore_bridge():
|
|
require_version('mcore-bridge>=1.4.0', 'please install mcore-bridge via `pip install mcore-bridge -U`')
|
|
import mcore_bridge
|
|
from mcore_bridge import GPTBridge
|
|
logger.info(f'mcore_bridge.__version__: {mcore_bridge.__version__}')
|
|
origin_save_weights = GPTBridge.save_weights
|
|
|
|
def save_weights(
|
|
self,
|
|
mg_models,
|
|
output_dir: str,
|
|
peft_format: bool = False,
|
|
max_shard_size: str = '5GB',
|
|
args=None,
|
|
processor=None,
|
|
) -> None:
|
|
origin_save_weights(self, mg_models, output_dir, peft_format=peft_format, max_shard_size=max_shard_size)
|
|
if processor is None or args is None:
|
|
return
|
|
hf_config = self.config.hf_config
|
|
hf_config = deepcopy(hf_config)
|
|
if is_master() and not hasattr(self, 'hf_model'):
|
|
if hasattr(self, 'get_hf_meta_model'):
|
|
self.hf_model = self.get_hf_meta_model()
|
|
self.hf_model.model_meta = processor.model_meta
|
|
self.hf_model.model_info = processor.model_info
|
|
else:
|
|
with torch.device('meta'), disable_safe_ddp_context_use_barrier():
|
|
self.hf_model = get_model_processor(
|
|
args.model_dir, model_type=args.model_type, return_dummy_model=True)[0]
|
|
|
|
if is_master():
|
|
if peft_format:
|
|
peft_config = copy(mg_models[0].peft_config[self._adapter_name])
|
|
if self.config.task_type == 'seq_cls':
|
|
peft_config.task_type = 'SEQ_CLS'
|
|
if self.is_multimodal and 'all-linear' in args.target_modules:
|
|
peft_config.target_modules = get_multimodal_target_regex(
|
|
self.hf_model,
|
|
freeze_llm=args.freeze_llm,
|
|
freeze_vit=args.freeze_vit,
|
|
freeze_aligner=args.freeze_aligner,
|
|
include_embedding='all-embedding' in args.target_modules,
|
|
exclude_router='all-router' not in args.target_modules)
|
|
else:
|
|
assert not isinstance(peft_config.target_modules, str), (
|
|
'target_regex is not currently supported for LoRA conversion. Please set `--merge_lora true`.')
|
|
peft_config.target_modules = self._peft_target_modules
|
|
peft_config.modules_to_save = self._peft_modules_to_save
|
|
peft_config.save_pretrained(output_dir)
|
|
else:
|
|
config = self.config
|
|
llm_config = HfConfigFactory.get_text_config(hf_config)
|
|
if config.mtp_num_layers:
|
|
for key in ['num_nextn_predict_layers', 'mtp_num_hidden_layers']:
|
|
if hasattr(llm_config, key):
|
|
setattr(llm_config, key, config.mtp_num_layers)
|
|
break
|
|
else:
|
|
llm_config.num_nextn_predict_layers = config.mtp_num_layers
|
|
HfConfigFactory.del_config_attr(hf_config, 'quantization_config')
|
|
expert_dtype = None
|
|
if config.fp8 is not None and config.fp8_recipe == 'blockwise' and config.fp8_param:
|
|
from transformers.utils.quantization_config import FineGrainedFP8Config
|
|
modules_to_not_convert = get_modules_to_not_convert(self.hf_model)
|
|
if hasattr(self, '_fp8_skip_modules'):
|
|
modules_to_not_convert = (modules_to_not_convert or []) + list(self._fp8_skip_modules)
|
|
hf_config.quantization_config = FineGrainedFP8Config(modules_to_not_convert=modules_to_not_convert)
|
|
expert_dtype = 'fp8'
|
|
if args.model_type == 'deepseek_v4':
|
|
HfConfigFactory.set_config_attr(hf_config, 'expert_dtype', expert_dtype)
|
|
hf_config.save_pretrained(output_dir)
|
|
if getattr(self.hf_model, '_auto_class') is not None:
|
|
try:
|
|
custom_object_save(self.hf_model, output_dir, config=hf_config)
|
|
except FileNotFoundError as e:
|
|
logger.error(f'custom_object_save Error: {e}')
|
|
save_checkpoint(
|
|
None,
|
|
processor,
|
|
output_dir,
|
|
model_dirs=[args.model_dir],
|
|
additional_saved_files=self.hf_model.model_meta.additional_saved_files)
|
|
logger.info(f'Successfully saved `safetensors` model weights in `{output_dir}`.')
|
|
dist.barrier() # Ensure all weights are saved completely
|
|
|
|
GPTBridge.save_weights = save_weights
|
|
|
|
|
|
def init_megatron_env():
|
|
os.environ.pop('VLLM_USE_MODELSCOPE', None)
|
|
logging_level = logging.root.level
|
|
_patch_unified_memory()
|
|
_patch__batched_p2p_ops()
|
|
logging.root.setLevel(logging_level) # revert logger level
|
|
try:
|
|
_patch_torch_FileSystemReader()
|
|
except Exception:
|
|
logger.warning('Failed to patch FileSystemReader.')
|
|
try:
|
|
_patch_validate_non_overlapping_shards_metadata()
|
|
except Exception:
|
|
logger.warning('Patch validate_non_overlapping_shards_metadata failed.')
|
|
pass
|
|
import megatron.core
|
|
logger.info(f'megatron.core.__version__: {megatron.core.__version__}')
|