Distillflow 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- distillflow/common/__init__.py +10 -0
- distillflow/common/common.py +98 -0
- distillflow/common/logger.py +101 -0
- distillflow/config/__init__.py +6 -0
- distillflow/config/config.py +26 -0
- distillflow/config/validator.py +38 -0
- distillflow/datasets/__init__.py +6 -0
- distillflow/datasets/args.py +99 -0
- distillflow/datasets/loader.py +191 -0
- distillflow/datasets/template/__init__.py +12 -0
- distillflow/datasets/template/alpaca.py +85 -0
- distillflow/datasets/template/args.py +56 -0
- distillflow/datasets/template/role.py +10 -0
- distillflow/datasets/template/sharegpt.py +84 -0
- distillflow/datasets/template/template.py +6 -0
- distillflow/evaluation/__init__.py +0 -0
- distillflow/evaluation/rouge.py +30 -0
- distillflow/model/__init__.py +0 -0
- distillflow/model/adapter.py +257 -0
- distillflow/model/args.py +173 -0
- distillflow/model/checkpoint.py +146 -0
- distillflow/model/finetuning_args.py +132 -0
- distillflow/model/generating_args.py +72 -0
- distillflow/model/liger_kernel.py +46 -0
- distillflow/model/loader.py +286 -0
- distillflow/model/quantization.py +193 -0
- distillflow/model/tokenizer.py +55 -0
- distillflow/model/unsloth.py +92 -0
- distillflow/trainer/AdaptationLayer.py +82 -0
- distillflow/trainer/__init__.py +0 -0
- distillflow/trainer/args.py +56 -0
- distillflow/trainer/attention_distillation.py +113 -0
- distillflow/trainer/fine_tuning.py +51 -0
- distillflow/trainer/layers_distillation.py +106 -0
- distillflow/trainer/logits_distillation.py +96 -0
- distillflow-0.2.0.dist-info/LICENSE +201 -0
- distillflow-0.2.0.dist-info/METADATA +162 -0
- distillflow-0.2.0.dist-info/RECORD +39 -0
- distillflow-0.2.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from typing import Literal, Optional, Union
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field, model_validator
|
|
4
|
+
|
|
5
|
+
from . import ShareGptArgs, AlpacaArgs
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TemplateArgs(BaseModel):
|
|
9
|
+
name: Literal["sharegpt", "alpaca"] = Field(
|
|
10
|
+
default="sharegpt",
|
|
11
|
+
description="Template used by the dataset. Based on the template, the data will be converted to a standard format before training"
|
|
12
|
+
)
|
|
13
|
+
args: Optional[Union[ShareGptArgs, AlpacaArgs]] = Field(
|
|
14
|
+
default=None,
|
|
15
|
+
description="Template args to map the corresponding dataset columns to the ones expected by sharegpt/alpaca"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# Validate and transform args based on template name
|
|
19
|
+
@model_validator(mode='after')
|
|
20
|
+
def validate_args(self) -> 'TemplateArgs':
|
|
21
|
+
if self.args is None:
|
|
22
|
+
# Set default args based on template
|
|
23
|
+
self.args = {
|
|
24
|
+
"sharegpt": ShareGptArgs(),
|
|
25
|
+
"alpaca": AlpacaArgs()
|
|
26
|
+
}[self.name]
|
|
27
|
+
return self
|
|
28
|
+
|
|
29
|
+
# If args is a dict, convert to appropriate type
|
|
30
|
+
if isinstance(self.args, dict):
|
|
31
|
+
template_classes = {
|
|
32
|
+
"sharegpt": ShareGptArgs,
|
|
33
|
+
"alpaca": AlpacaArgs
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
template_class = template_classes.get(self.name)
|
|
37
|
+
if template_class is None:
|
|
38
|
+
raise ValueError(f"Unknown template: {self.name}")
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
self.args = template_class(**self.args)
|
|
42
|
+
except Exception as e:
|
|
43
|
+
raise ValueError(f"Invalid args for template '{self.name}': {str(e)}")
|
|
44
|
+
|
|
45
|
+
# Validate that args matches template
|
|
46
|
+
expected_type = ShareGptArgs if self.name == "sharegpt" else AlpacaArgs
|
|
47
|
+
if not isinstance(self.args, expected_type):
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"Template '{self.name}' expects {expected_type.__name__} "
|
|
50
|
+
f"but got {type(self.args).__name__}"
|
|
51
|
+
)
|
|
52
|
+
return self
|
|
53
|
+
|
|
54
|
+
model_config = {
|
|
55
|
+
"extra": "forbid"
|
|
56
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Dict, Any, Optional
|
|
4
|
+
|
|
5
|
+
from .template import Template
|
|
6
|
+
from .role import Role
|
|
7
|
+
from ...common import get_logger
|
|
8
|
+
|
|
9
|
+
logger = get_logger(__name__)
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class ShareGptArgs:
|
|
13
|
+
messages: Optional[str] = "conversations"
|
|
14
|
+
# sharegpt tags
|
|
15
|
+
role_tag: Optional[str] = "from"
|
|
16
|
+
content_tag: Optional[str] = "value"
|
|
17
|
+
user_tag: Optional[str] = "human"
|
|
18
|
+
assistant_tag: Optional[str] = "gpt"
|
|
19
|
+
observation_tag: Optional[str] = "observation"
|
|
20
|
+
function_tag: Optional[str] = "function_call"
|
|
21
|
+
system_tag: Optional[str] = "system"
|
|
22
|
+
|
|
23
|
+
class ShareGpt(Template):
|
|
24
|
+
|
|
25
|
+
def __init__(self, args: Optional[ShareGptArgs] = ShareGptArgs()):
|
|
26
|
+
self.args = args
|
|
27
|
+
|
|
28
|
+
def convert(self, example: Dict[str, Any]) -> Dict[str, Any]:
|
|
29
|
+
r"""
|
|
30
|
+
Converts sharegpt format dataset to the standard format.
|
|
31
|
+
"""
|
|
32
|
+
tag_mapping = {
|
|
33
|
+
self.args.user_tag: Role.USER.value,
|
|
34
|
+
self.args.assistant_tag: Role.ASSISTANT.value,
|
|
35
|
+
self.args.observation_tag: Role.OBSERVATION.value,
|
|
36
|
+
self.args.function_tag: Role.FUNCTION.value,
|
|
37
|
+
self.args.system_tag: Role.SYSTEM.value,
|
|
38
|
+
}
|
|
39
|
+
odd_tags = (self.args.user_tag, self.args.observation_tag)
|
|
40
|
+
even_tags = (self.args.assistant_tag, self.args.function_tag)
|
|
41
|
+
accept_tags = (odd_tags, even_tags)
|
|
42
|
+
messages = example[self.args.messages]
|
|
43
|
+
system = []
|
|
44
|
+
if (
|
|
45
|
+
self.args.system_tag
|
|
46
|
+
and len(messages) != 0
|
|
47
|
+
and messages[0][self.args.role_tag] == self.args.system_tag
|
|
48
|
+
):
|
|
49
|
+
system.append({"role": Role.SYSTEM.value, "content": messages[0][self.args.content_tag]})
|
|
50
|
+
messages = messages[1:]
|
|
51
|
+
else:
|
|
52
|
+
system.append({"role": Role.SYSTEM.value, "content": example[self.args.system_tag]} if self.args.system_tag in example else {"role": Role.SYSTEM.value,
|
|
53
|
+
"content": "You are a helpful assistant."})
|
|
54
|
+
|
|
55
|
+
aligned_messages = []
|
|
56
|
+
broken_data = False
|
|
57
|
+
for turn_idx, message in enumerate(messages):
|
|
58
|
+
if message[self.args.role_tag] not in accept_tags[turn_idx % 2]:
|
|
59
|
+
logger.warning("Invalid role tag in {}.".format(messages))
|
|
60
|
+
broken_data = True
|
|
61
|
+
|
|
62
|
+
aligned_messages.append(
|
|
63
|
+
{"role": tag_mapping[message.get(self.args.role_tag, '')], "content": message.get(self.args.content_tag, '')}
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
prompt = aligned_messages[:-1]
|
|
67
|
+
response = aligned_messages[-1:]
|
|
68
|
+
|
|
69
|
+
if isinstance(response, str):
|
|
70
|
+
try:
|
|
71
|
+
response = json.loads(response)
|
|
72
|
+
except json.JSONDecodeError as e:
|
|
73
|
+
raise ValueError(f"Invalid JSON in response: {response}") from e
|
|
74
|
+
if broken_data:
|
|
75
|
+
logger.warning("Skipping this abnormal example.")
|
|
76
|
+
prompt, response = [], []
|
|
77
|
+
|
|
78
|
+
output = {
|
|
79
|
+
"_prompt": prompt,
|
|
80
|
+
"_response": response,
|
|
81
|
+
"_system": system,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return output
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import evaluate
|
|
2
|
+
|
|
3
|
+
def read_file(file_path):
|
|
4
|
+
"""Reads text from a file and returns it as a list of lines."""
|
|
5
|
+
with open(file_path, 'r', encoding='utf-8') as file:
|
|
6
|
+
return [line.strip() for line in file.readlines()]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def compute_rouge_scores(reference_file, generated_file):
|
|
10
|
+
# Load the ROUGE metric from the evaluate library
|
|
11
|
+
rouge = evaluate.load('rouge')
|
|
12
|
+
|
|
13
|
+
reference_texts = read_file(reference_file)
|
|
14
|
+
generated_texts = read_file(generated_file)
|
|
15
|
+
|
|
16
|
+
# Compute ROUGE scores for the provided texts
|
|
17
|
+
scores = rouge.compute(predictions=generated_texts, references=reference_texts)
|
|
18
|
+
|
|
19
|
+
# Return the scores
|
|
20
|
+
return scores
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Example usage
|
|
24
|
+
# reference_texts = ["The cat sat on the mat."]
|
|
25
|
+
# generated_texts = ["The cat is sitting on the mat."]
|
|
26
|
+
#
|
|
27
|
+
# # Compute and print ROUGE scores
|
|
28
|
+
# rouge_scores = compute_rouge_scores(reference_texts, generated_texts)
|
|
29
|
+
# for metric, score in rouge_scores.items():
|
|
30
|
+
# print(f"{metric}: {score:.4f}")
|
|
File without changes
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
from peft import LoraConfig, LoraModel, PeftModel, TaskType, get_peft_model
|
|
5
|
+
from transformers.integrations import is_deepspeed_zero3_enabled
|
|
6
|
+
from transformers.modeling_utils import is_fsdp_enabled
|
|
7
|
+
|
|
8
|
+
from .args import ModelArgs
|
|
9
|
+
from .finetuning_args import FinetuningArgs
|
|
10
|
+
from ..common.logger import get_logger
|
|
11
|
+
from .unsloth import get_unsloth_peft_model, load_unsloth_peft_model
|
|
12
|
+
|
|
13
|
+
from transformers import PretrainedConfig, PreTrainedModel
|
|
14
|
+
|
|
15
|
+
from ..common import find_all_linear_modules
|
|
16
|
+
|
|
17
|
+
logger = get_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _setup_full_tuning(model: PreTrainedModel, is_trainable: bool, cast_trainable_params_to_fp32: bool) -> None:
|
|
21
|
+
if not is_trainable:
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
logger.info("Fine-tuning method: Full")
|
|
25
|
+
for name, param in model.named_parameters():
|
|
26
|
+
if cast_trainable_params_to_fp32:
|
|
27
|
+
param.data = param.data.to(torch.float32)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _setup_freeze_tuning(
|
|
31
|
+
model: PreTrainedModel,
|
|
32
|
+
finetuning_args: FinetuningArgs,
|
|
33
|
+
is_trainable: bool,
|
|
34
|
+
cast_trainable_params_to_fp32: bool,
|
|
35
|
+
) -> None:
|
|
36
|
+
if not is_trainable:
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
logger.info("Fine-tuning method: Freeze")
|
|
40
|
+
if hasattr(model.config, "text_config"): # composite models
|
|
41
|
+
config = getattr(model.config, "text_config")
|
|
42
|
+
else:
|
|
43
|
+
config = model.config
|
|
44
|
+
|
|
45
|
+
num_layers = (
|
|
46
|
+
getattr(config, "num_hidden_layers", None)
|
|
47
|
+
or getattr(config, "num_layers", None)
|
|
48
|
+
or getattr(config, "n_layer", None)
|
|
49
|
+
)
|
|
50
|
+
if not num_layers:
|
|
51
|
+
raise ValueError("Current model does not support freeze tuning.")
|
|
52
|
+
|
|
53
|
+
if finetuning_args.use_llama_pro:
|
|
54
|
+
if num_layers % finetuning_args.freeze_trainable_layers != 0:
|
|
55
|
+
raise ValueError(
|
|
56
|
+
"`num_layers` {} should be divisible by `num_layer_trainable` {}.".format(
|
|
57
|
+
num_layers, finetuning_args.freeze_trainable_layers
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
stride = num_layers // finetuning_args.freeze_trainable_layers
|
|
62
|
+
trainable_layer_ids = range(stride - 1, num_layers + stride - 1, stride)
|
|
63
|
+
elif finetuning_args.freeze_trainable_layers > 0: # fine-tuning the last n layers if num_layer_trainable > 0
|
|
64
|
+
trainable_layer_ids = range(max(0, num_layers - finetuning_args.freeze_trainable_layers), num_layers)
|
|
65
|
+
else: # fine-tuning the first n layers if num_layer_trainable < 0
|
|
66
|
+
trainable_layer_ids = range(min(-finetuning_args.freeze_trainable_layers, num_layers))
|
|
67
|
+
|
|
68
|
+
hidden_modules = set()
|
|
69
|
+
non_hidden_modules = set()
|
|
70
|
+
for name, _ in model.named_parameters():
|
|
71
|
+
if ".0." in name:
|
|
72
|
+
hidden_modules.add(name.split(".0.")[-1].split(".")[0])
|
|
73
|
+
elif ".1." in name: # MoD starts from layer 1
|
|
74
|
+
hidden_modules.add(name.split(".1.")[-1].split(".")[0])
|
|
75
|
+
|
|
76
|
+
if re.search(r"\.\d+\.", name) is None:
|
|
77
|
+
non_hidden_modules.add(name.split(".")[-2])
|
|
78
|
+
|
|
79
|
+
trainable_layers = []
|
|
80
|
+
for module_name in finetuning_args.freeze_trainable_modules:
|
|
81
|
+
if module_name != "all" and module_name not in hidden_modules:
|
|
82
|
+
raise ValueError(
|
|
83
|
+
"Module {} is not found, please choose from {}".format(module_name, ", ".join(hidden_modules))
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
for idx in trainable_layer_ids:
|
|
87
|
+
trainable_layers.append(".{:d}.{}".format(idx, module_name if module_name != "all" else ""))
|
|
88
|
+
|
|
89
|
+
if finetuning_args.freeze_extra_modules:
|
|
90
|
+
for module_name in finetuning_args.freeze_extra_modules:
|
|
91
|
+
if module_name not in non_hidden_modules:
|
|
92
|
+
raise ValueError(
|
|
93
|
+
"Module {} is not found, please choose from {}".format(module_name, ", ".join(non_hidden_modules))
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
trainable_layers.append(module_name)
|
|
97
|
+
|
|
98
|
+
for name, param in model.named_parameters():
|
|
99
|
+
if any(trainable_layer in name for trainable_layer in trainable_layers):
|
|
100
|
+
if cast_trainable_params_to_fp32:
|
|
101
|
+
param.data = param.data.to(torch.float32)
|
|
102
|
+
else:
|
|
103
|
+
param.requires_grad_(False)
|
|
104
|
+
|
|
105
|
+
logger.info("Set trainable layers: {}".format(",".join(trainable_layers)))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _setup_lora_tuning(
|
|
109
|
+
config: PretrainedConfig,
|
|
110
|
+
model: PreTrainedModel,
|
|
111
|
+
model_args: ModelArgs,
|
|
112
|
+
is_trainable: bool,
|
|
113
|
+
cast_trainable_params_to_fp32: bool,
|
|
114
|
+
) -> PeftModel:
|
|
115
|
+
finetuning_args = model_args.finetuning_args
|
|
116
|
+
if is_trainable:
|
|
117
|
+
logger.info("Fine-tuning method: {}".format("DoRA" if finetuning_args.use_dora else "LoRA"))
|
|
118
|
+
|
|
119
|
+
adapter_to_resume = None
|
|
120
|
+
|
|
121
|
+
if model_args.adapter_name_or_path is not None:
|
|
122
|
+
is_mergeable = True
|
|
123
|
+
if getattr(model, "quantization_method", None): # merge lora in quantized model is unstable
|
|
124
|
+
assert len(model_args.adapter_name_or_path) == 1, "Quantized model only accepts a single adapter."
|
|
125
|
+
is_mergeable = False
|
|
126
|
+
|
|
127
|
+
if is_deepspeed_zero3_enabled():
|
|
128
|
+
assert len(model_args.adapter_name_or_path) == 1, "Cannot use multiple adapters in DeepSpeed ZeRO-3."
|
|
129
|
+
is_mergeable = False
|
|
130
|
+
|
|
131
|
+
if model_args.use_unsloth:
|
|
132
|
+
assert len(model_args.adapter_name_or_path) == 1, "Unsloth model only accepts a single adapter."
|
|
133
|
+
is_mergeable = False
|
|
134
|
+
|
|
135
|
+
if (is_trainable and not finetuning_args.create_new_adapter) or (not is_mergeable):
|
|
136
|
+
adapter_to_merge = model_args.adapter_name_or_path[:-1]
|
|
137
|
+
adapter_to_resume = model_args.adapter_name_or_path[-1]
|
|
138
|
+
else:
|
|
139
|
+
adapter_to_merge = model_args.adapter_name_or_path
|
|
140
|
+
|
|
141
|
+
init_kwargs = {
|
|
142
|
+
"subfolder": model_args.adapter_folder,
|
|
143
|
+
"offload_folder": model_args.offload_folder,
|
|
144
|
+
"cache_dir": model_args.cache_dir,
|
|
145
|
+
"revision": model_args.model_revision,
|
|
146
|
+
"token": model_args.hf_hub_token,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
for adapter in adapter_to_merge:
|
|
150
|
+
model: LoraModel = PeftModel.from_pretrained(model, adapter, **init_kwargs)
|
|
151
|
+
model = model.merge_and_unload()
|
|
152
|
+
|
|
153
|
+
if len(adapter_to_merge) > 0:
|
|
154
|
+
logger.info("Merged {} adapter(s).".format(len(adapter_to_merge)))
|
|
155
|
+
|
|
156
|
+
if adapter_to_resume is not None: # resume lora training
|
|
157
|
+
if model_args.use_unsloth:
|
|
158
|
+
model = load_unsloth_peft_model(config, model_args, is_trainable=is_trainable)
|
|
159
|
+
else:
|
|
160
|
+
model = PeftModel.from_pretrained(model, adapter_to_resume, is_trainable=is_trainable, **init_kwargs)
|
|
161
|
+
|
|
162
|
+
logger.info("Loaded adapter(s): {}".format(",".join(model_args.adapter_name_or_path)))
|
|
163
|
+
|
|
164
|
+
if is_trainable and adapter_to_resume is None: # create new lora weights while training
|
|
165
|
+
if len(finetuning_args.lora_target) == 1 and finetuning_args.lora_target[0] == "all":
|
|
166
|
+
target_modules = find_all_linear_modules(model)
|
|
167
|
+
else:
|
|
168
|
+
target_modules = finetuning_args.lora_target
|
|
169
|
+
|
|
170
|
+
if model_args.resize_vocab and finetuning_args.additional_target is None:
|
|
171
|
+
input_embeddings = model.get_input_embeddings()
|
|
172
|
+
output_embeddings = model.get_output_embeddings()
|
|
173
|
+
module_names = set()
|
|
174
|
+
for name, module in model.named_modules():
|
|
175
|
+
if module in [input_embeddings, output_embeddings]:
|
|
176
|
+
module_names.add(name.split(".")[-1])
|
|
177
|
+
|
|
178
|
+
finetuning_args.additional_target = module_names
|
|
179
|
+
logger.warning("Vocab has been resized, add {} to trainable params.".format(",".join(module_names)))
|
|
180
|
+
|
|
181
|
+
peft_kwargs = {
|
|
182
|
+
"r": finetuning_args.lora_rank,
|
|
183
|
+
"target_modules": target_modules,
|
|
184
|
+
"lora_alpha": finetuning_args.lora_alpha,
|
|
185
|
+
"lora_dropout": finetuning_args.lora_dropout,
|
|
186
|
+
"use_rslora": finetuning_args.use_rslora,
|
|
187
|
+
"use_dora": finetuning_args.use_dora,
|
|
188
|
+
"modules_to_save": finetuning_args.additional_target,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if model_args.use_unsloth:
|
|
192
|
+
model = get_unsloth_peft_model(model, model_args, peft_kwargs)
|
|
193
|
+
else:
|
|
194
|
+
if finetuning_args.pissa_init:
|
|
195
|
+
if finetuning_args.pissa_iter == -1:
|
|
196
|
+
logger.info("Using PiSSA initialization.")
|
|
197
|
+
peft_kwargs["init_lora_weights"] = "pissa"
|
|
198
|
+
else:
|
|
199
|
+
logger.info("Using PiSSA initialization with FSVD steps {}.".format(finetuning_args.pissa_iter))
|
|
200
|
+
peft_kwargs["init_lora_weights"] = "pissa_niter_{}".format(finetuning_args.pissa_iter)
|
|
201
|
+
|
|
202
|
+
lora_config = LoraConfig(
|
|
203
|
+
task_type=TaskType.CAUSAL_LM,
|
|
204
|
+
inference_mode=False,
|
|
205
|
+
**peft_kwargs,
|
|
206
|
+
)
|
|
207
|
+
model = get_peft_model(model, lora_config)
|
|
208
|
+
|
|
209
|
+
if is_trainable and cast_trainable_params_to_fp32:
|
|
210
|
+
for param in filter(lambda p: p.requires_grad, model.parameters()):
|
|
211
|
+
param.data = param.data.to(torch.float32)
|
|
212
|
+
|
|
213
|
+
return model
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def init_adapter(
|
|
217
|
+
config: PretrainedConfig, model: PreTrainedModel, model_args: ModelArgs, is_trainable: bool) -> PreTrainedModel:
|
|
218
|
+
r"""
|
|
219
|
+
Initializes the adapters.
|
|
220
|
+
|
|
221
|
+
Support full-parameter, freeze and LoRA training.
|
|
222
|
+
|
|
223
|
+
Note that the trainable parameters must be cast to float32.
|
|
224
|
+
"""
|
|
225
|
+
finetuning_args = model_args.finetuning_args
|
|
226
|
+
if is_trainable and getattr(model, "quantization_method", None) is not None:
|
|
227
|
+
if finetuning_args.finetuning_type != "lora":
|
|
228
|
+
raise ValueError("Quantized models can only be used for the LoRA tuning.")
|
|
229
|
+
|
|
230
|
+
if finetuning_args.pissa_init:
|
|
231
|
+
raise ValueError("Cannot initialize PiSSA adapter on quantized models.")
|
|
232
|
+
|
|
233
|
+
# cast trainable parameters to float32 if:
|
|
234
|
+
# 1. is_trainable and not pure_bf16 and not badam and quantization_bit is not None (qlora)
|
|
235
|
+
# 2. is_trainable and not pure_bf16 and not badam and not zero3 and not fsdp (zero3 or fsdp already in fp32)
|
|
236
|
+
cast_trainable_params_to_fp32 = False
|
|
237
|
+
quantization_args = model_args.quantization_args
|
|
238
|
+
if not is_trainable:
|
|
239
|
+
pass
|
|
240
|
+
elif quantization_args.quantization_bit is None and (is_deepspeed_zero3_enabled() or is_fsdp_enabled()):
|
|
241
|
+
logger.info("ZeRO3 / FSDP detected, remaining trainable params in float32.")
|
|
242
|
+
else:
|
|
243
|
+
logger.info("Upcasting trainable params to float32.")
|
|
244
|
+
cast_trainable_params_to_fp32 = True
|
|
245
|
+
|
|
246
|
+
if finetuning_args.finetuning_type == "full":
|
|
247
|
+
_setup_full_tuning(model, is_trainable, cast_trainable_params_to_fp32)
|
|
248
|
+
elif finetuning_args.finetuning_type == "freeze":
|
|
249
|
+
_setup_freeze_tuning(model, finetuning_args, is_trainable, cast_trainable_params_to_fp32)
|
|
250
|
+
elif finetuning_args.finetuning_type == "lora":
|
|
251
|
+
model = _setup_lora_tuning(
|
|
252
|
+
config, model, model_args, is_trainable, cast_trainable_params_to_fp32
|
|
253
|
+
)
|
|
254
|
+
else:
|
|
255
|
+
raise NotImplementedError("Unknown finetuning type: {}.".format(finetuning_args.finetuning_type))
|
|
256
|
+
|
|
257
|
+
return model
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
from typing import Optional, Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import Field, BaseModel
|
|
4
|
+
|
|
5
|
+
from distillflow.model.finetuning_args import FinetuningArgs
|
|
6
|
+
|
|
7
|
+
class ExportArguments(BaseModel):
|
|
8
|
+
r"""
|
|
9
|
+
Arguments pertaining to the model export.
|
|
10
|
+
"""
|
|
11
|
+
export_quantization_bit: Optional[int] = Field(
|
|
12
|
+
default=None,
|
|
13
|
+
description="The number of bits to quantize the exported model."
|
|
14
|
+
)
|
|
15
|
+
export_quantization_dataset: Optional[str] = Field(
|
|
16
|
+
default=None,
|
|
17
|
+
description="Path to the dataset or dataset name to use in quantizing the exported model."
|
|
18
|
+
)
|
|
19
|
+
export_quantization_nsamples: int = Field(
|
|
20
|
+
default=128,
|
|
21
|
+
description="The number of samples used for quantization."
|
|
22
|
+
)
|
|
23
|
+
export_quantization_maxlen: int = Field(
|
|
24
|
+
default=1024,
|
|
25
|
+
description="The maximum length of the model inputs used for quantization."
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class QuantizationArgs(ExportArguments, BaseModel):
|
|
30
|
+
r"""
|
|
31
|
+
Arguments pertaining to the quantization method.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
quantization_method: Literal["bitsandbytes", "hqq", "eetq", "gptq"] = Field(
|
|
35
|
+
default="bitsandbytes",
|
|
36
|
+
description="Quantization method to use for on-the-fly quantization."
|
|
37
|
+
)
|
|
38
|
+
quantization_bit: Optional[int] = Field(
|
|
39
|
+
default=None,
|
|
40
|
+
description="The number of bits to quantize the model using on-the-fly quantization."
|
|
41
|
+
)
|
|
42
|
+
quantization_type: Literal["fp4", "nf4"] = Field(
|
|
43
|
+
default="nf4",
|
|
44
|
+
description="Quantization data type to use in bitsandbytes int4 training."
|
|
45
|
+
)
|
|
46
|
+
double_quantization: bool = Field(
|
|
47
|
+
default=True,
|
|
48
|
+
description="Whether or not to use double quantization in bitsandbytes int4 training."
|
|
49
|
+
)
|
|
50
|
+
quantization_device_map: Optional[Literal["auto"]] = Field(
|
|
51
|
+
default=None,
|
|
52
|
+
description="Device map used to infer the 4-bit quantized model, needs bitsandbytes>=0.43.0."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
model_config = {
|
|
56
|
+
"extra": "forbid"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ModelArgs(BaseModel):
|
|
61
|
+
r"""
|
|
62
|
+
Arguments pertaining to which model/config/tokenizer we are going to fine-tune or infer.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
model_name_or_path: str = Field(
|
|
66
|
+
description="Path to the model weight or identifier from huggingface.co/models or modelscope.cn/models.",
|
|
67
|
+
examples=["Qwen/Qwen2-0.5B"]
|
|
68
|
+
)
|
|
69
|
+
adapter_name_or_path: Optional[str] = Field(
|
|
70
|
+
default=None,
|
|
71
|
+
description="Path to the adapter weight or identifier from huggingface.co/models. "
|
|
72
|
+
"Use commas to separate multiple adapters."
|
|
73
|
+
)
|
|
74
|
+
adapter_folder: Optional[str] = Field(
|
|
75
|
+
default=None,
|
|
76
|
+
description="The folder containing the adapter weights to load."
|
|
77
|
+
)
|
|
78
|
+
cache_dir: Optional[str] = Field(
|
|
79
|
+
default=None,
|
|
80
|
+
description="Where to store the pre-trained models downloaded from huggingface.co or modelscope.cn."
|
|
81
|
+
)
|
|
82
|
+
resize_vocab: bool = Field(
|
|
83
|
+
default=False,
|
|
84
|
+
description="Whether or not to resize the tokenizer vocab and the embedding layers."
|
|
85
|
+
)
|
|
86
|
+
split_special_tokens: bool = Field(
|
|
87
|
+
default=False,
|
|
88
|
+
description="Whether or not the special tokens should be split during the tokenization process."
|
|
89
|
+
)
|
|
90
|
+
new_special_tokens: Optional[str] = Field(
|
|
91
|
+
default=None,
|
|
92
|
+
description="Special tokens to be added into the tokenizer. Use commas to separate multiple tokens."
|
|
93
|
+
)
|
|
94
|
+
model_revision: str = Field(
|
|
95
|
+
default="main",
|
|
96
|
+
description="The specific model version to use (can be a branch name, tag name or commit id)."
|
|
97
|
+
)
|
|
98
|
+
low_cpu_mem_usage: bool = Field(
|
|
99
|
+
default=True,
|
|
100
|
+
description="Whether or not to use memory-efficient model loading."
|
|
101
|
+
)
|
|
102
|
+
flash_attn: Literal["auto", "disabled", "sdpa", "fa2"] = Field(
|
|
103
|
+
default="auto",
|
|
104
|
+
description="Enable FlashAttention for faster training and inference."
|
|
105
|
+
)
|
|
106
|
+
use_unsloth: bool = Field(
|
|
107
|
+
default=False,
|
|
108
|
+
description="Whether or not to use unsloth's optimization for the LoRA training."
|
|
109
|
+
)
|
|
110
|
+
use_unsloth_gc: bool = Field(
|
|
111
|
+
default=False,
|
|
112
|
+
description="Whether or not to use unsloth's gradient checkpointing."
|
|
113
|
+
)
|
|
114
|
+
enable_liger_kernel: bool = Field(
|
|
115
|
+
default=False,
|
|
116
|
+
description="Whether or not to enable liger kernel for faster training."
|
|
117
|
+
)
|
|
118
|
+
disable_gradient_checkpointing: bool = Field(
|
|
119
|
+
default=False,
|
|
120
|
+
description="Whether or not to disable gradient checkpointing."
|
|
121
|
+
)
|
|
122
|
+
upcast_layernorm: bool = Field(
|
|
123
|
+
default=False,
|
|
124
|
+
description="Whether or not to upcast the layernorm weights in fp32."
|
|
125
|
+
)
|
|
126
|
+
upcast_lmhead_output: bool = Field(
|
|
127
|
+
default=False,
|
|
128
|
+
description="Whether or not to upcast the output of lm_head in fp32."
|
|
129
|
+
)
|
|
130
|
+
offload_folder: str = Field(
|
|
131
|
+
default="offload",
|
|
132
|
+
description="Path to offload model weights."
|
|
133
|
+
)
|
|
134
|
+
use_cache: bool = Field(
|
|
135
|
+
default=True,
|
|
136
|
+
description="Whether or not to use KV cache in generation."
|
|
137
|
+
)
|
|
138
|
+
infer_dtype: Literal["auto", "float16", "bfloat16", "float32"] = Field(
|
|
139
|
+
default="auto",
|
|
140
|
+
description="Data type for model weights and activations at inference."
|
|
141
|
+
)
|
|
142
|
+
hf_hub_token: Optional[str] = Field(
|
|
143
|
+
default=None,
|
|
144
|
+
description="Auth token to log in with Hugging Face Hub."
|
|
145
|
+
)
|
|
146
|
+
print_param_status: bool = Field(
|
|
147
|
+
default=False,
|
|
148
|
+
description="For debugging purposes, print the status of the parameters in the model."
|
|
149
|
+
)
|
|
150
|
+
output_attentions: bool = Field(
|
|
151
|
+
default=False,
|
|
152
|
+
description="Whether to output the attention mask for the model during forward pass"
|
|
153
|
+
)
|
|
154
|
+
quantization_args: Optional[QuantizationArgs] = Field(
|
|
155
|
+
default=QuantizationArgs(),
|
|
156
|
+
description="Arguments related to quantization"
|
|
157
|
+
)
|
|
158
|
+
finetuning_args: Optional[FinetuningArgs] = Field(
|
|
159
|
+
default=FinetuningArgs(),
|
|
160
|
+
description="Arguments related to finetuning (LoRA, Freeze etc.)"
|
|
161
|
+
)
|
|
162
|
+
deepspeed_config: str = Field(
|
|
163
|
+
default='./deepspeed/zero0.json',
|
|
164
|
+
description="Path to deepspeed config file. Defaults to stage-0 no optimization (Training/Inference)."
|
|
165
|
+
)
|
|
166
|
+
chat_template: Optional[str] = Field(
|
|
167
|
+
default=None,
|
|
168
|
+
description="Chat template to use when loading the model's tokenizer"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
model_config = {
|
|
172
|
+
"extra": "forbid"
|
|
173
|
+
}
|