第三章:huggingface的Trainner参数解读
文章目录
-
引言
- Trainer类中的参数来源解析
- 1.1 self.args参数的代码实现细节
- 2.1 data_collator参数的数据合并策略解析
- 1.2 model_init函数及其示例演示
- 2.2 model_init函数的实际应用案例
- 3.1 对Trainer类中的model_init函数与实际模型的关系进行代码实现分析
- 4.1 分析Trainer类中的model_init函数与实际模型之间的区别与联系
- 1.1 self.args参数的代码实现细节
- Trainer类中的参数来源解析
-
第二节 Trainers初始化相关参数解析
-
第一部分 随机设置
- 第一条 设置per_gpu_train_batch_size和per_device_batch_size以配置batch_size
- 第二条 利用data_collator构建collator方法
-
第一部分 data_collator相关的collate函数实现
- 第一条 基于default_data_collator实现collator函数
- 第二条 利用DataCollatorWithPadding实现带填充的collator函数
-
总结
-
前言
本教程主要介绍大模型的基本使用方法及应用场景。具体而言:
- 首先主要依赖huggingface框架来实现基础功能。
- 对于较为复杂的技术细节,则参考官方文档和相关博客教程进行操作。
- 在实际应用中会遇到开源代码二次开发的挑战。
- 通过实验测试验证源代码的运行效果。
- 教程内容全部基于huggingface源码与示例演示相结合的方式展开讲解。
- 重点深入解析训练机制、权重管理、高效微调(LoRA)方法、断点续训技巧以及模型推理权重处理等关键环节。
- 通过实际案例验证所学知识,并完成相应项目实践
本文旨在阐述huggingface类Trainer部分参数的来源与解读,并提供一个便于用户快速查阅相关参数的模板链接。
一、Trainer类部分参数来源你源码解读
上一章我们讲述了huggingface平台中的TrainingArguments参数以及trainer组件,并且仅仅关注其使用场景。下面我们将从源码的角度来阐述这些参数的来源以及huggingface是如何构建相关功能模块的
1、Trainer的self.args参数源码解读
我们通过Trainer类的init函数(如下所示)获取到self.args参数代码。正如上一章节所述,我们实现了Trainer类提供args=training_args参数的处理方式。如果未提供args参数,则huggingface会自动构建基于TrainingArguments类的默认配置参数设置。至于TrainingArguments类默认设置的内容,在上一章节已有详细说明,请不再赘述
if args is None:
output_dir = "tmp_trainer"
logger.info(f"No `TrainingArguments` passed, using `output_dir={output_dir}`.")
args = TrainingArguments(output_dir=output_dir)
self.args = args
2、Trainer的data_collator参数解读
data_collator是一个负责从train_dataset或eval_dataset的元素列表中生成批次(batches)的函数。它通过整合单个样本形成一个完整的数据批次,并有助于提升模型的训练效率与评估效果。该函数与tokenizer参数之间存在一定关联,在实际应用中通常需要同步配置以达到最佳效果
不指定tokenizer参数时,默认的数据收集器将采用[default_data_collator]函数,在此情况下,默认的数据收集器根据不同类型的样本进行处理:当样本为文本数据时,默认的数据收集器会自动地对批次中的样本进行padding操作
该模型允许设置tokenizer参数。在数据聚合过程中,默认会采用[DataCollatorWithPadding]类作为实现方案。具体而言,在提供 tokenizer 参数的情况下,默认情况下该聚合器将采用[DataCollatorWithPadding]类作为实现方案。
该聚合器基于指定的分词器(即 tokenizer)对输入数据执行填充处理,并将其整合为一个批量处理。
该部分内容,在后续处理模型与数据结合的部分中进行详细阐述。在这里,请了解data_collator参数的作用:它用于控制如何将一批次的数据样本整合在一起,并用于模型的训练或评估。
3、Trainer的model_init参数解读与Demo
1、model_init参数解读
model_init是一个可调用对象(callable),用于在训练过程中建立所需的模型架构。如果指定了该参数,则每当调用Trainer.train方法时都会从model_init函数返回的新实例开始建立新的训练过程。该参数的存在使得在每次训练迭代开始时能够动态生成新的模型实例这对于某些特定的训练场景尤其有用例如超参数优化或迁移学习等。
2、model_init参数使用Demo
通过Trainer实现的方式会传递出一个模型,在具体实施中可以选择以下两种形式:一种是以...形式存在的模型参数作为输入配置项(即通过--命令行接口指定),另一种是以<parameter>形式存在的<parameter>字段作为初始化配置项(即通过-p <parameter>命令行接口指定)。在实际应用中我们可以提供一个简明扼要的例子来展示如何运用<parameter>初始化配置项
from transformers import BertForSequenceClassification
from transformers import Trainer, TrainingArguments
# 定义一个函数,用于实例化模型
def model_init():
return BertForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=2)
# 定义训练参数
training_args = TrainingArguments(
"model-init-demo",
evaluation_strategy="epoch",
per_device_train_batch_size=8,
per_device_eval_batch_size=8,
num_train_epochs=3,
logging_dir="./logs",
logging_steps=100,
save_steps=1000,
save_total_limit=2,
)
# 初始化Trainer,传入model_init参数
trainer = Trainer(
model_init=model_init,
args=training_args,
)
# 训练模型
trainer.train()
在这个示例中,我们编写了一个 model_init 函数用于创建 BertForSequenceClassification 模型的实例。接着将此函数作为参数提供给 Trainer 类的 model_init 参数。当 trainer.train() 方法被调用时,在线学习器会利用该 model_init 函数生成一个新的模型实例,并从该实例启动训练过程。
3、Trainer的model_init与model源码解读
if model is None:
if model_init is not None:
self.model_init = model_init
model = self.call_model_init()
else:
raise RuntimeError("`Trainer` requires either a `model` or `model_init` argument")
else:
if model_init is not None:
warnings.warn(
"`Trainer` requires either a `model` or `model_init` argument, but not both. `model_init` will"
" overwrite your model when calling the `train` method. This will become a fatal error in the next"
" release.",
FutureWarning,
)
self.model_init = model_init
4、Trainer的model_init与model参数区别
如果将model_init参数设置为某个值,则在初始化Trainer时不需显式指定model参数(即通过model_init函数动态生成模型)。当启用model_init参数时,在每次调用train()方法时都会从该函数返回的新模型实例开始训练。换言之,“无需预先指定一个模型实例”。换句话说,“通过设置model_init参数实现了对模型实例的动态创建功能”,从而使得在每一个训练迭代周期内都能启动一个新的独立模型实例。“值得注意的是,“此功能特别适用于那些需要频繁调整超参或进行迁移学习的任务场景。
二、Trainer初始化部分参数解读
1、随机参数设定
在Trainer类的初始化函数__init__中包含初始化随机种子的行为,在源代码中调用了相关函数
# Seed must be set before instantiating the model when using model
enable_full_determinism(self.args.seed) if self.args.full_determinism else set_seed(self.args.seed)
enable_full_determinism函数源码如下:
def enable_full_determinism(seed: int, warn_only: bool = False):
"""
Helper function for reproducible behavior during distributed training. See
- https://pytorch.org/docs/stable/notes/randomness.html for pytorch
- https://www.tensorflow.org/api_docs/python/tf/config/experimental/enable_op_determinism for tensorflow
"""
# set seed first
set_seed(seed)
if is_torch_available():
# Enable PyTorch deterministic mode. This potentially requires either the environment
# variable 'CUDA_LAUNCH_BLOCKING' or 'CUBLAS_WORKSPACE_CONFIG' to be set,
# depending on the CUDA version, so we set them both here
os.environ["CUDA_LAUNCH_BLOCKING"] = "1"
os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":16:8"
torch.use_deterministic_algorithms(True, warn_only=warn_only)
# Enable CUDNN deterministic mode
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
if is_tf_available():
tf.config.experimental.enable_op_determinism()
set_seed函数源码如下:
def set_seed(seed: int):
"""
Helper function for reproducible behavior to set the seed in `random`, `numpy`, `torch` and/or `tf` (if installed).
Args:
seed (`int`): The seed to set.
"""
random.seed(seed)
np.random.seed(seed)
if is_torch_available():
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
# ^^ safe to call this function even if cuda is not available
if is_tf_available():
tf.random.set_seed(seed)
已知在设置环境中与相关库的内容同样重要,在后续文章中提到resume中的rng状态重加载需要用
1、per_gpu_train_batch_size与per_device_batch_size参数设定batch_size
per_gpu_train_batch_size与per_device_batch_size参数均源自self.args中的TrainingArguments类参数设置。其中per_device_batch_size是一个较为常用的配置选项;而per_gpu_train_batch_size在该类中被设定为None值,默认情况下未做特别指定。具体定义如下:
per_gpu_train_batch_size: Optional[int] = field(
default=None,
metadata={
"help": (
"Deprecated, the use of `--per_device_train_batch_size` is preferred. "
"Batch size per GPU/TPU core/CPU for training."
)
},
)
为了设定相应的batch_size,
huggingface会基于TrainingArguments类完成,
从而配置一个参数 train_batch_size,
该参数由其内部方法进行设置,
如上所示。
@property
def train_batch_size(self) -> int:
"""
The actual batch size for training (may differ from `per_gpu_train_batch_size` in distributed training).
"""
if self.per_gpu_train_batch_size:
logger.warning(
"Using deprecated `--per_gpu_train_batch_size` argument which will be removed in a future "
"version. Using `--per_device_train_batch_size` is preferred."
)
per_device_batch_size = self.per_gpu_train_batch_size or self.per_device_train_batch_size
train_batch_size = per_device_batch_size * max(1, self.n_gpu)
return train_batch_size
而在Trainer中初始化调用源码如下:
# Internal variables to help with automatic batch size reduction
self._train_batch_size = args.train_batch_size
2、data_collator参数构建collator方法
当Trainer设定该参数时,从dataset中获取数据,并将这些数据打包成batch并输入模型。此时可以直接调用data_collator方法进行处理。
如果未提供数据,则需判断具备Tokenizer时应采用default_data_collator函数进行处理;否则应采用DataCollatorWithPadding(tokenizer)方法进行数据处理。
该方法在Trainer初始化中,源码如下:
default_collator = default_data_collator if tokenizer is None else DataCollatorWithPadding(tokenizer)
self.data_collator = data_collator if data_collator is not None else default_collator
该方法本质上类似于pytorch的collate_fn函数。具体而言,在此过程中...
1、default_data_collator函数方法
def torch_default_data_collator(features: List[InputDataClass]) -> Dict[str, Any]:
import torch
if not isinstance(features[0], Mapping):
features = [vars(f) for f in features]
first = features[0]
batch = {}
# Special handling for labels.
# Ensure that tensor is created with the correct type
# (it should be automatically the case, but let's make sure of it.)
if "label" in first and first["label"] is not None:
label = first["label"].item() if isinstance(first["label"], torch.Tensor) else first["label"]
dtype = torch.long if isinstance(label, int) else torch.float
batch["labels"] = torch.tensor([f["label"] for f in features], dtype=dtype)
elif "label_ids" in first and first["label_ids"] is not None:
if isinstance(first["label_ids"], torch.Tensor):
batch["labels"] = torch.stack([f["label_ids"] for f in features])
else:
dtype = torch.long if type(first["label_ids"][0]) is int else torch.float
batch["labels"] = torch.tensor([f["label_ids"] for f in features], dtype=dtype)
# Handling of all other possible keys.
# Again, we will use the first element to figure out which key/values are not None for this model.
for k, v in first.items():
if k not in ("label", "label_ids") and v is not None and not isinstance(v, str):
if isinstance(v, torch.Tensor):
batch[k] = torch.stack([f[k] for f in features])
elif isinstance(v, np.ndarray):
batch[k] = torch.tensor(np.stack([f[k] for f in features]))
else:
batch[k] = torch.tensor([f[k] for f in features])
return batch
2、DataCollatorWithPadding函数方法
@dataclass
class DataCollatorWithPadding:
tokenizer: PreTrainedTokenizerBase
padding: Union[bool, str, PaddingStrategy] = True
max_length: Optional[int] = None
pad_to_multiple_of: Optional[int] = None
return_tensors: str = "pt"
def __call__(self, features: List[Dict[str, Any]]) -> Dict[str, Any]:
batch = self.tokenizer.pad(
features,
padding=self.padding,
max_length=self.max_length,
pad_to_multiple_of=self.pad_to_multiple_of,
return_tensors=self.return_tensors,
)
if "label" in batch:
batch["labels"] = batch["label"]
del batch["label"]
if "label_ids" in batch:
batch["labels"] = batch["label_ids"]
del batch["label_ids"]
return batch
总结
在模型训练过程中为便于理解与实现需创建一个参数解析模板,在Trainer类的初始化__init__方法中详细说明了模型配置的相关细节,并通过训练选项设置进一步优化了模型训练流程的具体实现步骤
