Merge branch 'dev0.8.0' of github.com:fastnlp/fastNLP into dev0.8.0

This commit is contained in:
x54-729 2022-05-09 14:54:40 +00:00
commit 3af4732f55
14 changed files with 321 additions and 105 deletions

View File

@ -10,8 +10,8 @@ from .callback_event import Event
from .callback import Callback
from fastNLP.core.log import logger
from .progress_callback import ProgressCallback, choose_progress_callback
from fastNLP.envs import rank_zero_call
from fastNLP.core.utils.utils import _get_fun_msg
from ..utils.exceptions import EarlyStopException
from ..utils.utils import _get_fun_msg
def _transfer(func):
@ -25,6 +25,8 @@ def _transfer(func):
for callback_fn in manager.callback_fns[func.__name__]:
try:
callback_fn(*arg, **kwargs)
except EarlyStopException as e:
raise e
except BaseException as e:
logger.error(f"The following callback_fn raise exception:{_get_fun_msg(callback_fn)}.")
raise e

View File

@ -19,7 +19,7 @@ class CheckpointCallback(Callback):
only_state_dict: bool = True, model_save_fn: Optional[Callable] = None, save_object: str = 'model',
save_evaluate_results=True, **kwargs):
"""
保存模型 checkpoint callback 其保存的文件目录以及文件名命名规则如下::
保存 checkpoint callback 其保存的文件目录以及文件名命名规则如下::
- folder/
- YYYY-mm-dd-HH_MM_SS_fffff/ # 自动根据当前脚本的启动时间创建的
@ -29,8 +29,9 @@ class CheckpointCallback(Callback):
- {save_object}-epoch_{epoch_idx}-batch_{global_batch_idx}-exception_{exception_type}/ # exception时保存。
- {save_object}-epoch_{epoch_idx}-batch_{global_batch_idx}-{monitor}_{monitor_value}/ # 满足topk条件存储文件名
model_save_fn None 则以上每个 folder 将生成 fastnlp_model.pkl.tar 文件
model_save_fn 不为 None fastNLP folder 绝对路径传递给该函数fastNLP 在该 folder 下不进行模型保存
model_save_fn None 则以上每个 folder 将生成 fastnlp_model.pkl.tar 文件 model_save_fn 不为 None
fastNLP folder 绝对路径传递给该函数fastNLP 在该 folder 下不进行模型保存默认情况下 checkpoint 只保存了 model
的状态如还需保存 Trainer 的状态以断点重训的话请使用 ``save_object='trainer'``
:param monitor: 监控的 metric 如果在 evaluation 结果中没有找到完全一致的名称将使用 最长公共字符串算法 找到最匹配
的那个作为 monitor 如果为 None将尝试使用 Trainer 设置的 monitor 也可以传入一个函数接受参数为 evaluation 的结
@ -46,22 +47,14 @@ class CheckpointCallback(Callback):
:param only_state_dict: 保存模型时是否只保存 state_dict model_save_fn 不为 None 该参数无效
:param model_save_fn: 个性化的保存函数当触发保存操作时就调用这个函数这个函数应当接受一个文件夹作为参数不返回任何东西
如果传入了 model_save_fn 函数fastNLP 将不再进行模型相关的保存在多卡场景下我们只在 rank 0 上会运行该函数
:param save_object: 可选 ['trainer', 'model']表示在保存时的保存对象为 trainer+model 还是 只是model
:param save_object: 可选 ['trainer', 'model']表示在保存时的保存对象为 ``trainer+model`` 还是 只是 ``model`` 如果
保存 ``trainer`` 对象的话将会保存 :class:~fastNLP.Trainer 的相关状态可以通过 :meth:`Trainer.load` 加载该断
点继续训练如果保存的是 ``Model`` 对象则可以通过 :meth:`Trainer.load_model` 加载该模型权重
:param save_evaluate_results: 是否保存 evaluate 的结果如果为 True 在保存 topk 模型的 folder 中还将额外保存一个
fastnlp_evaluate_results.json 文件记录当前的 results仅在设置了 topk 的场景下有用默认为 True
:param kwargs:
"""
super().__init__()
if folder is None:
logger.warning(
"Parameter `folder` is None, and we will use the current work directory to find and load your model.")
folder = Path.cwd()
folder = Path(folder)
if not folder.exists():
raise NotADirectoryError(f"Path '{folder.absolute()}' is not existed!")
elif folder.is_file():
raise ValueError("Parameter `folder` should be a directory instead of a file.")
if every_n_epochs is not None:
if not isinstance(every_n_epochs, int) or every_n_epochs < 1:
raise ValueError("Parameter `every_n_epochs` should be an int and greater than or equal to 1.")
@ -74,12 +67,6 @@ class CheckpointCallback(Callback):
else:
every_n_batches = sys.maxsize # 使得没有数字可以整除
if topk is not None:
if not isinstance(topk, int):
raise ValueError("Parameter `topk` should be an int.")
else:
topk = 0
if on_exceptions is not None:
if not isinstance(on_exceptions, Sequence):
on_exceptions = [on_exceptions]

View File

@ -19,7 +19,8 @@ class LoadBestModelCallback(HasMonitorCallback):
model_load_fn:Optional[Callable] = None,
delete_after_train:bool = True):
"""
保存最佳的 monitor 值最佳的模型并在训练结束的时候重新加载模型仅在训练正常结束的时候才能加载最好的模型
保存最佳的 monitor 值最佳的模型并在训练结束的时候重新加载模型默认会在加载之后删除权重文件仅在训练正常结束的时候才能加载
最好的模型
:param str monitor: 监控的 metric 如果在 evaluation 结果中没有找到完全一致的名称将使用 最长公共字符串算法 找到最匹配
的那个作为 monitor 如果为 None将尝试使用 Trainer 设置的 monitor 也可以传入一个函数接受参数为 evaluation 的结

View File

@ -33,9 +33,8 @@ class Saver:
:param kwargs: 更多需要传递给 Trainer.save() 或者 Trainer.save_model() 接口的参数
"""
if folder is None:
logger.rank_zero_warning(
"Parameter `folder` is None, and we will use the current work directory to find and load your model.")
folder = Path.cwd()
folder = Path.cwd().absolute()
logger.info(f"Parameter `folder` is None, and we will use {folder} to save and load your model.")
folder = Path(folder)
if not folder.exists():
folder.mkdir(parents=True, exist_ok=True)

View File

@ -121,7 +121,7 @@ def get_padder(batch_field:Sequence[Any], pad_val, dtype, backend, field_name)->
# 这里 ele_dtype 传入为 None 的原因是防止出现 paddle tensor 转换为 torch tensor
return TorchTensorPadder(pad_val=pad_val, ele_dtype=None, dtype=dtype)
elif backend == 'paddle':
return PaddleTensorPadder(pad_val=pad_val, ele_dtype=None, dtype=dtype)
return PaddleTensorPadder(pad_val=pad_val, ele_dtype=ele_dtype, dtype=dtype)
elif backend == 'jittor':
return JittorTensorPadder(pad_val=pad_val, ele_dtype=ele_dtype, dtype=dtype)
else:

View File

@ -141,7 +141,10 @@ class PaddleTensorPadder(Padder):
shapes = [field.shape for field in batch_field]
max_shape = [len(batch_field)] + [max(*_) for _ in zip(*shapes)]
array = np.full(max_shape, fill_value=pad_val)
if isinstance(batch_field[0], paddle.Tensor):
array = paddle.full(max_shape, fill_value=pad_val, dtype=dtype)
else:
array = np.full(max_shape, fill_value=pad_val, dtype=batch_field[0].dtype)
for i, field in enumerate(batch_field):
slices = (i, ) + tuple(slice(0, s) for s in shapes[i])
array[slices] = field

View File

@ -118,8 +118,8 @@ class TorchTensorPadder(Padder):
batch_field = [torch.tensor(field.tolist(), dtype=dtype) for field in batch_field]
else:
device = batch_field[0].device
if dtype is None:
dtype = batch_field[0].dtype
if dtype is None:
dtype = batch_field[0].dtype
except AttributeError:
raise RuntimeError(f"If the field is not a torch.Tensor (it is {type(batch_field[0])}), "
f"it must have tolist() method.")

View File

@ -56,6 +56,8 @@ class Evaluator:
* ddp_kwargs -- 用于在使用 ``TorchDDPDriver`` 时指定 ``DistributedDataParallel`` 初始化时的参数例如传入
{'find_unused_parameters': True} 来解决有参数不参与前向运算导致的报错等
* torch_non_blocking -- 表示用于 pytorch tensor to 方法的参数 non_blocking
* *data_device* -- 表示如果用户的模型 device Driver 中对应为参数 model_device None 我们会将数据迁移到 data_device
注意如果 model_device None那么 data_device 不会起作用
* *model_use_eval_mode* (``bool``) --
是否在 evaluate 的时候将 model 的状态设置成 eval 状态 eval 状态下model
dropout batch normalization 将会关闭默认为True如果为 FalsefastNLP 不会对 model evaluate 状态做任何设置无论
@ -234,8 +236,7 @@ class Evaluator:
"""
调用所有 metric reset() 方法清除累积的状态
Returns:
:return:
"""
self.metrics_wrapper.reset()
@ -357,6 +358,11 @@ class _MetricsWrapper:
metric.update(res)
def reset(self):
"""
Metric 中的状态重新设置
:return:
"""
for metric in self._metrics:
if _is_allennlp_metric(metric):
metric.get_metric(reset=True)

View File

@ -1,4 +1,10 @@
from typing import Union, Optional, List, Callable, Dict, Sequence, BinaryIO, IO
"""
``Trainer`` fastNLP 用于训练模型的专门的训练器其支持多种不同的驱动模式 ``Driver``不仅包括最为经常使用的 DDP而且还支持 jittor 等国产
的训练框架新版的 fastNLP 新加入了方便的 callback 函数修饰器并且支持定制用户自己特定的训练循环过程通过使用该训练器用户只需要自己实现
模型部分而将训练层面的逻辑完全地交给 fastNLP
"""
from typing import Union, Optional, List, Callable, Dict, BinaryIO
from functools import partial
from collections import defaultdict
import copy
@ -7,7 +13,6 @@ from dataclasses import is_dataclass
import os
from pathlib import Path
import io
import inspect
__all__ = [
'Trainer',
@ -62,12 +67,20 @@ class Trainer(TrainerEventTrigger):
**kwargs
):
r"""
`Trainer` fastNLP 用于训练模型的专门的训练器其支持多种不同的驱动模式不仅包括最为经常使用的 DDP而且还支持 jittor 等国产
的训练框架新版的 fastNLP 新加入了方便的 callback 函数修饰器并且支持定制用户自己特定的训练循环过程通过使用该训练器用户只需
要自己实现模型部分而将训练层面的逻辑完全地交给 fastNLP
:param model: 训练所需要的模型例如 ``torch.nn.Module``
:param model: 训练所需要的模型目前支持 pytorch
:param driver: 训练模型所使用的具体的驱动模式应当为以下选择中的一个["torch",]之后我们会加入 jittorpaddle
.. note::
当使用 pytorch 注意参数 ``model`` 在大多数情况下为 ``nn.Module``但是您仍能够通过使用一些特定的组合来使用情况如下所示
1. 当希望使用 ``DataParallel`` 您应当使用 ``TorchSingleDriver``意味着您在初始化 ``Trainer`` 时参数 ``device`` 不应当为
一个 ``List``
2. 当您选择自己初始化 ``init_process_group`` 这种情况要求您传入的 ``model`` 参数一定为 ``DistributedDataParallel``
您应当使用 ``TorchDDPDriver``意味着您需要通过 ``python -m torch.distributed.launch`` 的方式来启动训练此时参数 ``device``
应当设置为 None此时我们会忽略该参数具体见下面对于参数 ``device`` 的更详细的解释
:param driver: 训练模型所使用的具体的驱动模式应当为以下选择中的一个["torch"]之后我们会加入 jittorpaddle
国产框架的训练模式其中 "torch" 表示使用 ``TorchSingleDriver`` 或者 ``TorchDDPDriver``具体使用哪一种取决于参数 ``device``
的设置
:param train_dataloader: 训练数据集注意其必须是单独的一个数据集不能是 List 或者 Dict
@ -80,79 +93,248 @@ class Trainer(TrainerEventTrigger):
device 的可选输入如下所示
* *str*: 例如 'cpu', 'cuda', 'cuda:0', 'cuda:1'
* *torch.device*: 将模型装载到 ``torch.device``
* *torch.device*: 例如 'torch.device("cuda:0")'
* *int*: 将使用 ``device_id`` 为该值的 ``gpu`` 进行训练如果值为 -1那么默认使用全部的显卡此时使用的 driver 实例是 `TorchDDPDriver`
* *list(int)*: 如果多于 1 个device应当通过该种方式进行设定注意此时我们一定会使用 ``TorchDDPDriver``不管您传入的列表的长度是 1 还是其它值
* *None*: 为None则不对模型进行任何处理
* *None*: 仅当用户自己通过训练框架提供的并行训练启动脚本开启 ddp 进程时 None
.. node::
.. note::
如果希望使用 ``TorchDDPDriver``
如果希望使用 ``TorchDDPDriver``在初始化 ``Trainer`` 时您应当使用::
Trainer(driver="torch", device=[0, 1])
注意如果这时 ``device=[0]``我们仍旧会使用 ``TorchDDPDriver``
如果希望使用 ``TorchSingleDriver``则在初始化 ``Trainer`` 时您应当使用::
Trainer(driver="torch", device=0)
.. warning::
注意参数 ``device`` 仅当您通过 pytorch 或者其它训练框架自身的并行训练启动脚本启动 ddp 训练时才允许为 ``None``
例如当您使用::
python -m torch.distributed.launch --nproc_per_node 2 train.py
来使用 ``TorchDDPDriver`` 此时参数 ``device`` 不再有效不管您是否自己初始化 ``init_process_group``我们将直接
通过 ``torch.device(f"cuda:{local_rank}")`` 来获取当前进程所使用的的具体的 gpu 设备因此此时您需要使用 ``os.environ["CUDA_VISIBLE_DEVICES"]``
来指定要使用的具体的 gpu 设备
另一点需要注意的是当您没有选择自己初始化 ``init_process_group`` 我们仍旧会帮助您把模型和数据迁移到当前进程所使用的
具体的 gpu 设备上但是如果您选择自己在 ``Trainer`` 初始化前意味着在 ``driver`` ``setup`` 初始化 ``init_process_group``
那么对于模型的迁移应当完全由您自己来完成此时对于数据的迁移如果您在 ``Trainer`` 初始化时指定了参数 ``data_device``那么
我们会将数据迁移到 ``data_device`` 如果其为 None那么将数据迁移到正确的设备上应当由您自己来完成
对于使用 ``TorchDDPDriver`` 的更多细节请见 :class:`fastNLP.core.drivers.torch_driver.TorchDDPDriver`
:param n_epochs: 训练总共的 epoch 的数量默认为 20
:param evaluate_dataloaders: 验证数据集其可以是单独的一个数据集也可以是多个数据集当为多个数据集时注意其必须是 Dict默认
None
:param batch_step_fn: 定制每次 train batch 执行的函数该函数应接受两个参数为 `trainer` `batch`不需要要返回值可以
参考 fastNLP.core.controllers.loops.train_batch_loop.TrainBatchLoop中的batch_step_fn函数
:param evaluate_batch_step_fn: 定制每次 evaluate batch 执行的函数该函数应接受的两个参数为 `evaluator` `batch`
不需要有返回值可以参考 fastNLP.core.controllers.loops.evaluate_batch_loop.EvaluateBatchLoop中的batch_step_fn函数
:param train_fn: 用来控制 `Trainer` 在训练的前向传播过程中是调用模型的哪一个函数例如是 `train_step` 还是 `forward`
默认为 None如果该值是 None那么我们会默认使用 `train_step` 当做前向传播的函数如果在模型中没有找到该方法
则使用模型默认的前向传播函数
:param evaluate_fn: 用来控制 `Trainer` 中内置的 `Evaluator` 的模式应当为 None 或者一个字符串其使用方式和 train_fn 类似
注意该参数我们会直接传给 Trainer 中内置的 Evaluator如果不为 None如果该值为 None 将首先尝试寻找模型中是否有
evaluate_step 这个函数如果没有则使用 forward 函数
:param callbacks: 训练当中触发的 callback 该参数应当为一个列表其中的每一个元素都应当继承 `Callback`
:param metrics: 应当为一个字典其中 key 表示 monitor例如 {"acc1": AccMetric(), "acc2": AccMetric()}
:param evaluate_every: 可以为负数正数或者函数为负数时表示每隔几个 epoch evaluate 一次为正数则表示每隔几个 batch evaluate 一次
为函数时表示用户自己传入的用于控制 Trainer 中的 evaluate 的频率的函数该函数的应该接受当前 trainer 对象作为参数
返回一个 bool 返回为 True 说明需要进行 evaluate 将在每个 batch 结束后调用该函数判断是否需要 evaluate
:param input_mapping: 应当为一个字典或者一个函数表示在当前 step 拿到一个 batch 的训练数据后应当做怎样的映射处理如果其是
一个字典并且 batch 也是一个 `Dict`那么我们会把 batch 中同样在 input_mapping 中的 key 修改为 input_mapping 的对应 key
value如果 batch 是一个 `dataclass`那么我们会先将该 dataclass 转换为一个 Dict然后再进行上述转换如果 batch 此时是其它
类型那么我们将会直接报错如果 input_mapping 是一个函数那么对于取出的 batch我们将不会做任何处理而是直接将其传入该函数里
注意该参数会被传进 `Evaluator` 因此你可以通过该参数来实现将训练数据 batch 移到对应机器上的工作例如当参数 `device` None
如果 train evaluate 需要使用不同的 input_mapping, 请使用 train_input_mapping evaluate_input_mapping 设置
:param output_mapping: 应当为一个字典或者函数作用和 input_mapping 类似区别在于其用于转换输出如果 output_mapping 是一个
函数那么我们将会直接将模型的输出传给该函数如果其是一个 `Dict`那么我们需要 batch 必须是 `Dict` 或者 `dataclass` 类型
如果 batch 是一个 `Dict`那么我们会把 batch 中同样在 output_mapping 中的 key 修改为 output_mapping 的对应 key value
如果 batch 是一个 `dataclass`那么我们会先将该 dataclass 转换为一个 Dict然后再进行上述转换
如果 train evaluate 需要使用不同的 output_mapping, 请使用 train_output_mapping evaluate_output_mapping 设置
:param model_wo_auto_param_call: 是否关闭在训练时调用我们的 auto_param_call 来自动匹配 batch forward 函数的参数的行为
如果该值为 False并且当 batch 为字典时我们会根据 forward 所需要的参数从 batch 中提取对应的对象传入到 forward 函数中如果该值
True那么我们会将 batch 直接透传给模型注意该参数应用于 `train_step`, `evaluate_step` `test_step`
:param accumulation_steps: 梯度累积的步数表示每隔几个 batch 优化器迭代一次默认为 1
:param fp16: 是否开启混合精度训练默认为 False
:param monitor: 当存在 evaluate_dataloaders 默认的 monitor metric 的名字传入的 callback 如果有 monitor 参数且没有
callback 初始化设定的将采取这个值如果在 evaluation 结果中没有找到完全一致的名称将使用 最长公共字符串算法 找到最匹配
的那个作为 monitor 也可以传入一个函数接受参数为 evaluation 的结果(字典类型)返回一个 float 值作为 monitor 的结果
如果 evaluate_dataloaders metrics 没有提供该参数无意义
:param larger_better: monitor 的值是否是越大越好
:param marker: 用于标记一个 Trainer 实例从而在用户调用 `Trainer.on` 函数时标记该 callback 函数属于哪一个具体的 'trainer' 实例默认为 None
:param kwargs: 一些其它的可能需要的参数见下方的说明
None
:param batch_step_fn: 定制每次训练时前向运行一个 batch 的数据所执行的函数该函数应接受两个参数为 ``trainer`` ``batch``
不需要要返回值更详细的使用位置和说明请见 :meth:`fastNLP.core.controllers.TrainBatchLoop.batch_step_fn`
:param evaluate_batch_step_fn: 定制每次验证时前向运行一个 batch 的数据所执行的函数该函数应接受的两个参数为 ``evaluator`` ``batch``
不需要有返回值可以参考 :meth:`fastNLP.core.controllers.EvaluateBatchLoop.batch_step_fn`
:param train_fn: 用来控制 ``Trainer`` 在训练的前向传播过程中是调用模型的哪一个函数例如是 ``train_step`` 还是 ``forward``
默认为 ``None``如果该值是 ``None``那么我们会默认使用 ``train_step`` 当做前向传播的函数如果在模型的定义类中没有找到该方法
则使用模型默认的前向传播函数例如对于 pytorch 来说就是 ``forward``
.. note::
fastNLP 对于训练时使用的前向传播函数的查找逻辑如下所示
1. 如果 ``train_fn`` None那么在 model 的类 Model 中寻找方法 ``Model.train_step``;如果没有找到那么默认使用 ``Model.forward``
2. 如果 ``train_fn`` 为一个字符串例如 'my_step_fn'那么我们首先会在 model 的类 Model 中寻找方法 ``Model.my_step_fn``
如果没有找到那么会直接报错
:param evaluate_fn: 用来控制 ``Trainer`` 中内置的 ``Evaluator`` 在验证的前向传播过程中是调用模型的哪一个函数应当为 ``None``
或者一个字符串其使用方式和 train_fn 类似具体可见 :class:`fastNLP.core.controllers.Evaluator`
:param callbacks: 训练当中触发的 callback 该参数应当为一个列表其中的每一个元素都应当继承 ``Callback`` 具体可见
:class:`fastNLP.core.callbacks.Callback`
:param metrics: 用于传给 ``Trainer`` 内部的 ``Evaluator`` 实例来进行训练过程中的验证其应当为一个字典其中 key 表示 monitor
例如 {"acc1": AccMetric(), "acc2": AccMetric()}
目前我们支持的 ``metric`` 的种类有以下几种
1. fastNLP 自己的 ``metric``详见 :class:`fastNLP.core.metrics.Metric`
2. torchmetrics
3. allennlp.training.metrics
4. paddle.metric
:param evaluate_every: 用来控制 ``Trainer`` 内部的 ``Evaluator`` 验证的频率其可以为负数正数或者函数
1. 为负数时表示每隔几个 ``epoch`` evaluate 一次
2. 为正数则表示每隔几个 ``batch`` evaluate 一次
3. 为函数时表示用户自己传入的用于控制 evaluate 的频率的函数该函数的应该接受当前 trainer 对象作为参数
返回一个 bool 返回为 True 说明需要进行 evaluate 将在每个 ``batch`` 结束后调用该函数判断是否需要 evaluate
.. note::
如果参数 ``evaluate_every`` 为函数其应当类似
>>> def my_evaluate_every(trainer) -> bool:
... if (trainer.global_forward_batches+1) % 1000 == 0:
... return True
... else:
... return False
该函数表示当每经过 1000 batch``Trainer`` 中内置的 ``Evaluator`` 就会验证一次
另一个需要注意的事情在于该函数会在每一次 batch 的结尾进行调用当该函数返回 ``True`` ``Evaluator`` 才会进行验证
:param input_mapping: 应当为一个字典或者一个函数表示在当前 step 拿到一个 batch 的训练数据后应当做怎样的映射处理
1. 如果 ``input_mapping`` 是一个字典:
1. 如果此时 batch 也是一个 ``Dict``那么我们会把 batch 中同样在 ``input_mapping`` 中的 key 修改为 ``input_mapping`` 的对应 ``key`` ``value``
2. 如果此时 batch 是一个 ``dataclass``那么我们会先将其转换为一个 ``Dict``然后再进行上述转换
3. 如果此时 batch 此时是其它类型那么我们将会直接报错
2. 如果 ``input_mapping`` 是一个函数那么对于取出的 batch我们将不会做任何处理而是直接将其传入该函数里
注意该参数会被传进 ``Evaluator`` 因此你可以通过该参数来实现将训练数据 batch 移到对应机器上的工作例如当参数 ``device`` ``None``
如果 ``Trainer`` ``Evaluator`` 需要使用不同的 ``input_mapping``, 请使用 ``train_input_mapping`` ``evaluate_input_mapping`` 分别进行设置
:param output_mapping: 应当为一个字典或者函数作用和 ``input_mapping`` 类似区别在于其用于转换输出
1. 如果 ``output_mapping`` 是一个 ``Dict``那么我们需要模型的输出必须是 ``Dict`` 或者 ``dataclass`` 类型
1. 如果此时模型的输出是一个 ``Dict``那么我们会把输出中同样在 ``output_mapping`` 中的 key 修改为 ``output_mapping`` 的对应 key value
2. 如果此时模型的输出是一个 ``dataclass``那么我们会先将其转换为一个 Dict然后再进行上述转换
2. 如果 ``output_mapping`` 是一个函数那么我们将会直接将模型的输出传给该函数
如果 ``Trainer`` ``Evaluator`` 需要使用不同的 ``output_mapping``, 请使用 ``train_output_mapping`` ``evaluate_output_mapping`` 分别进行设置
.. note::
``input_mapping`` ``output_mapping`` fastNLP 的一个特殊的概念 **'参数绑定'** 高度相关它们的存在也是为了 fastNLP
中的参数匹配能够正确地运行
.. todo::
之后链接上 参数匹配 的文档
.. warning::
如果 ``Trainer`` 的参数 ``output_mapping`` 不为 ``None``请保证其返回的一定是一个字典并且其中含有关键字 **'loss'**
:param model_wo_auto_param_call: 是否关闭在训练时调用我们的 ``auto_param_call`` 函数来自动匹配 batch 和前向函数的参数的行为
1. 如果该值为 ``False``并且当 batch 为字典时我们会根据**前向函数**所需要的参数从 batch 中提取对应的对象然后传入到**前向函数**
2. 如果该值为 ``True``那么我们会将 batch 直接透传给模型
.. todo::
之后链接上 参数匹配 的文档
函数 ``auto_param_call`` 详见 :func:`fastNLP.core.utils.auto_param_call`
:param accumulation_steps: 梯度累积的步数表示每隔几个 batch 才让优化器迭代一次默认为 1
:param fp16: 是否开启混合精度训练默认为 False
:param monitor: 对于一些特殊的 ``Callback``例如 :class:`fastNLP.core.callbacks.CheckpointCallback`它们需要参数 ``monitor``
来从 ``Evaluator`` 的验证结果中获取当前评测的值从而来判断是否执行一些特殊的操作例如对于 ``CheckpointCallback`` 而言如果我们
想要每隔一个 epoch ``Evaluator`` 进行一次验证然后保存训练以来的最好的结果那么我们需要这样设置
.. code-block::
trainer = Trainer(
...,
metrics={'acc': accMetric()},
callbacks=[CheckpointCallback(
...,
monitor='acc',
topk=1
)]
)
这意味着对于 ``CheckpointCallback`` 来说*'acc'* 就是一个监测的指标用于在 ``Evaluator`` 验证后取出其需要监测的那个指标的值
``Trainer`` 中的参数 ``monitor`` 的作用在于为没有设置 ``monitor`` 参数但是需要该参数的 *callback* 实例设置该值关于 ``monitor``
参数更详细的说明请见 :class:`fastNLP.core.callbacks.CheckpointCallback`
注意该参数仅当 ``Trainer`` 内置的 ``Evaluator`` 不为 None 时且有需要该参数但是没有设置该参数的 *callback* 实例才有效
:param larger_better: 对于需要参数 ``monitor`` *callback* 来说``monitor`` 的值是否是越大越好类似于 ``monitor``其作用
在于为没有设置 ``larger_better`` 参数但是需要该参数的 *callback* 实例设置该值
注意该参数仅当 ``Trainer`` 内置的 ``Evaluator`` 不为 None 时且有需要该参数但是没有设置该参数的 *callback* 实例才有效
:param marker: 用于标记一个 ``Trainer`` 实例从而在用户调用 ``Trainer.on`` 函数时标记该函数属于哪一个具体的 ``Trainer`` 实例默认为 None
.. note::
marker 的使用场景主要在于如果一个脚本中含有多个 ``Trainer`` 实例并且含有多个使用 ``Trainer.on`` 修饰的函数时不同的函数属于
不同的 ``Trainer`` 实例
此时通过将修饰器 ``Trainer.on`` 的参数 ``marker`` ``Trainer`` 的参数 ``marker`` 置为相同就可以使得该函数只会在这一
``Trainer`` 实例中被调用例如
.. code-block::
@Trainer.on(Event.on_train_begin(), marker='trainer1')
def fn(trainer):
...
trainer = Trainer(
...,
marker='trainer1'
)
另一点需要说明的是如果一个被 ``Trainer.on`` 修饰的函数其修饰时没有指明 ``marker``那么会将该函数传给代码位于其之后的
第一个 ``Trainer`` 实例即使该 ``Trainer`` 实例的 marker 不为 None这一点详见 :meth:`~fastNLP.core.controllers.Trainer.on`
:kwargs:
* *torch_kwargs* -- 用于在指定 ``driver`` 'torch' 时设定具体 driver 实例的一些参数
* ddp_kwargs -- 用于在使用 ``TorchDDPDriver`` 时指定 ``DistributedDataParallel`` 初始化时的参数例如传入
{'find_unused_parameters': True} 来解决有参数不参与前向运算导致的报错等
* set_grad_to_none -- 是否在训练过程中在每一次 optimizer 更新后将 grad 置为 None
* torch_non_blocking -- 表示用于 pytorch tensor to 方法的参数 non_blocking
* *data_device* -- 表示如果用户的模型 device Driver 中对应为参数 model_device None 我们会将数据迁移到 data_device
注意如果 model_device None那么 data_device 不会起作用
* *use_dist_sampler* -- 表示是否使用分布式的 sampler 在多卡时分布式 sampler 将自动决定每张卡上读取的 sample 使得一个epoch
内所有卡的 sample 加起来为一整个数据集的 sample默认会根据 driver 是否为分布式进行设置
* *evaluate_use_dist_sampler* -- 表示在 Evaluator 中在使用 分布式 的时候是否将 dataloader sampler 替换为分布式的 sampler默认为 True
* *data_device* -- 一个具体的 driver 实例中 ``model_device`` ``data_device``前者表示模型所在的设备后者表示
``model_device`` None 时应当将数据迁移到哪个设备
.. note::
注意您在绝大部分情况下不会用到该参数
1. driver 实例的 ``model_device`` 不为 None 该参数无效
2. 对于 pytorch仅当用户自己通过 ``python -m torch.distributed.launch`` 并且自己初始化 ``init_process_group``
driver 实例的 ``model_device`` 才会为 None
* *use_dist_sampler* -- 表示是否使用分布式的 ``sampler``在多卡时分布式 ``sampler`` 将自动决定每张卡上读取的 sample 使得一个 epoch
内所有卡的 sample 加起来为一整个数据集的 sample默认会根据 driver 是否为分布式进行设置
* *evaluate_use_dist_sampler* -- 表示在 ``Evaluator`` 中在使用分布式的时候是否将 dataloader ``sampler`` 替换为分布式的 ``sampler``默认为 ``True``
* *output_from_new_proc* -- 应当为一个字符串表示在多进程的 driver 中其它进程的输出流应当被做如何处理其值应当为以下之一
["all", "ignore", "only_error"]当该参数的值不是以上值时该值应当表示一个文件夹的名字我们会将其他 rank 的输出流重定向到
log 文件中然后将 log 文件保存在通过该参数值设定的文件夹中默认为 "only_error"
* *progress_bar* -- 以哪种方式显示 progress 目前支持[None, 'raw', 'rich', 'auto'] 或者 RichCallback, RawTextCallback对象
默认为 auto , auto 表示如果检测到当前 terminal 为交互型则使用 RichCallback否则使用 RawTextCallback对象如果
需要定制 progress bar 的参数例如打印频率等可以传入 RichCallback, RawTextCallback 对象
* *train_input_mapping* -- input_mapping 一致但是只用于 train input_mapping 互斥
* *train_output_mapping* -- output_mapping 一致但是只用于 train output_mapping 互斥
* *evaluate_input_mapping* -- input_mapping 一致但是只用于 evaluate input_mapping 互斥
* *evaluate_output_mapping* -- output_mapping 一致但是只用于 evaluate output_mapping 互斥
注意该参数仅当使用分布式的 ``driver`` 时才有效例如 ``TorchDDPDriver``
* *progress_bar* -- 以哪种方式显示 progress 目前支持[None, 'raw', 'rich', 'auto'] 或者 RichCallback, RawTextCallback对象
默认为 auto , auto 表示如果检测到当前 terminal 为交互型则使用 RichCallback否则使用 RawTextCallback对象如果
需要定制 progress bar 的参数例如打印频率等可以传入 RichCallback, RawTextCallback 对象
* *train_input_mapping* -- input_mapping 一致但是只用于 ``Trainer`` input_mapping 互斥
* *train_output_mapping* -- output_mapping 一致但是只用于 ``Trainer`` output_mapping 互斥
* *evaluate_input_mapping* -- input_mapping 一致但是只用于 ``Evaluator`` input_mapping 互斥
* *evaluate_output_mapping* -- output_mapping 一致但是只用于 ``Evaluator`` output_mapping 互斥
.. note::
``Trainer`` 是通过在内部直接初始化一个 ``Evaluator`` 来进行验证
``Trainer`` 内部的 ``Evaluator`` 默认是 None如果您需要在训练过程中进行验证你需要保证这几个参数得到正确的传入
必须的参数1. ``metrics``2. ``evaluate_dataloaders``
可选的其它参数1. ``evaluate_batch_step_fn2. ``evaluate_fn``3. ``evaluate_every``4. ``input_mapping``
5. ``output_mapping`` 6. ``model_wo_auto_param_call``7. ``fp16``8. ``monitor``9. ``larger_better``
.. warning::
如果 ``Trainer`` 中内置的 ``Evaluator`` 实例不为 ``None``那么需要注意 ``Trainer`` 中的一些参数是与 ``Evaluator`` 一致的它们分别为
1. ``Evaluator`` 在初始化时的 ``driver`` 参数是 ``Trainer`` 中已经实例化过的 driver这一点使得一些参数对于 ``Trainer`` 内部的
``Evaluator`` 没有用处例如 ``device````torch_kwargs````data_device`` ``output_from_new_proc``
2. ``input_mapping````output_mapping````model_wo_auto_param_call`` ``fp16`` ``Trainer`` 和其内部默认的
``Evaluator`` 是一致的
当然对于 ``input_mapping`` ``output_mapping``您可以通过添加 ``kwargs`` 中的参数 ``evaluate_input_mapping``
``evaluate_output_mapping`` 来单独为 ``Evaluator`` 进行更细致的订制
另一方面注意一些专门独属于 ``Evaluator`` 的参数仅当 ``Evaluator`` 不为 None 时才会生效
"""
self.model = model
@ -174,7 +356,7 @@ class Trainer(TrainerEventTrigger):
evaluate_input_mapping = kwargs.get('evaluate_input_mapping', None)
evaluate_output_mapping = kwargs.get('evaluate_output_mapping', None)
train_input_mapping, train_output_mapping, evaluate_input_mapping, evaluate_output_mapping = \
train_input_mapping, train_output_mapping, evaluate_input_mapping, evaluate_output_mapping = \
_get_input_output_mapping(input_mapping, output_mapping, train_input_mapping, train_output_mapping,
evaluate_input_mapping, evaluate_output_mapping)
@ -273,7 +455,7 @@ class Trainer(TrainerEventTrigger):
if not (isinstance(progress_bar, str) or progress_bar is None): # 应该是ProgressCallback获取其名称。
progress_bar = progress_bar.name
self.evaluator = Evaluator(model=model, dataloaders=evaluate_dataloaders, metrics=metrics,
driver=self.driver, device=device, evaluate_batch_step_fn=evaluate_batch_step_fn,
driver=self.driver, evaluate_batch_step_fn=evaluate_batch_step_fn,
evaluate_fn=evaluate_fn, input_mapping=evaluate_input_mapping,
output_mapping=evaluate_output_mapping, fp16=fp16, verbose=0,
use_dist_sampler=kwargs.get("evaluate_use_dist_sampler", None),
@ -302,7 +484,7 @@ class Trainer(TrainerEventTrigger):
def run(self, num_train_batch_per_epoch: int = -1, num_eval_batch_per_dl: int = -1,
num_eval_sanity_batch: int = 2, resume_from: str = None, resume_training: bool = True,
catch_KeyboardInterrupt=None):
"""
r"""
注意如果是断点重训的第一次训练即还没有保存任何用于断点重训的文件那么其应当置 resume_from None并且使用 ModelCheckpoint
去保存断点重训的文件
:param num_train_batch_per_epoch: 每个 epoch 运行多少个 batch 即停止-1 为根据 dataloader 有多少个 batch 决定
@ -491,6 +673,36 @@ class Trainer(TrainerEventTrigger):
# do something
# 以上函数会在 Trainer 每个新的 batch 开始的时候执行,但是是两个 batch 才执行一次。
.. note::
例如
.. code-block::
@Trainer.on(Event.on_train_begin())
def fn1(trainer):
...
@Trainer.on(Event.on_train_epoch_begin())
def fn2(trainer):
...
trainer1 = Trainer(
...,
marker='trainer1'
)
@Trainer.on(Event.on_fetch_data_begin())
def fn3(trainer):
...
trainer2 = Trainer(
...,
marker='trainer2'
)
注意如果你使用该函数修饰器来为你的训练添加 callback请务必保证你加入 callback 函数的代码在实例化 `Trainer` 之前
:param event: 特定的 callback 时机用户需要为该 callback 函数指定其属于哪一个 callback 时机每个时机运行的函数应该包含
@ -646,7 +858,7 @@ class Trainer(TrainerEventTrigger):
self.driver.save_model(folder, only_state_dict, **kwargs)
self.driver.barrier()
def load_model(self, folder: Union[str, Path, BinaryIO, io.BytesIO], only_state_dict: bool = False,
def load_model(self, folder: Union[str, Path, BinaryIO, io.BytesIO], only_state_dict: bool = True,
model_load_fn: Optional[Callable] = None, **kwargs):
"""
加载模型

View File

@ -10,7 +10,7 @@ from ..samplers import RandomBatchSampler, RandomSampler
from .torch_dataloader import prepare_torch_dataloader
from .paddle_dataloader import prepare_paddle_dataloader
from .jittor_dataloader import prepare_jittor_dataloader
from ...envs import FASTNLP_BACKEND, SUPPORT_BACKENDS, _module_available
from ...envs import FASTNLP_BACKEND, SUPPORT_BACKENDS
from ..log import logger

View File

@ -452,8 +452,8 @@ class DataSet:
apply_out = self._apply_process(num_proc, func, progress_desc=progress_desc,
show_progress_bar=show_progress_bar, _apply_field=field_name)
# 只检测第一个数据是否为dict类型若是则默认所有返回值为dict否则报错。
if not isinstance(apply_out[0], dict):
raise Exception("The result of func is not a dict")
if not isinstance(apply_out[0], Mapping):
raise Exception(f"The result of func is not a Mapping, but a {type(apply_out[0])}")
for key, value in apply_out[0].items():
results[key] = [value]

View File

@ -55,8 +55,8 @@ def initialize_torch_driver(driver: str, device: Optional[Union[str, "torch.devi
elif each < 0:
raise ValueError("When parameter `device` is 'Sequence' type, the value in it should be bigger than 0.")
elif each >= _could_use_device_num:
raise ValueError("When parameter `device` is 'Sequence' type, the value in it should not be bigger than"
" the available gpu number.")
raise ValueError(f"When parameter `device` is 'Sequence' type, the value in it should not be bigger than"
f" the available gpu number:{_could_use_device_num}.")
device = [torch.device(f"cuda:{w}") for w in device]
elif device is not None and not isinstance(device, torch.device):
raise ValueError("Parameter `device` is wrong type, please check our documentation for the right use.")

View File

@ -167,6 +167,12 @@ class TorchDriver(Driver):
"""
model = self.unwrap_model()
res = torch.load(filepath, map_location='cpu')
if isinstance(res, dict) and only_state_dict is False:
logger.rank_zero_warning(f"It seems like that {filepath} only contains state, you may need to use "
f"`only_state_dict=True`")
elif not isinstance(res, dict) and only_state_dict is True:
logger.rank_zero_warning(f"It seems like that {filepath} is not state, you may need to use "
f"`only_state_dict=False`")
if only_state_dict:
model.load_state_dict(res)
else:

View File

@ -334,9 +334,9 @@ def test_torch_dl():
dl = TorchDataLoader(ds, batch_size=2)
batch = next(iter(dl))
assert 'x' in batch and 'y' in batch and 'z' in batch and 'i' in batch and 'j' in batch
assert isinstance(batch['z'], torch.Tensor)
assert isinstance(batch['z'], torch.FloatTensor)
assert isinstance(batch['j'], list)
assert isinstance(batch['i']['j'], torch.Tensor)
assert isinstance(batch['i']['j'], torch.LongTensor)
dl.set_ignore('x')
batch = next(iter(dl))