Compare commits

...

15 Commits

Author SHA1 Message Date
Zhong Hui
b39e701e21 [Release] 2.7.2 for paddlenlp bugfix. (#7892)
* Release 2.7.2

* Update __init__.py
2024-01-30 15:42:54 +08:00
Kunbo Ding
b9d9d1dc43 RuntimeTimer for the toolkit (#7913) (#7921)
* RuntimeTimer for the toolekit

* RuntimeTimer for the toolekit

* reformat

* fix timer and load checkpoints

* remove reset
2024-01-29 22:49:57 +08:00
Kunbo Ding
9c5ff0d3cb [Cherry-pick] logger level (#7920)
Cherry-pick of #7903
2024-01-29 22:22:08 +08:00
yujun
797efa690e [Fix Download] update converted logic & fix hf hub download subfolder bug (#7911)
* update converted logic & fix hf hub download subfolder bug
2024-01-29 15:26:46 +08:00
greycooker
fff730e6cd fix_trainer_argparser (#7860) 2024-01-22 16:49:43 +08:00
greycooker
b583f11948 Trainer support simultaneously parse JSON files and cmd arguments. (#7768)
* add parse_json_file_and_cmd_lines

* change unit test file path

* Change the way the JSON file is determined

* Merge parameter parsing judgment branches and add comments.

* remove the special handling of output_dir

* Add remaining_args warning
2024-01-22 16:49:26 +08:00
Zhong Hui
17434661aa [Unified Checkpoint] Fix unified checkpoint by empty cache. (#7855) 2024-01-17 15:55:25 +08:00
lugimzzz
38ef1f660a Cherry pick lora fix (#7826)
* fix lora (#7824)

* [llm]support qlora pp (#7801)

* supprt qlora pp

* fix scale dtype
2024-01-11 14:43:21 +08:00
DrownFish19
d4acbfcd4e [Unified Checkpoint] Fix last checkpoint save (#7810)
* fix(unified checkpoint): fix last ckpt save

* fix(unified checkpoint): unified config format
2024-01-10 15:56:36 +08:00
DrownFish19
672ee985a7 [Unified Checkpoint] fix checkpoint names (#7794)
when skipping model weighs save and saving master weights as model weights, unified checkpoint needs choose the model weights to load into master weights.
2024-01-08 17:54:21 +08:00
Zhong Hui
bb9062e48c [Release] 2.7.1 Update version (#7779)
* Update setup.py

* Update __init__.py
2024-01-04 22:21:56 +08:00
DrownFish19
49520692fb [GPT-3] Fix shared weights sync for PipelineLayer (#7775) 2024-01-04 20:20:02 +08:00
Siming Dai
fe3c052fba [BugFix] fix dist dataloader eval (#7778) 2024-01-04 20:19:02 +08:00
Zhong Hui
065825877d [BugFix] Fix release 2.7 (#7771)
* Update release.yml to release tags

* quick fix bugs.
2024-01-04 14:27:51 +08:00
Zhong Hui
adf9e6fac8 [Release] Change version to 2.7.0 change version. (#7763) 2024-01-03 11:50:28 +08:00
20 changed files with 418 additions and 122 deletions

View File

@@ -35,14 +35,14 @@ jobs:
- name: Check Branch
id: check-branch
run: |
if [[ ${{ github.ref }} =~ ^refs/heads/(develop)$ ]]; then
if [[ ${{ github.ref }} =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "match=true" >> $GITHUB_OUTPUT
fi # See: https://stackoverflow.com/a/58869470/1123955
- name: Is A Publish Branch
if: steps.check-branch.outputs.match == 'true' && steps.should-deploy.outputs.should_deploy == 'true'
env:
TWINE_USERNAME: paddle-dev
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.paddlenlp }}
PACKAGE: ${{ inputs.package }}
PADDLENLP_STABLE_VERSION: "true"

View File

@@ -58,10 +58,10 @@ def read_local_dataset(path):
def main():
# Arguments
parser = PdArgumentParser((GenerateArgument, QuantArgument, ModelArgument, DataArgument, TrainingArguments))
if len(sys.argv) == 2 and sys.argv[1].endswith(".json"):
gen_args, quant_args, model_args, data_args, training_args = parser.parse_json_file(
json_file=os.path.abspath(sys.argv[1])
)
# Support format as "args.json --arg1 value1 --arg2 value2.”
# In case of conflict, command line arguments take precedence.
if len(sys.argv) >= 2 and sys.argv[1].endswith(".json"):
gen_args, quant_args, model_args, data_args, training_args = parser.parse_json_file_and_cmd_lines()
else:
gen_args, quant_args, model_args, data_args, training_args = parser.parse_args_into_dataclasses()
training_args.print_config(model_args, "Model")

View File

@@ -0,0 +1,132 @@
# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import os
import sys
import tempfile
import unittest
from unittest.mock import patch
from llm.run_pretrain import PreTrainingArguments
from paddlenlp.trainer.argparser import PdArgumentParser
def parse_args():
parser = PdArgumentParser((PreTrainingArguments,))
# Support format as "args.json --arg1 value1 --arg2 value2.”
# In case of conflict, command line arguments take precedence.
if len(sys.argv) >= 2 and sys.argv[1].endswith(".json"):
model_args = parser.parse_json_file_and_cmd_lines()
else:
model_args = parser.parse_args_into_dataclasses()
return model_args
def create_json_from_dict(data_dict, file_path):
with open(file_path, "w") as f:
json.dump(data_dict, f)
class ArgparserTest(unittest.TestCase):
script_name = "test_argparser.py"
args_dict = {
"max_steps": 3000,
"amp_master_grad": False,
"adam_beta1": 0.9,
"adam_beta2": 0.999,
"amp_custom_black_list": ["reduce_sum", "sin", "cos"],
"adam_epsilon": 1e-08,
"bf16": False,
"enable_linear_fused_grad_add": False,
"eval_steps": 3216,
"flatten_param_grads": False,
"fp16": 1,
"log_on_each_node": True,
"logging_dir": "./checkpoints/llama2_pretrain_ckpts/runs/Dec27_04-28-35_instance-047hzlt0-4",
"logging_first_step": False,
"logging_steps": 1,
"lr_end": 1e-07,
"max_evaluate_steps": -1,
"max_grad_norm": 1.0,
"min_learning_rate": 3e-06,
"no_cuda": False,
"num_cycles": 0.5,
"num_train_epochs": 3.0,
"output_dir": "./checkpoints/llama2_pretrain_ckpts",
}
def test_parse_cmd_lines(self):
cmd_line_args = [ArgparserTest.script_name]
for key, value in ArgparserTest.args_dict.items():
if isinstance(value, list):
cmd_line_args.extend([f"--{key}", *[str(v) for v in value]])
else:
cmd_line_args.extend([f"--{key}", str(value)])
with patch("sys.argv", cmd_line_args):
model_args = vars(parse_args()[0])
for key, value in ArgparserTest.args_dict.items():
self.assertEqual(model_args.get(key), value)
def test_parse_json_file(self):
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmpfile:
create_json_from_dict(ArgparserTest.args_dict, tmpfile.name)
tmpfile_path = tmpfile.name
with patch("sys.argv", [ArgparserTest.script_name, tmpfile_path]):
model_args = vars(parse_args()[0])
for key, value in ArgparserTest.args_dict.items():
self.assertEqual(model_args.get(key), value)
os.remove(tmpfile_path)
def test_parse_json_file_and_cmd_lines(self):
half_size = len(ArgparserTest.args_dict) // 2
json_part = {k: ArgparserTest.args_dict[k] for k in list(ArgparserTest.args_dict)[:half_size]}
cmd_line_part = {k: ArgparserTest.args_dict[k] for k in list(ArgparserTest.args_dict)[half_size:]}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmpfile:
create_json_from_dict(json_part, tmpfile.name)
tmpfile_path = tmpfile.name
cmd_line_args = [ArgparserTest.script_name, tmpfile_path]
for key, value in cmd_line_part.items():
if isinstance(value, list):
cmd_line_args.extend([f"--{key}", *[str(v) for v in value]])
else:
cmd_line_args.extend([f"--{key}", str(value)])
with patch("sys.argv", cmd_line_args):
model_args = vars(parse_args()[0])
for key, value in ArgparserTest.args_dict.items():
self.assertEqual(model_args.get(key), value)
os.remove(tmpfile_path)
def test_parse_json_file_and_cmd_lines_with_conflict(self):
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmpfile:
json.dump(ArgparserTest.args_dict, tmpfile)
tmpfile_path = tmpfile.name
cmd_line_args = [
ArgparserTest.script_name,
tmpfile_path,
"--min_learning_rate",
"2e-5",
"--max_steps",
"3000",
"--log_on_each_node",
"False",
]
with patch("sys.argv", cmd_line_args):
model_args = vars(parse_args()[0])
self.assertEqual(model_args.get("min_learning_rate"), 2e-5)
self.assertEqual(model_args.get("max_steps"), 3000)
self.assertEqual(model_args.get("log_on_each_node"), False)
for key, value in ArgparserTest.args_dict.items():
if key not in ["min_learning_rate", "max_steps", "log_on_each_node"]:
self.assertEqual(model_args.get(key), value)
os.remove(tmpfile_path)

View File

@@ -372,8 +372,10 @@ class PretrainingTrainer(Trainer):
def main():
parser = PdArgumentParser((ModelArguments, DataArguments, PreTrainingArguments))
if len(sys.argv) == 2 and sys.argv[1].endswith(".json"):
model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1]))
# Support format as "args.json --arg1 value1 --arg2 value2.”
# In case of conflict, command line arguments take precedence.
if len(sys.argv) >= 2 and sys.argv[1].endswith(".json"):
model_args, data_args, training_args = parser.parse_json_file_and_cmd_lines()
else:
model_args, data_args, training_args = parser.parse_args_into_dataclasses()

View File

@@ -18,7 +18,7 @@ import sys
PADDLENLP_STABLE_VERSION = "PADDLENLP_STABLE_VERSION"
__version__ = "2.6.1.post"
__version__ = "2.7.2.post"
if os.getenv(PADDLENLP_STABLE_VERSION):
__version__ = __version__.replace(".post", "")

View File

@@ -231,12 +231,12 @@ class LoRAModel(nn.Layer):
if self.is_pipelinemodel:
self.model._single_to_pp_mapping = None
if self.quantized and merge_tensor_parallel and self.lora_config.tensor_parallel_degre > 1:
if self.quantized and merge_tensor_parallel and self.lora_config.tensor_parallel_degree > 1:
merge_tensor_parallel = False
logger.warning(
"Quantized strategy does not support merge_tensor_parallel. Set merge_tensor_parallel to False."
)
if self.is_pipelinemodel and merge_tensor_parallel and self.lora_config.tensor_parallel_degre > 1:
if self.is_pipelinemodel and merge_tensor_parallel and self.lora_config.tensor_parallel_degree > 1:
merge_tensor_parallel = False
logger.warning(
"Pipeline parallism does not support merge_tensor_parallel. Set merge_tensor_parallel to False."
@@ -489,7 +489,7 @@ class LoRAModel(nn.Layer):
freeze_numel += np.prod(weight.shape)
else:
trainable_numel += np.prod(weight.shape)
logger.info(
logger.debug(
f"Frozen parameters: {freeze_numel:.2e} || Trainable parameters:{trainable_numel:.2e} || Total parameters:{freeze_numel+trainable_numel:.2e}|| Trainable:{trainable_numel / (freeze_numel+trainable_numel):.2%}"
)

View File

@@ -282,7 +282,7 @@ class PrefixModelForCausalLM(paddle.nn.Layer):
freeze_numel += np.prod(weight.shape)
else:
trainable_numel += np.prod(weight.shape)
logger.info(
logger.debug(
f"Frozen parameters: {freeze_numel:.2e} || Trainable parameters:{trainable_numel:.2e} || Total parameters:{freeze_numel+trainable_numel:.2e}|| Trainable:{trainable_numel / (freeze_numel+trainable_numel):.2%}"
)

View File

@@ -83,7 +83,7 @@ class QuantizationLinear(nn.Layer):
self.quant_scale = self.create_parameter(
shape=[out_features],
attr=scale_attr,
dtype="float32",
dtype=self._dtype,
is_bias=False,
)
if self.quant_algo in ["fp4", "nf4"]:
@@ -231,7 +231,7 @@ class ColumnParallelQuantizationLinear(nn.Layer):
self.quant_scale = self.create_parameter(
shape=[self.output_size_per_partition],
attr=scale_attr,
dtype="float32",
dtype=self._dtype,
is_bias=False,
)
self.quant_scale.is_distributed = True if self.is_mp else False
@@ -345,7 +345,7 @@ class RowParallelQuantizationLinear(nn.Layer):
self.quant_scale = self.create_parameter(
shape=[out_features],
attr=scale_attr,
dtype="float32",
dtype=self._dtype,
is_bias=False,
)
self.quant_scale.is_distributed = True if self.is_mp else False

View File

@@ -109,9 +109,12 @@ def convert_to_quantize_state_dict_with_check(state_dict, quantization_linear_li
raise ValueError(
f"{quant_weight_name} should be {paddle.int8} in state_dict but received dtype {state_dict[quant_weight_name].dtype}."
)
if state_dict[quant_scale_name].dtype != paddle.float32:
if (
state_dict[quant_scale_name].dtype != paddle.float16
and state_dict[quant_scale_name].dtype != paddle.bfloat16
):
raise ValueError(
f"{quant_scale_name} should be {paddle.float32} in state_dict but received dtype {state_dict[quant_scale_name].dtype}."
f"{quant_scale_name} should be {paddle.float16} or {paddle.bfloat16} in state_dict but received dtype {state_dict[quant_scale_name].dtype}."
)
elif weight_name in state_dict:
target_weight = state_dict.pop(weight_name).cast(dtype)

View File

@@ -13,11 +13,12 @@
# limitations under the License.
from .argparser import *
from .training_args import *
from .compression_args import *
from .plugins.timer import *
from .trainer import *
from .trainer_callback import *
from .trainer_utils import *
from .trainer_compress import *
from .training_args_seq2seq import *
from .trainer_seq2seq import *
from .trainer_utils import *
from .training_args import *
from .training_args_seq2seq import *

View File

@@ -19,6 +19,7 @@
import dataclasses
import json
import sys
import warnings
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, ArgumentTypeError
from copy import copy
from enum import Enum
@@ -247,6 +248,49 @@ class PdArgumentParser(ArgumentParser):
outputs.append(obj)
return (*outputs,)
def parse_json_file_and_cmd_lines(self) -> Tuple[DataClass, ...]:
"""
Extend the functionality of `parse_json_file` to handle command line arguments in addition to loading a JSON
file.
When there is a conflict between the command line arguments and the JSON file configuration,
the command line arguments will take precedence.
Returns:
Tuple consisting of:
- the dataclass instances in the same order as they were passed to the initializer.abspath
"""
if not sys.argv[1].endswith(".json"):
raise ValueError(f"The first argument should be a JSON file, but it is {sys.argv[1]}")
json_file = Path(sys.argv[1])
if json_file.exists():
with open(json_file, "r") as file:
data = json.load(file)
json_args = []
for key, value in data.items():
if isinstance(value, list):
json_args.extend([f"--{key}", *[str(v) for v in value]])
else:
json_args.extend([f"--{key}", str(value)])
else:
raise FileNotFoundError(f"The argument file {json_file} does not exist.")
# In case of conflict, command line arguments take precedence
args = json_args + sys.argv[2:]
namespace, remaining_args = self.parse_known_args(args=args)
outputs = []
for dtype in self.dataclass_types:
keys = {f.name for f in dataclasses.fields(dtype) if f.init}
inputs = {k: v for k, v in vars(namespace).items() if k in keys}
for k in keys:
delattr(namespace, k)
obj = dtype(**inputs)
outputs.append(obj)
if remaining_args:
warnings.warn(f"Some specified arguments are not used by the PdArgumentParser: {remaining_args}")
return (*outputs,)
def parse_dict(self, args: dict) -> Tuple[DataClass, ...]:
"""
Alternative helper method that does not use `argparse` at all, instead uses a dict and populating the dataclass

View File

@@ -96,7 +96,7 @@ class VisualDLCallback(TrainerCallback):
if self.vdl_writer is not None:
self.vdl_writer.add_text("args", args.to_json_string())
if "model" in kwargs:
if "model" in kwargs and logger.logger.level < 20:
model = kwargs["model"]
if isinstance(model, LoRAModel) or isinstance(model, PrefixModelForCausalLM):
model = kwargs["model"].model

View File

@@ -31,14 +31,14 @@ class _Timer:
def start(self):
"""Start the timer."""
assert not self.started_, "timer has already started"
assert not self.started_, f"{self.name} timer has already started"
paddle.device.synchronize()
self.start_time = time.time()
self.started_ = True
def stop(self):
"""Stop the timers."""
assert self.started_, "timer is not started."
assert self.started_, f"{self.name} timer is not started."
paddle.device.synchronize()
self.elapsed_ += time.time() - self.start_time
self.started_ = False
@@ -65,6 +65,32 @@ class _Timer:
return elapsed_
class RuntimeTimer:
"""A timer that can be dynamically adjusted during runtime."""
def __init__(self, name):
self.timer = _Timer(name)
def start(self, name):
"""Start the RuntimeTimer."""
self.timer.name = name
self.timer.start()
def stop(self):
"""Stop the RuntimeTimer."""
self.timer.stop()
def log(self):
"""Log, stop and reset the RuntimeTimer."""
runtime = self.timer.elapsed(reset=True)
if self.timer.started_ is True:
self.timer.stop()
self.timer.reset()
string = "[timelog] {}: {:.2f}s ({}) ".format(self.timer.name, runtime, time.strftime("%Y-%m-%d %H:%M:%S"))
return string
class Timers:
"""Group of timers."""

View File

@@ -115,6 +115,7 @@ def save_unified_checkpoint(args, model, optimizer, output_dir, safe_serializati
else:
raise ValueError("Unified checkpoint only supports PretrainedModel")
skip_save_model_weight = False
if UnifiedCheckpointOption.SKIP_SAVE_MODEL_WEIGHT.value in args.unified_checkpoint_config:
if is_need_master_weight(optimizer, is_fp16_or_bp16=(args.fp16 or args.bf16)):
logger.info(
@@ -122,36 +123,40 @@ def save_unified_checkpoint(args, model, optimizer, output_dir, safe_serializati
"The master weight will be loaded as model weights for next resumption."
)
# not save model weight, load from master weight
return
config_to_save = None
state_dict, config_to_save, shard_file, sharded_index = unified_checkpoint_into_shards(
args, model_to_save, safe_serialization=safe_serialization
)
skip_save_model_weight = True
save_directory = output_dir
os.makedirs(save_directory, exist_ok=True)
is_sync_save = True
if "async_save" in args.unified_checkpoint_config:
is_sync_save = False
file_save_async_or_sync(
state_dict, os.path.join(save_directory, shard_file), safe_serialization, is_sync=is_sync_save
)
# save model weights
if not skip_save_model_weight:
state_dict, shard_file, sharded_index = unified_checkpoint_into_shards(
args, model_to_save, safe_serialization=safe_serialization
)
is_sync_save = True
if "async_save" in args.unified_checkpoint_config:
is_sync_save = False
file_save_async_or_sync(
state_dict, os.path.join(save_directory, shard_file), safe_serialization, is_sync=is_sync_save
)
if sharded_index is not None:
if not safe_serialization:
path = os.path.join(output_dir, PADDLE_WEIGHTS_INDEX_NAME)
else:
path = os.path.join(output_dir, SAFE_WEIGHTS_INDEX_NAME)
with open(path, "w") as f:
json.dump(sharded_index, f, indent=4)
# save the config
config_to_save = save_config(model_to_save)
# Attach architecture to the config
config_to_save.architectures = [model_to_save.__class__.__name__]
# Save the config
if args.should_save:
config_to_save.save_pretrained(save_directory)
if sharded_index is not None:
if not safe_serialization:
path = os.path.join(output_dir, PADDLE_WEIGHTS_INDEX_NAME)
else:
path = os.path.join(output_dir, SAFE_WEIGHTS_INDEX_NAME)
with open(path, "w") as f:
json.dump(sharded_index, f, indent=4)
paddle.device.cuda.empty_cache()
def load_unified_checkpoint(args, model, optimizer, resume_from_checkpoint: str, safe_serialization=False) -> None:
@@ -252,6 +257,18 @@ def load_unified_checkpoint_locally(args, model, resume_from_checkpoint: str, sa
raise RuntimeError(f"Error(s) in loading state_dict for {model.__class__.__name__}:\n\t{error_msg}")
def save_config(model_to_save):
dtype = get_parameter_dtype(model_to_save)
model_to_save.config.dtype = str(dtype).split(".")[1]
config_to_save = copy.deepcopy(model_to_save.config)
if config_to_save.tensor_parallel_degree > 1:
# do we need to change?
config_to_save.tensor_parallel_degree = 1
return config_to_save
def unified_checkpoint_into_shards(
args,
model_to_save,
@@ -266,14 +283,13 @@ def unified_checkpoint_into_shards(
Returns:
tuple: state_dict, config, shard_file: file name, sharded_index: map for weight to file name.
"""
paddle.device.cuda.empty_cache()
assert hasattr(model_to_save, "config")
state_dict = model_to_save.state_dict()
all_filter_keys = filter_params(model_to_save, state_dict)
dtype = get_parameter_dtype(model_to_save)
model_to_save.config.dtype = str(dtype).split(".")[1]
config_to_save = copy.deepcopy(model_to_save.config)
if config_to_save.tensor_parallel_degree > 1:
@@ -282,10 +298,6 @@ def unified_checkpoint_into_shards(
)
state_dict = merge_tensor_parallel_with_shard(state_dict, tp_actions, all_filter_keys)
if config_to_save.tensor_parallel_degree > 1:
# do we need to change?
config_to_save.tensor_parallel_degree = 1
# build index json file
index_weight_file = {}
total_size = 0
@@ -302,7 +314,9 @@ def unified_checkpoint_into_shards(
total_size_list,
)
return state_dict, config_to_save, shard_file, sharded_index
paddle.device.cuda.empty_cache()
return state_dict, shard_file, sharded_index
def save_unified_optimizer(args, model, optimizer, output_dir, safe_serialization=False):
@@ -324,6 +338,8 @@ def save_unified_optimizer(args, model, optimizer, output_dir, safe_serializatio
optim_state_dict, shard_optim_file, sharded_optim_index = results[0]
master_weight_state_dict, shard_master_weight_file, sharded_master_weight_index = results[1]
paddle.device.cuda.empty_cache()
save_directory = output_dir
os.makedirs(save_directory, exist_ok=True)
@@ -343,16 +359,17 @@ def save_unified_optimizer(args, model, optimizer, output_dir, safe_serializatio
)
if sharded_optim_index is not None:
if not safe_serialization:
path = os.path.join(output_dir, PADDLE_OPTIMIZER_INDEX_NAME)
master_path = os.path.join(output_dir, PADDLE_MASTER_WEIGHTS_INDEX_NAME)
else:
path = os.path.join(output_dir, SAFE_OPTIMIZER_INDEX_NAME)
master_path = os.path.join(output_dir, SAFE_MASTER_WEIGHTS_INDEX_NAME)
optimizer_index_name = SAFE_OPTIMIZER_INDEX_NAME if safe_serialization else PADDLE_OPTIMIZER_INDEX_NAME
path = os.path.join(output_dir, optimizer_index_name)
with open(path, "w") as f:
json.dump(sharded_optim_index, f, indent=4)
master_weights_name = (
SAFE_MASTER_WEIGHTS_INDEX_NAME if safe_serialization else PADDLE_MASTER_WEIGHTS_INDEX_NAME
)
if UnifiedCheckpointOption.SKIP_SAVE_MODEL_WEIGHT.value in args.unified_checkpoint_config:
master_weights_name = SAFE_WEIGHTS_INDEX_NAME if safe_serialization else PADDLE_WEIGHTS_INDEX_NAME
master_path = os.path.join(output_dir, master_weights_name)
if master_weight_state_dict is not None:
with open(master_path, "w") as f:
json.dump(sharded_master_weight_index, f, indent=4)
@@ -504,6 +521,7 @@ def unified_optimizer_into_shards(
optimizer (Optimizer): optimizer to save.
safe_serialization (bool, optional): safe serialization using safetensors. Defaults to False.
"""
paddle.device.cuda.empty_cache()
optim_state_dict = nested_copy(optimizer.state_dict())
master_weights = None
if "master_weights" in optim_state_dict.keys():
@@ -549,18 +567,23 @@ def unified_optimizer_into_shards(
tp_actions,
filter_optim_keys,
)
paddle.device.cuda.empty_cache()
if master_weights is not None:
master_weights = merge_tensor_parallel_for_optimizer(
master_weights,
tp_actions,
filter_master_keys,
)
paddle.device.cuda.empty_cache()
# build index json file
index_optimizer_file, index_master_weight_file = {}, {}
total_optim_size, total_master_weight_size = 0, 0
optimizer_name = SAFE_OPTIMIZER_NAME if safe_serialization else PADDLE_OPTIMIZER_NAME
master_weights_name = SAFE_MASTER_WEIGHTS_NAME if safe_serialization else PADDLE_MASTER_WEIGHTS_NAME
if UnifiedCheckpointOption.SKIP_SAVE_MODEL_WEIGHT.value in args.unified_checkpoint_config:
master_weights_name = SAFE_WEIGHTS_NAME if safe_serialization else PADDLE_WEIGHTS_NAME
shard_optimizer_file = get_sharded_file_name(args, optimizer_name, is_optimizer=True)
shard_master_weight_file = get_sharded_file_name(args, master_weights_name, is_optimizer=True)
@@ -589,6 +612,7 @@ def unified_optimizer_into_shards(
else:
sharded_optim_index["master_weights"] = False
paddle.device.cuda.empty_cache()
if master_weights is None:
return [(optim_state_dict, shard_optimizer_file, sharded_optim_index)]
else:
@@ -1648,6 +1672,10 @@ def update_master_weight_status(args, optimizer, has_master_weight, safe_seriali
index_filename_master_weights = (
PADDLE_MASTER_WEIGHTS_INDEX_NAME if not safe_serialization else SAFE_MASTER_WEIGHTS_INDEX_NAME
)
if UnifiedCheckpointOption.SKIP_SAVE_MODEL_WEIGHT.value in args.unified_checkpoint_config:
index_filename_master_weights = (
PADDLE_WEIGHTS_INDEX_NAME if not safe_serialization else SAFE_WEIGHTS_INDEX_NAME
)
else:
has_master_weight = False
index_filename_master_weights = None

View File

@@ -104,7 +104,7 @@ from ..utils.import_utils import is_datasets_available, is_paddle_cuda_available
from ..utils.log import logger
from .argparser import strtobool
from .integrations import get_reporting_integration_callbacks
from .plugins.timer import get_timers, set_timers
from .plugins.timer import RuntimeTimer, get_timers, set_timers
from .plugins.unified_checkpoint import (
load_unified_checkpoint,
load_unified_optimizer,
@@ -304,6 +304,7 @@ class Trainer:
if not args.skip_profile_timer:
set_timers()
self.timers = get_timers()
self.runtime_timer = RuntimeTimer("RuntimeTimer")
self.model_wrapped = model
self.model = model
@@ -506,6 +507,7 @@ class Trainer:
`bool` and equals `True`, load the last checkpoint in *args.output_dir* as saved by a previous instance
of [`Trainer`]. Only load model state dict.
"""
self.runtime_timer.start("checkpoint loading time")
resume_from_checkpoint = None if not resume_from_checkpoint else resume_from_checkpoint
# Load potential model checkpoint
@@ -531,10 +533,12 @@ class Trainer:
safe_serialization=True,
)
logger.info(f"Loading model from {resume_from_checkpoint} using unified checkpoint.")
self.runtime_timer.stop()
return
if isinstance(self.model, LoRAModel) or isinstance(self.model, PrefixModelForCausalLM):
self._load_from_peft_checkpoint(resume_from_checkpoint)
self.runtime_timer.stop()
return
weight_name = PADDLE_WEIGHTS_NAME
@@ -584,6 +588,7 @@ class Trainer:
elif resume_from_checkpoint is not None:
logger.info(f"not loading ckpt :{self.args.dataset_rank}")
self.runtime_timer.stop()
def _wrap_model_and_load_sharded_checkpoint(self, resume_from_checkpoint):
# In the sharded mode, should invoke _load_from_checkpoint after _wrap_model.
@@ -639,7 +644,6 @@ class Trainer:
# memory metrics - must set up as early as possible
self._memory_tracker.start()
if not self.args.should_load_sharding_stage1_model:
self._load_from_checkpoint(resume_from_checkpoint)
@@ -695,6 +699,7 @@ class Trainer:
if self.args.should_load_sharding_stage1_model:
model = self._wrap_model_and_load_sharded_checkpoint(resume_from_checkpoint)
elif self.args.should_save_sharding_stage1_model:
# In the non-sharded mode, should invoke _load_from_checkpoint before _wrap_model.
# In this mode, the rank0 load all params and the _wrap_model implicitly broadcast params from rank0 to the other ranks.
@@ -718,6 +723,8 @@ class Trainer:
self.create_optimizer_and_scheduler(num_training_steps=max_steps)
self._load_optimizer_and_scheduler(resume_from_checkpoint)
logger.info(f"{self.runtime_timer.log()}")
logger.info("***** Running training *****")
logger.info(f" Num examples = {num_examples:,}")
logger.info(f" Num Epochs = {num_train_epochs}")
@@ -729,7 +736,7 @@ class Trainer:
# per_device_trainable_numel = sum(p.numel().item() for p in model.parameters() if not p.stop_gradient)
# TODO: Temporary fix since Tensor.numel() not supported in distributed mode
per_device_trainable_numel = sum(np.prod(p.shape) for p in model.parameters() if not p.stop_gradient)
logger.info(f" Number of trainable parameters = {per_device_trainable_numel:,} (per device)")
logger.debug(f" Number of trainable parameters = {per_device_trainable_numel:,} (per device)")
if self.args.use_hybrid_parallel:
# todo fix for pipeline_parallel_degree
parts_num = max(self.args.tensor_parallel_degree, 1) * max(self.args.pipeline_parallel_degree, 1)
@@ -745,7 +752,7 @@ class Trainer:
trainable_numel = trainable_numel // self.args.sep_parallel_degree
# the numel is roughly, because the tensor parallel still hold own bias or layer_norm weight without splited
# so, the trainable numel is a little bigger than real.
logger.info(f" Number of trainable parameters = {trainable_numel:,} (all devices, roughly)")
logger.debug(f" Number of trainable parameters = {trainable_numel:,} (all devices, roughly)")
start_time = time.time()
self._globalstep_last_start_time = time.time()
@@ -1239,6 +1246,7 @@ class Trainer:
paddle.device.cuda.synchronize()
self._save_checkpoint(model, metrics=metrics)
logger.info(f"{self.runtime_timer.log()}")
self.control = self.callback_handler.on_save(self.args, self.state, self.control)
def _get_learning_rate(self):
@@ -1293,6 +1301,9 @@ class Trainer:
)
def _get_eval_sampler(self, eval_dataset: Dataset):
if eval_dataset is None or not has_length(eval_dataset):
return None
if self.args.world_size <= 1:
return paddle.io.BatchSampler(
eval_dataset,
@@ -2024,11 +2035,20 @@ class Trainer:
self.model_wrapped.get_all_parameters(convert2cpu=True)
if self.args.should_save_model_state:
unified_checkpoint_config_backup = self.args.unified_checkpoint_config
# backup and remove unified_checkpoint_config for not trine stage
if not self.is_in_train:
self.args.unified_checkpoint_config = []
self._save(output_dir=output_dir, merge_tensor_parallel=merge_tensor_parallel)
# recover unified_checkpoint_config for not trine stage
if not self.is_in_train:
self.args.unified_checkpoint_config = unified_checkpoint_config_backup
def _save_checkpoint(self, model, metrics=None):
# assert unwrap_model(model) is self.model, "internal model should be a reference to self.model"
self.runtime_timer.start("checkpoint saving time")
# Save model checkpoint
checkpoint_folder = f"{PREFIX_CHECKPOINT_DIR}-{self.state.global_step}"
@@ -2074,6 +2094,7 @@ class Trainer:
if self.do_grad_scaling:
paddle.save(self.scaler.state_dict(), os.path.join(output_dir, SCALER_NAME))
self.runtime_timer.stop()
# Determine the new best metric / best model checkpoint
if metrics is not None and self.args.metric_for_best_model is not None:
metric_to_check = self.args.metric_for_best_model
@@ -2292,10 +2313,13 @@ class Trainer:
def _load_optimizer_and_scheduler(self, checkpoint):
"""If optimizer and scheduler states exist, load them."""
self.runtime_timer.start("checkpoint loading time")
if checkpoint is None:
self.runtime_timer.stop()
return
if (not self.args.should_load_sharding_stage1_model) and self.args.ignore_load_lr_and_optim:
self.runtime_timer.stop()
return
opt_state_dict = None
@@ -2304,8 +2328,8 @@ class Trainer:
checkpoint, OPTIMIZER_NAME, self.model_wrapped
)
else:
use_unified_checkpoint = False
if self.args.unified_checkpoint:
use_unified_checkpoint = False
if self.is_unified_checkpoint(checkpoint):
use_unified_checkpoint = True
else:
@@ -2354,6 +2378,7 @@ class Trainer:
self.scaler.load_state_dict(
paddle.load(distributed_file(os.path.join(checkpoint, SCALER_NAME)), return_numpy=True)
)
self.runtime_timer.stop()
def log(self, logs: Dict[str, float], **kwargs) -> None:
"""
@@ -2380,7 +2405,7 @@ class Trainer:
kwargs.update(timer=self.timers, paddle_pipeline_timers=paddle_pipeline_timers)
if self.state.epoch is not None:
logs["epoch"] = round(self.state.epoch, 4)
logs["progress_or_epoch"] = round(self.state.epoch, 4)
output = {**logs, **{"step": self.state.global_step}}
self.state.log_history.append(output)
self.control = self.callback_handler.on_log(self.args, self.state, self.control, logs, **kwargs)
@@ -2941,23 +2966,23 @@ class Trainer:
"""
print config values
"""
logger.info("=" * 60)
logger.debug("=" * 60)
if args is None:
args = self.args
key = "Training"
import paddlenlp
logger.info("{:^40}".format("{} Configuration Arguments".format(key)))
logger.info("{:30}: {}".format("paddle commit id", paddle.version.commit))
logger.info("{:30}: {}".format("paddlenlp commit id", paddlenlp.version.commit))
logger.debug("{:^40}".format("{} Configuration Arguments".format(key)))
logger.debug("{:30}: {}".format("paddle commit id", paddle.version.commit))
logger.debug("{:30}: {}".format("paddlenlp commit id", paddlenlp.version.commit))
for a in dir(args):
if a[:2] != "__": # don't print double underscore methods
v = getattr(args, a)
if not isinstance(v, types.MethodType):
logger.info("{:30}: {}".format(a, v))
logger.debug("{:30}: {}".format(a, v))
logger.info("")
logger.debug("")
def is_unified_checkpoint(self, resume_from_checkpoint, safe_serialization=True):
is_unified_checkpoint_type = False

View File

@@ -515,7 +515,7 @@ class ProgressCallback(TrainerCallback):
logs_str = ", ".join(f"{k}: {v}" for k, v in logs.items())
else:
logs_str = str(logs)
self.training_bar.write(logs_str)
logger.info(logs_str)
def on_train_end(self, args, state, control, **kwargs):
if state.is_local_process_zero:

View File

@@ -1308,8 +1308,10 @@ class TrainingArguments:
self.unified_checkpoint_config = [
"skip_save_model_weight",
"master_weight_compatible",
"async_save",
# "async_save",
]
else:
self.unified_checkpoint_config = self.unified_checkpoint_config.split(" ")
if self.report_to is None:
logger.info(
@@ -1701,21 +1703,21 @@ class TrainingArguments:
"""
print all config values.
"""
logger.info("=" * 60)
logger.debug("=" * 60)
if args is None:
args = self
key = "Training"
import paddlenlp
logger.info("{:^40}".format("{} Configuration Arguments".format(key)))
logger.info("{:30}: {}".format("paddle commit id", paddle.version.commit))
logger.info("{:30}: {}".format("paddlenlp commit id", paddlenlp.version.commit))
logger.debug("{:^40}".format("{} Configuration Arguments".format(key)))
logger.debug("{:30}: {}".format("paddle commit id", paddle.version.commit))
logger.debug("{:30}: {}".format("paddlenlp commit id", paddlenlp.version.commit))
for a in dir(args):
if a[:2] != "__": # don't print double underscore methods
v = getattr(args, a)
if not isinstance(v, types.MethodType):
logger.info("{:30}: {}".format(a, v))
logger.debug("{:30}: {}".format(a, v))
logger.info("")
logger.debug("")

View File

@@ -42,14 +42,17 @@ from huggingface_hub import (
)
from huggingface_hub.utils import EntryNotFoundError
from paddle import Tensor
from paddle.distributed.fleet.meta_parallel.parallel_layers import SharedLayerDesc
from paddle.distributed.fleet.meta_parallel.parallel_layers import (
PipelineLayer,
SharedLayerDesc,
)
from paddle.nn import Embedding, Layer
# TODO(fangzeyang) Temporary fix and replace by paddle framework downloader later
from paddle.utils.download import is_url as is_remote_url
from tqdm.auto import tqdm
from paddlenlp.utils.downloader import get_path_from_url_with_filelock, hf_file_exists
from paddlenlp.utils.downloader import get_path_from_url_with_filelock
from paddlenlp.utils.env import (
CONFIG_NAME,
LEGACY_CONFIG_NAME,
@@ -364,28 +367,7 @@ def resolve_weight_file_from_hf_hub(repo_id: str, cache_dir: str, support_conver
support_conversion (bool): whether support converting pytorch weight file to paddle weight file
subfolder (str, optional) An optional value corresponding to a folder inside the repo.
"""
is_local = os.path.isdir(repo_id)
if not is_local:
if hf_file_exists(repo_id, PADDLE_WEIGHTS_NAME, subfolder=subfolder):
file_name = PADDLE_WEIGHTS_NAME
assert (
support_conversion is False
), "Please call set convert_from_torch for paddle weights on huggingface hub, eg. Model.from_pretrained(model_name, from_hf_hub=True, convert_from_torch=False)"
elif hf_file_exists(repo_id, PYTORCH_WEIGHTS_NAME, subfolder=subfolder):
if not support_conversion:
raise EntryNotFoundError(
f"can not download `{PADDLE_WEIGHTS_NAME} from https://huggingface.co/{repo_id}` "
"and current model doesn't support conversion from pytorch weight file to paddle weight file"
)
file_name = PYTORCH_WEIGHTS_NAME
else:
raise EntryNotFoundError(
message=f"can not find the paddle/pytorch weight file from: https://huggingface.co/{repo_id}",
response=None,
)
else:
# for local file, we use support_conversion to select paddle or torch weight.
file_name = PYTORCH_WEIGHTS_NAME if support_conversion else PADDLE_WEIGHTS_NAME
file_name = PYTORCH_WEIGHTS_NAME if support_conversion else PADDLE_WEIGHTS_NAME
file_name_list = [SAFE_WEIGHTS_NAME] + [file_name] + [PYTORCH_WEIGHTS_INDEX_NAME] + [SAFE_WEIGHTS_INDEX_NAME]
resolved_file = None
@@ -935,6 +917,18 @@ class PretrainedModel(Layer, GenerationMixin, ConversionMixin):
):
self.init_weights()
# Note:
# 1. PipelineLayer will create parameters for each layer and
# call `_synchronize_shared_weights()` to synchronize the shared parameters.
# 2. When setting the model `state_dict`, `_synchronize_shared_weights` will be called to
# synchronize the shared parameters.
# However, `self._init_weights` will re-initialize the parameters without
# synchronizing the shared parameters. If the following step does not load a checkpoint,
# the shared parameters will be different.
if isinstance(self, PipelineLayer):
self._synchronize_shared_weights()
def _init_weights(self, layer):
"""
Initialize the weights. This method should be overridden by derived class.
@@ -1744,9 +1738,15 @@ class PretrainedModel(Layer, GenerationMixin, ConversionMixin):
loaded_keys, quantization_linear_list, config.quantization_config
)
if keep_in_fp32_modules is None:
keep_in_fp32_modules = ["quant_scale"]
keep_in_fp32_modules = (
["quant_scale"] if config.quantization_config.weight_quantize_algo in ["nf4", "fp4"] else None
)
else:
keep_in_fp32_modules += ["quant_scale"]
keep_in_fp32_modules = (
keep_in_fp32_modules + ["quant_scale"]
if config.quantization_config.weight_quantize_algo in ["nf4", "fp4"]
else keep_in_fp32_modules
)
missing_keys = list(set(expected_keys) - set(loaded_keys))
unexpected_keys = list(set(loaded_keys) - set(expected_keys))
@@ -1929,15 +1929,23 @@ class PretrainedModel(Layer, GenerationMixin, ConversionMixin):
raise RuntimeError(f"Error(s) in loading state_dict for {model.__class__.__name__}:\n\t{error_msg}")
if len(unexpected_keys) > 0:
logger.warning(
f"Some weights of the model checkpoint at {pretrained_model_name_or_path} were not used when"
f" initializing {model.__class__.__name__}: {sorted(unexpected_keys)}\n- This IS expected if you are"
f" initializing {model.__class__.__name__} from the checkpoint of a model trained on another task or"
" with another architecture (e.g. initializing a BertForSequenceClassification model from a"
" BertForPreTraining model).\n- This IS NOT expected if you are initializing"
f" {model.__class__.__name__} from the checkpoint of a model that you expect to be exactly identical"
" (initializing a BertForSequenceClassification model from a BertForSequenceClassification model)."
)
if logger.logger.level < 20:
logger.warning(
f"Some weights of the model checkpoint at {pretrained_model_name_or_path} were not used when"
f" initializing {model.__class__.__name__}: {sorted(unexpected_keys)}\n- This IS expected if you are"
f" initializing {model.__class__.__name__} from the checkpoint of a model trained on another task or"
" with another architecture (e.g. initializing a BertForSequenceClassification model from a"
" BertForPreTraining model).\n- This IS NOT expected if you are initializing"
f" {model.__class__.__name__} from the checkpoint of a model that you expect to be exactly identical"
" (initializing a BertForSequenceClassification model from a BertForSequenceClassification model)."
)
else:
logger.warning(
f"Some weights of the model checkpoint at {pretrained_model_name_or_path} were not used when"
f" initializing the model, - This IS expected if you are"
f" initializing the model from a checkpoint of a model trained on another task or"
" with another architecture."
)
else:
logger.info(f"All model checkpoint weights were used when initializing {model.__class__.__name__}.\n")
@@ -2135,12 +2143,31 @@ class PretrainedModel(Layer, GenerationMixin, ConversionMixin):
or resolved_archive_file.endswith(SAFE_WEIGHTS_NAME)
or resolved_archive_file.endswith(SAFE_WEIGHTS_INDEX_NAME)
):
# try to get the name-mapping info
logger.info(
f"Starting to convert pytorch weight file<{resolved_archive_file}> to "
f"paddle weight file<{os.path.join(cache_dir, PADDLE_WEIGHTS_NAME)}> ..."
converted_paddle_weights = os.path.join(
os.path.dirname(resolved_archive_file), PADDLE_WEIGHTS_NAME
)
state_dict = cls.convert(resolved_archive_file, config, cache_dir)
if not os.path.exists(converted_paddle_weights):
# try to get the name-mapping info
logger.info(
f"Starting to convert pytorch weight file <{resolved_archive_file}> to "
f"paddle weight file <{converted_paddle_weights}> ..."
)
state_dict = cls.convert(resolved_archive_file, config, os.path.dirname(resolved_archive_file))
else:
# try to load the converted paddle weight file
resolved_archive_file = converted_paddle_weights
sharded_metadata = None
is_sharded = False
logger.info(
f"Detect the converted Paddle weight file <{converted_paddle_weights}>. We intend to reuse this file."
)
if config.tensor_parallel_degree > 1 and resolved_archive_file.endswith(
"model_state.pdparams"
):
state_dict = cls.convert_tensor_parallel(resolved_archive_file, config)
else:
state_dict = load_state_dict(resolved_archive_file)
logger.info("Loaded weights file from disk, setting weights to model.")
else:
raise ValueError(f"Unexpected file: {resolved_archive_file} for weight conversion.")
else:
@@ -2158,7 +2185,9 @@ class PretrainedModel(Layer, GenerationMixin, ConversionMixin):
logger.info("Loaded weights file from disk, setting weights to model.")
# Check if `_keep_in_fp32_modules` is not None
use_keep_in_fp32_modules = (cls._keep_in_fp32_modules is not None) and dtype == "float16"
use_keep_in_fp32_modules = (cls._keep_in_fp32_modules is not None) and (
dtype == "float16" or dtype == "bfloat16"
)
if is_sharded:
loaded_state_dict_keys = sharded_metadata["all_checkpoint_keys"]
@@ -2193,6 +2222,10 @@ class PretrainedModel(Layer, GenerationMixin, ConversionMixin):
quantization_config=config.quantization_config,
llm_int8_threshold=config.quantization_config.llm_int8_threshold,
)
quantization_linear_list = []
for key in model.state_dict().keys():
if "quant_weight" in key:
quantization_linear_list.append(key[:-13])
model, missing_keys, unexpected_keys, mismatched_keys = cls._load_pretrained_model(
model=model,

View File

@@ -587,7 +587,7 @@ def cached_file_for_hf_hub(
download_check(path_or_repo_id, full_filename, addition="from_hf_hub")
resolved_file = hf_hub_download(
repo_id=path_or_repo_id,
filename=full_filename,
filename=filename,
cache_dir=cache_dir,
subfolder=subfolder,
library_name="PaddleNLP",

View File

@@ -109,7 +109,7 @@ def show():
f.write(content)
__version__ = "2.6.1.post"
__version__ = "2.7.2.post"
if os.getenv(PADDLENLP_STABLE_VERSION):
__version__ = __version__.replace(".post", "")