From 93012a1004dbeece6cace8b59ef38d9cdf762817 Mon Sep 17 00:00:00 2001 From: YWMditto Date: Thu, 12 May 2022 13:49:38 +0800 Subject: [PATCH] =?UTF-8?q?evaluator=20=E7=9A=84=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastNLP/core/controllers/evaluator.py | 120 +++++++++++++++----------- fastNLP/core/controllers/trainer.py | 43 +++++---- fastNLP/core/utils/utils.py | 2 +- 3 files changed, 98 insertions(+), 67 deletions(-) diff --git a/fastNLP/core/controllers/evaluator.py b/fastNLP/core/controllers/evaluator.py index 8ac35ad2..18e81c8d 100644 --- a/fastNLP/core/controllers/evaluator.py +++ b/fastNLP/core/controllers/evaluator.py @@ -1,7 +1,15 @@ +r""" +``Evaluator`` 是新版 fastNLP 中用来进行评测模型的评测器,其与 ``Trainer`` 相对应,二者共同构建起了 fastNLP 中**训练**和**评测**的框架。 +``Evaluator`` 的整体架构与 ``Trainer`` 类似,也是利用 ``Driver`` 来负责底层的评测逻辑。通过使用 ``Evaluator``,您可以快速、方便、准确地 +对您的模型进行全方位地评测。 + +.. note:: + + ``Trainer`` 通过来自己内部内置一个 ``Evaluator`` 实例来支持在训练过程中进行验证的功能; +""" + from typing import Union, List, Optional, Dict, Callable -from functools import partial from dataclasses import is_dataclass -import sys __all__ = [ 'Evaluator' @@ -20,6 +28,65 @@ from fastNLP.core.log import logger class Evaluator: + """ + 用于评测模型性能好坏的评测器; + + .. note:: + + ``Evaluator`` 与 ``Trainer`` 类似,都是使用 ``Driver`` 作为底层来实现评测或者训练,因此大多数与 ``Trainer`` 同名的参数的意义和使用都与 + ``Trainer`` 中的参数相同,对于这些参数,您可以参考 ``Trainer`` 的文档来获取更详细的信息;详见 :class:`~fastNLP.core.controllers.trainer.Trainer`; + + :param model: 训练所需要的模型,例如 ``torch.nn.Module``,等价于 ``Trainer`` 中的 ``model`` 参数; + :param dataloaders: 用于评测的数据集。如果为多个,您需要使用 ``dict`` 传入,即对每一个数据集标上用于标识它们的标签; + :param metrics: 评测时使用的指标。注意该参数必须为 ``dict`` 类型,其中 ``key`` 为一个 ``metric`` 的名称,``value`` 为具体的 ``Metric`` 对象。目前支持以下 metrics: + + 1. fastNLP 自己的 ``metric``:详见 :class:`fastNLP.core.metrics.Metric`; + 2. torchmetrics; + 3. allennlp.training.metrics; + 4. paddle.metric; + + :param driver: 等价于 ``Trainer`` 中的 ``driver`` 参数; + :param device: 等价于 ``Trainer`` 中的 ``device`` 参数; + :param evaluate_batch_step_fn: 您可以传入该参数来定制每次评测一个 batch 的数据时所执行的函数。该函数应接受的两个参数为 ``evaluator`` 和 ``batch``, + 不需要有返回值;可以参考 :meth:`~fastNLP.core.controllers.loops.evaluate_batch_loop.EvaluateBatchLoop.batch_step_fn`; + :param evaluate_fn: 用来控制 ``Evaluator`` 在评测的前向传播过程中调用的是哪一个函数,例如对于 pytorch 而言,通过该参数确定使用的是 ``model.evaluate_step`` 还是 + ``model.forward``(不同训练框架所使用的的前向传播函数的方法名称不同); + + 1. 如果该值是 ``None``,那么我们会默认使用 ``evaluate_step`` 当做前向传播的函数,如果在模型中没有找到该方法,则使用训练框架默认的前向传播函数; + 2. 如果为 ``str`` 类型,例如为 ``my_evaluate_step_fn``,则尝试寻找 ``model.my_evaluate_step_fn``,如果找不到则直接报错; + + :param input_mapping: 等价于 ``Trainer`` 中的 ``input_mapping`` 参数;对具体的用于评测一个 batch 的数据使用 ``input_mapping`` 处理之后再输入到 ``model`` 以及 ``metric`` 中。如果针对 + ``model`` 和 ``metric`` 需要不同的 ``mapping``,请考虑使用 ``evaluate_batch_step_fn`` 参数定制; + + .. todo:: + + 之后链接上 参数匹配 的文档; + + :param output_mapping: 等价于 ``Trainer`` 中的 ``output_mapping`` 参数;对 ``model`` 输出的内容,将通过 ``output_mapping`` 处理之后再输入到 ``metric`` 中; + :param model_wo_auto_param_call: 等价于 ``Trainer`` 中的 ``model_wo_auto_param_call`` 参数; + + .. note:: + + 一个十分需要注意的问题在于 ``model_wo_auto_param_call`` 只会关闭部分的参数匹配,即指挥关闭前向传播时的参数匹配,但是由于 ``Evaluator`` 中 + ``metric`` 的计算都是自动化的,因此其一定需要参数匹配:根据 ``metric.update`` 的函数签名直接从字典数据中抽取其需要的参数传入进去; + + + :param fp16: 是否在评测时使用 fp16; + :param verbose: 是否打印 evaluate 的结果; + :kwargs: + * *torch_kwargs* -- 等价于 ``Trainer`` 中的 ``torch_kwargs`` 参数; + * *data_device* -- 等价于 ``Trainer`` 中的 ``data_device`` 参数; + * *model_use_eval_mode* (``bool``) -- + 是否在评测的时候将 ``model`` 的状态设置成 ``eval`` 状态。在 ``eval`` 状态下,``model`` 的 + ``dropout`` 与 ``batch normalization`` 将会关闭。默认为 ``True``。如果为 ``False``,``fastNLP`` 不会对 ``model`` 的 ``evaluate`` 状态做任何设置。无论 + 该值是什么,``fastNLP`` 都会在评测后将 ``model`` 的状态设置为 ``train``; + * *use_dist_sampler* -- + 是否使用分布式评测的方式。仅当 ``driver`` 为分布式类型时,该参数才有效。默认为根据 ``driver`` 是否支持 + 分布式进行设置。如果为 ``True``,将使得每个进程上的 ``dataloader`` 自动使用不同数据,所有进程的数据并集是整个数据集; + * *output_from_new_proc* -- 等价于 ``Trainer`` 中的 ``output_from_new_proc`` 参数; + * *progress_bar* -- 等价于 ``Trainer`` 中的 ``progress_bar`` 参数; + """ + driver: Driver _evaluate_batch_loop: Loop @@ -29,51 +96,6 @@ class Evaluator: input_mapping: Optional[Union[Callable, Dict]] = None, output_mapping: Optional[Union[Callable, Dict]] = None, model_wo_auto_param_call: bool = False, fp16: bool = False, verbose: int = 1, **kwargs): - """ - 用于对数据进行评测。 - - :param model: 待测试的模型,如果传入的 driver 为 Driver 实例,该参数将被忽略。 - :param dataloaders: 待评测的数据集。如果为多个,请使用 dict 传入。 - :param metrics: 使用的 metric 。必须为 dict 类型,其中 key 为 metric 的名称,value 为一个 Metric 对象。支持 fastNLP 的 - metric ,torchmetrics,allennlpmetrics 等。 - :param driver: 使用 driver 。 - :param device: 使用的设备。 - :param evaluate_batch_step_fn: 定制每次 evaluate batch 执行的函数。该函数应接受的两个参数为 `evaluator` 和 `batch`, - 不需要有返回值;可以参考 fastNLP.core.controllers.loops.evaluate_batch_loop.EvaluateBatchLoop中的batch_step_fn函数。 - :param evaluate_fn: 用来控制 `Evaluator` 在评测的前向传播过程中是调用哪一个函数,例如是 `model.evaluate_step` 还是 - `model.forward`;(1) 如果该值是 None,那么我们会默认使用 `evaluate_step` 当做前向传播的函数,如果在模型中没有 - 找到该方法,则使用 `model.forward` 函数;(2) 如果为 str 类型,则尝试从 model 中寻找该方法,找不到则报错。 - :param input_mapping: 对 dataloader 中输出的内容将通过 input_mapping 处理之后再输入到 model 以及 metric 中。如果针对 - model 和 metric 需要不同的 mapping,请考虑使用 evaluate_batch_step_fn 参数定制。 - :param output_mapping: 对 model 输出的内容,将通过 output_mapping 处理之后再输入到 metric 中。 - :param model_wo_auto_param_call: 是否关闭在训练时调用我们的 auto_param_call 来自动匹配 batch 和 forward 函数的参数的行为; - 如果该值为 True,并且当 batch 为字典时,我们会根据 forward 所需要的参数从 batch 中提取对应的对象,传入到 forward 函数中;如果该值 - 为 False,那么我们会将 batch 直接透传给 forward 函数。注意上述逻辑同样应用于 `train_step`, `evaluate_step` 和 `test_step`; - :param fp16: 是否使用 fp16 。 - :param verbose: 是否打印 evaluate 的结果。 - :kwargs: - * *torch_kwargs* -- 用于在指定 ``driver`` 为 'torch' 时设定具体 driver 实例的一些参数: - * 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。如果为 False,fastNLP 不会对 model 的 evaluate 状态做任何设置。无论 - 该值是什么,fastNLP 都会在 evaluate 接受后将 model 的状态设置为 train 。 - * *use_dist_sampler* -- - 是否使用分布式evaluate的方式。仅当 driver 为分布式类型时,该参数才有效。默认为根据 driver 是否支持 - 分布式进行设置。如果为True,将使得每个进程上的 dataloader 自动使用不同数据,所有进程的数据并集是整个数据集。 - * *output_from_new_proc* -- - 应当为一个字符串,表示在多进程的 driver 中其它进程的输出流应当被做如何处理;其值应当为以下之一: - ["all", "ignore", "only_error"];当该参数的值不是以上值时,该值应当表示一个文件夹的名字,我们会将其他 rank 的输出流重定向到 - log 文件中,然后将 log 文件保存在通过该参数值设定的文件夹中;默认为 "only_error"; - * *progress_bar* -- - evaluate 的时候显示的 progress bar 。目前支持三种 [None, 'raw', 'rich', 'auto'], auto 表示如果检测 - 到当前terminal为交互型则使用 rich,否则使用 raw。 - """ - self.model = model self.metrics = metrics self.driver = choose_driver(model, driver, device, fp16=fp16, model_wo_auto_param_call=model_wo_auto_param_call, @@ -127,8 +149,10 @@ class Evaluator: self.driver.barrier() - def run(self, num_eval_batch_per_dl: int = -1, **kwargs) -> Dict: + def run(self, num_eval_batch_per_dl: int = -1) -> Dict: """ + 该函数是在 ``Evaluator`` 初始化后用于真正开始评测的函数; + 返回一个字典类型的数据,其中key为metric的名字,value为对应metric的结果。 如果存在多个metric,一个dataloader的情况,key的命名规则是 metric_indicator_name#metric_name diff --git a/fastNLP/core/controllers/trainer.py b/fastNLP/core/controllers/trainer.py index d64a39fe..36b066d6 100644 --- a/fastNLP/core/controllers/trainer.py +++ b/fastNLP/core/controllers/trainer.py @@ -43,20 +43,27 @@ class Trainer(TrainerEventTrigger): :param model: 训练所需要的模型,例如 ``torch.nn.Module``; - .. note:: + .. note:: - 当使用 pytorch 时,注意参数 ``model`` 在大多数情况下为 ``nn.Module``。但是您仍能够通过使用一些特定的组合来使用情况,如下所示: + 当使用 pytorch 时,注意参数 ``model`` 在大多数情况下为 ``nn.Module``。但是您仍能够通过使用一些特定的组合来使用情况,如下所示: - 1. 当希望使用 ``DataParallel`` 时,您应当使用 ``TorchSingleDriver``,意味着您在初始化 ``Trainer`` 时参数 ``device`` 不应当为 - 一个 ``List``; + 1. 当希望使用 ``DataParallel`` 时,您应当使用 ``TorchSingleDriver``,意味着您在初始化 ``Trainer`` 时参数 ``device`` 不应当为 + 一个 ``List``; - 2. 当您选择自己初始化 ``init_process_group`` 时(这种情况要求您传入的 ``model`` 参数一定为 ``DistributedDataParallel``), - 您应当使用 ``TorchDDPDriver``,意味着您需要通过 ``python -m torch.distributed.launch`` 的方式来启动训练,此时参数 ``device`` - 应当设置为 None(此时我们会忽略该参数),具体见下面对于参数 ``device`` 的更详细的解释。 + 2. 当您选择自己初始化 ``init_process_group`` 时(这种情况要求您传入的 ``model`` 参数一定为 ``DistributedDataParallel``), + 您应当使用 ``TorchDDPDriver``,意味着您需要通过 ``python -m torch.distributed.launch`` 的方式来启动训练,此时参数 ``device`` + 应当设置为 None(此时我们会忽略该参数),具体见下面对于参数 ``device`` 的更详细的解释。 :param driver: 训练模型所使用的具体的驱动模式,应当为以下选择中的一个:["torch"],之后我们会加入 jittor、paddle 等 国产框架的训练模式;其中 "torch" 表示使用 ``TorchSingleDriver`` 或者 ``TorchDDPDriver``,具体使用哪一种取决于参数 ``device`` 的设置; + + .. warning:: + + 因为设计上的原因,您可以直接传入一个初始化好的 ``driver`` 实例,但是需要注意的是一个 ``Driver`` 在初始化时需要 ``model`` 这一参数, + 这意味着当您传入一个 ``Driver`` 实例时,您传入给 ``Trainer`` 的 ``model`` 参数将会被忽略;也就是说模型在训练时使用的真正的模型是 + 您传入的 ``Driver`` 实例中的模型; + :param train_dataloader: 训练数据集,注意其必须是单独的一个数据集,不能是 List 或者 Dict; :param optimizers: 训练所需要的优化器;可以是单独的一个优化器实例,也可以是多个优化器组成的 List; :param device: 该参数用来指定具体训练时使用的机器;注意当该参数仅当您通过 `torch.distributed.launch/run` 启动时可以为 None, @@ -268,28 +275,28 @@ class Trainer(TrainerEventTrigger): * role_maker -- 初始化 ``fleet`` 分布式训练 API 时使用的 ``RoleMaker`` * 其它用于初始化 ``DataParallel`` 的参数; * *data_device* -- 一个具体的 driver 实例中,有 ``model_device`` 和 ``data_device``,前者表示模型所在的设备,后者表示 - 当 ``model_device`` 为 None 时应当将数据迁移到哪个设备; + 当 ``model_device`` 为 None 时应当将数据迁移到哪个设备; .. note:: - 注意您在绝大部分情况下不会用到该参数! + 注意您在绝大部分情况下不会用到该参数! - 1. 当 driver 实例的 ``model_device`` 不为 None 时,该参数无效; - 2. 对于 pytorch,仅当用户自己通过 ``python -m torch.distributed.launch`` 并且自己初始化 ``init_process_group`` 时, - driver 实例的 ``model_device`` 才会为 None; - 3. 对于 paddle,该参数无效; + 1. 当 driver 实例的 ``model_device`` 不为 None 时,该参数无效; + 2. 对于 pytorch,仅当用户自己通过 ``python -m torch.distributed.launch`` 并且自己初始化 ``init_process_group`` 时, + driver 实例的 ``model_device`` 才会为 None; + 3. 对于 paddle,该参数无效; * *use_dist_sampler* -- 表示是否使用分布式的 ``sampler``。在多卡时,分布式 ``sampler`` 将自动决定每张卡上读取的 sample ,使得一个 epoch - 内所有卡的 sample 加起来为一整个数据集的 sample。默认会根据 driver 是否为分布式进行设置。 + 内所有卡的 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"; + ["all", "ignore", "only_error"];当该参数的值不是以上值时,该值应当表示一个文件夹的名字,我们会将其他 rank 的输出流重定向到 + log 文件中,然后将 log 文件保存在通过该参数值设定的文件夹中;默认为 "only_error"; 注意该参数仅当使用分布式的 ``driver`` 时才有效,例如 ``TorchDDPDriver``; * *progress_bar* -- 以哪种方式显示 progress ,目前支持[None, 'raw', 'rich', 'auto'] 或者 RichCallback, RawTextCallback对象, - 默认为 auto , auto 表示如果检测到当前 terminal 为交互型则使用 RichCallback,否则使用 RawTextCallback对象。如果 - 需要定制 progress bar 的参数,例如打印频率等,可以传入 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 互斥。 diff --git a/fastNLP/core/utils/utils.py b/fastNLP/core/utils/utils.py index 4d8bbb5e..fd5844a5 100644 --- a/fastNLP/core/utils/utils.py +++ b/fastNLP/core/utils/utils.py @@ -142,7 +142,7 @@ def auto_param_call(fn: Callable, *args, signature_fn: Optional[Callable] = None if _name not in _has_params: _has_params[_name] = _value - if len(_has_params)