chore: import upstream snapshot with attribution
Lint test / lint (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:34:58 +08:00
commit a203934033
1368 changed files with 175001 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
from .app_args import AppArguments
from .base_args import BaseArguments, DataArguments, ModelArguments, TemplateArguments, get_supported_tuners
from .deploy_args import DeployArguments, RolloutArguments
from .eval_args import EvalArguments
from .export_args import ExportArguments
from .infer_args import InferArguments
from .pretrain_args import PretrainArguments
from .rlhf_args import RLHFArguments
from .sampling_args import SamplingArguments
from .sft_args import SftArguments
from .tuner_args import TunerArguments
from .webui_args import WebUIArguments
+57
View File
@@ -0,0 +1,57 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
from dataclasses import dataclass
from typing import Literal, Optional
from swift.model import get_matched_model_meta
from swift.template import get_template_meta
from swift.utils import find_free_port, get_logger
from .deploy_args import DeployArguments
from .webui_args import WebUIArguments
logger = get_logger()
@dataclass
class AppArguments(WebUIArguments, DeployArguments):
"""Arguments for configuring the Web UI inference.
This dataclass inherits from WebUIArguments and DeployArguments, combining their settings to configure the user
interface for model inference.
Args:
base_url (Optional[str]): The base URL for the model deployment API, e.g., `http://localhost:8000/v1`. If set
to `None`, a local deployment will be used instead. Defaults to None.
studio_title (Optional[str]): The title for the Web UI studio. If set to `None`, the title will default to the
model's name. Defaults to None.
is_multimodal (Optional[bool]): Whether to launch the multimodal version of the application. If `None`, the
app will attempt to auto-detect this setting based on the model. If auto-detection is not possible, it
defaults to `False`. Defaults to None.
lang (str): Overrides the language setting for the Web UI. Defaults to 'en'.
verbose (bool): Whether to log detailed request information. Defaults to False.
stream (bool): Whether to enable streaming output for model responses. Defaults to True.
"""
base_url: Optional[str] = None
studio_title: Optional[str] = None
is_multimodal: Optional[bool] = None
lang: Literal['en', 'zh'] = 'en'
verbose: bool = False
stream: bool = True
def _init_torch_dtype(self) -> None:
if self.base_url:
self.model_meta = get_matched_model_meta(self.model)
self.model_info = None
return
super()._init_torch_dtype()
def __post_init__(self):
DeployArguments.__post_init__(self)
self.server_port = find_free_port(self.server_port)
if self.model_meta:
if self.system is None:
self.system = get_template_meta(self.model_info, self.model_meta).default_system
if self.is_multimodal is None:
self.is_multimodal = self.model_meta.is_multimodal
if self.is_multimodal is None:
self.is_multimodal = False
+7
View File
@@ -0,0 +1,7 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
from .base_args import BaseArguments, get_supported_tuners
from .data_args import DataArguments
from .generation_args import GenerationArguments
from .model_args import ModelArguments
from .quant_args import QuantizeArguments
from .template_args import TemplateArguments
+367
View File
@@ -0,0 +1,367 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import json
import os
import peft
import shutil
from dataclasses import dataclass, field, fields
from packaging import version
from typing import Any, Dict, List, Literal, Optional, Union
import swift
from swift.dataset import load_dataset
from swift.hub import get_hub
from swift.model import get_ckpt_dir, get_model_processor, load_by_unsloth
from swift.ray_utils import RayArguments
from swift.template import Template, get_template
from swift.tuner_plugin import tuners_map
from swift.utils import (Processor, check_json_format, get_dist_setting, get_logger, import_external_file, is_dist,
is_master, json_parse_to_dict, safe_snapshot_download, set_device, use_hf_hub)
from .data_args import DataArguments
from .generation_args import GenerationArguments
from .model_args import ModelArguments
from .quant_args import QuantizeArguments
from .template_args import TemplateArguments
logger = get_logger()
def get_supported_tuners():
return {'lora', 'full', 'longlora', 'adalora', 'llamapro', 'adapter', 'vera', 'boft', 'fourierft', 'reft', 'bone'
} | set(tuners_map.keys())
def _patch_peft():
"""Patch peft functions that are incompatible with SWIFT.
1. _maybe_shard_state_dict_for_tp: TP sharding is not used by SWIFT, and causes errors
when torch.distributed is initialized (e.g. MoE training with target_parameters).
2. _maybe_shard_state_dict_for_tp internal logic accesses base_layer.weight.device which
fails for expert modules that don't have a `weight` attribute.
"""
if version.parse(peft.__version__) >= version.parse('0.19.0'):
from peft.utils import save_and_load
save_and_load._maybe_shard_state_dict_for_tp = lambda model, state_dict, adapter_name: None
@dataclass
class BaseArguments(GenerationArguments, QuantizeArguments, DataArguments, TemplateArguments, ModelArguments,
RayArguments):
"""BaseArguments class is a dataclass that inherits from multiple argument classes.
This class consolidates arguments from GenerationArguments, QuantizeArguments, DataArguments,
TemplateArguments, ModelArguments, RayArguments.
Args:
tuner_backend (str): The tuner backend to use. Choices are 'peft' or 'unsloth'. Default is 'peft'.
tuner_type (str): The tuner type. Choices include 'lora', 'full', 'longlora', 'adalora', 'llamapro',
'adapter', 'vera', 'boft', 'fourierft', 'reft'. Default is 'lora'.
adapters (List[str]): A list of adapter IDs or paths. This is typically used for inference or deployment.
It can also resume training by only loading adapter weights, differing from `resume_from_checkpoint`
which also loads optimizer states. Default is [].
external_plugins (List[str]): A list of external 'plugin.py' files to be registered and imported into
the plugin module. Default is [].
seed (int): The global random seed for reproducibility. Note that this does not affect `data_seed`,
which controls dataset randomization. Default is 42.
model_kwargs (Optional[str]): Additional keyword arguments for specific models, passed as a JSON string
(e.g., '{"key": "value"}'). It's recommended to use the same arguments for inference as for training.
Default is None.
enable_npu_model_patch (bool): Whether to enable model-related NPU patches. Default is True.
load_args (bool): Whether to load `args.json` from a checkpoint when using `--resume_from_checkpoint`,
`--model`, or `--adapters`. Defaults to True for inference/export and False for training. Usually,
this does not need to be modified. Default is True.
load_data_args (bool): If True, will also load data-related arguments from `args.json`. This is useful
for running inference on the same validation split used during training. Default is False.
packing (bool): Whether to enable packing of datasets. Default is False.
packing_length (Optional[int]): Length of packing. Default is None.
packing_num_proc (int): Number of processes used for packing, Default is 1.
packing_strategy (Literal['binpack', 'sequential']): Packing algorithm. 'binpack' (default) uses
best-fit-decreasing bin packing (reorders samples); 'sequential' uses order-preserving greedy
packing (next-fit: a single open pack, flushed when the next sample doesn't fit) so the sample
order / pack boundaries follow a sequential sampler (use packing_num_proc=1). Default is 'binpack'.
lazy_tokenize (Optional[bool]): Whether to enable lazy tokenization. Default is None.
use_hf (bool): Whether to use Hugging Face for downloading/uploading models and datasets. If False,
ModelScope is used. Default is False.
hub_token (Optional[str]): The authentication token for ModelScope or Hugging Face Hub. Default is None.
ddp_timeout (int): Timeout for DDP (Distributed Data Parallel) operations, in seconds. Default is 18000000.
ddp_backend (Optional[str]): The backend for DDP. Choices include "nccl", "gloo", "mpi", "ccl", "hccl",
"cncl", "mccl". If None, it will be automatically selected. Default is None.
ignore_args_error (bool): Whether to ignore argument errors. This is useful for compatibility with Jupyter
notebooks. Default is False.
use_swift_lora (bool): Whether to use swift lora. This is a compatible argument. Default is False.
"""
tuner_backend: Literal['peft', 'unsloth'] = 'peft'
tuner_type: str = field(default='lora', metadata={'help': f'tuner_type choices: {list(get_supported_tuners())}'})
adapters: List[str] = field(default_factory=list)
external_plugins: List[str] = field(default_factory=list)
# This parameter is kept for swift3.x compatibility. Please use `external_plugins` as a replacement.
custom_register_path: List[str] = field(default_factory=list)
seed: int = 42
model_kwargs: Optional[Union[dict, str]] = None
enable_npu_model_patch: bool = True
load_args: bool = True
load_data_args: bool = False
# dataset
packing: bool = False
packing_length: Optional[int] = None
packing_num_proc: int = 1
packing_strategy: Literal['binpack', 'sequential'] = 'binpack'
lazy_tokenize: Optional[bool] = None
# hub
use_hf: bool = False
# None: use env var `MODELSCOPE_API_TOKEN`
hub_token: Optional[str] = field(
default=None, metadata={'help': 'SDK token can be found in https://modelscope.cn/my/myaccesstoken'})
# dist
ddp_timeout: int = 18000000
ddp_backend: Optional[str] = None
# extra
ignore_args_error: bool = False # True: notebook compatibility
use_swift_lora: bool = False # True for using tuner_backend == swift, don't specify this unless you know what you are doing # noqa
def _prepare_training_args(self, training_args: Dict[str, Any]) -> None:
pass
def _init_lazy_tokenize(self):
if self.lazy_tokenize is None:
if self.cached_dataset or self.cached_val_dataset:
self.lazy_tokenize = False
elif (self.model_meta is not None and self.model_meta.is_multimodal and not self.streaming
and not self.packing and not getattr(self, 'group_by_length', False)):
self.lazy_tokenize = True
else:
self.lazy_tokenize = False
logger.info(f'Setting args.lazy_tokenize: {self.lazy_tokenize}')
if self.lazy_tokenize:
if self.packing:
raise ValueError('Packing and lazy_tokenize are incompatible.')
if self.streaming:
raise ValueError('Streaming and lazy_tokenize are incompatible.')
def _import_external_plugins(self):
if isinstance(self.external_plugins, str):
self.external_plugins = [self.external_plugins]
# swift v3.x compatibility
if isinstance(self.custom_register_path, str):
self.custom_register_path = [self.custom_register_path]
if self.custom_register_path:
self.external_plugins += self.custom_register_path
if not self.external_plugins:
return
for external_plugin in self.external_plugins:
import_external_file(external_plugin)
logger.info(f'Successfully imported external_plugins: {self.external_plugins}.')
@staticmethod
def _check_is_adapter(adapter_dir: str) -> bool:
if (os.path.exists(os.path.join(adapter_dir, 'adapter_config.json'))
or os.path.exists(os.path.join(adapter_dir, 'default', 'adapter_config.json'))
or os.path.exists(os.path.join(adapter_dir, 'reft'))):
return True
return False
def _init_adapters(self):
if isinstance(self.adapters, str):
self.adapters = [self.adapters]
self.adapters = [
safe_snapshot_download(adapter, use_hf=self.use_hf, hub_token=self.hub_token) for adapter in self.adapters
]
def __post_init__(self):
_patch_peft()
self.swift_version = swift.__version__
if self.use_hf or use_hf_hub():
self.use_hf = True
os.environ['USE_HF'] = '1'
self._init_adapters()
self._init_ckpt_dir()
self._import_external_plugins()
self._init_model_kwargs()
# The Seq2SeqTrainingArguments has a property called world_size, which cannot be assigned a value.
self.rank, self.local_rank, self.global_world_size, self.local_world_size = get_dist_setting()
logger.info(f'rank: {self.rank}, local_rank: {self.local_rank}, '
f'world_size: {self.global_world_size}, local_world_size: {self.local_world_size}')
if self.tuner_type not in tuners_map: # build-in tuner
for adapter in self.adapters:
assert self._check_is_adapter(adapter), (
f'`{adapter}` is not an adapter, please try using `--model` to pass it.')
ModelArguments.__post_init__(self)
QuantizeArguments.__post_init__(self)
TemplateArguments.__post_init__(self)
DataArguments.__post_init__(self)
RayArguments.__post_init__(self)
self._init_stream()
if self.max_length is None and self.model_info is not None:
self.max_length = self.model_info.max_model_len
if self.packing and self.packing_length is None:
self.packing_length = self.max_length
self._init_lazy_tokenize()
self.hub = get_hub(self.use_hf)
if self.hub.try_login(self.hub_token):
logger.info('hub login successful!')
def _init_model_kwargs(self):
"""Prepare model kwargs and set them to the env"""
self.model_kwargs: Dict[str, Any] = json_parse_to_dict(self.model_kwargs)
for k, v in self.model_kwargs.items():
k = k.upper()
os.environ[k] = str(v)
@property
def is_adapter(self) -> bool:
return self.tuner_type not in {'full'}
@property
def supported_tuners(self):
return get_supported_tuners()
@property
def adapters_can_be_merged(self):
return {'lora', 'longlora', 'llamapro', 'adalora'}
@classmethod
def from_pretrained(cls, checkpoint_dir: str):
self = super().__new__(cls)
self.load_data_args = True
self.ckpt_dir = checkpoint_dir
self.load_args_from_ckpt()
all_keys = list(f.name for f in fields(BaseArguments))
for key in all_keys:
if not hasattr(self, key):
setattr(self, key, None)
return self
def _init_ckpt_dir(self, adapters=None):
# compat megatron
model = self.model or getattr(self, 'mcore_model', None)
adapters = adapters or self.adapters or getattr(self, 'mcore_adapter', None)
if isinstance(adapters, str):
adapters = [adapters]
self.ckpt_dir = get_ckpt_dir(model, adapters)
if self.ckpt_dir and self.load_args:
self.load_args_from_ckpt()
def load_args_from_ckpt(self) -> None:
args_path = os.path.join(self.ckpt_dir, 'args.json')
assert os.path.exists(args_path), f'args_path: {args_path}'
with open(args_path, 'r', encoding='utf-8') as f:
old_args = json.load(f)
force_load_keys = [
# base_args
'tuner_type',
# model_args
'task_type',
# quant_args
'bnb_4bit_quant_type',
'bnb_4bit_use_double_quant',
]
# If the current value is None or an empty list and it is among the following keys
load_keys = [
'external_plugins',
# model_args
'model',
'model_type',
'model_revision',
'torch_dtype',
'attn_impl',
'experts_impl',
'new_special_tokens',
'num_labels',
'problem_type',
'rope_scaling',
'max_model_len',
# quant_args
'quant_method',
'quant_bits',
'hqq_axis',
'bnb_4bit_compute_dtype',
# template_args
'template',
'system',
'truncation_strategy',
'agent_template',
'norm_bbox',
'use_chat_template',
'response_prefix',
]
data_keys = list(f.name for f in fields(DataArguments))
swift_version = old_args.get('swift_version')
if swift_version is None or version.parse(swift_version) < version.parse('4.0.0.dev'):
load_keys.remove('model_type')
for key, old_value in old_args.items():
if old_value is None:
continue
if key in force_load_keys or self.load_data_args and key in data_keys:
setattr(self, key, old_value)
value = getattr(self, key, None)
if key in load_keys and (value is None or isinstance(value, (list, tuple)) and len(value) == 0):
setattr(self, key, old_value)
logger.info(f'Successfully loaded {args_path}.')
def save_args(self, output_dir=None) -> None:
if is_master():
output_dir = output_dir or self.output_dir
os.makedirs(output_dir, exist_ok=True)
fpath = os.path.join(output_dir, 'args.json')
logger.info(f'The {self.__class__.__name__} will be saved in: {fpath}')
with open(fpath, 'w', encoding='utf-8') as f:
json.dump(check_json_format(self.__dict__), f, ensure_ascii=False, indent=2)
config_file = os.getenv('SWIFT_CONFIG_FILE')
if config_file:
shutil.copy(config_file, output_dir)
def _init_device(self):
if is_dist():
set_device()
def get_template(self, processor: Optional[Processor] = None, **kwargs) -> Template:
if processor is None:
processor = self.get_model_processor(load_model=False)[1]
template_kwargs = self.get_template_kwargs()
if 'template_type' in kwargs:
template_type = kwargs.get('template_type')
else:
template_type = self.template
template_kwargs['template_type'] = template_type
template = get_template(processor, **template_kwargs)
return template
def get_model_processor(self,
*,
model=None,
model_type=None,
revision=None,
task_type=None,
num_labels=None,
**kwargs):
if self.tuner_backend == 'unsloth':
return load_by_unsloth(self)
res = self.get_model_kwargs()
res.update(kwargs)
# compat rlhf
res['model_id_or_path'] = model or self.model
res['model_type'] = model_type or self.model_type
res['revision'] = revision or self.model_revision
res['task_type'] = task_type or self.task_type
res['num_labels'] = num_labels or self.num_labels
return get_model_processor(**res)
def load_dataset(self):
dataset_kwargs = self.get_dataset_kwargs()
train_dataset, val_dataset = None, None
if self.dataset:
train_dataset, val_dataset = load_dataset(
self.dataset,
split_dataset_ratio=self.split_dataset_ratio,
shuffle=self.dataset_shuffle,
**dataset_kwargs)
if len(self.val_dataset) > 0:
# Loading val dataset
dataset_kwargs.pop('interleave_prob', None)
_, val_dataset = load_dataset(
self.val_dataset, split_dataset_ratio=1.0, shuffle=self.val_dataset_shuffle, **dataset_kwargs)
assert self.split_dataset_ratio == 0.
return train_dataset, val_dataset
+145
View File
@@ -0,0 +1,145 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
from dataclasses import dataclass, field
from typing import List, Literal, Optional, Union
from swift.dataset import register_dataset_info
from swift.utils import get_logger, json_parse_to_dict
logger = get_logger()
@dataclass
class DataArguments:
"""Holds arguments related to dataset handling and processing.
Args:
dataset (List[str]): A list of dataset IDs or paths. Defaults to [].
Format for each dataset: 'dataset_id_or_path:subset#count'. Both subset and count are optional.
- Subsets: Only effective for dataset IDs or folders. Use '/' to select multiple subsets (e.g.,
'dataset_id:subset1/subset2') or 'all' to select all registered subsets. If only one subset is
registered, it will be used by default; otherwise, 'default' is the default.
- Sampling Count: By default, the full dataset is used. Use '#count' to sample. If count <
total samples, it performs random sampling without replacement. If count > total, it repeats
the full dataset `count // total` times and then randomly samples an additional `count % total`
samples. Note: Streaming datasets or setting `--dataset_shuffle false` will result in sequential
sampling.
- Local datasets: Supports formats like jsonl, csv, json, and folders.
val_dataset (List[str]): A list of validation dataset IDs or paths. Defaults to [].
cached_dataset (List[str]): Use cached datasets to avoid GPU time being occupied by tokenization during
training/inference on large datasets. This parameter is used to set the folder path(s) of
cached training datasets, and defaults to `[]`.
This is generated by the `swift export --to_cached_dataset true ...` command.
ms-swift only stores an extra 'length' field and filters out erroneous samples
to reduce storage. Actual preprocessing happens concurrently with training.
cached_val_dataset (List[str]): Folder path(s) for cached validation datasets, default is [].
split_dataset_ratio (float): The ratio to split from the training set for validation if `val_dataset` is not
provided. Defaults to 0.0. Note: The default was 0.01 in `ms-swift<3.6`.
data_seed (int): The random seed for dataset shuffling. Defaults to 42.
dataset_num_proc (int): The number of processes to use for dataset preprocessing. Defaults to 1.
load_from_cache_file (bool): Whether to load the dataset from cache files. Recommended to set to `True` during
actual runs and `False` during debugging. Defaults to False.
Note: The default was `True` in `ms-swift<3.9`.
dataset_shuffle (bool): Whether to shuffle the training dataset. Defaults to True.
Note: For CPT/SFT, shuffling occurs at both the dataset level (controlled by this flag) and the dataloader
level.
val_dataset_shuffle (bool): Whether to shuffle the validation dataset. Defaults to False.
streaming (bool): Enables streaming to read and process the dataset on-the-fly. `--max_steps` must be set as the
dataset length is unknown. This allows preprocessing to overlap with training but can become a bottleneck
with a large `world_size` as preprocessing only runs on rank 0. Defaults to False.
interleave_prob (Optional[List[float]]): If set, combines datasets using `interleave_datasets` with the
provided probabilities instead of `concatenate_datasets`. Typically used for streaming. Defaults to None.
stopping_strategy (str): The stopping strategy for `interleave_datasets`. Can be "first_exhausted" or
"all_exhausted". Defaults to "first_exhausted".
shuffle_buffer_size (int): The buffer size for shuffling in streaming mode. Only effective if `dataset_shuffle`
is `True`. Defaults to 1000.
download_mode (str): The dataset download mode. Options are 'reuse_dataset_if_exists' and 'force_redownload'.
Defaults to 'reuse_dataset_if_exists'.
columns (Optional[str]): A JSON string for column mapping to fit the format required by `AutoPreprocessor`.
Example: '{"text1": "query", "text2": "response"}'. Defaults to None.
strict (bool): If `True`, raises an error on any problematic data row. If `False`, discards the problematic
sample and continues. Typically used for debugging. Defaults to False.
remove_unused_columns (bool): Whether to remove columns not used by the model. If `False`, extra columns are
passed to the trainer's `compute_loss` function, which is useful for custom loss calculations.
Defaults to True. Note: The default is `False` for GPRO.
disable_auto_column_mapping (bool): By default, column names in the dataset are automatically mapped.
This parameter disables that behavior (the `columns` parameter remains effective), defaulting to `False`.
model_name (Optional[List[str]]): For self-cognition tasks, replaces the `{{NAME}}` placeholder in the
`swift/self-cognition` dataset. Pass Chinese and English names.
Example: `--model_name 小黄 'Xiao Huang'`. Defaults to None.
model_author (Optional[List[str]]): For self-cognition tasks, replaces the `{{AUTHOR}}` placeholder in the
`swift/self-cognition` dataset. Pass author's Chinese and English names.
Example: `--model_author '魔搭' 'ModelScope'`. Defaults to None.
custom_dataset_info (List[str]): Path to a custom dataset registration JSON file. Defaults to [].
"""
# dataset_id or dataset_dir or dataset_path
dataset: List[str] = field(default_factory=list)
val_dataset: List[str] = field(default_factory=list)
cached_dataset: List[str] = field(default_factory=list)
cached_val_dataset: List[str] = field(default_factory=list)
split_dataset_ratio: float = 0.
data_seed: int = 42
dataset_num_proc: int = 1
load_from_cache_file: bool = False
dataset_shuffle: bool = True
val_dataset_shuffle: bool = False
streaming: bool = False
interleave_prob: Optional[List[float]] = None
stopping_strategy: Literal['first_exhausted', 'all_exhausted'] = 'first_exhausted'
shuffle_buffer_size: int = 1000
download_mode: Literal['force_redownload', 'reuse_dataset_if_exists'] = 'reuse_dataset_if_exists'
columns: Optional[Union[dict, str]] = None
strict: bool = False
remove_unused_columns: bool = True
disable_auto_column_mapping: bool = False
# Chinese name and English name
model_name: Optional[List[str]] = field(default=None, metadata={'help': "e.g. ['小黄', 'Xiao Huang']"})
model_author: Optional[List[str]] = field(default=None, metadata={'help': "e.g. ['魔搭', 'ModelScope']"})
custom_dataset_info: List[str] = field(default_factory=list) # .json
def _init_custom_dataset_info(self):
"""register custom dataset_info.json to datasets"""
if isinstance(self.custom_dataset_info, str):
self.custom_dataset_info = [self.custom_dataset_info]
for path in self.custom_dataset_info:
register_dataset_info(path)
def __post_init__(self):
self.columns = json_parse_to_dict(self.columns)
if len(self.val_dataset) > 0 or self.streaming and self.split_dataset_ratio > 0:
self.split_dataset_ratio = 0.
if len(self.val_dataset) > 0:
msg = 'len(args.val_dataset) > 0'
else:
msg = 'args.streaming is True'
logger.info(f'Because {msg}, setting split_dataset_ratio: {self.split_dataset_ratio}')
self._init_custom_dataset_info()
if isinstance(self.cached_dataset, str):
self.cached_dataset = [self.cached_dataset]
self._init_val_dataset_exists()
def _init_val_dataset_exists(self):
self._val_dataset_exists = bool(self.dataset and self.split_dataset_ratio > 0 or self.val_dataset
or self.cached_val_dataset)
def get_dataset_kwargs(self):
return {
'seed': self.data_seed,
'num_proc': self.dataset_num_proc,
'load_from_cache_file': self.load_from_cache_file,
'streaming': self.streaming,
'interleave_prob': self.interleave_prob,
'stopping_strategy': self.stopping_strategy,
'shuffle_buffer_size': self.shuffle_buffer_size,
'use_hf': self.use_hf,
'hub_token': self.hub_token,
'download_mode': self.download_mode,
'columns': self.columns,
'strict': self.strict,
'model_name': self.model_name,
'model_author': self.model_author,
'remove_unused_columns': self.remove_unused_columns,
'disable_auto_column_mapping': self.disable_auto_column_mapping,
}
@@ -0,0 +1,76 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
from dataclasses import dataclass, field
from typing import List, Optional
from swift.infer_engine import RequestConfig
from swift.utils import get_logger
logger = get_logger()
@dataclass
class GenerationArguments:
"""A dataclass that holds arguments for text generation.
Args:
max_new_tokens (Optional[int]): The maximum number of new tokens to generate. Defaults to None (unlimited).
temperature (Optional[float]): The sampling temperature. A higher temperature makes the output more random. To
disable randomness, you can set this to 0 or `top_k` to 1. Defaults to None, which means loading from
'generation_config.json'.
top_k (Optional[int]): The number of highest probability vocabulary tokens to keep for top-k-filtering.
Defaults to None (reads from 'generation_config.json').
top_p (Optional[float]): The cumulative probability for nucleus sampling. Filters the vocabulary to the
smallest set of tokens whose cumulative probability exceeds `top_p`. Defaults to None (reads from
'generation_config.json').
repetition_penalty (Optional[float]): The penalty applied to repeated tokens. A value of 1.0 means no penalty.
Defaults to None (reads from 'generation_config.json').
num_beams (Optional[int]): The number of beams to use for beam search. Defaults to 1.
stream (bool): Whether to enable streaming output. Defaults to None, which is `True` for interactive mode and
`False` for batch inference. Note: For ms-swift < 3.6, the default is `False`.
stop_words (List[str]): A list of extra stop words, in addition to the end-of-sequence token. Note: The
`eos_token` is removed from the output, while these stop words are preserved. Defaults to an empty list.
logprobs (bool): Whether to output log probabilities of the generated tokens. Defaults to False.
top_logprobs (Optional[int]): The number of top log probabilities to return for each token position. Requires
`logprobs` to be True. Defaults to None.
structured_outputs_regex (Optional[str]): A regular expression pattern for structured outputs (guided decoding).
When set, the model's generation is constrained to match the specified regex pattern. This is useful for
tasks requiring structured outputs like reasoning chains. Only effective when `infer_backend` is 'vllm'.
Defaults to None.
"""
# generation config
max_new_tokens: Optional[int] = None # Unlimited, constrained by max_model_len.
# If it is None, use the parameters from generation_config.
temperature: Optional[float] = None # Set to 0, which means do_sample is False.
top_k: Optional[int] = None
top_p: Optional[float] = None
repetition_penalty: Optional[float] = None
num_beams: int = 1
stream: Optional[bool] = None
stop_words: List[str] = field(default_factory=list)
logprobs: bool = False
top_logprobs: Optional[int] = None
# structured outputs (guided decoding), only effective for vllm backend
structured_outputs_regex: Optional[str] = None
def _init_stream(self):
if self.stream is None:
self.stream = False
def get_request_config(self):
if getattr(self, 'task_type') != 'causal_lm':
return
return RequestConfig(
max_tokens=self.max_new_tokens,
temperature=self.temperature,
top_p=self.top_p,
top_k=self.top_k,
num_beams=self.num_beams,
stop=self.stop_words,
stream=self.stream,
repetition_penalty=self.repetition_penalty,
logprobs=self.logprobs,
top_logprobs=self.top_logprobs,
structured_outputs_regex=self.structured_outputs_regex)
+249
View File
@@ -0,0 +1,249 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import ast
import math
import os
import torch
from dataclasses import dataclass, field
from transformers.utils import is_torch_mps_available
from typing import Any, Dict, List, Literal, Optional, Union
from swift.model import MODEL_MAPPING, get_model_info_meta, get_model_name
from swift.utils import HfConfigFactory, get_dist_setting, get_logger, json_parse_to_dict
logger = get_logger()
@dataclass
class ModelArguments:
"""A dataclass that holds various arguments related to model configuration and usage.
Args:
model (Optional[str]): The model ID from the Hub or a local path to the model. Defaults to None.
model_type (Optional[str]): The model type. In ms-swift, a 'model_type' groups models with the same
architecture, loading process, and template. Defaults to None, which enables auto-selection based on
the suffix of `--model` and the 'architectures' attribute in `config.json`. The `model_type` for a
corresponding model can be found in the list of supported models. Note: The concept of `model_type`
in ms-swift differs from the `model_type` in `config.json`. Custom models usually require registering
their own `model_type` and `template`.
model_revision (Optional[str]): The revision of the model. Defaults to None.
task_type (str): The task type. Can be 'causal_lm', 'seq_cls', 'embedding', 'reranker', or
'generative_reranker'. If set to 'seq_cls', you usually need to specify `--num_labels` and
`--problem_type`. Defaults to 'causal_lm'.
torch_dtype (Optional[str]): The data type of the model weights. Supports 'float16', 'bfloat16', 'float32'.
Defaults to None, in which case it's read from the 'config.json' file.
attn_impl (Optional[str]): The attention implementation to use. Options include 'sdpa', 'eager', 'flash_attn',
'flash_attention_2', 'flash_attention_3', 'flash_attention_4', etc.
Defaults to None, which means it will be read from 'config.json'.
Note: Support for these implementations depends on the model's transformers implementation.
If set to 'flash_attn' (for backward compatibility), 'flash_attention_2' will be used.
experts_impl (Optional[str]): Expert implementation type, options are 'grouped_mm', 'batched_mm', 'eager'.
Defaults to None. This feature requires "transformers>=5.0.0".
new_special_tokens (List[str]): Additional special tokens to be added to the tokenizer. Can also be a path to
a `.txt` file, where each line is a special token. Defaults to an empty list `[]`.
num_labels (Optional[int]): The number of labels for classification tasks (when `--task_type` is 'seq_cls').
Required for such tasks. Defaults to None.
problem_type (Optional[str]): The problem type for classification tasks (`--task_type` 'seq_cls'). Options are
'regression', 'single_label_classification', 'multi_label_classification'. Defaults to None, but is
automatically set to 'regression' if the model is a reward_model or `num_labels` is 1, and
'single_label_classification' otherwise.
rope_scaling (Optional[str]): The RoPE scaling type. You can pass a string like 'linear', 'dynamic', or
'yarn', and ms-swift will automatically set the corresponding `rope_scaling` and override the
'config.json' value. Alternatively, you can pass a JSON string (e.g., '{"factor":2.0, "type":"yarn"}'),
which will directly override the `rope_scaling` in 'config.json'. Defaults to None.
device_map (Optional[str]): The device map configuration for the model, e.g., 'auto', 'cpu', a JSON string,
or a path to a JSON file. This argument is passed directly to the `from_pretrained` method of transformers.
Defaults to None, and will be set automatically based on the device and distributed training settings.
max_memory (Optional[str]): The maximum memory allocation for each device when `device_map` is 'auto' or
'sequential'. Example: '{0: "20GB", 1: "20GB"}'. This argument is passed directly to the `from_pretrained`
method of transformers. Defaults to None.
max_model_len (Optional[int]): The maximum model length. This is used to calculate the RoPE scaling factor
when `rope_scaling` is specified as a string. If not None, it overrides the `max_position_embeddings`
value in 'config.json'. Defaults to None.
local_repo_path (Optional[str]): Path to a local repository for models that require a GitHub repo during
loading (e.g., deepseek-vl2). This avoids network issues during `git clone`. Defaults to None.
init_strategy (Optional[str]): The strategy to initialize all uninitialized parameters when loading a model
(especially for custom architectures). Options include 'zero', 'uniform', 'normal', 'xavier_uniform',
'xavier_normal', 'kaiming_uniform', 'kaiming_normal', 'orthogonal'. Defaults to None.
"""
model: Optional[str] = None # model id or model path
model_type: Optional[str] = field(
default=None, metadata={'help': f'model_type choices: {list(MODEL_MAPPING.keys())}'})
model_revision: Optional[str] = None
task_type: Literal['causal_lm', 'seq_cls', 'embedding', 'reranker', 'generative_reranker'] = None
torch_dtype: Literal['bfloat16', 'float16', 'float32', None] = None
# flash_attn: It will automatically convert names based on the model.
# None: It will be automatically selected between sdpa and eager.
# 'flash_attn', 'sdpa', 'eager', 'flex_attention',
# 'flash_attention_2', 'flash_attention_3', 'flash_attention_4'
attn_impl: Optional[str] = None
experts_impl: Optional[str] = None
new_special_tokens: List[str] = field(default_factory=list)
num_labels: Optional[int] = None
problem_type: Literal['regression', 'single_label_classification', 'multi_label_classification'] = None
rope_scaling: Optional[str] = None
device_map: Optional[Union[dict, str]] = None
max_memory: Optional[Union[dict, str]] = None
max_model_len: Optional[int] = None
# When some model code needs to be downloaded from GitHub,
# this parameter specifies the path to the locally downloaded repository.
local_repo_path: Optional[str] = None
init_strategy: Literal['zero', 'uniform', 'normal', 'xavier_uniform', 'xavier_normal', 'kaiming_uniform',
'kaiming_normal', 'orthogonal'] = None
def _init_device_map(self):
"""Prepare device map args"""
if self.device_map:
self.device_map: Union[str, Dict[str, Any], None] = json_parse_to_dict(self.device_map, strict=False)
# compat mp&ddp
_, local_rank, _, local_world_size = get_dist_setting()
if local_world_size > 1 and isinstance(self.device_map, dict) and local_rank > 0:
for k, v in self.device_map.items():
if isinstance(v, int):
self.device_map[k] += local_rank
def _init_max_memory(self):
if isinstance(self.max_memory, str):
try:
self.max_memory = ast.literal_eval(self.max_memory)
except Exception:
pass
self.max_memory = json_parse_to_dict(self.max_memory)
# compat mp&ddp
_, local_rank, _, local_world_size = get_dist_setting()
if local_world_size > 1 and isinstance(self.max_memory, dict) and local_rank > 0:
for k in list(self.max_memory.keys()):
if isinstance(k, int):
self.max_memory[k + local_rank] = self.max_memory.pop(k)
def _init_torch_dtype(self) -> None:
""""If torch_dtype is None, find a proper dtype by the config.json/GPU"""
from ..sft_args import SftArguments
self.torch_dtype: Optional[torch.dtype] = HfConfigFactory.to_torch_dtype(self.torch_dtype)
self.torch_dtype: torch.dtype = self._init_model_info()
# Mixed Precision Training
if isinstance(self, SftArguments):
self._init_mixed_precision()
def _init_mixed_precision(self):
if is_torch_mps_available():
fp16, bf16 = False, False
elif self.torch_dtype in {torch.float16, torch.float32}:
fp16, bf16 = True, False
elif self.torch_dtype == torch.bfloat16:
fp16, bf16 = False, True
else:
raise ValueError(f'args.torch_dtype: {self.torch_dtype}')
if self.fp16 is None:
self.fp16 = fp16
if self.bf16 is None:
self.bf16 = bf16
def _init_rope_scaling(self):
if self.rope_scaling:
rope_scaling: dict = json_parse_to_dict(self.rope_scaling, strict=False)
if isinstance(rope_scaling, str):
assert rope_scaling in ['linear', 'dynamic', 'yarn']
rope_scaling = {'type': rope_scaling}
else:
rope_scaling = self.model_info.rope_scaling
# reset the factor
rope_scaling.pop('factor', None)
rope_type = rope_scaling.get('rope_type', rope_scaling.get('type', 'default'))
if 'factor' not in rope_scaling and self.max_model_len is None and rope_type == 'default':
# fix megatron qwen2_5_vl
self.rope_scaling = rope_scaling
logger.info(f'Setting args.rope_scaling: {rope_scaling}')
return
# get origin_max_model_len
origin_max_model_len = None
if rope_scaling and rope_scaling.get('original_max_position_embeddings') is not None:
origin_max_model_len = rope_scaling['original_max_position_embeddings']
elif self.model_info.rope_scaling:
if self.model_info.rope_scaling.get('original_max_position_embeddings') is not None:
origin_max_model_len = self.model_info.rope_scaling['original_max_position_embeddings']
elif self.model_info.rope_scaling.get('factor') is not None:
origin_max_model_len = self.model_info.max_model_len // self.model_info.rope_scaling['factor']
if origin_max_model_len is None:
origin_max_model_len = self.model_info.max_model_len
assert origin_max_model_len is not None, '`origin_max_model_len` from model config is not set'
rope_scaling['original_max_position_embeddings'] = origin_max_model_len
if 'factor' not in rope_scaling:
assert self.max_model_len is not None, (
'max_model_len must be set if rope_scaling does not contain a "factor"')
rope_scaling['factor'] = max(float(math.ceil(self.max_model_len / origin_max_model_len)), 1.0)
rope_model_len = int(origin_max_model_len * rope_scaling['factor'])
if self.max_model_len is None:
self.max_model_len = rope_model_len
elif self.max_model_len > rope_model_len:
logger.warning(f'rope config ({rope_model_len} = {rope_scaling["factor"]} * '
f'{origin_max_model_len}) should be bigger than max_model_len '
f'from command line ({self.max_model_len})')
self.rope_scaling = rope_scaling
logger.info(f'Setting args.rope_scaling: {rope_scaling}')
logger.info(f'Setting args.max_model_len: {self.max_model_len}')
def _init_model_info(self) -> torch.dtype:
model_kwargs = self.get_model_kwargs()
if self.tuner_backend == 'unsloth':
model_kwargs['download_model'] = True
self.model_info, self.model_meta = get_model_info_meta(**model_kwargs)
self.task_type = self.model_info.task_type
self.num_labels = self.model_info.num_labels
self.model_dir = self.model_info.model_dir
self.model_type = self.model_info.model_type
if self.rope_scaling or self.model_info.rope_scaling and self.max_model_len is not None:
self._init_rope_scaling()
return self.model_info.torch_dtype
def _init_new_special_tokens(self):
if isinstance(self.new_special_tokens, str):
self.new_special_tokens = [self.new_special_tokens]
new_special_tokens = []
for token in self.new_special_tokens:
if token.endswith('.txt'):
assert os.path.isfile(token), f'special_tokens_path: {token}'
with open(token, 'r', encoding='utf-8') as f:
text = f.read()
new_special_tokens += text.split()
else:
new_special_tokens.append(token)
self.new_special_tokens = new_special_tokens
def __post_init__(self):
if self.model is None:
raise ValueError(f'Please set --model <model_id_or_path>`, model: {self.model}')
self._init_new_special_tokens()
self.model_suffix = get_model_name(self.model)
self._init_device_map()
self._init_max_memory()
self._init_torch_dtype()
def get_model_kwargs(self):
return {
'model_id_or_path': self.model,
'torch_dtype': self.torch_dtype,
'model_type': self.model_type,
'revision': self.model_revision,
'use_hf': self.use_hf,
'hub_token': self.hub_token,
'local_repo_path': self.local_repo_path,
'device_map': self.device_map,
'max_memory': self.max_memory,
'quantization_config': self.get_quantization_config(),
'attn_impl': self.attn_impl,
'experts_impl': self.experts_impl,
'new_special_tokens': self.new_special_tokens,
'rope_scaling': self.rope_scaling,
'max_model_len': self.max_model_len,
'task_type': self.task_type,
'num_labels': self.num_labels,
'problem_type': self.problem_type,
'init_strategy': self.init_strategy,
}
+122
View File
@@ -0,0 +1,122 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import torch
from dataclasses import dataclass
from typing import Literal, Optional
from swift.model import get_model_processor
from swift.utils import HfConfigFactory, get_modules_to_not_convert
@dataclass
class QuantizeArguments:
"""A dataclass that holds the configuration for model quantization.
Args:
quant_method (Optional[str]): The quantization method to use when loading the model. Can be one of {'bnb',
'hqq', 'eetq', 'quanto', 'fp8'}. Note: This is not required for QLoRA training on pre-quantized AWQ/GPTQ
models. Defaults to None.
quant_bits (Optional[Union[int, str]]): The number of bits for quantization, e.g., {1, 2, 3, 4, 8, 'float8'}.
Defaults to None.
hqq_axis (Optional[int]): The quantization axis for HQQ quantization. Defaults to None.
bnb_4bit_compute_dtype (Optional[str]): The compute data type for 4-bit BNB quantization. Can be one of {
'float16', 'bfloat16', 'float32'}. Defaults to None, which will use the model's `torch_dtype`.
bnb_4bit_quant_type (str): The quantization type for 4-bit BNB quantization. Can be one of {'fp4', 'nf4'}.
Defaults to 'nf4'.
bnb_4bit_use_double_quant (bool): Whether to use double quantization for 4-bit BNB quantization.
Defaults to True.
bnb_4bit_quant_storage (Optional[str]): The storage type for packing quantized 4-bit parameters in BNB.
Defaults to None.
"""
# awq, gptq, and aqlm need to be pre-quantized models.
# It can be detected automatically, without the need to pass in.
# while bnb, hqq, and eetq can be quantized during SFT using the original models.
quant_method: Literal['bnb', 'hqq', 'eetq', 'quanto', 'fp8'] = None
# bnb: 4,8; hqq: 1,2,3,4,8'; eetq: 8
# awq: 4; gptq: 2,3,4,8
quant_bits: Literal[1, 2, 3, 4, 8, 'float8'] = None
# hqq
hqq_axis: Optional[int] = None
# bnb
bnb_4bit_compute_dtype: Literal['float16', 'bfloat16', 'float32', None] = None
bnb_4bit_quant_type: Literal['fp4', 'nf4'] = 'nf4'
bnb_4bit_use_double_quant: bool = True
bnb_4bit_quant_storage: Optional[str] = None
def get_quantization_config(self):
if self.quant_method is None or self.quant_method in {'awq', 'gptq', 'gptq_v2'}:
return None
assert self.quant_method in {'bnb', 'hqq', 'eetq', 'quanto', 'fp8'}
if self.quant_method != 'fp8' and self.quant_bits is None:
raise ValueError(f'Please set the quant_bits. args.quant_bits: {self.quant_bits}')
if self.quant_method == 'bnb':
if self.quant_bits == 4:
load_in_4bit, load_in_8bit = True, False
elif self.quant_bits == 8:
load_in_4bit, load_in_8bit = False, True
else:
raise ValueError(f'bnb not support quant_bits: {self.quant_bits}')
from transformers import BitsAndBytesConfig
llm_int8_skip_modules = self.get_modules_to_not_convert()
quantization_config = BitsAndBytesConfig(
load_in_4bit=load_in_4bit,
load_in_8bit=load_in_8bit,
bnb_4bit_compute_dtype=self.bnb_4bit_compute_dtype,
bnb_4bit_quant_type=self.bnb_4bit_quant_type,
bnb_4bit_use_double_quant=self.bnb_4bit_use_double_quant,
bnb_4bit_quant_storage=self.bnb_4bit_quant_storage,
llm_int8_skip_modules=llm_int8_skip_modules)
elif self.quant_method == 'fp8':
if not hasattr(self, 'model_info'):
return
from transformers import FineGrainedFP8Config
with torch.device('meta'):
hf_model, _ = get_model_processor(self.model_dir, model_type=self.model_type, return_dummy_model=True)
modules_to_not_convert = get_modules_to_not_convert(hf_model)
quantization_config = FineGrainedFP8Config(modules_to_not_convert=modules_to_not_convert)
elif self.quant_method == 'hqq':
from transformers import HqqConfig
quantization_config = HqqConfig(nbits=self.quant_bits, axis=self.hqq_axis)
elif self.quant_method == 'quanto':
from transformers import QuantoConfig
if self.quant_bits == 8:
weights = 'int8'
elif self.quant_bits == 'float8':
weights = 'float8'
elif self.quant_bits == 4:
weights = 'int4'
elif self.quant_bits == 2:
weights = 'int2'
else:
raise ValueError('quanto quantization only support quant bits 2/4/8/float8')
quantization_config = QuantoConfig(weights=weights)
else: # 'eetq'
from transformers import EetqConfig
quantization_config = EetqConfig(f'int{self.quant_bits}')
return quantization_config
def get_modules_to_not_convert(self):
if not hasattr(self, 'model_meta') or not hasattr(self, 'model_info'):
return None
model_arch = self.model_meta.model_arch
res = []
if self.model_info.is_moe_model:
res += ['mlp.gate', 'mlp.shared_expert_gate']
if model_arch is not None:
for key in ['vision_tower', 'aligner']:
value = getattr(model_arch, key, None)
if value:
res += value
if not res:
return None
res.append('lm_head')
return res
def __post_init__(self):
if self.bnb_4bit_compute_dtype is None:
if self.torch_dtype in {torch.float16, torch.float32}:
self.bnb_4bit_compute_dtype = torch.float32
elif self.torch_dtype == torch.bfloat16:
self.bnb_4bit_compute_dtype = torch.bfloat16
self.bnb_4bit_compute_dtype: torch.dtype = HfConfigFactory.to_torch_dtype(self.bnb_4bit_compute_dtype)
+204
View File
@@ -0,0 +1,204 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import os
from dataclasses import dataclass, field
from typing import Literal, Optional
from swift.template import TEMPLATE_MAPPING, get_template_meta
from swift.utils import get_logger
logger = get_logger()
@dataclass
class TemplateArguments:
"""TemplateArguments class holds various arguments for template configuration.
This dataclass manages settings related to how data is formatted and processed using templates, including
tokenization, truncation, loss calculation, and special handling for multimodal and agent-based models.
Args:
template (Optional[str]): The dialogue template type. Defaults to None, which automatically selects the
template corresponding to the model type. Refer to the list of supported models for mappings.
system (Optional[str]): Custom system prompt. Can be a string or a path to a .txt file. Defaults to None,
which uses the default system from the registered template.
Note: The priority for the system prompt is as follows:
1. System prompt from the dataset.
2. The `--system` command-line argument.
3. The `default_system` set when the template was registered.
max_length (Optional[int]): The maximum number of tokens for a single sample after tokenization. Samples
exceeding this length are handled according to `truncation_strategy` to prevent OOM errors. Defaults to
None, which uses the model's maximum supported length (`max_model_len`). In PPO, GRPO, and inference
scenarios, this argument specifies the `max_prompt_length`.
truncation_strategy (Literal['delete', 'left', 'right', 'split']): Strategy for handling samples exceeding
`max_length`. Options are 'delete', 'left' (truncate from the left), 'right' (truncate from the right),
and 'split' (split into multiple samples). Defaults to 'delete'.
Note: The 'split' strategy is only supported during pre-training (e.g., `swift/megatron pt`),
and is incompatible with `cached_dataset`. It splits long samples to avoid wasting tokens.
Note: For multimodal models, setting this to 'left' or 'right' preserves all image tokens, which may lead
to OOM errors.
max_pixels (Optional[int]): The maximum number of pixels (H*W) for an input image in a multimodal model.
Images exceeding this limit will be scaled down to prevent OOM errors. Defaults to None, meaning no limit.
Note: This parameter applies to all multimodal models. The model-specific `MAX_PIXELS` parameter for
Qwen2.5-VL is separate and only applies to that model.
agent_template (Optional[str]): The Agent template to use. This determines how the 'tools' list is converted
into a 'system' prompt, how tool calls are extracted from the model's response during inference, and the
format for tool call messages. Options include "react_en", "hermes", "glm4", "qwen_en", "toolbench", etc.
Defaults to None, which auto-selects based on the model type. Refer to the Agent documentation for more
details.
norm_bbox (Optional[Literal['norm1000', 'none']]): Controls how bounding box coordinates (from the "bbox"
field in the dataset) are scaled. 'norm1000' scales coordinates to a 1000x1000 grid, while 'none' performs
no scaling. Defaults to None, which auto-selects based on the model. This handles cases where images are
resized during training (e.g., due to `max_pixels`).
use_chat_template (bool): Whether to use the chat template or the generation template. The generation template
is typically used for pre-training. Defaults to True.
Note: Defaults to False for `swift pt`, which uses the generation template. This parameter is compatible
with multimodal models.
padding_side (Literal['left', 'right']): The side to pad on when `batch_size >= 2` during training.
Options are 'left' or 'right'. Defaults to 'right'. For inference with `batch_size >= 2`, padding is always
on the left.
Note: Defaults to 'left' for PPO and GKD.
padding_free (bool): If True, flattens the data within a batch to avoid padding, reducing memory usage and
speeding up training. Sequences within the batch remain causally isolated. Defaults to False. Supported for
CPT/SFT/DPO/GRPO/KTO/GKD.
Note: This requires `--attn_impl flash_attn` and `transformers>=4.44`. Compared to packing, padding_free
has no preprocessing overhead, but packing offers faster training speeds and more stable memory usage.
loss_scale (str): Loss weight configuration for training tokens. Default is `'default'`.
loss_scale includes 3 basic strategies: 'default', 'last_round', 'all', and other strategies:
'ignore_empty_think' and agent-specific ones: 'react', 'hermes', 'qwen', 'agentflan', 'alpha_umi', etc.
For available options, refer to
[loss_scale module](https://github.com/modelscope/ms-swift/blob/main/swift/loss_scale/mapping.py).
ms-swift supports mixing basic strategies with other strategies,
for example: `'default+ignore_empty_think'`, `'last_round+ignore_empty_think'`.
If no basic strategy is specified, it defaults to 'default',
for example: 'hermes' is equivalent to 'default+hermes'.
Multiple non-base strategies can be chained together
(each strategy processes the output segments of the previous one, with weights
multiplied accordingly). For example: `'last_round+hermes+ignore_empty_think'`, where
`'last_round'` is the base strategy, and `'hermes+ignore_empty_think'` represents a
chain of multiple non-base strategies that share the same base strategy.
- 'default': All responses (including history) are calculated with weight 1 for cross-entropy loss
(**system/user/multimodal tokens in messages and `tool_response` parts in Agent training are
not included in loss calculation**). (**Default value for SFT**)
- 'last_round': Only calculate loss for the last round response. The last round
means all content after the last "user". (**Default value for RLHF**)
- 'all': Calculate loss for all tokens. (**Default value for `swift pt`**)
- 'ignore_empty_think': Ignore loss computation for empty `'<think>\n\n</think>\n\n'`
(as long as it matches the regex `'<think>\\s*</think>\\s*'`).
- 'react', 'hermes', 'qwen': Adjust the loss weight of the `tool_call` part to 2.
sequence_parallel_size (int): The size of sequence parallelism. Defaults to 1. Currently supported for CPT,
SFT, DPO, and GRPO.
template_backend (Literal['swift', 'jinja']): The backend to use for templating. Options are 'swift' or
'jinja'. Defaults to 'swift'. If 'jinja' is used, it will leverage `transformers.apply_chat_template`.
Note: The 'jinja' backend is only supported for inference, not for training, as it cannot determine the
token range for loss calculation.
response_prefix (Optional[str]): A prefix string for the response, e.g., '<think>\\n' for Qwen-32B. This
parameter only affects inference. Defaults to None, which is auto-set based on the model.
enable_thinking (Optional[bool]): This parameter takes effect during inference,
indicating whether to enable thinking mode. Default is None, the default value is determined by the
template (model) type (True for thinking/hybrid thinking templates, False for non-thinking templates).
If enable_thinking is False, a non-thinking prefix is added, for example the Qwen3-8B hybrid thinking
model adds the prefix `'<think>\n\n</think>\n\n'`, while Qwen3-8B-Thinking does not add a prefix.
If enable_thinking is True, a thinking prefix is added, for example `'<think>\n'`.
Note: The priority of this parameter is lower than the response_prefix parameter.
preserve_thinking (Optional[bool]): Whether to preserve historical thinking content during inference and
training. When set to `True`, thinking content from all rounds is retained. When set to `False`,
only the thinking content from the last round is retained (i.e., the content following the last
user message). Defaults to `None`.
Default behavior: For thinking models (thinking/hybrid-thinking) or when `enable_thinking` is
explicitly enabled, this is set to `False` by default during inference and training, retaining
only the last round of thinking content. If the `loss_scale` base strategy during training is
not `'last_round'` (e.g., `'default'`), it defaults to `True`, and historical thinking content will
not be removed.
add_non_thinking_prefix (bool): This parameter only takes effect during training, indicating whether to
add a non-thinking prefix to data samples whose assistant part does not start with the thinking
marker `'<think>'` (typically hybrid thinking models contain a non-thinking prefix).
This feature allows swift's built-in datasets to train hybrid thinking models. Default value is True.
For example: the non-thinking prefix for the Qwen3-8B hybrid thinking model is
`'<think>\n\n</think>\n\n'`, while the non-thinking prefix for Qwen3-8B-Thinking/Instruct is `''`.
Note: During training, if the basic strategy of loss_scale is last_round, this modification is only
applied to the last round; otherwise, for example 'default' or 'all', this modification is applied to
every round of data. If set to False, no non-thinking prefix is added to data samples.
"""
template: Optional[str] = field(
default=None, metadata={'help': f'template choices: {list(TEMPLATE_MAPPING.keys())}'})
system: Optional[str] = None # Override the default_system in the template.
max_length: Optional[int] = None
truncation_strategy: Literal['delete', 'left', 'right', 'split', None] = None
max_pixels: Optional[int] = None
agent_template: Optional[str] = None
norm_bbox: Literal['norm1000', 'none', None] = None
use_chat_template: Optional[bool] = None
padding_side: Literal['left', 'right'] = 'right'
# train
padding_free: bool = False
loss_scale: str = 'default'
sequence_parallel_size: int = 1
is_binary_loss_scale: Optional[bool] = None
# infer/deploy
template_backend: Literal['swift', 'jinja'] = 'swift'
# thinking
response_prefix: Optional[str] = None
enable_thinking: Optional[bool] = None
preserve_thinking: Optional[bool] = None
add_non_thinking_prefix: bool = True
disable_ignore_empty_think: bool = False
def __post_init__(self):
if getattr(self, 'model_meta', None) is not None:
self.template_meta = get_template_meta(self.model_info, self.model_meta, template_type=self.template)
self.template = self.template_meta.template_type
if self.use_chat_template is None:
self.use_chat_template = True
if self.system is not None:
if self.system.endswith('.txt'):
assert os.path.isfile(self.system), f'self.system: {self.system}'
with open(self.system, 'r', encoding='utf-8') as f:
self.system = f.read()
else:
self.system = self.system.replace('\\n', '\n')
if self.response_prefix is not None:
self.response_prefix = self.response_prefix.replace('\\n', '\n')
if self.truncation_strategy is None:
self.truncation_strategy = 'delete'
self._set_loss_scale()
def _set_loss_scale(self):
"""For hybrid thinking models, automatically append '+ignore_empty_think' to loss_scale."""
if not self.disable_ignore_empty_think and getattr(self, 'template_meta', None) is not None:
template_meta = self.template_meta
if template_meta.is_thinking and template_meta.non_thinking_prefix:
# hybrid thinking model detected
if self.loss_scale and 'ignore_empty_think' not in self.loss_scale:
self.loss_scale = self.loss_scale + '+ignore_empty_think'
def get_template_kwargs(self):
truncation_strategy = self.truncation_strategy
if truncation_strategy == 'delete':
truncation_strategy = 'raise'
return {
'template_type': self.template,
'default_system': self.system,
'max_length': self.max_length,
'truncation_strategy': truncation_strategy,
'max_pixels': self.max_pixels,
'agent_template': self.agent_template,
'norm_bbox': self.norm_bbox,
'use_chat_template': self.use_chat_template,
'remove_unused_columns': self.remove_unused_columns, # from DataArguments
'padding_side': self.padding_side,
# train
'padding_free': self.padding_free,
'loss_scale': self.loss_scale,
'is_binary_loss_scale': self.is_binary_loss_scale,
'sequence_parallel_size': self.sequence_parallel_size,
# infer/deploy
'template_backend': self.template_backend,
# thinking
'response_prefix': self.response_prefix,
'enable_thinking': self.enable_thinking,
'preserve_thinking': self.preserve_thinking,
'add_non_thinking_prefix': self.add_non_thinking_prefix,
}
+180
View File
@@ -0,0 +1,180 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import os
from dataclasses import dataclass
from typing import Literal, Optional
from swift.utils import find_free_port, get_device_count, get_logger, safe_snapshot_download
from .base_args import BaseArguments
from .infer_args import InferArguments
logger = get_logger()
@dataclass
class DeployArguments(InferArguments):
"""Arguments for model deployment.
This dataclass, which extends InferArguments, is used to define the arguments required for deploying a model.
Args:
host (str): The host address to bind the server to. Defaults to '0.0.0.0'.
port (int): The port number to bind the server to. Defaults to 8000.
api_key (Optional[str]): The API key for authentication. Defaults to None.
ssl_keyfile (Optional[str]): The path to the SSL key file. Defaults to None.
ssl_certfile (Optional[str]): The path to the SSL certificate file. Defaults to None.
owned_by (str): The owner of the deployment. Defaults to 'swift'.
served_model_name (Optional[str]): The name of the model being served. If None, the model's suffix is used by
default.
verbose (bool): Whether to log detailed request information. Defaults to True.
Note: This defaults to False when used in 'swift app' or 'swift eval'.
log_interval (int): The interval in seconds for printing tokens/s statistics. Set to -1 to disable. Defaults
to 20.
log_level (Literal['critical', 'error', 'warning', 'info', 'debug', 'trace']): Log level. Defaults to 'info'.
max_logprobs (int): The maximum number of logprobs to return to the client. Defaults to 20.
vllm_use_async_engine (Optional[bool]): Whether to use async engine for vLLM.If not set, it defaults to `True`
for deployment scenarios.
"""
host: str = '0.0.0.0'
port: int = 8000
api_key: Optional[str] = None
ssl_keyfile: Optional[str] = None
ssl_certfile: Optional[str] = None
owned_by: str = 'swift'
served_model_name: Optional[str] = None
verbose: bool = True # Whether to log request_info
log_interval: int = 20 # Interval for printing global statistics
log_level: Literal['critical', 'error', 'warning', 'info', 'debug', 'trace'] = 'info'
max_logprobs: int = 20
vllm_use_async_engine: Optional[bool] = None
def __post_init__(self):
# default to True for deployment scenarios
if self.vllm_use_async_engine is None:
self.vllm_use_async_engine = True
super().__post_init__()
self.port = find_free_port(self.port)
def _init_adapters(self):
if isinstance(self.adapters, str):
self.adapters = [self.adapters]
self.adapter_mapping = {}
adapters = []
for i, adapter in enumerate(self.adapters):
adapter_path = adapter.split('=')
if len(adapter_path) == 1:
adapter_path = (None, adapter_path[0])
adapter_name, adapter_path = adapter_path
adapter_path = safe_snapshot_download(adapter_path, use_hf=self.use_hf, hub_token=self.hub_token)
if adapter_name is None:
adapters.append(adapter_path)
else:
self.adapter_mapping[adapter_name] = adapter_path
self.adapters = adapters
def _init_ckpt_dir(self, adapters=None):
return super()._init_ckpt_dir(self.adapters + list(self.adapter_mapping.values()))
def _init_stream(self):
return BaseArguments._init_stream(self)
@dataclass
class RolloutArguments(DeployArguments):
"""Arguments for the Rollout phase in online/reinforcement learning.
This dataclass inherits from DeployArguments and adds specific parameters for the Rollout process in online
learning, such as GRPO.
Args:
multi_turn_scheduler (Optional[str]): The scheduler for multi-turn GRPO training. Pass the name of the
corresponding plugin implemented in `swift/rollout/multi_turn.py`. Defaults to None. Refer to the
documentation for details.
max_turns (Optional[int]): The maximum number of turns in multi-turn GRPO training. If None, no limit is
imposed. Defaults to None.
vllm_enable_lora (bool): Whether to enable the vLLM Engine to load LoRA adapters. Enabling this can accelerate
weight synchronization during LoRA training. Defaults to False. Refer to the documentation for details.
vllm_max_lora_rank (int): The LoRA rank parameter for the vLLM Engine. This value must be greater than or
equal to the `lora_rank` used for training; setting them as equal is recommended. Defaults to 16.
"""
vllm_use_async_engine: Optional[bool] = None
use_gym_env: Optional[bool] = None
# only for GRPO rollout with AsyncEngine, see details in swift/rollout/multi_turn
multi_turn_scheduler: Optional[str] = None
max_turns: Optional[int] = None
vllm_enable_lora: bool = False
vllm_max_lora_rank: int = 16
# GYM env
gym_env: Optional[str] = None
context_manager: Optional[str] = None
def __post_init__(self):
self._set_default_engine_type()
super().__post_init__()
self._check_args()
self._check_device_count()
self._check_vllm_enable_expert_parallel()
self._check_deprecated_args()
self._set_default_audio_load_backend()
def _set_default_engine_type(self):
if self.vllm_use_async_engine is None:
if self.multi_turn_scheduler:
self.vllm_use_async_engine = True
else:
self.vllm_use_async_engine = False
if self.use_gym_env is None:
self.use_gym_env = self.gym_env is not None
def _check_args(self):
if self.vllm_pipeline_parallel_size > 1:
raise ValueError('RolloutArguments does not support pipeline parallelism, '
'please set vllm_pipeline_parallel_size to 1.')
if self.vllm_reasoning_parser is not None:
raise ValueError('vllm_reasoning_parser is not supported for Rollout, please unset it.')
if self.multi_turn_scheduler and not self.vllm_use_async_engine:
raise ValueError('please set vllm_use_async_engine to True with multi-turn scheduler.')
def _check_device_count(self):
local_device_count = get_device_count()
required_device_count = self.vllm_data_parallel_size * self.vllm_tensor_parallel_size
if local_device_count < required_device_count:
msg = (f'Error: local_device_count ({local_device_count}) must be greater than or equal to '
f'the product of vllm_data_parallel_size ({self.vllm_data_parallel_size}) and '
f'vllm_tensor_parallel_size ({self.vllm_tensor_parallel_size}). '
f'Current required_device_count = {required_device_count}.')
raise ValueError(msg)
if local_device_count > required_device_count:
logger.warning_once(
f'local_device_count ({local_device_count}) is greater than required_device_count ({required_device_count}). ' # noqa
f'Only the first {required_device_count} devices will be utilized for rollout. '
f'To fully utilize resources, set vllm_tensor_parallel_size * vllm_data_parallel_size = device_count. ' # noqa
f'device_count: {local_device_count}, '
f'vllm_tensor_parallel_size: {self.vllm_tensor_parallel_size}, '
f'vllm_data_parallel_size: {self.vllm_data_parallel_size}, '
f'required_device_count: {required_device_count}.')
def _check_vllm_enable_expert_parallel(self):
if self.vllm_enable_expert_parallel and not self.vllm_use_async_engine:
self.vllm_use_async_engine = True
logger.warning('vllm_enable_expert_parallel is only supported with vllm_use_async_engine, '
'set vllm_use_async_engine to True.')
def _check_deprecated_args(self):
if self.context_manager is not None:
raise ValueError('The "context_manager" argument has been removed. '
'If you need to dynamically modify the conversation history between rollout turns '
'(e.g. history compression, prompt injection), implement that logic in a custom '
'`MultiTurnScheduler` subclass by overriding `step` / `run`, '
'and pass it via `--multi_turn_scheduler your_scheduler_name`.')
def _set_default_audio_load_backend(self):
# Rollout uses GRPOVllmEngine (vLLM-only); align audio decode with vLLM multimodal loader.
if os.getenv('SWIFT_AUDIO_LOAD_BACKEND') is None:
os.environ['SWIFT_AUDIO_LOAD_BACKEND'] = 'soundfile_pyav'
+130
View File
@@ -0,0 +1,130 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import datetime as dt
import os
from dataclasses import dataclass, field
from typing import List, Literal, Optional, Union
from swift.model import get_matched_model_meta
from swift.utils import get_logger, json_parse_to_dict, to_abspath
from .deploy_args import DeployArguments
logger = get_logger()
@dataclass
class EvalArguments(DeployArguments):
"""A dataclass that extends DeployArguments to define model evaluation arguments.
These arguments control the evaluation process, including the choice of backend, datasets, generation parameters,
and other configurations.
Args:
eval_dataset (List[str]): List of evaluation datasets. Please refer to the evaluation documentation for
available options. Defaults to [].
eval_limit (Optional[int]): The number of samples to take from each evaluation dataset. If None, all samples
are used. Defaults to None.
eval_dataset_args (Optional[Union[Dict, str]]): Evaluation dataset parameters, in JSON format, can be set for
multiple datasets. Defaults to None.
eval_generation_config (Optional[Union[Dict, str]]): The model's inference configuration for evaluation,
provided as a JSON string (e.g., '{"max_new_tokens": 512}'). Defaults to None.
eval_output_dir (str): The directory to store evaluation results. Defaults to 'eval_output'.
eval_backend (str): The evaluation backend. Can be 'Native', 'OpenCompass', or 'VLMEvalKit'. Defaults to
'Native'.
local_dataset (bool): Whether to automatically download extra datasets required for certain evaluations
(e.g., CMB). If True, a 'data' folder will be created in the current directory for the datasets. This
download occurs only once, and subsequent runs will use the cache. Defaults to False.
Note: By default, evaluation uses datasets from `~/.cache/opencompass`. When this is set to True, the
`data` folder in the current directory is used instead.
temperature (float): The temperature for sampling, which overrides the default generation config. Defaults
to 0.0.
verbose (bool): Whether to output verbose information during the evaluation process. Defaults to False.
eval_num_proc (int): The maximum number of concurrent clients for evaluation. Defaults to 16.
extra_eval_args (Optional[Union[Dict, str]]): Additional evaluation arguments, provided as a JSON string.
These are only effective when using the 'Native' backend. Refer to the documentation for more details on
available arguments. Defaults to {}.
eval_url (Optional[str]): The URL for the evaluation service (e.g., 'http://localhost:8000/v1'). If not
specified, evaluation runs on the locally deployed model. See documentation for more examples. Defaults
to None.
"""
eval_dataset: List[str] = field(default_factory=list)
eval_limit: Optional[int] = None
eval_dataset_args: Optional[Union[dict, str]] = None
eval_generation_config: Optional[Union[dict, str]] = None
eval_output_dir: str = 'eval_output'
eval_backend: Literal['Native', 'OpenCompass', 'VLMEvalKit'] = 'Native'
local_dataset: bool = False
temperature: Optional[float] = 0.
verbose: bool = False
eval_num_proc: int = 16
extra_eval_args: Optional[Union[dict, str]] = field(default_factory=dict)
# If eval_url is set, ms-swift will not perform deployment operations and
# will directly use the URL for evaluation.
eval_url: Optional[str] = None
def __post_init__(self):
super().__post_init__()
self._init_eval_url()
self._init_eval_dataset()
self.eval_dataset_args = json_parse_to_dict(self.eval_dataset_args)
self.eval_generation_config = json_parse_to_dict(self.eval_generation_config)
self.extra_eval_args = json_parse_to_dict(self.extra_eval_args)
self.eval_output_dir = to_abspath(self.eval_output_dir)
logger.info(f'eval_output_dir: {self.eval_output_dir}')
def _init_eval_url(self):
# [compat]
if self.eval_url and 'chat/completions' in self.eval_url:
self.eval_url = self.eval_url.split('/chat/completions', 1)[0]
@staticmethod
def list_eval_dataset(eval_backend=None):
from evalscope.api.registry import BENCHMARK_REGISTRY
from evalscope.backend.opencompass import OpenCompassBackendManager
from evalscope.constants import EvalBackend
res = {
EvalBackend.NATIVE: list(sorted(BENCHMARK_REGISTRY.keys())),
EvalBackend.OPEN_COMPASS: sorted(OpenCompassBackendManager.list_datasets()),
}
try:
from evalscope.backend.vlm_eval_kit import VLMEvalKitBackendManager
vlm_datasets = VLMEvalKitBackendManager.list_supported_datasets()
res[EvalBackend.VLM_EVAL_KIT] = sorted(vlm_datasets)
except ImportError:
# fix cv2 import error
if eval_backend == 'VLMEvalKit':
raise
return res
def _init_eval_dataset(self):
if isinstance(self.eval_dataset, str):
self.eval_dataset = [self.eval_dataset]
all_eval_dataset = self.list_eval_dataset(self.eval_backend)
dataset_mapping = {dataset.lower(): dataset for dataset in all_eval_dataset[self.eval_backend]}
valid_dataset = []
for dataset in self.eval_dataset:
if dataset.lower() not in dataset_mapping:
raise ValueError(
f'eval_dataset: {dataset} is not supported.\n'
f'eval_backend: {self.eval_backend} supported datasets: {all_eval_dataset[self.eval_backend]}')
valid_dataset.append(dataset_mapping[dataset.lower()])
self.eval_dataset = valid_dataset
logger.info(f'eval_backend: {self.eval_backend}')
logger.info(f'eval_dataset: {self.eval_dataset}')
def _init_result_path(self, folder_name: str) -> None:
self.time = dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
result_dir = self.ckpt_dir or f'result/{self.model_suffix}'
os.makedirs(result_dir, exist_ok=True)
self.result_jsonl = to_abspath(os.path.join(result_dir, 'eval_result.jsonl'))
if not self.eval_url:
super()._init_result_path('eval_result')
def _init_torch_dtype(self) -> None:
if self.eval_url:
self.model_meta = get_matched_model_meta(self.model)
self.model_info = None
return
super()._init_torch_dtype()
+150
View File
@@ -0,0 +1,150 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import os
import torch
import torch.distributed as dist
from dataclasses import dataclass
from typing import Literal, Optional
from swift.utils import HfConfigFactory, get_logger, init_process_group, set_default_ddp_config, to_abspath
from .base_args import BaseArguments
from .merge_args import MergeArguments
logger = get_logger()
@dataclass
class ExportArguments(MergeArguments, BaseArguments):
"""ExportArguments is a dataclass that inherits from BaseArguments and MergeArguments.
Args:
output_dir (Optional[str]): Directory to save the exported results. Defaults to None, which automatically sets
a path with an appropriate suffix.
quant_method (Optional[str]): The quantization method. Can be 'awq', 'gptq', 'bnb', 'fp8', or 'gptq_v2'.
Defaults to None. See examples for more details.
quant_n_samples (int): Number of samples for GPTQ/AWQ calibration. Defaults to 256.
quant_batch_size (int): The batch size for quantization. Defaults to 1.
group_size (int): The group size for quantization. Defaults to 128.
to_cached_dataset (bool): Whether to tokenize and export the dataset in advance as a cached dataset. Defaults
to False. Note: You can specify the validation set content through
`--split_dataset_ratio` or `--val_dataset`.
to_ollama (bool): Whether to generate the `Modelfile` required by Ollama. Defaults to False.
to_mcore (bool): Whether to convert Hugging Face format weights to Megatron-Core format. Defaults to False.
to_hf (bool): Whether to convert Megatron-Core format weights to Hugging Face format. Defaults to False.
mcore_model (Optional[str]): The path to the Megatron-Core format model. Defaults to None.
mcore_adapter (Optional[str]): A list of adapter paths for the Megatron-Core format model. Defaults to [].
thread_count (Optional[int]): The number of model shards when `to_mcore` is True. Defaults to None, which
automatically sets the number based on the model size to keep the largest shard under 10GB.
test_convert_precision (bool): Whether to test the precision error of weight conversion between Hugging Face
and Megatron-Core formats. Defaults to False.
test_convert_dtype (str): The dtype to use for the conversion precision test. Defaults to 'float32'.
push_to_hub (bool): Whether to push the output to the Model Hub. Defaults to False. See examples for more
details.
hub_model_id (Optional[str]): The model ID for pushing to the Hub (e.g., 'user_name/repo_name' or 'repo_name').
Defaults to None.
hub_private_repo (bool): Whether the Hub repository is private. Defaults to False.
commit_message (str): The commit message for pushing to the Hub. Defaults to 'update files'.
to_peft_format (bool): Whether to export in PEFT format. This argument is for compatibility and currently has
no effect. Defaults to False.
exist_ok (bool): If the output_dir exists, do not raise an exception and overwrite its contents. Defaults to
False.
"""
output_dir: Optional[str] = None
# awq/gptq
quant_method: Literal['awq', 'gptq', 'bnb', 'fp8', 'gptq_v2'] = None
quant_n_samples: int = 256
quant_batch_size: int = 1
group_size: int = 128
# cached_dataset
to_cached_dataset: bool = False
template_mode: Literal['train', 'rlhf', 'kto'] = 'train'
# ollama
to_ollama: bool = False
# megatron
to_mcore: bool = False
to_hf: bool = False
mcore_model: Optional[str] = None
mcore_adapter: Optional[str] = None
thread_count: Optional[int] = None
test_convert_precision: bool = False
test_convert_dtype: str = 'float32'
# push to ms hub
push_to_hub: bool = False
# 'user_name/repo_name' or 'repo_name'
hub_model_id: Optional[str] = None
hub_private_repo: bool = False
commit_message: str = 'update files'
# compat
to_peft_format: bool = False
exist_ok: bool = False
def load_args_from_ckpt(self) -> None:
if self.to_cached_dataset:
return
super().load_args_from_ckpt()
def _init_output_dir(self):
if self.output_dir is None:
ckpt_dir = self.ckpt_dir or f'./{self.model_suffix}'
ckpt_dir, ckpt_name = os.path.split(ckpt_dir)
if self.to_peft_format:
suffix = 'peft'
elif self.quant_method:
suffix = f'{self.quant_method}'
if self.quant_bits is not None:
suffix += f'-int{self.quant_bits}'
elif self.to_ollama:
suffix = 'ollama'
elif self.merge_lora:
suffix = 'merged'
elif self.to_mcore:
suffix = 'mcore'
elif self.to_hf:
suffix = 'hf'
elif self.to_cached_dataset:
suffix = 'cached_dataset'
else:
return
self.output_dir = os.path.join(ckpt_dir, f'{ckpt_name}-{suffix}')
self.output_dir = to_abspath(self.output_dir)
if not self.exist_ok and os.path.exists(self.output_dir):
raise FileExistsError(f'args.output_dir: `{self.output_dir}` already exists.')
logger.info(f'args.output_dir: `{self.output_dir}`')
def __post_init__(self):
if self.quant_batch_size == -1:
self.quant_batch_size = None
if self.quant_bits and self.quant_method is None:
raise ValueError('Please specify the quantization method using `--quant_method awq/gptq/bnb`.')
if self.quant_method and self.quant_bits is None and self.quant_method != 'fp8':
raise ValueError('Please specify `--quant_bits`.')
if self.quant_method in {'gptq', 'awq'} and self.torch_dtype is None:
self.torch_dtype = torch.float16
if self.to_mcore or self.to_hf:
if self.merge_lora:
self.merge_lora = False
logger.warning('`swift export --to_mcore/to_hf` does not support the `--merge_lora` parameter. '
'To export LoRA delta weights, please use `megatron export`')
self.mcore_model = to_abspath(self.mcore_model, check_path_exist=True)
if not dist.is_initialized():
set_default_ddp_config()
init_process_group(backend=self.ddp_backend, timeout=self.ddp_timeout)
BaseArguments.__post_init__(self)
self._init_output_dir()
self.test_convert_dtype = HfConfigFactory.to_torch_dtype(self.test_convert_dtype)
if self.quant_method in {'gptq', 'awq'} and len(self.dataset) == 0:
raise ValueError(f'self.dataset: {self.dataset}, Please input the quant dataset.')
if self.to_cached_dataset:
self.lazy_tokenize = False
if self.packing:
raise ValueError('Packing will be handled during training; here we only perform tokenization '
'in advance, so you do not need to set up packing separately.')
assert not self.streaming, 'not supported'
+237
View File
@@ -0,0 +1,237 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import datetime as dt
import os
import torch.distributed as dist
from dataclasses import dataclass
from typing import Literal, Optional
from swift.rlhf_trainers import VllmArguments
from swift.utils import get_logger, init_process_group, is_dist, to_abspath
from .base_args import BaseArguments
from .merge_args import MergeArguments
logger = get_logger()
@dataclass
class LmdeployArguments:
"""Holds the configuration arguments for lmdeploy.
Args:
lmdeploy_tp (int): The tensor parallelism size. Defaults to 1.
lmdeploy_session_len (Optional[int]): The maximum session length. Defaults to None.
lmdeploy_cache_max_entry_count (float): The percentage of GPU memory to be used by the K/V cache. Defaults
to 0.8.
lmdeploy_quant_policy (int): The quantization policy for the K/V cache. Set to 4 or 8 for 4-bit or 8-bit
quantization respectively. Defaults to 0, which means no quantization.
lmdeploy_vision_batch_size (int): The `max_batch_size` parameter to be passed to `VisionConfig`. Defaults to 1.
"""
# lmdeploy
lmdeploy_tp: int = 1
lmdeploy_session_len: Optional[int] = None
lmdeploy_cache_max_entry_count: float = 0.8
lmdeploy_quant_policy: int = 0 # e.g. 4, 8
lmdeploy_vision_batch_size: int = 1 # max_batch_size in VisionConfig
def get_lmdeploy_engine_kwargs(self):
kwargs = {
'tp': self.lmdeploy_tp,
'session_len': self.lmdeploy_session_len,
'cache_max_entry_count': self.lmdeploy_cache_max_entry_count,
'quant_policy': self.lmdeploy_quant_policy,
'vision_batch_size': self.lmdeploy_vision_batch_size
}
if dist.is_initialized():
kwargs.update({'devices': [dist.get_rank()]})
return kwargs
@dataclass
class SglangArguments:
"""Arguments for configuring the SGLang backend.
Args:
sglang_tp_size (int): The number of tensor parallel workers. Defaults to 1.
sglang_pp_size (int): The number of pipeline parallel workers. Defaults to 1.
sglang_dp_size (int): The number of data parallel workers. Defaults to 1.
sglang_ep_size (int): The number of expert parallel workers. Defaults to 1.
sglang_enable_ep_moe (bool): Whether to enable expert parallelism for MoE.
Note: This argument has been removed in recent versions of SGLang. Defaults to False.
sglang_mem_fraction_static (Optional[float]): The fraction of GPU memory for the static allocation of model
weights and the KV cache memory pool. Try lowering this value if you encounter GPU out-of-memory errors.
Defaults to None.
sglang_context_length (Optional[int]): The maximum context length for the model. If None, the value from the
model's `config.json` will be used. Defaults to None.
sglang_disable_cuda_graph (bool): Disable CUDA graph for inference. Defaults to False.
sglang_quantization (Optional[str]): The quantization method to use. Defaults to None.
sglang_kv_cache_dtype (str): The data type for K/V cache storage. 'auto' will use the model's data type.
'fp8_e5m2' and 'fp8_e4m3' are available for CUDA 11.8 and later. Defaults to 'auto'.
sglang_enable_dp_attention (bool): Enables data parallelism for the attention mechanism and tensor parallelism
for the feed-forward network (FFN). The data parallel size (dp_size) must equal the tensor parallel size
(tp_size). Currently supported for DeepSeek-V2/3 and Qwen2/3 MoE models. Defaults to False.
sglang_disable_custom_all_reduce (bool): Disable the custom all-reduce kernel and fall back to NCCL. Enabled by
default (True) for stability. Defaults to True.
sglang_speculative_algorithm (Optional[str]): The speculative decoding algorithm. Options include "EAGLE",
"EAGLE3", "NEXTN", "STANDALONE", "NGRAM". Defaults to None.
sglang_speculative_num_steps (Optional[int]): The number of steps to sample from the draft model during
speculative decoding. Defaults to None.
sglang_speculative_eagle_topk (Optional[int]): The number of tokens to sample from the draft model at each step
for the EAGLE2 algorithm. Defaults to None.
sglang_speculative_num_draft_tokens (Optional[int]): The number of tokens to sample from the draft model during
speculative decoding. Defaults to None.
"""
sglang_tp_size: int = 1
sglang_pp_size: int = 1
sglang_dp_size: int = 1
sglang_ep_size: int = 1
sglang_enable_ep_moe: bool = False
sglang_mem_fraction_static: Optional[float] = None
sglang_context_length: Optional[int] = None
sglang_disable_cuda_graph: bool = False
sglang_quantization: Optional[str] = None
sglang_kv_cache_dtype: str = 'auto'
sglang_enable_dp_attention: bool = False
sglang_disable_custom_all_reduce: bool = True
# speculative decoding
# e.g. EAGLE, EAGLE3, NEXTN
sglang_speculative_algorithm: Optional[str] = None
sglang_speculative_num_steps: Optional[int] = None
sglang_speculative_eagle_topk: Optional[int] = None
sglang_speculative_num_draft_tokens: Optional[int] = None
def get_sglang_engine_kwargs(self):
kwargs = {
'tp_size': self.sglang_tp_size,
'pp_size': self.sglang_pp_size,
'dp_size': self.sglang_dp_size,
'ep_size': self.sglang_ep_size,
'enable_ep_moe': self.sglang_enable_ep_moe,
'mem_fraction_static': self.sglang_mem_fraction_static,
'context_length': self.sglang_context_length,
'disable_cuda_graph': self.sglang_disable_cuda_graph,
'quantization': self.sglang_quantization,
'kv_cache_dtype': self.sglang_kv_cache_dtype,
'enable_dp_attention': self.sglang_enable_dp_attention,
'disable_custom_all_reduce': self.sglang_disable_custom_all_reduce,
'speculative_algorithm': self.sglang_speculative_algorithm,
'speculative_num_steps': self.sglang_speculative_num_steps,
'speculative_eagle_topk': self.sglang_speculative_eagle_topk,
'speculative_num_draft_tokens': self.sglang_speculative_num_draft_tokens,
}
if self.task_type == 'embedding':
kwargs['task_type'] = 'embedding'
return kwargs
@dataclass
class InferArguments(MergeArguments, LmdeployArguments, SglangArguments, VllmArguments, BaseArguments):
"""Arguments for model inference.
A dataclass that extends BaseArguments, MergeArguments, VllmArguments, and LmdeployArguments to define all
arguments required for model inference.
Args:
infer_backend (Literal['transformers', 'vllm', 'sglang', 'lmdeploy']): The inference acceleration
backend to use. Defaults to 'transformers'.
result_path (Optional[str]): The path to store inference results in JSONL format. If the file already exists,
new results will be appended. If None, results are saved in the checkpoint directory (if available) or
'./result'. The final path will be printed to the console. Defaults to None.
write_batch_size (int): The batch size for writing results to `result_path`. A value of -1 means no limit.
Defaults to 1000.
metric (Optional[str]): The metric to use for evaluating inference results. Supported values are 'acc' and
'rouge'. If None, no evaluation is performed. Defaults to None.
max_batch_size (int): The maximum batch size for inference, effective only when `infer_backend` is
'transformers'. A value of -1 means no limit. Defaults to 1.
val_dataset_sample (Optional[int]): The number of samples to use from the inference dataset. If None, the
entire dataset is used. Defaults to None.
reranker_use_activation (bool): Whether to apply a sigmoid activation to the scores during reranker inference.
Defaults to True.
"""
# `pt` is used for swift3.x shell script compatibility.
infer_backend: Literal['vllm', 'transformers', 'sglang', 'lmdeploy', 'pt'] = 'transformers'
result_path: Optional[str] = None
write_batch_size: int = 1000
metric: Literal['acc', 'rouge'] = None
# for transformers engine
max_batch_size: int = 1
# only for inference
val_dataset_sample: Optional[int] = None
# for reranker
reranker_use_activation: bool = True
def _get_result_path(self, folder_name: str) -> str:
result_dir = self.ckpt_dir or f'result/{self.model_suffix}'
os.makedirs(result_dir, exist_ok=True)
result_dir = to_abspath(os.path.join(result_dir, folder_name))
os.makedirs(result_dir, exist_ok=True)
time = dt.datetime.now().strftime('%Y%m%d-%H%M%S')
return os.path.join(result_dir, f'{time}.jsonl')
def _init_result_path(self, folder_name: str) -> None:
if self.result_path is not None:
self.result_path = to_abspath(self.result_path)
return
# By default, a result_path file is automatically created
# when a validation or evaluation dataset is present.
if self._val_dataset_exists or getattr(self, 'eval_dataset', None):
self.result_path = self._get_result_path(folder_name)
logger.info(f'args.result_path: {self.result_path}')
def _init_stream(self):
self.eval_human = not self._val_dataset_exists
logger.info(f'Setting args.eval_human: {self.eval_human}')
if self.stream is None:
self.stream = self.eval_human
if self.stream and self.num_beams != 1:
self.stream = False
logger.info('Setting args.stream: False')
def _init_ddp(self):
if not is_dist():
return
eval_human = getattr(self, 'eval_human', False)
assert not eval_human and not self.stream, (
'In DDP scenarios, interactive interfaces and streaming output are not supported.'
f'args.eval_human: {eval_human}, args.stream: {self.stream}')
self._init_device()
init_process_group(backend=self.ddp_backend, timeout=self.ddp_timeout)
def __post_init__(self) -> None:
if self.infer_backend == 'pt':
self.infer_backend = 'transformers' # compat swift3.x
logger.warning('args.infer_backend: `pt` is deprecated, please use args.infer_backend: `transformers`.')
BaseArguments.__post_init__(self)
VllmArguments.__post_init__(self)
self._init_vllm_async_engine()
# Default to False for swift infer (non-encode tasks)
if self.vllm_use_async_engine is None:
self.vllm_use_async_engine = False
self._init_result_path('infer_result')
self._init_ddp()
def _init_vllm_async_engine(self):
"""Initialize vllm_use_async_engine based on task_type.
Encode tasks (embedding, seq_cls, reranker, generative_reranker) require
async engine because vLLM's synchronous LLMEngine does not have the `encode` method.
Note: This method only handles encode tasks. For non-encode tasks, the default value
should be set by subclasses (DeployArguments sets True, RolloutArguments uses
_set_default_engine_type, InferArguments defaults to False).
"""
# Task types that require vLLM's encode() method, which is only available in AsyncLLMEngine
encode_task_types = ('embedding', 'seq_cls', 'reranker', 'generative_reranker')
is_vllm_encode_task = self.infer_backend == 'vllm' and self.task_type in encode_task_types
if is_vllm_encode_task:
if self.vllm_use_async_engine is None:
self.vllm_use_async_engine = True
elif not self.vllm_use_async_engine:
raise ValueError(
f'task_type={self.task_type} requires vllm_use_async_engine=True. '
f'The synchronous vLLM LLMEngine does not support the `encode` method for encode tasks. '
f'Please set --vllm_use_async_engine true or remove the explicit false setting.')
+23
View File
@@ -0,0 +1,23 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
from dataclasses import dataclass
from swift.utils import get_logger
logger = get_logger()
@dataclass
class MergeArguments:
"""A dataclass that holds configuration for merging models.
This dataclass stores all the arguments needed to configure the model merging process.
Args:
merge_lora (bool): Whether to merge LoRA adapters. This parameter supports `lora`, `llamapro`, and `longlora`.
Defaults to False.
safe_serialization (bool): Whether to use safetensors for serialization. Defaults to True.
max_shard_size (str): The maximum size of a single saved shard file. Defaults to '5GB'.
"""
merge_lora: bool = False
safe_serialization: bool = True
max_shard_size: str = '5GB'
+10
View File
@@ -0,0 +1,10 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
from dataclasses import dataclass
from .sft_args import SftArguments
@dataclass
class PretrainArguments(SftArguments):
use_chat_template: bool = False
loss_scale: str = 'all'
+675
View File
@@ -0,0 +1,675 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import os
from dataclasses import dataclass, field
from typing import Any, Dict, List, Literal, Optional
from swift.model import MODEL_MAPPING
from swift.rlhf_trainers import GRPOArgumentsMixin
from swift.template import TEMPLATE_MAPPING
from swift.utils import get_current_device, get_logger, is_master, is_mp, json_parse_to_dict, set_default_ddp_config
from .sft_args import SftArguments
logger = get_logger()
rlhf_support_vllm_types = ['grpo', 'gkd']
@dataclass
class RewardModelArguments:
"""Arguments pertaining to the reward model.
Args:
reward_model (Optional[List[str]]): The model ID or a local path to the reward model. Same as the `model`
argument. Defaults to None.
reward_adapters (List[str]): The path(s) to LoRA adapter weights to be loaded for the reward model. Useful for
using LoRA weights from SFT as the reward model. Defaults to an empty list (`[]`).
reward_model_type (Optional[List[str]]): The model type of the reward model. Same as the `model_type` argument.
If not specified, it's often inferred. Defaults to None.
reward_model_revision (Optional[List[str]]): The specific model version to use for the reward model. Same as
the `model_revision` argument. Defaults to None.
reward_template (Optional[List[str]]): The template to use for the reward model. Defaults to None.
"""
reward_model: Optional[List[str]] = None
reward_adapters: List[str] = field(default_factory=list)
reward_model_type: Optional[List[str]] = field(
default=None, metadata={'help': f'model_type choices: {list(MODEL_MAPPING.keys())}'})
reward_model_revision: Optional[List[str]] = None
reward_template: Optional[List[str]] = field(
default=None, metadata={'help': f'template choices: {list(TEMPLATE_MAPPING.keys())}'})
@dataclass
class TeacherModelArguments:
"""Arguments for configuring the teacher model.
Args:
teacher_model (Optional[str]): The model ID or a local path to the teacher model. Analogous to the main
`model` argument. For GKD, there are three modes:
- Not set (None): Self-distillation with dynamic teacher (teacher = current student weights).
- Same as `model` with LoRA training: Self-distillation with fixed teacher. Automatically optimized
to use `disable_adapter()` to get base model logits without loading an extra model.
- Different from `model`: Standard GKD with an independent frozen teacher model.
Defaults to None.
teacher_adapters (List[str]): A list of paths to LoRA weights. These weights, often produced by SFT, are loaded
to form the teacher model. Defaults to an empty list (`[]`).
teacher_model_type (Optional[str]): The model type of the teacher model. If not specified, it's often inferred.
Analogous to the main `model_type` argument. Defaults to None.
teacher_model_revision (Optional[str]): The specific model version of the teacher model to use. Analogous to
the main `model_revision` argument. Defaults to None.
teacher_deepspeed (Optional[str]): The teacher model's deepspeed configuration. This can be a JSON file path or
one of the following values: 'zero0', 'zero1', 'zero2', 'zero3', 'zero2_offload', 'zero3_offload'. If not
provided, it defaults to using the same DeepSpeed configuration as the main training model. Analogous to
the main `deepspeed` argument.
teacher_model_server (Optional[str]): The URL of the teacher model server (e.g., 'http://localhost:8000').
When set, the teacher logprobs will be fetched from the external API service (e.g., swift deploy, vLLM)
instead of loading a local teacher model. This enables using larger teacher models or services hosted
remotely. When this is set, `teacher_model` is not required. Defaults to None.
offload_teacher_model (bool): Whether to offload the teacher model to CPU memory to save VRAM during GKD
or OPD-RL training. When enabled, the teacher model is loaded to GPU only during forward pass and
offloaded back to CPU afterwards. Defaults to False.
"""
teacher_model: Optional[str] = None
teacher_adapters: List[str] = field(default_factory=list)
teacher_model_type: Optional[str] = field(
default=None, metadata={'help': f'model_type choices: {list(MODEL_MAPPING.keys())}'})
teacher_model_revision: Optional[str] = None
teacher_deepspeed: Optional[str] = field(
default=None,
metadata={
'help':
'DeepSpeed configuration for teacher model. '
'Can be a path to a json file or one of: zero0, zero1, zero2, zero3, zero2_offload, zero3_offload'
})
teacher_model_server: Optional[str] = field(
default=None,
metadata={
'help':
'URL of the teacher model server (e.g., http://localhost:8000). '
'When set, teacher logprobs are fetched via API instead of loading a local model. '
'Supports multi-teacher via JSON list of {url, tags}.'
})
offload_teacher_model: bool = False
@dataclass
class PPOArguments:
"""Arguments for configuring the PPO training.
Args:
num_ppo_epochs (int): Number of epochs to train. Defaults to 4.
whiten_rewards (bool): Whether to whiten the rewards. Defaults to False.
kl_coef (float): KL coefficient. Defaults to 0.05.
cliprange (float): Clip range. Defaults to 0.2.
vf_coef (float): Value function coefficient. Defaults to 0.1.
cliprange_value (float): Clip range for the value function. Defaults to 0.2.
gamma (float): Discount factor. Defaults to 1.0.
lam (float): Lambda value for GAE. Defaults to 0.95.
num_mini_batches (int): Defaults to 1.
local_rollout_forward_batch_size (int): Defaults to 64.
num_sample_generations (int): Number of generations. Defaults to 10.
response_length (Optional[int]): (Deprecated) Compatibility parameter. Use `max_completion_length` instead.
Defaults to None.
missing_eos_penalty (Optional[float]): Defaults to None.
"""
num_ppo_epochs: int = 4
whiten_rewards: bool = False
kl_coef: float = 0.05
cliprange: float = 0.2
vf_coef: float = 0.1
cliprange_value: float = 0.2
gamma: float = 1.0
lam: float = 0.95
num_mini_batches: int = 1
local_rollout_forward_batch_size: int = 64
num_sample_generations: int = 10
response_length: Optional[int] = None # compat. use max_completion_length instead
missing_eos_penalty: Optional[float] = None
@dataclass
class GRPOArguments(GRPOArgumentsMixin):
"""A dataclass for configuring GRPO training.
These arguments control the hyperparameters specific to the GRPO algorithm.
Args:
num_generations (int): The number of completions to generate for each prompt. This corresponds to the G value
in the GRPO paper. The total generation batch size (e.g., `generation_batch_size` or `steps_per_generation
* per_device_batch_size * num_processes`) must be divisible by this number. Defaults to 8.
num_generations_eval (Optional[int]): Number of generations to sample during evaluation. This allows
using fewer generations during evaluation to save computation. If `None`, uses the value of
`num_generations`. Defaults to None.
reward_funcs (List[str]): A list of reward function names to use for the GRPO algorithm. Available built-in
options include 'accuracy', 'format', 'cosine', 'repetition', and 'soft_overlong'
(see swift/rewards/orm.py). Custom reward functions can also be defined. Defaults to an empty list.
reward_weights (List[float]): A list of weights for each reward source. The length must match the total number
of reward functions (from `reward_funcs`) plus any external reward models. If `None`, all rewards are
weighted equally with a value of 1.0. Note: If an external `--reward_model` is used, it is treated as the
last reward source in the sequence. Defaults to None.
log_completions (bool): Whether to log the model's generated completions during training. This is designed to
be used with an experiment tracker like WandB or SwanLab (`--report_to wandb`/`swanlab`). If enabled
without a tracker, completions are saved to `completions.jsonl` in the checkpoint directory. Defaults to
False.
num_iterations (int): The number of update steps to perform for each data sample. This corresponds to the K
value in the GRPO paper. Defaults to 1.
truncation_strategy (Literal['delete', 'left', 'right', 'split', None]): The strategy for handling input
sequences that exceed `max_length`. Supported options: 'delete' to discard the sample, 'left' to truncate
from the beginning, 'right' to truncate from the end. Defaults to None, and then sets to 'left' in the
`_init_grpo` function.
Note that for multimodal models, left pruning may prune multimodal tokens, causing shape mismatch errors
in the forward feed. Using the `delete` method will resample other data from the original dataset to
supplement excessively long data and examples with encoding failures.
"""
num_generations: int = 8 # G in the GRPO paper
reward_funcs: List[str] = field(default_factory=list)
reward_weights: List[float] = None
log_completions: bool = False
# multi step
num_iterations: int = 1
truncation_strategy: Literal['delete', 'left', 'right', 'split', None] = None
@dataclass
class RLHFArguments(TeacherModelArguments, GRPOArguments, PPOArguments, RewardModelArguments, SftArguments):
"""A dataclass holding arguments for Reinforcement Learning from Human Feedback.
Args:
rlhf_type (str): The type of human alignment algorithm to use. Supports 'dpo', 'orpo', 'simpo', 'kto', 'cpo',
'rm', 'ppo', 'grpo', and 'gkd'. Defaults to 'dpo'.
ref_model (Optional[str]): The model path for the reference model. Required when using 'dpo', 'kto', 'ppo',
or 'grpo' with full-parameter training. Defaults to None, which will set it to the value of the `--model`
argument.
ref_adapters (List[str]): LoRA adapters for the reference model. If you are using LoRA weights from SFT for
DPO/KTO/GRPO, set both `--adapters` and `--ref_adapters` to the SFT checkpoint path. When resuming from an
RLHF checkpoint, set `--resume_from_checkpoint` to the RLHF checkpoint and `--ref_adapters` to the SFT
checkpoint. Defaults to an empty list.
ref_model_type (Optional[str]): The model type of the reference model. Same as `model_type`. Defaults to None.
ref_model_revision (Optional[str]): The model revision of the reference model. Same as `model_revision`.
Defaults to None.
beta (Optional[float]): The beta parameter for RLHF, controlling the deviation from the reference model.
A higher value implies less deviation. If None, uses algorithm-specific defaults: 2.0 for 'simpo', 0.04
for 'grpo', 0.5 for 'gkd', and 0.1 for others. Defaults to None.
label_smoothing (float): The label smoothing value for DPO. A value of 0 disables it. Defaults to 0.
max_completion_length (int): The maximum generation length for GRPO/PPO/GKD algorithms. Defaults to 512.
loss_scale (Optional[str]): Overrides the template parameter. During RLHF training, this defaults to
'last_round'.
rpo_alpha (Optional[float]): The alpha parameter from the RPO paper, controlling the weight of the SFT loss
(NLL term). The loss is calculated as `dpo_loss + rpo_alpha * sft_loss`. If None, the SFT loss is not
included.
ld_alpha (Optional[float]): The alpha parameter from the LD-DPO paper, which weights the log probabilities of
the sequence part beyond the common prefix to mitigate length preference. Defaults to None.
discopop_tau (float): The temperature parameter from the DiscoPOP paper, used to scale the log-ratio. Effective
when `loss_type` is 'discopop'. Defaults to 0.05.
loss_type (Optional[List[str]]): The type of loss function. Defaults to algorithm-specific values (e.g.,
'sigmoid' for DPO). Multiple values can be passed for mixed training (MPO), which requires `loss_weights`
to be set.
loss_weights (Optional[List[float]]): When multiple `loss_type` values are set for DPO, this specifies the
weights for each loss term. Defaults to None.
cpo_alpha (float): The coefficient for the NLL loss in the CPO/SimPO loss function. Defaults to 1.0.
simpo_gamma (float): The reward margin term in the SimPO algorithm. The paper suggests a value between 0.5 and
1.5. Defaults to 1.0.
desirable_weight (float): In KTO, the weight applied to the desirable loss to counteract data imbalance.
Defaults to 1.0.
undesirable_weight (float): In KTO, the weight applied to the undesirable loss to counteract data imbalance.
Defaults to 1.0.
temperature (float): The temperature for sampling, used in PPO, GRPO, and GKD algorithms. Defaults to 0.9.
center_rewards_coefficient (Optional[float]): Used for Reward Model (RM) training. A coefficient to encourage
the reward model to output rewards with a mean of zero. A value of 0.01 is recommended. Defaults to None.
sft_alpha (float): The weight for the SFT loss component in GKD. The final loss is calculated as
gkd_loss + sft_alpha * sft_loss`. Defaults to 0.
lmbda (float): The lambda parameter for GKD, balancing policy and value losses. Defaults to 0.5.
seq_kd (bool): Deprecated. Sequential KD (teacher-generated responses) is not implemented.
gkd_logits_topk (Optional[int]): The number of top-k logits to use for KL divergence computation in GKD.
If None, uses full vocabulary for KL computation (more accurate but memory-intensive).
If set to a positive integer, only top-k teacher logits are used (more efficient).
When using `teacher_model_server`, this is limited by the server's `max_logprobs` setting
(vLLM default is 20, can be increased with `--max-logprobs`). Defaults to None.
max_new_tokens (Optional[int]): A backward-compatibility argument. Please use `max_completion_length` instead.
Defaults to None.
"""
rlhf_type: Literal['dpo', 'orpo', 'simpo', 'kto', 'cpo', 'rm', 'ppo', 'grpo', 'gkd'] = 'dpo'
ref_model: Optional[str] = None
ref_adapters: List[str] = field(default_factory=list)
ref_model_type: Optional[str] = field(
default=None, metadata={'help': f'model_type choices: {list(MODEL_MAPPING.keys())}'})
ref_model_revision: Optional[str] = None
beta: Optional[float] = None
label_smoothing: float = 0
max_completion_length: int = 512
loss_scale: Optional[str] = None # 'last_round'
# DPO
rpo_alpha: Optional[float] = None
ld_alpha: Optional[float] = None # α parameter from the LD-DPO paper
discopop_tau: float = 0.05 # τ/temperature parameter from the DiscoPOP paper
loss_type: Optional[List[str]] = None
loss_weights: Optional[List[float]] = None
# CPO
cpo_alpha: float = 1.
# SimPO
simpo_gamma: float = 1
# KTO
desirable_weight: float = 1.0
undesirable_weight: float = 1.0
# PPO/GRPO/GKD
temperature: float = 0.9
# RM
center_rewards_coefficient: Optional[float] = None
# GKD
sft_alpha: float = 0
lmbda: float = 0.5
seq_kd: bool = False # Deprecated
gkd_logits_topk: Optional[int] = None
# compat
max_new_tokens: Optional[int] = None # use max_completion_length instead
def _prepare_training_args(self, training_args: Dict[str, Any]) -> None:
if self.rlhf_type == 'ppo':
training_args['world_size'] = self.global_world_size
def __post_init__(self):
self._process_loss_type()
self._init_grpo()
self._init_rm()
self._init_simpo()
self._init_max_completion_length()
self._init_padding_side()
self._set_default()
self._init_rollout()
self._init_teacher_deepspeed()
GRPOArguments.__post_init__(self)
SftArguments.__post_init__(self)
self._check_sequence_parallel()
self._check_teacher()
self._check_grpo()
self._check_gkd()
if isinstance(self.ref_adapters, str):
self.ref_adapters = [self.ref_adapters]
if self.rlhf_type == 'grpo' and self.beta == 0.0:
self.ref_model = None
elif self.rlhf_type in ['dpo', 'kto', 'ppo', 'grpo'] and self.tuner_type == 'full':
self.ref_model = self.ref_model or self.model
self.ref_model_type = self.ref_model_type or self.model_type
self.ref_model_revision = self.ref_model_revision or self.model_revision
elif self.ref_model is not None:
raise ValueError('CPO/ORPO or LoRA training does not require a ref_model to be passed in.')
def _set_loss_scale(self):
if self.loss_scale is None:
if self.rlhf_type == 'orpo' and not self.model_meta.is_multimodal:
# Avoid padding labels during the model's forward pass in multimodal models.
# Some multimodal models do not expand the image pad token.
self.loss_scale = 'default'
elif self.rlhf_type in ('grpo', 'gkd'):
if self.multi_turn_scheduler:
self.loss_scale = 'default'
else:
self.loss_scale = 'last_round'
else:
self.loss_scale = 'last_round'
super()._set_loss_scale()
def _process_loss_type(self):
if self.loss_type is None:
return
if isinstance(self.loss_type, list):
num_loss_types = len(self.loss_type)
if num_loss_types > 1:
assert self.rlhf_type == 'dpo', (f'Multiple loss types ({self.loss_type}) are only supported for DPO. '
f'Current rlhf_type: {self.rlhf_type}.')
from trl.trainer.dpo_config import DPOConfig
assert 'loss_weights' in DPOConfig.__dict__, (
'Multiple loss types requires trl >= 0.20, please install trl `pip install -U trl`')
if hasattr(self.loss_type, '__len__') and len(self.loss_type) == 1:
self.loss_type = self.loss_type[0]
# Validate loss_type
if self.loss_weights is not None:
assert self.rlhf_type == 'dpo'
loss_types = self.loss_type if isinstance(self.loss_type, list) else [self.loss_type]
if len(self.loss_weights) != len(loss_types):
raise ValueError(f'Length of loss_weights list ({self.loss_weights}) must match number of loss types '
f'({loss_types}).')
def _init_grpo(self):
if self.rlhf_type != 'grpo':
return
if self.cached_dataset or self.cached_val_dataset:
raise ValueError('cached_dataset is not supported for GRPO.')
if self.use_vllm:
set_default_ddp_config()
if self.async_generate or not self.use_vllm or self.vllm_mode == 'server':
self.sleep_level = 0
self.remove_unused_columns = False
logger.info(f'Setting args.remove_unused_columns: {self.remove_unused_columns}')
if self.truncation_strategy is None:
self.truncation_strategy = 'left'
if self.truncation_strategy not in {'left', 'delete'}:
raise ValueError("GRPO requires `truncation_strategy 'left' or 'delete'`, "
f"Current value: `truncation_strategy='{self.truncation_strategy}'`.")
if self.beta is None:
self.beta = 0.04 # https://arxiv.org/abs/2402.03300
if self.async_generate:
logger.info('Using async mode. This is a approximate version which '
'will use the old weights to generate responses to accelerate. '
'This will ignore the `CLIP` of advantages, if you found the training '
'is unstable, you may consider using --async_generate false.')
if 'soft_overlong' in self.reward_funcs:
assert self.soft_cache_length is not None, \
'The soft_cache_length must be set when using soft overlong rewards.'
if self.soft_max_length is None:
self.soft_max_length = self.max_completion_length
logger.info(f'Auto-configured soft_max_length = max_completion_length {self.max_completion_length}')
if self.kl_in_reward is None:
if self.advantage_estimator == 'grpo':
self.kl_in_reward = False
elif self.advantage_estimator in ['rloo', 'reinforce_plus_plus']:
self.kl_in_reward = True
else:
raise ValueError(f'Invalid advantage_estimator: {self.advantage_estimator}')
# disable normalization, REAL https://arxiv.org/abs/2602.05630
if self.loss_type == 'real':
self.scale_rewards = 'none'
logger.warning(
f"[REAL] scale_rewards='{self.scale_rewards}' is ignored. "
"It will be forced to 'none' because 'loss_type = real' does not support reward normalization.")
if self.scale_rewards is None:
if self.advantage_estimator == 'grpo':
self.scale_rewards = 'group'
elif self.advantage_estimator == 'rloo':
self.scale_rewards = 'none'
elif self.advantage_estimator == 'reinforce_plus_plus':
self.scale_rewards = 'batch'
else:
raise ValueError(f'Invalid advantage_estimator: {self.advantage_estimator}')
def _check_teacher(self):
self._teacher_use_disable_adapter = False
if self.rlhf_type not in ['grpo', 'gkd']:
if self.teacher_model is not None or self.teacher_model_server is not None:
logger.warning(f'teacher_model / teacher_model_server is ignored for rlhf_type={self.rlhf_type!r} '
'(only used by gkd and grpo/OPD-RL).')
return
teacher_set = self.teacher_model is not None or self.teacher_model_server is not None
if not teacher_set:
if self.rlhf_type == 'gkd':
logger.info('No teacher_model specified. Using self-distillation mode (teacher = student).')
if self.use_liger_kernel:
raise ValueError('Self-distillation mode with liger kernel loss is not supported yet')
if self.rlhf_type == 'grpo' and self.num_generations == 1:
raise ValueError('num_generations must be greater than 1 for GRPO')
return
if self.rlhf_type == 'grpo' and self.use_liger_kernel:
raise ValueError('OPD-RL is not supported with use_liger_kernel.')
if self.teacher_model is not None and self.teacher_model_server is not None:
raise ValueError('setting both `teacher_model` and `teacher_model_server` is not supported.')
# Validate teacher_model_server: accept single URL or JSON multi-teacher config.
if self.teacher_model_server is not None:
from swift.rlhf_trainers.gkd_helpers import parse_teacher_model_server
# Parse early to fail fast on invalid JSON; result is re-parsed by the trainer.
parse_teacher_model_server(self.teacher_model_server)
# Self-distillation: teacher_model == student model
if self.teacher_model is not None and self.teacher_model == self.model:
if self.tuner_type == 'lora':
logger.info('LoRA + same teacher_model: using disable_adapter() for fixed teacher (no extra model).')
self._teacher_use_disable_adapter = True
self.teacher_model = None
else:
# Full training + same teacher_model: a separate frozen copy will be loaded as fixed teacher.
pass
def _init_rollout(self):
if self.rlhf_type not in rlhf_support_vllm_types:
return
if self.use_vllm and os.getenv('SWIFT_AUDIO_LOAD_BACKEND') is None:
# align with vLLM audio load backend
os.environ['SWIFT_AUDIO_LOAD_BACKEND'] = 'soundfile_pyav'
if self.vllm_mode is not None and not self.use_vllm:
raise ValueError('vllm_mode is not supported when use_vllm is false')
if self.vllm_mode is None and self.use_vllm:
raise ValueError('vllm_mode is required when use_vllm is true')
self._init_external_vllm()
if self.vllm_mode == 'server':
assert not self.use_vllm or self.vllm_server_host is not None or self.vllm_server_base_url is not None
if self.async_generate:
assert self.vllm_mode == 'server', 'async generate require vllm_mode == server, '
'please deploy vLLM server by `swift rollout` and assign with `vllm_server_host` '
'for more infomations, please check '
'https://swift.readthedocs.io/en/latest/Instruction/GRPO/getstarted/GRPO.html'
if not self.use_vllm and self.vllm_tensor_parallel_size != 1:
self.vllm_tensor_parallel_size = 1
logger.warning('set vllm_tensor_parallel_size to 1 since use_vllm false')
self._external_vllm_warning()
def _init_padding_side(self):
if self.rlhf_type in {'ppo', 'gkd'}:
self.padding_side = 'left'
# TODO: streaming, MLLM
def _init_max_completion_length(self):
max_completion_length = self.response_length or self.max_new_tokens or self.max_completion_length
self.max_completion_length = self.max_new_tokens = self.response_length = max_completion_length
def _init_metric_for_best_model(self):
if self.rlhf_type == 'grpo' and self.metric_for_best_model is None:
self.metric_for_best_model = 'reward'
super()._init_metric_for_best_model()
if self.rlhf_type == 'ppo':
self.metric_for_best_model = None
self.greater_is_better = None
def _init_simpo(self):
if self.rlhf_type != 'simpo':
return
self.rlhf_type = 'cpo'
if self.loss_type is None:
self.loss_type = 'simpo'
if self.beta is None:
self.beta = 2.
def _init_rm(self):
if self.rlhf_type == 'rm':
self.task_type = 'seq_cls'
self.num_labels = 1
def _init_external_vllm(self):
if self.rlhf_type not in rlhf_support_vllm_types or (self.vllm_server_host is None
and self.vllm_server_base_url is None):
return
from swift.rlhf_trainers import VLLMClient
if is_master():
logger.info('Start connecting to vLLM server')
self.vllm_client = VLLMClient(
base_urls=self.vllm_server_base_url,
hosts=self.vllm_server_host,
server_ports=self.vllm_server_port,
group_ports=self.vllm_server_group_port,
connection_timeout=self.vllm_server_timeout)
self.vllm_client.close_communicator()
self.vllm_client.init_communicator(device=get_current_device())
logger.info('Connected to vLLM server')
def _set_default(self):
if self.beta is None:
if self.rlhf_type == 'gkd':
self.beta = 0.5
else:
self.beta = 0.1
if self.loss_type is None:
if self.rlhf_type in ['dpo', 'cpo']:
self.loss_type = 'sigmoid' # else None
elif self.rlhf_type in ['kto']:
self.loss_type = 'kto'
elif self.rlhf_type == 'grpo':
self.loss_type = 'grpo'
if self.gradient_accumulation_steps is None:
if self.rlhf_type == 'grpo':
self.gradient_accumulation_steps = 1
logger.info('Setting default gradient_accumulation_steps to 1 for GRPO.')
def _check_grpo(self):
if self.rlhf_type != 'grpo':
return
import importlib.metadata
import trl
from packaging import version
trl_version = version.parse(trl.__version__)
assert trl_version >= version.parse('0.20'), ('Your current version of `trl` is outdated. '
'Please update it by running: pip install -U trl')
if is_mp() and self.use_vllm:
raise ValueError('GRPO with vLLM is not compatible with `device_map`. '
'Please set NPROC_PER_NODE equal to num_processes.')
if self.use_liger_kernel:
liger_kernel_version = version.parse(importlib.metadata.version('liger-kernel'))
if liger_kernel_version < version.parse('0.7.0'):
raise ValueError('Please update liger-kernel to 0.7.0 or later: pip install -U liger-kernel')
if self.delta is not None:
raise ValueError('Liger loss does not support two-sided GRPO loss yet.')
if self.sequence_parallel_size > 1:
raise ValueError('Liger loss does not support sequence parallel yet.')
if self.padding_free:
raise ValueError('Liger loss does not support padding free yet.')
if self.top_entropy_quantile < 1.0:
raise ValueError('Liger loss does not support entropy mask yet.')
if self.log_entropy:
raise ValueError('Liger loss does not support log entropy yet.')
if self.off_policy_sequence_mask_delta is not None:
raise ValueError('Liger loss does not support off-policy sequence masking yet.')
assert self.importance_sampling_level in [
'token', 'sequence'
], ('Liger loss currently only support token-level and sequence-level importance sampling. '
'Please set `importance_sampling_level` to `token` or `sequence`.')
if self.advantage_estimator != 'grpo':
raise ValueError('Liger loss currently only support grpo advantage estimator')
if self.async_generate and self.multi_turn_scheduler is not None:
raise NotImplementedError('Currently, async_generate is not supported with multi-turn functionality.')
self._check_opd_rl()
def _check_opd_rl(self):
"""Fail-fast OPD-RL (teacher distillation on GRPO) parameter compatibility.
A teacher turns GRPO into OPD-RL, where the teacher signal is a *per-token* advantage
(the signed teacher log-ratio). Features that require a *per-sequence* advantage (typically
sign-based judgments) or reward variance are incompatible; reject them here rather than
deep inside the loss / advantage code. ``_check_teacher`` has already run, so
``_teacher_use_disable_adapter`` is resolved.
"""
opd_rl = (
self.teacher_model is not None or self.teacher_model_server is not None
or self._teacher_use_disable_adapter)
if not opd_rl:
return
# loss types / masks that reduce the advantage to a per-sequence scalar (sign-based).
if self.loss_type in ['real', 'fipo']:
raise ValueError(f'OPD-RL (teacher) does not support loss_type={self.loss_type!r} '
'(it needs a per-sequence advantage).')
if self.off_policy_sequence_mask_delta is not None:
raise ValueError('OPD-RL (teacher) does not support off_policy_sequence_mask_delta '
'(it needs a per-sequence advantage).')
# Pure distillation (no reward functions): the base GRPO advantage is 0, so reward-variance
# driven features have no signal to act on.
if not self.reward_funcs:
if self.dynamic_sample:
raise ValueError('dynamic_sample requires reward_funcs (it filters groups by reward std); '
'pure OPD-RL distillation has no reward variance.')
if self.scale_rewards == 'gdpo':
raise ValueError("scale_rewards='gdpo' requires reward_funcs; pure OPD-RL distillation has none.")
def _external_vllm_warning(self):
if self.rlhf_type not in rlhf_support_vllm_types or not self.vllm_server_host:
return
if self.vllm_max_model_len is not None:
logger.warning(
"Configuration conflict: 'vllm_max_model_len=%s' is ignored for external vLLM. "
'Please specify it when launching the inference service: '
'`swift rollout --vllm_max_model_len <value>`', self.vllm_max_model_len)
def _check_padding_free(self):
super()._check_padding_free()
if self.padding_free or self.packing:
supported_types = ['grpo', 'dpo', 'kto', 'gkd']
if self.rlhf_type not in supported_types:
raise NotImplementedError(
f"The current rlhf_type '{self.rlhf_type}' does not support padding_free/packing. "
'Please set --padding_free/packing to false.')
def _check_sequence_parallel(self):
if self.sequence_parallel_size > 1:
supported_types = ['grpo', 'dpo']
if self.rlhf_type not in supported_types:
raise NotImplementedError(
f"The current rlhf_type '{self.rlhf_type}' does not support sequence_parallel. "
'Please set --sequence_parallel_size to 1.')
def _init_teacher_deepspeed(self):
"""Initialize teacher_deepspeed configuration similar to _init_deepspeed in SftArguments"""
if not self.teacher_deepspeed:
return
# Get the same ds_config_folder as main model
ds_config_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'config'))
deepspeed_mapping = {
name: f'{name}.json'
for name in ['zero0', 'zero1', 'zero2', 'zero3', 'zero2_offload', 'zero3_offload']
}
# Check if teacher_deepspeed is a predefined name
for ds_name, ds_config in deepspeed_mapping.items():
if self.teacher_deepspeed == ds_name:
self.teacher_deepspeed = os.path.join(ds_config_folder, ds_config)
break
# Parse the config file to dict
self.teacher_deepspeed = json_parse_to_dict(self.teacher_deepspeed)
logger.info(f'Using teacher_deepspeed config: {self.teacher_deepspeed}')
def _check_gkd(self):
if self.rlhf_type != 'gkd':
return
if is_mp() and self.use_vllm:
raise ValueError('GKD with vLLM is not compatible with `device_map`. '
'Please set NPROC_PER_NODE equal to num_processes.')
if self.async_generate:
raise NotImplementedError('Currently, async_generate is not supported for GKD.')
# seq_kd (teacher-generated responses) is not implemented; raise early.
if self.seq_kd:
raise NotImplementedError('seq_kd=True (Sequential KD with teacher generation) is deprecated.')
# When using teacher_model_server, gkd_logits_topk is required (API only returns top-k logprobs)
if self.teacher_model_server is not None:
if self.gkd_logits_topk is None:
raise ValueError('gkd_logits_topk is required when using teacher_model_server')
# Validate gkd_logits_topk
if self.gkd_logits_topk is not None and self.gkd_logits_topk <= 0:
raise ValueError(f'gkd_logits_topk must be a positive integer, got {self.gkd_logits_topk}')
if self.gkd_logits_topk is not None and self.use_liger_kernel:
raise ValueError('gkd_logits_topk is not supported when using liger kernel')
+121
View File
@@ -0,0 +1,121 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import dataclasses
import json
from dataclasses import dataclass
from datetime import datetime
from typing import List, Literal, Optional
from swift.utils import get_logger
from .base_args import BaseArguments
logger = get_logger()
@dataclass
class SamplingArguments(BaseArguments):
"""A dataclass for configuring sampling parameters.
Args:
prm_model (Optional[str]): The type of the Process Reward Model (PRM). Can be a model ID (loaded via
'transformers' engine) or a PRM key defined in a plugin for custom inference. Defaults to None.
orm_model (Optional[str]): The type of the Outcome Reward Model (ORM). Typically a wildcard or test case,
usually defined in a plugin. Defaults to None.
sampler_type (Literal['sample', 'distill']): The type of sampling to perform. Supported types are 'sample' and
'distill'. Defaults to 'sample'.
sampler_engine (Literal['transformers', 'lmdeploy', 'vllm', 'no', 'client']): The inference engine for the
sampling model. Supported options are 'transformers', 'lmdeploy', 'vllm', 'client', and 'no'.
Defaults to 'transformers'.
output_dir (str): The directory to save the output files. Defaults to 'sample_output'.
output_file (Optional[str]): The name of the output file. If None, a timestamp will be used as the filename.
The path should not be included, only the filename. Only the '.jsonl' format is supported. Defaults to
None.
resume (bool): Whether to resume file. Defaults to False.
override_exist_file (bool): Whether to override the output file if it already exists. This is only effective
when `output_file` is specified. Defaults to False.
num_return_sequences (int): The number of raw sequences to return from sampling. Effective for the 'sample'
`sampler_type`. Defaults to 64.
num_sampling_batch_size (int): The batch size for each sampling iteration. Defaults to 1.
num_sampling_batches (Optional[int]): The total number of batches to sample. Defaults to None.
n_best_to_keep (int): The number of best sequences to keep after evaluation. Defaults to 5.
data_range (List[int]): Specifies the data shard to process. A list of two integers `[shard_index,
num_shards]`. For example, `[1, 3]` means the dataset is split into 3 shards and this process handles the
second shard (0-indexed). Defaults to [].
temperature (float): The temperature for sampling. Defaults to 1.0.
prm_threshold (float): The threshold for the Process Reward Model (PRM). Results with a score below this
threshold will be filtered out. Defaults to 0.0.
easy_query_threshold (Optional[float]): For a single query, if the proportion of correctly sampled sequences
(as evaluated by the ORM) is greater than this threshold, the query will be discarded. This prevents overly
simple queries from appearing in the final results. Defaults to None, which disables this filter.
engine_kwargs (Optional[str]): Additional arguments to pass to the `sampler_engine`, provided as a JSON string.
For example: '{"cache_max_entry_count":0.7}'. Defaults to None.
cache_files (List[str]): A list of cache files for a two-step sampling process to avoid OOM errors.
Step 1: Set `prm_model`, and `orm_model` to None. All generated sequences are saved to a file.
Step 2: Set `sampler_engine` to 'no' and provide the output file from Step 1 to `cache_files`.
This run will perform PRM and ORM evaluation on the cached results.
Note: The `--dataset` argument must still be provided, as IDs in the cache files are MD5 hashes of the
original data and need to be linked.
"""
# rm models
prm_model: Optional[str] = None
orm_model: Optional[str] = None
# sampler settings
sampler_type: Literal['sample', 'distill'] = 'sample'
sampler_engine: Literal['transformers', 'lmdeploy', 'vllm', 'no', 'client'] = 'transformers'
output_dir: str = 'sample_output'
output_file: Optional[str] = None
resume: bool = False
override_exist_file: bool = False
num_return_sequences: int = 64
num_sampling_batch_size: int = 1
num_sampling_batches: Optional[int] = None
n_best_to_keep: int = 5
data_range: List[int] = dataclasses.field(default_factory=list)
# generate settings
temperature: float = 1.0
prm_threshold: float = 0.0
easy_query_threshold: Optional[float] = None
# engine settings
engine_kwargs: Optional[str] = None
# Vanilla
cache_files: List[str] = dataclasses.field(default_factory=list)
def _init_model_info(self):
if self.sampler_engine != 'client':
return super()._init_model_info()
else:
self.model_info = None
self.model_meta = None
self.task_type = 'causal_lm'
return
def __post_init__(self):
if self.sampler_engine == 'pt':
self.sampler_engine = 'transformers' # compat swift3.x
if self.output_file is None:
now = datetime.now()
formatted_time = now.strftime('%Y-%m-%d-%H-%M-%S')
self.output_file = formatted_time + '.jsonl'
logger.info(f'Setting output_file to {self.output_file}')
else:
if '/' in self.output_file or '\\' in self.output_file:
raise ValueError(f'Please use a string prefix without directory to '
f'`--output_file` but now is: {self.output_file}')
self.padding_side = 'left'
if self.engine_kwargs is not None:
self.engine_kwargs = json.loads(self.engine_kwargs)
else:
self.engine_kwargs = {}
super().__post_init__()
if self.system is not None:
self.system_message = [{
'role': 'system',
'content': self.system,
}]
else:
self.system_message = []
+425
View File
@@ -0,0 +1,425 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import os
from dataclasses import dataclass
from transformers.utils.versions import require_version
from typing import Literal, Optional
from swift.trainers import Seq2SeqTrainingArguments, TrainerFactory
from swift.trainers.utils import prepare_deepspeed_elastic_config
from swift.utils import (add_version_to_work_dir, get_device_count, get_logger, get_pai_tensorboard_dir, is_mp,
is_pai_training_job, is_swanlab_available, json_parse_to_dict, to_abspath)
from .base_args import BaseArguments
from .tuner_args import TunerArguments
logger = get_logger()
@dataclass
class SwanlabArguments:
"""Arguments for configuring Swanlab for experiment result logging.
This dataclass stores all the configuration parameters required for initializing and using Swanlab to track
experiments.
Args:
swanlab_token (Optional[str]): The API key for SwanLab. You can also specify it using the `SWANLAB_API_KEY`
environment variable.
swanlab_project (str): The SwanLab project, which can be created in advance on the page
[https://swanlab.cn/space/~](https://swanlab.cn/space/~) or created automatically.
The default is "ms-swift".
swanlab_workspace (Optional[str]): The SwanLab workspace. Defaults to `None`, in which case the username
associated with the API key will be used.
swanlab_exp_name (Optional[str]): The name of the experiment. If `None`, it will default to the value of the
`output_dir` argument.
swanlab_notification_method (Optional[str]): The notification method for SwanLab when training completes
or errors occur. For details, refer to [here](https://docs.swanlab.cn/plugin/notification-dingtalk.html).
Supports 'dingtalk', 'lark', 'email', 'discord', 'wxwork', 'slack'.
swanlab_webhook_url (Optional[str]): Defaults to None. The webhook URL corresponding to
SwanLab's `swanlab_notification_method`.
swanlab_secret (Optional[str]): Defaults to None. The secret corresponding to
SwanLab's `swanlab_notification_method`.
swanlab_sender_email (Optional[str]): The email address of the sender. Required when
`swanlab_notification_method` is 'email'.
swanlab_receiver_email (Optional[str]): The email address of the receiver. Required when
`swanlab_notification_method` is 'email'.
swanlab_smtp_server (Optional[str]): The SMTP server address for email notification (e.g., 'smtp.qq.com').
swanlab_smtp_port (Optional[int]): The SMTP server port for email notification (e.g., 465).
swanlab_email_language (Optional[str]): email messages language. Supports 'zh', 'en'. The default is "zh".
swanlab_mode (Literal['cloud', 'local']): The operation mode, either 'cloud' for cloud-based logging or 'local'
for local-only logging.
"""
swanlab_token: Optional[str] = None
swanlab_project: str = 'ms-swift'
swanlab_workspace: Optional[str] = None
swanlab_exp_name: Optional[str] = None
swanlab_notification_method: Optional[str] = None
swanlab_webhook_url: Optional[str] = None
swanlab_secret: Optional[str] = None
swanlab_sender_email: Optional[str] = None
swanlab_receiver_email: Optional[str] = None
swanlab_smtp_server: Optional[str] = None
swanlab_smtp_port: Optional[int] = None
swanlab_email_language: Optional[str] = 'zh'
swanlab_mode: Literal['cloud', 'local'] = 'cloud'
def _init_swanlab(self):
if not is_swanlab_available():
raise ValueError('You are using swanlab as `report_to`, please install swanlab by '
'`pip install swanlab`')
if not self.swanlab_exp_name:
self.swanlab_exp_name = self.output_dir
import swanlab
from swanlab.integration.transformers import SwanLabCallback
from transformers.integrations import INTEGRATION_TO_CALLBACK
if self.swanlab_token:
swanlab.login(self.swanlab_token)
if self.swanlab_notification_method is not None:
from swanlab.plugin.notification import (DingTalkCallback, DiscordCallback, EmailCallback, LarkCallback,
SlackCallback, WXWorkCallback)
notification_mapping = {
'lark': LarkCallback,
'dingtalk': DingTalkCallback,
'email': EmailCallback,
'discord': DiscordCallback,
'wxwork': WXWorkCallback,
'slack': SlackCallback,
}
callback_cls = notification_mapping.get(self.swanlab_notification_method)
if callback_cls is None:
raise ValueError(
f'Unsupported swanlab_notification_method: "{self.swanlab_notification_method}". Supported methods'
f' are: {list(notification_mapping.keys())}')
if self.swanlab_notification_method == 'email':
if not (self.swanlab_sender_email and self.swanlab_receiver_email and self.swanlab_smtp_server
and self.swanlab_smtp_port):
raise ValueError("When 'swanlab_notification_method' is 'email', both 'swanlab_sender_email' "
"and 'swanlab_receiver_email' and 'swanlab_smtp_server' and 'swanlab_smtp_port' "
'must be provided.')
callback = EmailCallback(
sender_email=self.swanlab_sender_email,
receiver_email=self.swanlab_receiver_email,
password=self.swanlab_secret,
smtp_server=self.swanlab_smtp_server,
port=self.swanlab_smtp_port,
language=self.swanlab_email_language)
else:
callback = callback_cls(
webhook_url=self.swanlab_webhook_url,
secret=self.swanlab_secret,
)
swanlab.register_callbacks([callback])
INTEGRATION_TO_CALLBACK['swanlab'] = SwanLabCallback(
project=self.swanlab_project,
workspace=self.swanlab_workspace,
experiment_name=self.swanlab_exp_name,
config={'UPPERFRAME': '🐦‍⬛ms-swift'},
mode=self.swanlab_mode,
)
@dataclass
class SftArguments(SwanlabArguments, TunerArguments, BaseArguments, Seq2SeqTrainingArguments):
"""Arguments pertaining to the training process.
SftArguments is a dataclass that inherits from multiple argument classes: SwanlabArguments, TunerArguments,
BaseArguments, Seq2SeqTrainingArguments.
Args:
add_version (bool): Whether to add a versioned subdirectory like '<version>-<timestamp>' to the `output_dir` to
prevent overwriting existing checkpoints. Defaults to True.
create_checkpoint_symlink (bool): Whether to create additional symbolic links for checkpoints, which can be
useful for automated training scripts. The symlinks for the best and last models will be created at
`f'{output_dir}/best'` and `f'{output_dir}/last'`, respectively. Defaults to False.
output_dir (Optional[str]): The directory to save model outputs. Defaults to 'output/<model_name>'.
learning_rate (Optional[float]): The learning rate. Defaults to 1e-5 for full-parameter training and 1e-4 for
tuners like LoRA.
Note: To set a minimum learning rate (min_lr), you can pass the arguments
--lr_scheduler_type cosine_with_min_lr --lr_scheduler_kwargs '{"min_lr": 1e-6}'.
eval_strategy (Optional[str]): The evaluation strategy. By default, it aligns with `save_strategy`. It will
default to 'no' if no validation dataset is provided (i.e., `val_dataset` and `eval_dataset` are not used,
and `split_dataset_ratio` is 0).
fp16 (Optional[bool]): Defaults to None.
bf16 (Optional[bool]): Defaults to None.
max_new_tokens (int): Overrides generation parameters. The maximum number of new tokens to generate when
`predict_with_generate` is True. Defaults to 64.
temperature (float): Overrides generation parameters. The temperature for sampling when `predict_with_generate`
is True. Defaults to 0.0.
load_args (bool): Whether to load `args.json` from a saved directory when `--resume_from_checkpoint`,
`--model`, or `--adapters` is specified. For details on which keys are loaded, refer to `base_args.py`.
Defaults to `True` for inference and exporting, and `False` for training. This argument typically does not
need to be modified.
zero_hpz_partition_size (Optional[int]): A feature of ZeRO++. Enables model sharding within a node and data
sharding between nodes. If you encounter `grad_norm` NaN issues, consider trying `--torch_dtype float16`.
Defaults to None.
deepspeed_autotp_size (Optional[int]): The tensor parallelism size for DeepSpeed AutoTP. To use this, the
`--deepspeed` argument must be set to 'zero0', 'zero1', or 'zero2'. Note: This feature only supports
full-parameter fine-tuning. Defaults to None.
"""
add_version: bool = True
create_checkpoint_symlink: bool = False
# override
output_dir: Optional[str] = None
learning_rate: Optional[float] = None
eval_strategy: Optional[str] = None # steps, epoch
fp16: Optional[bool] = None
bf16: Optional[bool] = None
# extra
max_new_tokens: int = 64
temperature: float = 0.
load_args: bool = False
# zero++
zero_hpz_partition_size: Optional[int] = None
# auto_tp
deepspeed_autotp_size: Optional[int] = None
# fsdp
fsdp: Optional[str] = None
def _check_padding_free(self):
if self.padding_free or self.packing:
if self.packing:
feature = 'packing'
self.padding_free = True
else:
feature = 'padding_free'
supported_impls = ['flash_attn', 'flash_attention_2', 'flash_attention_3', 'flash_attention_4']
if self.attn_impl not in supported_impls:
supported_impls_str = ', '.join([f'"{impl}"' for impl in supported_impls])
raise ValueError(f'The "{feature}" feature requires a flash attention implementation. '
f'Please use one of: {supported_impls_str}.')
def __post_init__(self) -> None:
if self.resume_from_checkpoint:
self.resume_from_checkpoint = to_abspath(self.resume_from_checkpoint, True)
# The non-resume_only_model will have its weights loaded in the trainer.
if self.resume_only_model:
if self.tuner_type == 'full':
self.model = self.resume_from_checkpoint
else:
self.adapters = [self.resume_from_checkpoint]
BaseArguments.__post_init__(self)
self._init_override()
TunerArguments.__post_init__(self)
self._check_padding_free()
if self.vit_gradient_checkpointing is None:
self.vit_gradient_checkpointing = not self.freeze_vit
if self.optimizer is None:
if self.lorap_lr_ratio:
self.optimizer = 'lorap'
elif self.use_galore:
self.optimizer = 'galore'
if len(self.dataset) == 0 and len(self.cached_dataset) == 0:
raise ValueError(f'self.dataset: {self.dataset}, self.cached_dataset: {self.cached_dataset}. '
'Please input the training dataset.')
self._handle_pai_compat()
self._init_deepspeed()
self._init_fsdp()
self._init_device()
if getattr(self, 'accelerator_config', None) is None:
self.accelerator_config = {'dispatch_batches': False}
if not (self.eval_dataset or self._val_dataset_exists):
self.eval_strategy = 'no'
self.training_args = TrainerFactory.get_training_args(self)
self.training_args.remove_unused_columns = False
self._add_version()
if 'swanlab' in self.report_to:
self._init_swanlab()
def _init_override(self):
self._init_output_dir()
self._init_metric()
if self.learning_rate is None:
if self.tuner_type == 'full':
self.learning_rate = 1e-5
else:
self.learning_rate = 1e-4
self._init_eval_strategy()
def _init_deepspeed(self):
if self.deepspeed:
require_version('deepspeed')
if is_mp() and not self.use_ray:
raise ValueError('DeepSpeed is not compatible with `device_map`. '
f'n_gpu: {get_device_count()}, '
f'local_world_size: {self.local_world_size}.')
ds_config_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'config'))
deepspeed_mapping = {
name: f'{name}.json'
for name in ['zero0', 'zero1', 'zero2', 'zero3', 'zero2_offload', 'zero3_offload']
}
for ds_name, ds_config in deepspeed_mapping.items():
if self.deepspeed == ds_name:
self.deepspeed = os.path.join(ds_config_folder, ds_config)
break
self.deepspeed = json_parse_to_dict(self.deepspeed)
if self.zero_hpz_partition_size is not None:
assert 'zero_optimization' in self.deepspeed
self.deepspeed['zero_optimization']['zero_hpz_partition_size'] = self.zero_hpz_partition_size
logger.warn('If `zero_hpz_partition_size`(ZeRO++) causes grad_norm NaN, please'
' try `--torch_dtype float16`')
if self.deepspeed_autotp_size is not None:
assert self.deepspeed is not None, (
'To use `deepspeed_autotp_size`, you need to additionally set the `--deepspeed` argument.')
self.deepspeed.setdefault('tensor_parallel', {})['autotp_size'] = self.deepspeed_autotp_size
self.deepspeed.setdefault('zero_optimization', {})['gather_16bit_weights_on_model_save'] = True
if 'deepspeed_elastic' in set(getattr(self, 'callbacks', []) or []):
prepare_deepspeed_elastic_config(self)
logger.info(f'Using deepspeed: {self.deepspeed}')
def _init_fsdp(self):
if not self.fsdp:
self.fsdp = []
return
if is_mp() and not self.use_ray:
raise ValueError('FSDP2 is not compatible with `device_map`. '
f'n_gpu: {get_device_count()}, '
f'local_world_size: {self.local_world_size}.')
if self.deepspeed:
raise ValueError('FSDP2 is not compatible with DeepSpeed.')
fsdp_config_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'config'))
# FSDP2 preset configurations
fsdp_mapping = {
'fsdp2': 'fsdp2.json',
}
fsdp_config_path = self.fsdp
for fsdp_name, fsdp_config in fsdp_mapping.items():
if self.fsdp == fsdp_name:
fsdp_config_path = os.path.join(fsdp_config_folder, fsdp_config)
break
fsdp_config_dict = json_parse_to_dict(fsdp_config_path)
# Extract fsdp string options (e.g., "full_shard auto_wrap offload")
fsdp_options = fsdp_config_dict.get('fsdp', 'full_shard auto_wrap')
self.fsdp = fsdp_options
# Extract fsdp_config dict
self.fsdp_config = fsdp_config_dict.get('fsdp_config', {})
# Set FSDP_VERSION environment variable for accelerate to recognize FSDP2
fsdp_version = self.fsdp_config.get('fsdp_version', 2)
os.environ['FSDP_VERSION'] = str(fsdp_version)
# Set environment variable to optimize NCCL memory usage
if 'TORCH_NCCL_AVOID_RECORD_STREAMS' not in os.environ:
os.environ['TORCH_NCCL_AVOID_RECORD_STREAMS'] = '1'
# Check FSDP2 compatibility with other training arguments
self._check_fsdp2_compatibility()
logger.info(f'Using FSDP2: fsdp={self.fsdp}, fsdp_config={self.fsdp_config}')
def _check_fsdp2_compatibility(self):
"""Check for incompatible argument combinations with FSDP2.
FSDP2 has several known limitations:
1. save_only_model=True + SHARDED_STATE_DICT: Can't save only model weights with sharded state dict
2. gradient_checkpointing=True: Should use activation_checkpointing in fsdp_config instead
"""
state_dict_type = self.fsdp_config.get('state_dict_type', 'SHARDED_STATE_DICT')
# Check 1: save_only_model + SHARDED_STATE_DICT
if getattr(self, 'save_only_model', False) and 'SHARDED' in state_dict_type.upper():
raise ValueError(
'FSDP2 with SHARDED_STATE_DICT is not compatible with save_only_model=True. '
'Either set save_only_model=False, or change state_dict_type to FULL_STATE_DICT in fsdp_config. '
'Note: FULL_STATE_DICT requires more memory and is slower.')
# Check 2: gradient_checkpointing should be disabled, use activation_checkpointing instead
if getattr(self, 'gradient_checkpointing', False):
activation_checkpointing = self.fsdp_config.get('activation_checkpointing', False)
if activation_checkpointing:
logger.warning('Both gradient_checkpointing and fsdp_config.activation_checkpointing are enabled. '
'For FSDP2, it is recommended to use only activation_checkpointing in fsdp_config. '
'Disabling gradient_checkpointing automatically.')
self.gradient_checkpointing = False
else:
logger.warning(
'gradient_checkpointing is enabled with FSDP2. '
'For better performance, consider using activation_checkpointing in fsdp_config instead. '
'Add "activation_checkpointing": true to your fsdp_config.')
def _handle_pai_compat(self) -> None:
if not is_pai_training_job():
return
logger.info('Handle pai compat...')
pai_tensorboard_dir = get_pai_tensorboard_dir()
if self.logging_dir is None and pai_tensorboard_dir is not None:
self.logging_dir = pai_tensorboard_dir
logger.info(f'Setting args.logging_dir: {self.logging_dir}')
self.add_version = False
logger.info(f'Setting args.add_version: {self.add_version}')
def _add_version(self):
"""Prepare the output_dir"""
if self.add_version:
self.output_dir = add_version_to_work_dir(self.output_dir)
logger.info(f'output_dir: {self.output_dir}')
if self.logging_dir is None:
self.logging_dir = f'{self.output_dir}/runs'
self.logging_dir = to_abspath(self.logging_dir)
os.makedirs(self.output_dir, exist_ok=True)
if self.run_name is None:
self.run_name = self.output_dir
self.training_args.output_dir = self.output_dir
self.training_args.run_name = self.run_name
self.training_args.logging_dir = self.logging_dir
def _init_output_dir(self):
if self.output_dir is None:
self.output_dir = f'output/{self.model_suffix}'
self.output_dir = to_abspath(self.output_dir)
def _init_eval_strategy(self):
if self.eval_strategy is None:
self.eval_strategy = self.save_strategy
if self.eval_strategy == 'no':
self.eval_steps = None
if self.split_dataset_ratio > 0:
self.split_dataset_ratio = 0.
logger.info(f'Setting args.split_dataset_ratio: {self.split_dataset_ratio}')
elif self.eval_strategy == 'steps' and self.eval_steps is None:
self.eval_steps = self.save_steps
self.evaluation_strategy = self.eval_strategy
def _init_metric(self):
if self.eval_metric is None:
if self.task_type == 'causal_lm' and self.predict_with_generate:
self.eval_metric = 'nlg'
elif self.task_type == 'embedding':
self.eval_metric = 'infonce' if self.loss_type == 'infonce' else 'paired'
elif self.task_type in {'reranker', 'generative_reranker'}:
self.eval_metric = 'reranker'
if self.eval_metric == 'nlg':
require_version('jieba', 'Setting `--eval_metric nlg` requires installing the jieba dependency.')
self._init_metric_for_best_model()
def _init_metric_for_best_model(self):
if self.metric_for_best_model is None:
self.metric_for_best_model = 'rouge-l' if self.predict_with_generate else 'loss'
if self.greater_is_better is None and self.metric_for_best_model is not None:
self.greater_is_better = 'loss' not in self.metric_for_best_model
+220
View File
@@ -0,0 +1,220 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
from dataclasses import dataclass, field
from transformers.utils import strtobool
from typing import List, Literal, Optional
from swift.utils import get_logger
logger = get_logger()
@dataclass
class TunerArguments:
"""
TunerArguments is a dataclass that holds configuration for various tuners.
Args:
freeze_parameters (List[str]): A list of prefixes for parameters that should be frozen during training.
Defaults to an empty list `[]`.
freeze_parameters_regex (Optional[str]): A regular expression to match the names of parameters that should be
frozen. Defaults to `None`.
freeze_parameters_ratio (float): The ratio of parameters to freeze, starting from the bottom layers upwards
(from 0.0 to 1.0). Setting this to 1.0 freezes all model parameters, which can be useful when selectively
unfreezing specific parameters with `trainable_parameters`. Defaults to 0.0.
trainable_parameters (List[str]): A list of prefixes for parameters that should be made explicitly trainable.
Defaults to an empty list `[]`.
trainable_parameters_regex (Optional[str]): A regular expression to match the names of parameters that should
be made explicitly trainable. Defaults to `None`.
Note on parameter freezing priority: The `trainable_*` arguments have higher priority than the `freeze_*`
arguments. The freezing logic is applied as follows:
Firstly, all parameters are set to trainable.
Then, `freeze_parameters`, `freeze_parameters_regex`, and `freeze_parameters_ratio` are applied to freeze
parts of the model.
Finally, `trainable_parameters` and `trainable_parameters_regex` are used to unfreeze specific parameters,
ensuring they are trainable regardless of the freezing rules.
freeze_llm (bool): For multi-modal models only. If `True`, it affects the Large Language Model (LLM) part.
In full fine-tuning, this freezes the LLM weights. In LoRA training with `target_modules=['all-linear']`,
this prevents adding LoRA modules to the LLM. Defaults to `False`.
freeze_vit (bool): For multi-modal models only. If `True`, it affects the Vision/Audio Transformer (ViT) part.
In full fine-tuning, this freezes the ViT weights. In LoRA training with `target_modules=['all-linear']`,
this prevents adding LoRA modules to the ViT. Note: 'vit' can refer to `vision_tower` and `audio_tower`.
Defaults to `True`.
freeze_aligner (bool): For multi-modal models only. If `True`, it affects the aligner (projector) part.
In full fine-tuning, this freezes the aligner weights. In LoRA training with
`target_modules=['all-linear']`, this prevents adding LoRA modules to the aligner. Defaults to `True`.
target_modules (List[str]): List of target modules for tuning. Default is ['all-linear'].
target_regex (Optional[str]): Regular expression to match target modules. Default is None.
target_parameters (Optional[List[str]]): A list of parameter names to be replaced by LoRA modules. This is
similar to `target_modules` but targets parameters directly, which is useful for layers like MoE that use
`nn.Parameter` instead of `nn.Linear`. Requires `peft>=0.17.0`. Defaults to `None`.
modules_to_save (List[str]): List of modules to save. Default is an empty list.
lora_rank (int): Rank for LoRA. Default is 8.
lora_alpha (int): Alpha value for LoRA. Default is 32.
lora_dropout (float): Dropout rate for LoRA. Default is 0.05.
lora_bias (Literal['none', 'all']): The possible values are 'none' and 'all'. If set to 'all', all biases
will be trainable. Default is 'none'.
lora_dtype (Literal): Data type for LoRA. Default is 'AUTO'. Allowed values are 'fp16', 'bf16', 'fp32', 'AUTO'.
lorap_lr_ratio (float): Learning rate ratio for LoRA. Default is None.
use_rslora (bool): Flag to indicate if RSLora is used. Default is False.
use_dora (bool): Flag to indicate if Dora is used. Default is False.
lora_ga_batch_size (int): Batch size used for estimating gradients during initialization in LoRA-GA. Default
value is 2.
lora_ga_iters (int): Number of iterations for estimating gradients during initialization in LoRA-GA. Default
value is 2.
lora_ga_max_length (int): Maximum input length for estimating gradients during initialization in LoRA-GA.
Default value is 1024.
lora_ga_direction (str): Initial direction used for gradient estimation during initialization in LoRA-GA.
Default value is `ArB2r`. Allowed: `ArBr`, `A2rBr`, `ArB2r`, and `random`.
lora_ga_scale (str): The scaling method for initialization in LoRA-GA.
Default value is `stable`. Allowed values are: `gd`, `unit`, `stable`, and `weightS`.
lora_ga_stable_gamma (int): The gamma value when choosing `stable` scaling for initialization. Default
value is 16.
init_weights (str): The method for initializing adapter weights. For LoRA, options include 'true', 'false',
'gaussian', 'pissa', 'pissa_niter_[number of iters]', 'olora', 'loftq', and 'lora-ga'. For BoNE,
options are 'true', 'false', and 'bat'. Defaults to 'true'.
fourier_n_frequency (int): Number of frequencies for FourierFT. Default is 2000.
fourier_scaling (float): Scaling factor for FourierFT. Default is 300.0.
boft_block_size (int): Block size for BOFT. Default is 4.
boft_block_num (int): Number of blocks for BOFT. Default is 0.
boft_n_butterfly_factor (int): Butterfly factor for BOFT. Default is 1.
boft_dropout (float): Dropout rate for BOFT. Default is 0.0.
vera_rank (int): Rank for Vera. Default is 256.
vera_projection_prng_key (int): PRNG key for Vera projection. Default is 0.
vera_dropout (float): Dropout rate for Vera. Default is 0.0.
vera_d_initial (float): Initial value for Vera D. Default is 0.1.
adapter_act (str): Activation function for adapter. Default is 'gelu'.
adapter_length (int): Length of the adapter. Default is 128.
adalora_target_r (int): Target rank for AdaLoRA. Default is 8.
adalora_init_r (int): Initial rank for AdaLoRA. Default is 12.
adalora_tinit (int): Initial T value for AdaLoRA. Default is 100.
adalora_tfinal (int): Final T value for AdaLoRA. Default is 1000.
adalora_deltaT (int): Delta T value for AdaLoRA. Default is 10.
adalora_beta1 (float): Beta1 value for AdaLoRA. Default is 0.85.
adalora_beta2 (float): Beta2 value for AdaLoRA. Default is 0.85.
adalora_orth_reg_weight (float): Orthogonal regularization weight for AdaLoRA. Default is 0.5.
llamapro_num_new_blocks (int): Number of new blocks for LLaMAPro. Default is 4.
llamapro_num_groups (Optional[int]): Number of groups for LLaMAPro. Default is None.
reft_layer_key (Optional[str]): Key identifier for ReFT layer. Default is None.
reft_layers (Optional[List[int]]): List of layers involved in ReFT. Default is None.
reft_rank (int): Rank parameter for ReFT. Default is 4.
reft_intervention_type (Literal): Type of intervention for ReFT. Default is 'LoreftIntervention'.
reft_args (Optional[str]): Additional arguments for ReFT. Default is None.
"""
# full
freeze_parameters: List[str] = field(default_factory=list)
freeze_parameters_regex: Optional[str] = None
freeze_parameters_ratio: float = 0. # 0 ~ 1
trainable_parameters: List[str] = field(default_factory=list)
trainable_parameters_regex: Optional[str] = None
# lora or full
freeze_llm: bool = False
freeze_vit: bool = True
freeze_aligner: bool = True
# tuners
target_modules: List[str] = field(default_factory=lambda: ['all-linear'])
target_regex: Optional[str] = None
target_parameters: Optional[List[str]] = None
# e.g. ['wte', 'ln_1', 'ln_2', 'ln_f', 'lm_head']
modules_to_save: List[str] = field(default_factory=list)
# lora
lora_rank: int = 8
lora_alpha: int = 32
lora_dropout: float = 0.05
lora_bias: Literal['none', 'all'] = 'none'
lora_dtype: Literal['float16', 'bfloat16', 'float32', None] = None
lorap_lr_ratio: Optional[float] = None
use_rslora: bool = False
use_dora: bool = False
# lora_ga
lora_ga_batch_size: int = 2
lora_ga_iters: int = 2
lora_ga_max_length: int = 1024
lora_ga_direction: str = 'ArB2r'
lora_ga_scale: str = 'stable'
lora_ga_stable_gamma: int = 16
# Lora: Literal['gaussian', 'pissa', 'pissa_niter_[number of iters]', 'olora', 'loftq', 'true', 'false', 'lora-ga']
# Bone: Literal['bat', 'true', 'false']
init_weights: str = 'true'
# fourierft
fourier_n_frequency: int = 2000
fourier_scaling: float = 300.0
# BOFT
boft_block_size: int = 4
boft_block_num: int = 0
boft_n_butterfly_factor: int = 1
boft_dropout: float = 0.0
# Vera
vera_rank: int = 256
vera_projection_prng_key: int = 0
vera_dropout: float = 0.0
vera_d_initial: float = 0.1
# adapter
adapter_act: str = 'gelu'
adapter_length: int = 128
# adalora
adalora_target_r: int = 8
adalora_init_r: int = 12
adalora_tinit: int = 0
adalora_tfinal: int = 0
adalora_deltaT: int = 1
adalora_beta1: float = 0.85
adalora_beta2: float = 0.85
adalora_orth_reg_weight: float = 0.5
# llamapro
llamapro_num_new_blocks: int = 4
llamapro_num_groups: Optional[int] = None
# reft
reft_layer_key: Optional[str] = None
reft_layers: Optional[List[int]] = None
reft_rank: int = 4
reft_intervention_type: Literal['NoreftIntervention', 'LoreftIntervention', 'ConsreftIntervention',
'LobireftIntervention', 'DireftIntervention',
'NodireftIntervention'] = 'LoreftIntervention'
reft_args: Optional[str] = None
def __post_init__(self):
if isinstance(self.init_weights, str) and self.init_weights.lower() in {'true', 'false'}:
self.init_weights = bool(strtobool(self.init_weights))
self._init_multimodal_full()
if self.target_regex:
self.target_modules = self.target_regex
def _init_multimodal_full(self):
model_arch = self.model_meta.model_arch
if not self.model_meta.is_multimodal or not model_arch or self.tuner_type != 'full':
return
if self.freeze_llm:
self.freeze_parameters += model_arch.language_model
if self.freeze_vit:
self.freeze_parameters += model_arch.vision_tower
if self.freeze_aligner:
self.freeze_parameters += model_arch.aligner
else:
self.trainable_parameters += model_arch.aligner
self.freeze_parameters += model_arch.generator
if self.freeze_parameters:
logger.info(f'freeze_parameters: {self.freeze_parameters}')
if self.trainable_parameters:
logger.info(f'additional trainable_parameters: {self.trainable_parameters}')
+18
View File
@@ -0,0 +1,18 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
from dataclasses import dataclass
@dataclass
class WebUIArguments:
"""A dataclass for web UI configuration arguments.
Args:
server_name (str): The hostname or IP address to be bound to the Web UI server. Defaults to '0.0.0.0'.
server_port (int): The port number to be bound to the Web UI server. Defaults to 7860.
share (bool): Whether to create a public, shareable link for the web UI. Defaults to False.
lang (str): The language for the web UI, chosen from {'zh', 'en'}. Defaults to 'zh'.
"""
server_name: str = '0.0.0.0'
server_port: int = 7860
share: bool = False
lang: str = 'zh'