@@ -2,7 +2,6 @@ __all__ = [ | |||
'Callback', | |||
'Event', | |||
'Filter', | |||
'CallbackManager', | |||
'CheckpointCallback', | |||
'choose_progress_callback', | |||
'ProgressCallback', | |||
@@ -30,20 +30,20 @@ def check_legality(fn): | |||
class Event: | |||
""" | |||
与 Trainer.on 函数配合使用,达到控制 callback 函数运行时机的目的。 | |||
:param value: Trainer 的 callback 时机。 | |||
:param int every: 触发了多少次,才真正运行一次。 | |||
:param bool once: 是否只在第一次运行后就不再执行了。 | |||
:param Callable filter_fn: 输入参数的应该为 (filter, trainer),其中 filter 对象中包含了 filter.num_called 和 | |||
filter.num_executed 两个变量分别获取当前被调用了多少次,真正执行了多少次。trainer 对象即为当前正在运行的 Trainer 。 | |||
""" | |||
every: Optional[int] | |||
once: Optional[int] | |||
def __init__(self, value: str, every: Optional[int] = None, once: Optional[int] = None, | |||
filter_fn: Optional[Callable] = None): | |||
""" | |||
请勿直接使用本对象,而是通过调用 Event.on_after_trainer_initialized() 等方式调用。 | |||
:param value: Trainer 的 callback 时机。 | |||
:param int every: 触发了多少次,才真正运行一次。 | |||
:param bool once: 是否只在第一次运行后就不再执行了。 | |||
:param Callable filter_fn: 输入参数的应该为 (filter, trainer),其中 filter 对象中包含了 filter.num_called 和 | |||
filter.num_executed 两个变量分别获取当前被调用了多少次,真正执行了多少次。trainer 对象即为当前正在运行的 Trainer 。 | |||
""" | |||
self.every = every | |||
self.once = once | |||
self.filter_fn = filter_fn | |||
@@ -456,7 +456,7 @@ class Event: | |||
class Filter: | |||
def __init__(self, every: Optional[int] = None, once: Optional[bool] = None, filter_fn: Optional[Callable] = None): | |||
r""" | |||
通过该 `Filter` 作为函数修饰器来控制一个函数的实际的运行频率; | |||
通过该 `Filter` 作为函数修饰器来控制一个函数的实际的运行频率。 | |||
:param every: 表示一个函数隔多少次运行一次; | |||
:param once: 表示一个函数只运行一次; | |||
@@ -2,10 +2,6 @@ import inspect | |||
from typing import List, Optional, Dict, Sequence | |||
from collections import defaultdict | |||
__all__ = [ | |||
'CallbackManager' | |||
] | |||
from .callback_event import Event | |||
from .callback import Callback | |||
from fastNLP.core.log import logger | |||
@@ -13,55 +13,55 @@ from ..utils.exceptions import EarlyStopException | |||
class CheckpointCallback(Callback): | |||
""" | |||
保存 checkpoint 的 callback ,其保存的文件目录以及文件名命名规则如下:: | |||
- folder/ | |||
- YYYY-mm-dd-HH_MM_SS_fffff/ # 自动根据当前脚本的启动时间创建的 | |||
- {save_object}-epoch_{epoch_idx}/ # 满足 every_n_epochs 条件保存的模型 | |||
- {save_object}-epoch_{epoch_idx}-batch_{global_batch_idx}/ # 满足 every_n_batches 保存的模型 | |||
- {save_object}-last/ # 最后一个 epoch 的保存 | |||
- {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 下不进行模型保存。默认情况下,本 checkpoint 只保存了 model | |||
的状态;如还需保存 Trainer 的状态以断点重训的话,请使用 ``save_object='trainer'`` 。 | |||
:param monitor: 监控的 metric 值。 | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param folder: 保存的文件夹,fastNLP 将在该文件下以时间戳创建子文件夹,并在里面保存。因此不同次运行可以将被保存到不同的 | |||
时间戳文件夹中。如果为 None ,默认使用当前文件夹。 | |||
:param every_n_epochs: 多少个 epoch 保存一次。 | |||
:param every_n_batches: 多少个 batch 保存一次。 | |||
:param last: 如果为 True ,将在每次 epoch 运行结束都保存一次,会覆盖之前的保存。 | |||
:param topk: 保存 monitor 结果 topK 个。 | |||
:param on_exceptions: 在出异常信息时,是否保存。传入需要捕获的异常的类。默认将捕获 EarlyStopException 。 | |||
:param larger_better: monitor 的值是否时越大越好。 | |||
: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`` 。如果 | |||
保存 ``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: | |||
""" | |||
def __init__(self, folder: Optional[Union[str, Path]] = None, every_n_epochs: Optional[int] = None, | |||
every_n_batches: Optional[int] = None, last: bool = False, topk: int = 0, | |||
on_exceptions: Optional[Union[BaseException, Sequence[BaseException]]] = [EarlyStopException], | |||
monitor: Optional[Union[str, Callable]] = None, larger_better: bool = True, | |||
only_state_dict: bool = True, model_save_fn: Optional[Callable] = None, save_object: str = 'model', | |||
save_evaluate_results=True, **kwargs): | |||
""" | |||
保存 checkpoint 的 callback ,其保存的文件目录以及文件名命名规则如下:: | |||
- folder/ | |||
- YYYY-mm-dd-HH_MM_SS_fffff/ # 自动根据当前脚本的启动时间创建的 | |||
- {save_object}-epoch_{epoch_idx}/ # 满足 every_n_epochs 条件保存的模型 | |||
- {save_object}-epoch_{epoch_idx}-batch_{global_batch_idx}/ # 满足 every_n_batches 保存的模型 | |||
- {save_object}-last/ # 最后一个 epoch 的保存 | |||
- {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 下不进行模型保存。默认情况下,本 checkpoint 只保存了 model | |||
的状态;如还需保存 Trainer 的状态以断点重训的话,请使用 ``save_object='trainer'`` 。 | |||
:param monitor: 监控的 metric 值。 | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param folder: 保存的文件夹,fastNLP 将在该文件下以时间戳创建子文件夹,并在里面保存。因此不同次运行可以将被保存到不同的 | |||
时间戳文件夹中。如果为 None ,默认使用当前文件夹。 | |||
:param every_n_epochs: 多少个 epoch 保存一次。 | |||
:param every_n_batches: 多少个 batch 保存一次。 | |||
:param last: 如果为 True ,将在每次 epoch 运行结束都保存一次,会覆盖之前的保存。 | |||
:param topk: 保存 monitor 结果 topK 个。 | |||
:param on_exceptions: 在出异常信息时,是否保存。传入需要捕获的异常的类。默认将捕获 EarlyStopException 。 | |||
:param larger_better: monitor 的值是否时越大越好。 | |||
: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`` 。如果 | |||
保存 ``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 every_n_epochs is not None: | |||
if not isinstance(every_n_epochs, int) or every_n_epochs < 1: | |||
@@ -133,10 +133,6 @@ class CheckpointCallback(Callback): | |||
self.topk_saver.save(trainer, folder_name=folder_name) | |||
def on_save_checkpoint(self, trainer) -> Dict: | |||
""" | |||
保存状态,以便之后可以继续使用 | |||
""" | |||
states = {} | |||
states['topk_saver'] = self.topk_saver.state_dict() | |||
return states | |||
@@ -9,22 +9,23 @@ from fastNLP.core.utils.exceptions import EarlyStopException | |||
class EarlyStopCallback(HasMonitorCallback): | |||
def __init__(self, monitor:Union[str, Callable]=None, larger_better:bool=True, patience:int=10): | |||
""" | |||
""" | |||
用于 early stop 的 callback 。当监控的结果连续多少次没有变好边 raise 一个 EarlyStopException 。 | |||
:param monitor: 监控的 metric 值。 | |||
:param monitor: 监控的 metric 值。 | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param larger_better: monitor 的值是否是越大越好。 | |||
:param patience: 多少次 evaluate 不没有提升就停止。 | |||
""" | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param larger_better: monitor 的值是否是越大越好。 | |||
:param patience: 多少次 evaluate 不没有提升就停止。 | |||
""" | |||
def __init__(self, monitor:Union[str, Callable]=None, larger_better:bool=True, patience:int=10): | |||
super(EarlyStopCallback, self).__init__(monitor=monitor, larger_better=larger_better, must_have_monitor=True) | |||
self.wait = 0 | |||
self.patience = patience | |||
@@ -42,7 +43,7 @@ class EarlyStopCallback(HasMonitorCallback): | |||
# 当是 step evaluate 的时候,下一步执行的就是这个, 所以在这里检查。 | |||
if self.wait >= self.patience: | |||
raise EarlyStopException(f"After {self.wait} validations, no improvement for " | |||
f"metric `{self._real_monitor}`") | |||
f"metric `{self._real_monitor}`(best value: {self.monitor_value})") | |||
def on_train_epoch_begin(self, trainer): | |||
# 当是 epoch evaluate 的时候,下一步执行的就是这个, 所以在这里检查。 | |||
@@ -16,11 +16,6 @@ from fastNLP.core.utils.utils import _check_valid_parameters_number | |||
class CanItemDataType(ABC): | |||
""" | |||
检测可以进行传输的对象。 | |||
""" | |||
@classmethod | |||
def __subclasshook__(cls, subclass: Any) -> Union[bool, Any]: | |||
if cls is CanItemDataType: | |||
@@ -30,15 +25,22 @@ class CanItemDataType(ABC): | |||
class ResultsMonitor: | |||
""" | |||
可用于监控某个数值,并通过 is_better_results() 等接口实现检测结果是否变得更好了。 | |||
:param monitor: 监控的 metric 值。 | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param larger_better: monitor 是否时越大越好 | |||
""" | |||
def __init__(self, monitor:Union[Callback, str], larger_better:bool=True): | |||
""" | |||
可用于监控某个数值,并通过 is_better_results() 等接口实现检测结果是否变得更好了。 | |||
:param monitor: 监控的 metric 值。如果在 evaluation 结果中没有找到完全一致的名称,将使用 最长公共字符串算法 找到最匹配 | |||
的那个作为 monitor 。如果为 None,将尝试使用 Trainer 设置的 monitor 。也可以传入一个函数,接受参数为 evaluation 的结 | |||
果(字典类型),返回一个 float 值作为 monitor 的结果,如果当前结果中没有相关的 monitor 值请返回 None 。 | |||
:param larger_better: monitor 是否时越大越好 | |||
""" | |||
self.set_monitor(monitor, larger_better) | |||
def set_monitor(self, monitor, larger_better): | |||
@@ -66,9 +68,9 @@ class ResultsMonitor: | |||
def get_monitor_value(self, results:Dict)->Union[float, None]: | |||
""" | |||
获取 monitor 的值,如果 monitor 没有直接找到,会尝试使用匹配的方式寻找,并把匹配到的设置到 self._real_monitor 属性上。 | |||
获取 monitor 的值,如果 monitor 没有直接找到,会尝试使用 最长公共字符串算法 匹配的方式寻找。 | |||
:param results: | |||
:param results: 评测结果。 | |||
:return: 如果为 None ,表明此次没有找到合适的monitor | |||
""" | |||
if len(results) == 0 or self.monitor is None: | |||
@@ -113,7 +115,7 @@ class ResultsMonitor: | |||
""" | |||
检测给定的 results 是否比上一次更好,如果本次 results 中没有找到相关的monitor 返回 False。 | |||
:param results: on_valid_ends() 接口中传入的 evaluation 结果。 | |||
:param results: evaluation 结果。 | |||
:param keep_if_better: 当返回为 True 时,是否保存到 self.monitor_value 中。 | |||
:return: | |||
""" | |||
@@ -166,24 +168,24 @@ class ResultsMonitor: | |||
class HasMonitorCallback(ResultsMonitor, Callback): | |||
""" | |||
该 callback 不直接进行使用,作为其它相关 callback 的父类使用,如果 callback 有使用 monitor 可以继承该函数里面实现了 | |||
(1)判断monitor合法性;(2)在需要时, 根据trainer的monitor设置自己的monitor名称。 | |||
:param monitor: 监控的 metric 值。 | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param larger_better: monitor 是否时越大越好 | |||
:param must_have_monitor: 这个 callback 是否必须有 monitor 设置。如果设置为 True ,且没检测到设置 monitor 会报错。 | |||
""" | |||
def __init__(self, monitor, larger_better, must_have_monitor=False): | |||
""" | |||
该 callback 不直接进行使用,作为其它相关 callback 的父类使用,如果 callback 有使用 monitor 可以继承该函数里面实现了 | |||
(1)判断monitor合法性;(2)在需要时, 根据trainer的monitor设置自己的monitor名称。 | |||
:param monitor: 监控的 metric 值。 | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param larger_better: monitor 是否时越大越好 | |||
:param must_have_monitor: 这个 callback 是否必须有 monitor 设置。如果设置为 True ,且没检测到设置 monitor 会报错。 | |||
""" | |||
super().__init__(monitor, larger_better) | |||
self.must_have_monitor = must_have_monitor | |||
@@ -212,16 +214,23 @@ class HasMonitorCallback(ResultsMonitor, Callback): | |||
class ExecuteOnceBetterMonitor(HasMonitorCallback): | |||
""" | |||
当监控的 monitor 结果更好的时候,调用 execute_fn 函数。 | |||
:param monitor: 监控的 metric 值。 | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param larger_better: monitor 是否时越大越好 | |||
:param execute_fn: 一个可执行的函数,不接受任何参数,不反回值。在 monitor 取得更好结果的时候会调用。 | |||
""" | |||
def __init__(self, monitor, larger_better, execute_fn): | |||
""" | |||
当监控的 monitor 结果更好的时候,调用 execute_fn 函数。 | |||
:param monitor: 监控的 metric 值。如果在 evaluation 结果中没有找到完全一致的名称,将使用 最长公共字符串算法 找到最匹配 | |||
的那个作为 monitor 。如果为 None,将尝试使用 Trainer 设置的 monitor 。也可以传入一个函数,接受参数为 evaluation 的结 | |||
果(字典类型),返回一个 float 值作为 monitor 的结果,如果当前结果中没有相关的 monitor 值请返回 None 。 | |||
:param larger_better: monitor 是否时越大越好 | |||
:param execute_fn: 一个可执行的函数,不接受任何参数,不反回值。在 monitor 取得更好结果的时候会调用。 | |||
""" | |||
super().__init__(monitor, larger_better, must_have_monitor=True) | |||
_check_valid_parameters_number(execute_fn, expected_params=[], fn_name='execute_fn') | |||
self.execute_fn = execute_fn | |||
@@ -14,34 +14,34 @@ from fastNLP.envs import all_rank_call_context | |||
class LoadBestModelCallback(HasMonitorCallback): | |||
""" | |||
保存最佳的 monitor 值最佳的模型,并在训练结束的时候重新加载模型,默认会在加载之后删除权重文件。仅在训练正常结束的时候才能加载 | |||
最好的模型。 | |||
:param monitor: 监控的 metric 值。 | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param larger_better: 该 metric 值是否是越大越好。 | |||
:param save_folder: 保存的文件夹,如果为空,则保存在内存中。不为空,则保存一份权重到文件中,当为多机训练,且本值不为空时,请确保 | |||
不同的机器均可访问当该路径。当 model_save_fn 不为 None 时该值一定不能为空。 | |||
:param only_state_dict: 是否只保存模型的参数。当 model_save_fn 不为空时,该值无效。 | |||
:param model_save_fn: 保存 model 的函数,与 model_load_fn 必须同时不为空。本函数的输入为一个已经创建好的文件夹,没有输出, | |||
请在函数内完成对模型的保存。 | |||
:param model_load_fn: 加载 model 的函数,与 model_save_fn 必须同时不为空。本函数的输入为一个已经创建好的文件夹,没有输出, | |||
请在函数内完成对模型的加载。 | |||
:param delete_after_train: 在训练结束后是否删掉模型。 | |||
""" | |||
def __init__(self, monitor:Union[str, Callable]=None, larger_better:bool = True, only_state_dict:bool = True, | |||
save_folder:Optional[str] = None, model_save_fn:Optional[Callable] = None, | |||
model_load_fn:Optional[Callable] = None, | |||
delete_after_train:bool = True): | |||
""" | |||
保存最佳的 monitor 值最佳的模型,并在训练结束的时候重新加载模型,默认会在加载之后删除权重文件。仅在训练正常结束的时候才能加载 | |||
最好的模型。 | |||
:param monitor: 监控的 metric 值。 | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param larger_better: 该 metric 值是否是越大越好。 | |||
:param save_folder: 保存的文件夹,如果为空,则保存在内存中。不为空,则保存一份权重到文件中,当为多机训练,且本值不为空时,请确保 | |||
不同的机器均可访问当该路径。当 model_save_fn 不为 None 时该值一定不能为空。 | |||
:param only_state_dict: 是否只保存模型的参数。当 model_save_fn 不为空时,该值无效。 | |||
:param model_save_fn: 保存 model 的函数,与 model_load_fn 必须同时不为空。本函数的输入为一个已经创建好的文件夹,没有输出, | |||
请在函数内完成对模型的保存。 | |||
:param model_load_fn: 加载 model 的函数,与 model_save_fn 必须同时不为空。本函数的输入为一个已经创建好的文件夹,没有输出, | |||
请在函数内完成对模型的加载。 | |||
:param delete_after_train: 在训练结束后是否删掉模型。 | |||
""" | |||
super().__init__(monitor=monitor, larger_better=larger_better, must_have_monitor=True) | |||
if model_load_fn is not None: | |||
assert callable(model_load_fn), "`model_load_fn` must be a callable object." | |||
@@ -6,14 +6,14 @@ __all__ = [ | |||
class LRSchedCallback(Callback): | |||
def __init__(self, scheduler, step_on:str='batch'): | |||
""" | |||
根据 step_on 参数在合适的时机调用 scheduler 的 step 函数。 | |||
""" | |||
根据 step_on 参数在合适的时机调用 scheduler 的 step 函数。 | |||
:param scheduler: 实现了 step() 函数的对象 | |||
:param step_on: 可选 ['batch', 'epoch'] 表示在何时调用 scheduler 的 step 函数。如果为 batch 的话在每次更新参数 | |||
之前调用;如果为 epoch 则是在一个 epoch 运行结束后调用。 | |||
""" | |||
:param scheduler: 实现了 step() 函数的对象 | |||
:param step_on: 可选 ['batch', 'epoch'] 表示在何时调用 scheduler 的 step 函数。如果为 batch 的话在每次更新参数 | |||
之前调用;如果为 epoch 则是在一个 epoch 运行结束后调用。 | |||
""" | |||
def __init__(self, scheduler, step_on:str='batch'): | |||
assert hasattr(scheduler, 'step') and callable(scheduler.step), "The scheduler object should have a " \ | |||
"step function." | |||
self.scheduler = scheduler | |||
@@ -11,6 +11,67 @@ from .topk_saver import TopkSaver | |||
class MoreEvaluateCallback(HasMonitorCallback): | |||
""" | |||
当评测时需要调用不同的 evaluate_fn (例如在大部分生成任务中,一般使用训练 loss 作为训练过程中的 evaluate ;但同时在训练到 | |||
一定 epoch 数量之后,会让 model 生成的完整的数据评测 bleu 等。此刻就可能需要两种不同的 evaluate_fn ),只使用 Trainer | |||
无法满足需求,可以通过调用本 callback 进行。如果需要根据本 callback 中的评测结果进行模型保存,请传入 topk 以及 | |||
topk_monitor 等相关参数。可以通过 evaluate_every 或 watch_monitor 控制触发进行 evaluate 的条件。 | |||
如果设置了 evaluate 结果更好就保存的话,将按如下文件结构进行保存:: | |||
- folder/ | |||
- YYYY-mm-dd-HH_MM_SS_fffff/ # 自动根据当前脚本的启动时间创建的 | |||
- {save_object}-epoch_{epoch_idx}-batch_{global_batch_idx}-{topk_monitor}_{monitor_value}/ # 满足topk条件存储文件名 | |||
:param dataloaders: 需要评估的数据 | |||
:param metrics: 使用的 metrics 。 | |||
: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 watch_monitor: 这个值用来表示监控的 Trainer 中的 evaluate 结果的,当该值不为 None ,evaluate_every 失效。本参数的 | |||
意义是,当检测到 Trainer 中 evaluate results 的 {watch_monitor} 的结果更好时,则进行一次 evaluate 。该参数有两种 | |||
取值: (1) str 类型,监控的 metric 值。如果在 evaluation 结果中没有找到完全一致的名称,将使用 最长公共字符串算法 找到最 | |||
匹配的那个作为 monitor ; (2) 也可以传入一个函数,接受参数为 evaluation 的结果(字典类型),返回一个 float 值作为 monitor | |||
的结果,如果当前结果中没有相关的monitor 值请返回 None 。 | |||
:param watch_monitor_larger_better: watch_monitor 是否越大越好。 | |||
:param evaluate_fn: 用来控制 `Evaluator` 在评测的前向传播过程中是调用哪一个函数,例如是 `model.evaluate_step` 还是 | |||
`model.forward`;(1) 如果该值是 None,那么我们会默认使用 `evaluate_step` 当做前向传播的函数,如果在模型中没有 | |||
找到该方法,则使用 `model.forward` 函数;(2) 如果为 str 类型,则尝试从 model 中寻找该方法,找不到则报错。 | |||
:param num_eval_sanity_batch: 在初始化 Evaluator 后运行多少个 sanity check 的 batch ,检测一下。 | |||
:param topk: 如果需要根据当前 callback 中的 evaluate 结果保存模型或 Trainer ,可以通过设置 tokp 实现。(1)为 -1 表示每次 | |||
evaluate 后都保存;(2)为 0 (默认),表示不保存;(3)为整数,表示保存性能最 topk 个。 | |||
:param topk_monitor: 如果需要根据当前 callback 中的 evaluate 结果保存。这个参数是指在当前 callback 中的 evaluate 结果寻找 | |||
:param topk_larger_better: topk_monitor 的值是否时越大越好。 | |||
:param folder: 保存的文件夹,fastNLP 将在该文件下以时间戳创建子文件夹,并在里面保存。因此不同次运行可以将被保存到不同的 | |||
时间戳文件夹中。如果为 None ,默认使用当前文件夹。 | |||
:param only_state_dict: 保存模型时是否只保存 state_dict 。当 model_save_fn 不为 None 时,该参数无效。 | |||
:param save_object: 可选 ['trainer', 'model'],表示在保存时的保存对象为 ``trainer+model`` 还是 只是 ``model`` 。如果 | |||
保存 ``trainer`` 对象的话,将会保存 :class:~fastNLP.Trainer 的相关状态,可以通过 :meth:`Trainer.load` 加载该断 | |||
点继续训练。如果保存的是 ``Model`` 对象,则可以通过 :meth:`Trainer.load_model` 加载该模型权重。 | |||
:param model_save_fn: 个性化的保存函数,当触发保存操作时,就调用这个函数,这个函数应当接受一个文件夹作为参数,不返回任何东西。 | |||
如果传入了 model_save_fn 函数,fastNLP 将不再进行模型相关的保存。在多卡场景下,我们只在 rank 0 上会运行该函数。 | |||
:param save_evaluate_results: 是否保存 evaluate 的结果。如果为 True ,在保存 topk 模型的 folder 中还将额外保存一个 | |||
``fastnlp_evaluate_results.json`` 文件,记录当前的 results。仅在设置了 topk 的场景下有用,默认为 True 。 | |||
:param save_kwargs: dict。更多的保存相关的参数。 | |||
:param kwargs: 其它与 Evaluator 相关的初始化参数,如果不传入,将从 Trainer 中获取。 | |||
""" | |||
def __init__(self, dataloaders, metrics:Dict, evaluate_every:Optional[Union[int, Callable]]=-1, | |||
watch_monitor:Union[str, Callable]=None, watch_monitor_larger_better:bool=True, | |||
evaluate_fn=None, num_eval_sanity_batch=2, | |||
@@ -18,48 +79,6 @@ class MoreEvaluateCallback(HasMonitorCallback): | |||
folder=None, only_state_dict=True, save_object='model', model_save_fn=None, | |||
save_evaluate_results=True, save_kwargs=None, | |||
**kwargs): | |||
""" | |||
当评测时需要调用不同的 evaluate_fn (例如在大部分生成任务中,一般使用训练 loss 作为训练过程中的 evaluate ;但同时在训练到 | |||
一定 epoch 数量之后,会让 model 生成的完整的数据评测 bleu 等。此刻就可能需要两种不同的 evaluate_fn ),只使用 Trainer | |||
无法满足需求,可以通过调用本 callback 进行。如果需要根据本 callback 中的评测结果进行模型保存,请传入 topk 以及 | |||
topk_monitor 等相关参数。可以通过 evaluate_every 或 watch_monitor 控制触发进行 evaluate 的条件。 | |||
如果设置了 evaluate 结果更好就保存的话,将按如下文件结构进行保存:: | |||
- folder/ | |||
- YYYY-mm-dd-HH_MM_SS_fffff/ # 自动根据当前脚本的启动时间创建的 | |||
- {save_object}-epoch_{epoch_idx}-batch_{global_batch_idx}-{topk_monitor}_{monitor_value}/ # 满足topk条件存储文件名 | |||
:param dataloaders: 需要评估的数据 | |||
:param metrics: 使用的 metrics 。 | |||
:param evaluate_every: 可以为负数、正数和函数;(1) 为负整数时表示每隔几个 epoch evaluate 一次;(2) 为正整数则表示每隔几个 batch | |||
evaluate 一次;(3) 为函数时表示用户自己传入的用于控制 evaluate 的频率的函数,该函数的应该接受 trainer 对象作为参数,并返回 | |||
一个 bool 值,返回为 True 说明需要进行 evaluate ;将在每个 batch 结束后调用该函数判断是否需要 evaluate 。 | |||
:param watch_monitor: 这个值用来表示监控的 Trainer 中的 evaluate 结果的,当该值不为 None ,evaluate_every 失效。本参数的 | |||
意义是,当检测到 Trainer 中 evaluate results 的 {watch_monitor} 的结果更好时,则进行一次 evaluate 。该参数有两种 | |||
取值: (1) str 类型,监控的 metric 值。如果在 evaluation 结果中没有找到完全一致的名称,将使用 最长公共字符串算法 找到最 | |||
匹配的那个作为 monitor ; (2) 也可以传入一个函数,接受参数为 evaluation 的结果(字典类型),返回一个 float 值作为 monitor | |||
的结果,如果当前结果中没有相关的monitor 值请返回 None 。 | |||
:param watch_monitor_larger_better: watch_monitor 是否越大越好。 | |||
:param evaluate_fn: 用来控制 `Evaluator` 在评测的前向传播过程中是调用哪一个函数,例如是 `model.evaluate_step` 还是 | |||
`model.forward`;(1) 如果该值是 None,那么我们会默认使用 `evaluate_step` 当做前向传播的函数,如果在模型中没有 | |||
找到该方法,则使用 `model.forward` 函数;(2) 如果为 str 类型,则尝试从 model 中寻找该方法,找不到则报错。 | |||
:param num_eval_sanity_batch: 在初始化 Evaluator 后运行多少个 sanity check 的 batch ,检测一下。 | |||
:param topk: 如果需要根据当前 callback 中的 evaluate 结果保存模型或 Trainer ,可以通过设置 tokp 实现。(1)为 -1 表示每次 | |||
evaluate 后都保存;(2)为 0 (默认),表示不保存;(3)为整数,表示保存性能最 topk 个。 | |||
:param topk_monitor: 如果需要根据当前 callback 中的 evaluate 结果保存。这个参数是指在当前 callback 中的 evaluate 结果寻找 | |||
:param topk_larger_better: topk_monitor 的值是否时越大越好。 | |||
:param folder: 保存的文件夹,fastNLP 将在该文件下以时间戳创建子文件夹,并在里面保存。因此不同次运行可以将被保存到不同的 | |||
时间戳文件夹中。如果为 None ,默认使用当前文件夹。 | |||
:param only_state_dict: 保存模型时是否只保存 state_dict 。当 model_save_fn 不为 None 时,该参数无效。 | |||
:param save_object: 可选 ['trainer', 'model'],表示在保存时的保存对象为 trainer+model 还是 只是model 。 | |||
:param model_save_fn: 个性化的保存函数,当触发保存操作时,就调用这个函数,这个函数应当接受一个文件夹作为参数,不返回任何东西。 | |||
如果传入了 model_save_fn 函数,fastNLP 将不再进行模型相关的保存。在多卡场景下,我们只在 rank 0 上会运行该函数。 | |||
:param save_evaluate_results: 是否保存 evaluate 的结果。如果为 True ,在保存 topk 模型的 folder 中还将额外保存一个 | |||
fastnlp_evaluate_results.json 文件,记录当前的 results。仅在设置了 topk 的场景下有用,默认为 True 。 | |||
:param save_kwargs: dict。更多的保存相关的参数。 | |||
:param kwargs: 其它与 Evaluator 相关的初始化参数,如果不传入,将从 Trainer 中获取。请特别留意 evaluate_fn 的设置。 | |||
""" | |||
super(MoreEvaluateCallback, self).__init__(watch_monitor, watch_monitor_larger_better, | |||
must_have_monitor=False) | |||
@@ -39,25 +39,27 @@ def choose_progress_callback(progress_bar: Union[str, ProgressCallback]) -> Prog | |||
class RichCallback(ProgressCallback): | |||
""" | |||
在训练过程中打印 rich progress bar 的 callback 。在 Trainer 中,默认就会使用这个 callback 来显示进度。如果需要定制这个 Callback 的 | |||
参数,请通过实例化本 Callback 并传入到 Trainer 中实现。 | |||
:param print_every: 多少个 batch 更新一次显示。 | |||
:param loss_round_ndigit: 显示的 loss 保留多少位有效数字 | |||
:param monitor: 监控的 metric 值。当检测到这个key的结果更好时,会打印出不同的颜色进行提示。 | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param larger_better: 是否是 monitor 的结果越大越好。 | |||
:param format_json: 是否格式化 json 再打印 | |||
""" | |||
def __init__(self, print_every:int = 1, loss_round_ndigit:int = 6, monitor:str=None, larger_better:bool=True, | |||
format_json=True): | |||
""" | |||
:param print_every: 多少个 batch 更新一次显示。 | |||
:param loss_round_ndigit: 显示的 loss 保留多少位有效数字 | |||
:param monitor: 监控的 metric 值。当检测到这个key的结果更好时,会打印出不同的颜色进行提示。 | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param larger_better: 是否是 monitor 的结果越大越好。 | |||
:param format_json: 是否格式化 json 再打印 | |||
""" | |||
super().__init__(monitor=monitor, larger_better=larger_better, must_have_monitor=False) | |||
self.print_every = print_every | |||
self.progress_bar = f_rich_progress | |||
@@ -16,22 +16,24 @@ from .has_monitor_callback import ResultsMonitor | |||
class Saver: | |||
""" | |||
执行保存的对象。保存的文件组织结构为:: | |||
- folder # 当前初始化的参数 | |||
- YYYY-mm-dd-HH_MM_SS_fffff/ # 自动根据当前脚本的启动时间创建的 | |||
- folder_name # 由 save() 调用时传入。 | |||
:param folder: 保存在哪个文件夹下,默认为当前 folder 下。 | |||
:param save_object: 可选 ['trainer', 'model'],表示在保存时的保存对象为 ``trainer+model`` 还是 只是 ``model`` 。如果 | |||
保存 ``trainer`` 对象的话,将会保存 :class:~fastNLP.Trainer 的相关状态,可以通过 :meth:`Trainer.load` 加载该断 | |||
点继续训练。如果保存的是 ``Model`` 对象,则可以通过 :meth:`Trainer.load_model` 加载该模型权重。 | |||
:param only_state_dict: 保存时是否仅保存权重,在 model_save_fn 不为 None 时无意义。 | |||
:param model_save_fn: 个性化的保存函数,当触发保存操作时,就调用这个函数,这个函数应当接受一个文件夹作为参数,不返回任何东西。 | |||
如果传入了 model_save_fn 函数,fastNLP 将不再进行模型相关的保存。在多卡场景下,我们只在 rank 0 上会运行该函数。 | |||
:param kwargs: 更多需要传递给 Trainer.save() 或者 Trainer.save_model() 接口的参数。 | |||
""" | |||
def __init__(self, folder:str=None, save_object:str='model', only_state_dict:bool=True, | |||
model_save_fn:Callable=None, **kwargs): | |||
""" | |||
执行保存的对象。保存的文件组织结构为:: | |||
- folder # 当前初始化的参数 | |||
- YYYY-mm-dd-HH_MM_SS_fffff/ # 自动根据当前脚本的启动时间创建的 | |||
- folder_name # 由 save() 调用时传入。 | |||
:param folder: 保存在哪个文件夹下,默认为当前 folder 下。 | |||
:param save_object: 可选 ['trainer', 'model'],表示在保存时的保存对象为 trainer+model 还是 只是model 。 | |||
:param only_state_dict: 保存时是否仅保存权重,在 model_save_fn 不为 None 时无意义。 | |||
:param model_save_fn: 个性化的保存函数,当触发保存操作时,就调用这个函数,这个函数应当接受一个文件夹作为参数,不返回任何东西。 | |||
如果传入了 model_save_fn 函数,fastNLP 将不再进行模型相关的保存。在多卡场景下,我们只在 rank 0 上会运行该函数。 | |||
:param kwargs: 更多需要传递给 Trainer.save() 或者 Trainer.save_model() 接口的参数。 | |||
""" | |||
if folder is None: | |||
folder = Path.cwd().absolute() | |||
logger.info(f"Parameter `folder` is None, and we will use {folder} to save and load your model.") | |||
@@ -79,8 +81,8 @@ class Saver: | |||
""" | |||
以 json 格式保存 results 到 path 中 | |||
:param results: | |||
:param path: | |||
:param results: 一般是评测后的结果。 | |||
:param path: 保存的文件名 | |||
:return: | |||
""" | |||
with open(path, 'w', encoding='utf8') as f: | |||
@@ -117,12 +119,12 @@ class Saver: | |||
class TopkQueue: | |||
def __init__(self, topk): | |||
""" | |||
用于维护处于 topk 的 key, value 对。 | |||
""" | |||
用于维护处于 topk 的 key, value 对。 | |||
:param int topk: 整数,-1 表示所有数据都是 topk 的; 如果是 0, 表示没有任何数据是满足 topk 的。 | |||
""" | |||
:param int topk: 整数,-1 表示所有数据都是 topk 的; 如果是 0, 表示没有任何数据是满足 topk 的。 | |||
""" | |||
def __init__(self, topk): | |||
assert isinstance(topk, int) | |||
self.topk = topk | |||
self.topk_dict = {} # 其中 key 为保存的内容, value 是对应的性能。 | |||
@@ -170,31 +172,39 @@ class TopkQueue: | |||
class TopkSaver(ResultsMonitor, Saver): | |||
""" | |||
用来识别 topk 模型并保存,也可以仅当一个保存 Saver 使用。保存路径为:: | |||
- folder/ | |||
- YYYY-mm-dd-HH_MM_SS_fffff/ # 自动根据当前脚本的启动时间创建的 | |||
- {save_object}-epoch_{epoch_idx}-batch_{global_batch_idx}-{topk_monitor}_{monitor_value}/ # 满足topk条件存储文件名 | |||
:param topk: 保存 topk 多少的模型,-1 为保存所有模型;0 为都不保存;大于 0 的数为保存 topk 个。 | |||
:param monitor: 监控的 metric 值。 | |||
* 为 ``None`` | |||
将尝试使用 :class:`~fastNLP.Trainer` 中设置 `monitor` 值(如果有设置)。 | |||
* 为 ``str`` | |||
尝试直接使用该名称从 ``evaluation`` 结果中寻找,如果在 ``evaluation`` 结果中没有找到完全一致的名称,将 | |||
使用 最长公共字符串算法 从 ``evaluation`` 结果中找到最匹配的那个作为 ``monitor`` 。 | |||
* 为 ``Callable`` | |||
接受参数为 ``evaluation`` 的结果(字典类型),返回一个 ``float`` 值作为 ``monitor`` 的结果,如果当前结果中没有相关 | |||
的 ``monitor`` 值请返回 ``None`` 。 | |||
:param larger_better: 该 monitor 是否越大越好。 | |||
:param folder: 保存在哪个文件夹下,默认为当前 folder 下。 | |||
:param save_object: 可选 ['trainer', 'model'],表示在保存时的保存对象为 ``trainer+model`` 还是 只是 ``model`` 。如果 | |||
保存 ``trainer`` 对象的话,将会保存 :class:~fastNLP.Trainer 的相关状态,可以通过 :meth:`Trainer.load` 加载该断 | |||
点继续训练。如果保存的是 ``Model`` 对象,则可以通过 :meth:`Trainer.load_model` 加载该模型权重。 | |||
:param only_state_dict: 保存时是否仅保存权重,在 model_save_fn 不为 None 时无意义。 | |||
:param model_save_fn: 个性化的保存函数,当触发保存操作时,就调用这个函数,这个函数应当接受一个文件夹作为参数,不返回任何东西。 | |||
如果传入了 model_save_fn 函数,fastNLP 将不再进行模型相关的保存。在多卡场景下,我们只在 rank 0 上会运行该函数。 | |||
:param save_evaluate_results: 是否保存 evaluate 的结果。如果为 True ,在保存 topk 模型的 folder 中还将额外保存一个 | |||
``fastnlp_evaluate_results.json`` 文件,记录当前的 metric results 。仅在设置了 topk 的场景下有用,默认为 True 。 | |||
:param kwargs: 更多需要传递给 Trainer.save() 或者 Trainer.save_model() 接口的参数。 | |||
""" | |||
def __init__(self, topk:int=0, monitor:str=None, larger_better:bool=True, folder:str=None, save_object:str='model', | |||
only_state_dict:bool=True, model_save_fn:Callable=None, save_evaluate_results:bool=True, | |||
**kwargs): | |||
""" | |||
用来识别 topk 模型并保存,也可以仅当一个保存 Saver 使用。保存路径为:: | |||
- folder/ | |||
- YYYY-mm-dd-HH_MM_SS_fffff/ # 自动根据当前脚本的启动时间创建的 | |||
- {save_object}-epoch_{epoch_idx}-batch_{global_batch_idx}-{topk_monitor}_{monitor_value}/ # 满足topk条件存储文件名 | |||
:param topk: 保存 topk 多少的模型,-1 为保存所有模型;0 为都不保存;大于 0 的数为保存 topk 个。 | |||
:param monitor: 监控哪个指标判断是否是 topk 的。监控的 metric 值。如果在 evaluation 结果中没有找到完全一致的名称,将使用 | |||
最长公共字符串算法 找到最匹配的那个作为 monitor 。如果为 None,将尝试使用 Trainer 设置的 monitor 。也可以传入一个函数, | |||
接受参数为 evaluation 的结果(字典类型),返回一个 float 值作为 monitor 的结果,如果当前结果中没有相关的 monitor 值请 | |||
返回 None 。 | |||
:param larger_better: 该 monitor 是否越大越好。 | |||
:param folder: 保存在哪个文件夹下,默认为当前 folder 下。 | |||
:param save_object: 可选 ['trainer', 'model'],表示在保存时的保存对象为 trainer+model 还是 只是model 。 | |||
:param only_state_dict: 保存时是否仅保存权重,在 model_save_fn 不为 None 时无意义。 | |||
:param model_save_fn: 个性化的保存函数,当触发保存操作时,就调用这个函数,这个函数应当接受一个文件夹作为参数,不返回任何东西。 | |||
如果传入了 model_save_fn 函数,fastNLP 将不再进行模型相关的保存。在多卡场景下,我们只在 rank 0 上会运行该函数。 | |||
:param save_evaluate_results: 是否保存 evaluate 的结果。如果为 True ,在保存 topk 模型的 folder 中还将额外保存一个 | |||
fastnlp_evaluate_results.json 文件,记录当前的 results。仅在设置了 topk 的场景下有用,默认为 True 。 | |||
:param kwargs: 更多需要传递给 Trainer.save() 或者 Trainer.save_model() 接口的参数。 | |||
""" | |||
ResultsMonitor.__init__(self, monitor, larger_better) | |||
Saver.__init__(self, folder, save_object, only_state_dict, model_save_fn, **kwargs) | |||
@@ -1,25 +1,26 @@ | |||
__all__ = [ | |||
'TorchGradClipCallback' | |||
] | |||
from typing import Union, List | |||
from ..callback import Callback | |||
class TorchGradClipCallback(Callback): | |||
def __init__(self, clip_value=1, clip_type='norm', parameters=None): | |||
r""" | |||
在每次 optimizer update 之前将 parameter 进行 clip | |||
r""" | |||
在每次 optimizer update 之前将 parameter 进行 clip 。 | |||
:param float clip_value: 将gradient 限制到[-clip_value, clip_value]。clip_value应该为正数 | |||
:param str clip_type: 支持'norm', 'value'两种: | |||
:param clip_value: 将gradient 限制到[-clip_value, clip_value]。clip_value应该为正数 | |||
:param clip_type: 支持'norm', 'value'两种: | |||
1. 'norm', 将gradient的norm rescale到[-clip_value, clip_value] | |||
2. 'value', 将gradient限制在[-clip_value, clip_value], | |||
小于-clip_value的gradient被赋值为-clip_value; | |||
大于clip_value的gradient被赋值为clip_value. | |||
1. 'norm', 将gradient的norm rescale到[-clip_value, clip_value] | |||
2. 'value', 将gradient限制在[-clip_value, clip_value], | |||
小于-clip_value的gradient被赋值为-clip_value;大于clip_value的gradient被赋值为clip_value. | |||
:param None,torch.Tensor,List[torch.Tensor] parameters: 一般通过model.parameters()获得。 | |||
如果为None则默认对 Trainer 的 optimizers 中所有参数进行梯度裁剪。 | |||
""" | |||
:param None,torch.Tensor,List[torch.Tensor] parameters: 一般通过model.parameters()获得。 | |||
如果为None则默认对 Trainer 的 optimizers 中所有参数进行梯度裁剪。 | |||
""" | |||
def __init__(self, clip_value:int=1, clip_type:str='norm', | |||
parameters:Union["torch.Tensor", List["torch.Tensor"]]=None): | |||
super().__init__() | |||
from torch import nn | |||
@@ -2,21 +2,23 @@ __all__ = [ | |||
'TorchWarmupCallback' | |||
] | |||
import math | |||
from typing import Union | |||
from ..callback import Callback | |||
class TorchWarmupCallback(Callback): | |||
def __init__(self, warmup=0.1, schedule='constant'): | |||
r""" | |||
调整 learning rate 的 callback 。仅在实际发生参数更新的情况下 | |||
:param int,float warmup: 如果warmup为int,则在该step之前,learning rate根据schedule的策略变化; 如果warmup为float, | |||
如0.1, 则前10%的step是按照schedule策略调整learning rate。 | |||
:param str schedule: 以哪种方式调整。 | |||
linear: 前warmup的step上升到指定的learning rate(从Trainer中的optimizer处获取的), 后warmup的step下降到0; | |||
constant前warmup的step上升到指定learning rate,后面的step保持learning rate. | |||
""" | |||
r""" | |||
调整 learning rate 的 callback 。 | |||
:param warmup: 如果warmup为int,则在该step之前,learning rate根据schedule的策略变化; 如果warmup为float, | |||
如0.1, 则前10%的step是按照schedule策略调整learning rate。 | |||
:param schedule: 以哪种方式调整。 | |||
1. linear: 前warmup的step上升到指定的learning rate(从Trainer中的optimizer处获取的), 后warmup的step下降到0; | |||
2. constant前warmup的step上升到指定learning rate,后面的step保持learning rate. | |||
""" | |||
def __init__(self, warmup:Union[int, float]=0.1, schedule:str='constant'): | |||
super().__init__() | |||
self.warmup = max(warmup, 0.) | |||
@@ -82,16 +82,26 @@ def _get_backend() -> str: | |||
class Collator: | |||
def __init__(self, backend='auto'): | |||
""" | |||
用于 pad 数据的对象。会自动将所有能够 pad (由 fastNLP 根据数据判定能否 pad )的数据都进行 pad 操作,默认 pad 的值为 0。 | |||
可使用 set_pad() 函数调整。如果有些 field 不想输出,可以使用 set_ignore() 函数进行设置。Collator 在第一次进行 pad 的 | |||
时候自动根据设置以及数据情况,为每个 field 获取一个 padder ,在之后的每次调用中,都将使用对应的 Padder 给对应的 field 。 | |||
""" | |||
用于 pad 数据的对象。会自动将所有能够 pad (由 fastNLP 根据数据判定能否 pad )的数据都进行 pad 操作,默认 pad 的值为 0。 | |||
哦安定一个 field 是否可以 pad 的方式为:(1)当前这个 field 是否所有对象都是一样的数据类型;(因此,如果某 field 的数据有些是float | |||
有些是 int 将知道该 field 被判定为不可 pad 类型。)(2)当前这个 field 是否每个 sample 都具有一样的深度;(因此,例如有个 field 的 | |||
数据转为 batch 类型后为 [1, [1,2]], 会被判定为不可 pad ,因为第一个 sample 与 第二个 sample 深度不同)(3)当前这个 field 的类 | |||
型是否是可以 pad (例如 str 类型的数据)。可以通过设置 logger.setLevel('debug') 来打印是判定不可 pad 的原因。 | |||
:param backend: 对于可以 pad 的 field,使用哪种 tensor,支持 ['torch','jittor','paddle','numpy','raw', auto, None]。 | |||
若为 'auto' ,则在进行 pad 的时候会根据调用的环境决定其 backend 。该参数对不能进行 pad 的数据没用影响,不能 pad | |||
的数据返回一定是 list 。 | |||
""" | |||
todo 补充 code example 。 | |||
如果需要将某个本可以 pad 的 field 设置为不可 pad ,则可以通过 :meth:`~fastNLP.Collator.set_pad` 的 pad_val 设置为 None 实现。 | |||
如果需要某些 field 不要包含在 pad 之后的结果中,可以使用 :meth:`~fastNLP.Collator.set_ignore` 进行设置。 | |||
Collator 在第一次进行 pad 的时候自动根据设置以及数据情况,为每个 field 获取一个 padder ,在之后的每次调用中,都将使用对应 | |||
的 Padder 给对应的 field 。 | |||
:param backend: 对于可以 pad 的 field,使用哪种 tensor,支持 ['torch','jittor','paddle','numpy','raw', auto, None]。 | |||
若为 'auto' ,则在进行 pad 的时候会根据调用的环境决定其 backend 。该参数对不能进行 pad 的数据没用影响,不能 pad | |||
的数据返回一定是 list 。 | |||
""" | |||
def __init__(self, backend='auto'): | |||
self.unpack_batch_func = None | |||
self.pack_batch_func = None | |||
self.ignore_fields = set() | |||
@@ -264,9 +274,8 @@ class Collator: | |||
def set_ignore(self, *field_names) -> "Collator": | |||
""" | |||
如果有的内容不希望输出,可以在此处进行设置,被设置的 field 将在 batch 的输出中被忽略。 | |||
Example:: | |||
collator.set_ignore('field1', 'field2') | |||
>>> collator = Collator().set_ignore('field1', 'field2') | |||
:param field_names: 需要忽略的 field 的名称。如果 Dataset 的 __getitem__ 方法返回的是 dict 类型的,则可以直接使用对应的 | |||
field 的 key 来表示,如果是 nested 的 dict,可以使用元组来表示,例如 {'a': {'b': 1}} 中的使用 ('a', 'b'); 如果 | |||
@@ -9,9 +9,9 @@ class MappingPackerUnpacker: | |||
""" | |||
将 Sequence[Mapping] 转为 Dict 。例如 [{'a': [1, 2], 'b': 1}, {'a': [3], 'b': 2}] -> {'a': [[1, 2], [3]], 'b': [1, 2]} | |||
:param batch: | |||
:param ignore_fields: | |||
:param input_fields: | |||
:param batch: 需要 unpack 的 batch 数据。 | |||
:param ignore_fields: 需要忽略的 field 。 | |||
:param input_fields: 需要设置为 input 的 field 。 | |||
:return: | |||
""" | |||
dict_batch = defaultdict(list) | |||
@@ -29,13 +29,13 @@ class MappingPackerUnpacker: | |||
class NestedMappingPackerUnpacker: | |||
@staticmethod | |||
def unpack_batch(batch:Sequence[Mapping], ignore_fields:set, input_fields:Dict): | |||
def unpack_batch(batch:Sequence[Mapping], ignore_fields:set, input_fields:Dict)->Dict: | |||
""" | |||
将 nested 的 dict 中的内容展开到一个 flat dict 中 | |||
:param batch: | |||
:param batch: 需要 unpack 的 batch 数据。 | |||
:param ignore_fields: 需要忽略的 field 。 | |||
:param input_fields: 不需要继续往下衍射的 | |||
:param input_fields: 需要设置为 input 的 field 。 | |||
:return: | |||
""" | |||
dict_batch = defaultdict(list) | |||
@@ -72,8 +72,9 @@ class SequencePackerUnpacker: | |||
""" | |||
将 Sequence[Sequence] 转为 Mapping 。例如 [[[1, 2], 2], [[3], 2]] -> {'_0': [[1, 2], [3]], '_1': [1, 2]} | |||
:param batch: | |||
:param ignore_fields: 需要忽略的field | |||
:param batch: 需要 unpack 的 batch 数据。 | |||
:param ignore_fields: 需要忽略的 field 。 | |||
:param input_fields: 需要设置为 input 的 field 。 | |||
:return: | |||
""" | |||
dict_batch = defaultdict(list) | |||
@@ -90,7 +90,7 @@ class JittorNumberPadder(Padder): | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
return jittor.Var(np.array(batch_field, dtype=dtype)) | |||
@@ -107,7 +107,7 @@ class JittorSequencePadder(Padder): | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
tensor = get_padded_jittor_tensor(batch_field, dtype=dtype, pad_val=pad_val) | |||
return tensor | |||
@@ -125,7 +125,7 @@ class JittorTensorPadder(Padder): | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
try: | |||
if not isinstance(batch_field[0], jittor.Var): | |||
batch_field = [jittor.Var(np.array(field.tolist(), dtype=dtype)) for field in batch_field] | |||
@@ -30,53 +30,65 @@ def _get_dtype(ele_dtype, dtype, class_name): | |||
class NumpyNumberPadder(Padder): | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
""" | |||
可以将形如 [1, 2, 3] 这类的数据转为 np.array([1, 2, 3]) | |||
""" | |||
可以将形如 [1, 2, 3] 这类的数据转为 np.array([1, 2, 3]) 。可以通过: | |||
>>> NumpyNumberPadder.pad([1, 2, 3]) | |||
使用。 | |||
:param pad_val: 该值无意义 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 np.array 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么 | |||
""" | |||
:param pad_val: 该值无意义 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 np.array 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么 | |||
""" | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
dtype = _get_dtype(ele_dtype, dtype, self.__class__.__name__) | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
return np.array(batch_field, dtype=dtype) | |||
class NumpySequencePadder(Padder): | |||
""" | |||
将类似于 [[1], [1, 2]] 的内容 pad 为 np.array([[1, 0], [1, 2]]) 可以 pad 多重嵌套的数据。 | |||
可以通过以下的方式直接使用: | |||
>>> NumpySequencePadder.pad([[1], [1, 2]], pad_val=-100, dtype=float) | |||
[[ 1. -100.] | |||
[ 1. 2.]] | |||
:param pad_val: pad 的值是多少。 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 np.array 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么 | |||
""" | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
""" | |||
将类似于 [[1], [1, 2]] 的内容 pad 为 np.array([[1, 0], [1, 2]]) 可以 pad 多重嵌套的数据。 | |||
:param pad_val: pad 的值是多少。 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 np.array 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么 | |||
""" | |||
dtype = _get_dtype(ele_dtype, dtype, self.__class__.__name__) | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
return get_padded_numpy_array(batch_field, dtype=dtype, pad_val=pad_val) | |||
class NumpyTensorPadder(Padder): | |||
""" | |||
pad 类似于 [np.array([3, 4]), np.array([1])] 的 field 。若内部元素不为 np.ndarray ,则必须含有 tolist() 方法。 | |||
>>> NumpyTensorPadder.pad([np.array([3, 4]), np.array([1])], pad_val=-100) | |||
[[ 3. 4.] | |||
[ 1. -100.]] | |||
:param pad_val: pad 的值是多少。 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 np.array 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么 | |||
""" | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
""" | |||
pad 类似于 [np.array([3, 4], np.array([1])] 的 field 。若内部元素不为 np.ndarray ,则必须含有 tolist() 方法。 | |||
:param pad_val: pad 的值是多少。 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 np.array 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么 | |||
""" | |||
dtype = _get_dtype(ele_dtype, dtype, self.__class__.__name__) | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
try: | |||
if not isinstance(batch_field[0], np.ndarray): | |||
batch_field = [np.array(field.tolist(), dtype=dtype) for field in batch_field] | |||
@@ -1,5 +1,9 @@ | |||
class Padder: | |||
""" | |||
所有 Padder 对象父类,所有的 Padder 对象都会实现 pad(batch_field, pad_val=0, dtype=None) 的静态函数。 | |||
""" | |||
def __init__(self, pad_val, dtype): | |||
self.pad_val = pad_val | |||
self.dtype = dtype | |||
@@ -8,19 +12,19 @@ class Padder: | |||
return self.pad(batch_field=batch_field, pad_val=self.pad_val, dtype=self.dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
raise NotImplementedError() | |||
class NullPadder(Padder): | |||
def __init__(self, ele_dtype=None, pad_val=None, dtype=None): | |||
""" | |||
不进行任何 检查 与 pad 的空 padder 。 | |||
""" | |||
不进行任何 检查 与 pad 的空 padder 。 | |||
:param ele_dtype: | |||
:param pad_val: | |||
:param dtype: | |||
""" | |||
:param ele_dtype: | |||
:param pad_val: | |||
:param dtype: | |||
""" | |||
def __init__(self, ele_dtype=None, pad_val=None, dtype=None): | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
def __call__(self, batch_field): | |||
@@ -80,55 +80,55 @@ def _get_dtype(ele_dtype, dtype, class_name): | |||
class PaddleNumberPadder(Padder): | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
""" | |||
可以将形如 [1, 2, 3] 这类的数据转为 paddle.Tensor([1, 2, 3]) | |||
""" | |||
可以将形如 [1, 2, 3] 这类的数据转为 paddle.Tensor([1, 2, 3]) | |||
:param pad_val: 该值无意义 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 paddle.tensor 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么。如 int, float, 'int32' 等 | |||
""" | |||
:param pad_val: 该值无意义 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 paddle.tensor 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么。如 int, float, 'int32' 等 | |||
""" | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
# 仅当 ele_dtype 是 python number/ numpy number 或者 tensor | |||
dtype = _get_dtype(ele_dtype, dtype, class_name=self.__class__.__name__) | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
return paddle.to_tensor(batch_field, dtype=dtype) | |||
class PaddleSequencePadder(Padder): | |||
def __init__(self, ele_dtype=None, pad_val=0, dtype=None): | |||
""" | |||
将类似于 [[1], [1, 2]] 的内容 pad 为 paddle.Tensor([[1, 0], [1, 2]]) 可以 pad 多重嵌套的数据。 | |||
""" | |||
将类似于 [[1], [1, 2]] 的内容 pad 为 paddle.Tensor([[1, 0], [1, 2]]) 可以 pad 多重嵌套的数据。 | |||
:param pad_val: pad 的值。 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 paddle.tensor 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么。如 int, float, 'int32' 等 | |||
""" | |||
:param pad_val: pad 的值。 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 paddle.tensor 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么。如 int, float, 'int32' 等 | |||
""" | |||
def __init__(self, ele_dtype=None, pad_val=0, dtype=None): | |||
dtype = _get_dtype(ele_dtype, dtype, class_name=self.__class__.__name__) | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
tensor = get_padded_paddle_tensor(batch_field, dtype=dtype, pad_val=pad_val) | |||
return tensor | |||
class PaddleTensorPadder(Padder): | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
""" | |||
目前支持 [paddle.tensor([3, 2], paddle.tensor([2, 1])] 类似的,若内部元素不为 paddle.tensor ,则必须含有 tolist() 方法。 | |||
""" | |||
目前支持 [paddle.tensor([3, 2], paddle.tensor([2, 1])] 类似的,若内部元素不为 paddle.tensor ,则必须含有 tolist() 方法。 | |||
:param pad_val: pad 的值。 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 paddle.tensor 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么。如 int, float, 'int32' 等 | |||
""" | |||
:param pad_val: pad 的值。 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 paddle.tensor 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么。如 int, float, 'int32' 等 | |||
""" | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
dtype = _get_dtype(ele_dtype, dtype, class_name=self.__class__.__name__) | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
try: | |||
if not isinstance(batch_field[0], paddle.Tensor): | |||
batch_field = [np.array(field.tolist()) for field in batch_field] | |||
@@ -26,14 +26,14 @@ def _get_dtype(ele_dtype, dtype, class_name): | |||
class RawNumberPadder(Padder): | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
""" | |||
可以将形如 [1, 2, 3] 这类的数据转为 [1, 2, 3] 。实际上该 padder 无意义。 | |||
""" | |||
可以将形如 [1, 2, 3] 这类的数据转为 [1, 2, 3] 。实际上该 padder 无意义。 | |||
:param pad_val: 该值无意义 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 np.array 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么 | |||
""" | |||
:param pad_val: 该值无意义 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 np.array 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么 | |||
""" | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
dtype = _get_dtype(ele_dtype, dtype, self.__class__.__name__) | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@@ -41,24 +41,24 @@ class RawNumberPadder(Padder): | |||
return batch_field | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
raise NotImplementedError() | |||
class RawSequencePadder(Padder): | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
""" | |||
将类似于 [[1], [1, 2]] 的内容 pad 为 [[1, 0], [1, 2]] 。可以 pad 多重嵌套的数据。 | |||
""" | |||
将类似于 [[1], [1, 2]] 的内容 pad 为 [[1, 0], [1, 2]] 。可以 pad 多重嵌套的数据。 | |||
:param pad_val: pad 的值 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 np.array 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么 | |||
""" | |||
:param pad_val: pad 的值 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 np.array 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么 | |||
""" | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
dtype = _get_dtype(ele_dtype, dtype, self.__class__.__name__) | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
""" | |||
:param batch_field: | |||
@@ -70,19 +70,19 @@ class RawSequencePadder(Padder): | |||
class RawTensorPadder(Padder): | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
""" | |||
将类似于 [[1], [1, 2]] 的内容 pad 为 [[1, 0], [1, 2]] 。可以 pad 多重嵌套的数据。 | |||
""" | |||
将类似于 [[1], [1, 2]] 的内容 pad 为 [[1, 0], [1, 2]] 。可以 pad 多重嵌套的数据。 | |||
:param pad_val: pad 的值 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 np.array 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么 | |||
""" | |||
:param pad_val: pad 的值 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 np.array 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么 | |||
""" | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
dtype = _get_dtype(ele_dtype, dtype, self.__class__.__name__) | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
""" | |||
:param batch_field: | |||
@@ -64,54 +64,61 @@ def _get_dtype(ele_dtype, dtype, class_name): | |||
class TorchNumberPadder(Padder): | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
""" | |||
可以将形如 [1, 2, 3] 这类的数据转为 torch.Tensor([1, 2, 3]) | |||
""" | |||
可以将形如 [1, 2, 3] 这类的数据转为 torch.Tensor([1, 2, 3]) | |||
:param pad_val: 该值无意义 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 torch.tensor 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么。如 torch.long, torch.float32, int, float 等 | |||
""" | |||
:param pad_val: 该值无意义 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 torch.tensor 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么。如 torch.long, torch.float32, int, float 等 | |||
""" | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
dtype = _get_dtype(ele_dtype, dtype, class_name=self.__class__.__name__) | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
return torch.tensor(batch_field, dtype=dtype) | |||
class TorchSequencePadder(Padder): | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
""" | |||
将类似于 [[1], [1, 2]] 的内容 pad 为 torch.Tensor([[1, 0], [1, 2]]) 可以 pad 多重嵌套的数据。 | |||
""" | |||
将类似于 [[1], [1, 2]] 的内容 pad 为 torch.Tensor([[1, 0], [1, 2]]) 可以 pad 多重嵌套的数据。 | |||
:param pad_val: 需要 pad 的值。 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 torch.tensor 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么。如 torch.long, torch.float32, int, float 等 | |||
""" | |||
:param pad_val: 需要 pad 的值。 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 torch.tensor 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么。如 torch.long, torch.float32, int, float 等 | |||
""" | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
dtype = _get_dtype(ele_dtype, dtype, class_name=self.__class__.__name__) | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
tensor = get_padded_torch_tensor(batch_field, dtype=dtype, pad_val=pad_val) | |||
return tensor | |||
class TorchTensorPadder(Padder): | |||
""" | |||
目前支持 [torch.tensor([3, 2], torch.tensor([1])] 类似的。若内部元素不为 torch.tensor ,则必须含有 tolist() 方法。 | |||
>>> TorchTensorPadder.pad([np.array([3, 4]), np.array([1])], pad_val=-100) | |||
[[ 3. 4.] | |||
[ 1. -100.]] | |||
>>> TorchTensorPadder.pad([torch.LongTensor([3, 4]), torch.LongTensor([1])], pad_val=-100) | |||
tensor([[ 3, 4], | |||
[ 1, -100]]) | |||
:param pad_val: 需要 pad 的值。 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 torch.tensor 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么。如 torch.long, torch.float32, int, float 等 | |||
""" | |||
def __init__(self, pad_val=0, ele_dtype=None, dtype=None): | |||
""" | |||
目前支持 [torch.tensor([3, 2], torch.tensor([1])] 类似的。若内部元素不为 torch.tensor ,则必须含有 tolist() 方法。 | |||
:param pad_val: 需要 pad 的值。 | |||
:param ele_dtype: 用于检测当前 field 的元素类型是否可以转换为 torch.tensor 类型。 | |||
:param dtype: 输出的数据的 dtype 是什么。如 torch.long, torch.float32, int, float 等 | |||
""" | |||
dtype = _get_dtype(ele_dtype, dtype, class_name=self.__class__.__name__) | |||
super().__init__(pad_val=pad_val, dtype=dtype) | |||
@staticmethod | |||
def pad(batch_field, pad_val, dtype): | |||
def pad(batch_field, pad_val=0, dtype=None): | |||
device = None | |||
try: | |||
if not isinstance(batch_field[0], torch.Tensor): | |||
@@ -20,6 +20,114 @@ from fastNLP.core.log import logger | |||
class Evaluator: | |||
""" | |||
用于对数据进行评测。 | |||
:param model: 待测试的模型,如果传入的 driver 为 Driver 实例,该参数将被忽略。 | |||
:param dataloaders: 待评测的数据集。如果为多个,请使用 dict 传入。 | |||
:param metrics: 使用的 metric 。必须为 dict 类型,其中 key 为 metric 的名称,value 为一个 Metric 对象。支持 fastNLP 的 | |||
metric ,torchmetrics,allennlpmetrics 等。 | |||
:param driver: 训练模型所使用的具体的驱动模式,应当为以下选择中的一个:``["torch", "jittor", "paddle"]`` | |||
其中 "torch" 表示使用 ``TorchSingleDriver`` 或者 ``TorchDDPDriver``,具体使用哪一种取决于参数 ``device`` | |||
的设置。 | |||
:param device: 该参数用来指定具体训练时使用的机器;注意当该参数仅当您通过 `torch.distributed.launch/run` 启动时可以为 None, | |||
此时 fastNLP 不会对模型和数据进行设备之间的移动处理,但是你可以通过参数 `input_mapping` 和 `output_mapping` 来实现设备之间 | |||
数据迁移的工作(通过这两个参数传入两个处理数据的函数);同时你也可以通过在 kwargs 添加参数 "data_device" 来让我们帮助您将数据 | |||
迁移到指定的机器上(注意这种情况理应只出现在用户在 Trainer 实例化前自己构造 DDP 的场景); | |||
device 的可选输入如下所示: | |||
* *str*: 例如 'cpu', 'cuda', 'cuda:0', 'cuda:1' 等; | |||
* *torch.device*: 例如 'torch.device("cuda:0")'; | |||
* *int*: 将使用 ``device_id`` 为该值的 ``gpu`` 进行训练;如果值为 -1,那么默认使用全部的显卡,此时使用的 driver 实例是 `TorchDDPDriver`; | |||
* *list(int)*: 如果多于 1 个device,应当通过该种方式进行设定;注意此时我们一定会使用 ``TorchDDPDriver``,不管您传入的列表的长度是 1 还是其它值; | |||
* *None*: 仅当用户自己通过训练框架提供的并行训练启动脚本开启 ddp 进程时为 None; | |||
.. note:: | |||
如果希望使用 ``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 evaluate_batch_step_fn: 定制每次 evaluate batch 执行的函数。该函数应接受的两个参数为 `evaluator` 和 `batch`, | |||
不需要有返回值;可以参考 :meth:`~fastNLP.core.controllers.loops.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: 应当为一个字典或者一个函数,表示在当前 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,我们将不会做任何处理,而是直接将其传入该函数里,并将其返回值 | |||
送入模型中; | |||
:param output_mapping: 应当为一个字典或者一个函数,表示在当前 step 拿到一个 model 的返回值后,应当做怎样的映射处理: | |||
1. 如果 ``output_mapping`` 是一个字典: | |||
1. 如果此时 batch 也是一个 ``Dict``,那么我们会把输出中同样在 ``output_mapping`` 中的 key 修改为 ``output_mapping`` 的对应 ``key`` 的 ``value``; | |||
例如输出结果为 {'a': 1},而 output_mapping={'a':'b'} ,那么结果就是 {'b': 1} | |||
2. 如果此时 batch 是一个 ``dataclass``,那么我们会先将其转换为一个 ``Dict``,然后再进行上述转换; | |||
3. 如果此时 batch 此时是其它类型,那么我们将会直接报错; | |||
2. 如果 ``output_mapping`` 是一个函数,我们将会把结果传入该函数里,并将其返回值送入到 metric 中。 | |||
:param model_wo_auto_param_call: 是否关闭在训练时调用我们的 auto_param_call 来自动匹配 batch 和 evaluate_fn 函数的参数的行为; | |||
如果该值为 True,并且当 batch 为字典时,我们会根据 evaluate_fn 所需要的参数从 batch 中提取对应的对象,传入到 evaluate_fn 函数中;如果该值 | |||
为 False,那么我们会将 batch 直接透传给 evaluate_fn 函数。 | |||
: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。 | |||
""" | |||
driver: Driver | |||
_evaluate_batch_loop: Loop | |||
@@ -29,51 +137,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, | |||
@@ -129,16 +192,13 @@ class Evaluator: | |||
def run(self, num_eval_batch_per_dl: int = -1, **kwargs) -> Dict: | |||
""" | |||
返回一个字典类型的数据,其中key为metric的名字,value为对应metric的结果。 | |||
如果存在多个metric,一个dataloader的情况,key的命名规则是 | |||
metric_indicator_name#metric_name | |||
如果存在多个数据集,一个metric的情况,key的命名规则是 | |||
metric_indicator_name#metric_name#dataloader_name (其中 # 是默认的 separator ,可以通过 Evaluator 初始化参数修改)。 | |||
如果存在多个metric,多个dataloader的情况,key的命名规则是 | |||
metric_indicator_name#metric_name#dataloader_name | |||
其中 metric_indicator_name 可能不存在。 | |||
:param num_eval_batch_per_dl: 每个 dataloader 测试多少个 batch 的数据,-1 为测试所有数据。 | |||
返回一个字典类型的数据,其中 key 为 metric 的名字,value 为对应结果。返回的字典中,key 的命名规则如下 | |||
``metric_indicator_name#metric_name#dataloader_name`` ,其中 ``metric_indicator_name`` 是由使用的 metric 返回的结果 | |||
决定的,仅当 metric 的结果返回是 dict 类型是才有该值;``metric_name`` 则由 ``Evaluator`` 初始化时传入的 ``metrics`` 参数 | |||
决定;``dataloader_name``仅在传入的 ``dataloaders`` 为 dict 时会有。此外其中的 ``#`` 符号通过 ``Evaluator`` 初始化 | |||
参数 ``separator`` 进行设置。 | |||
:param num_eval_batch_per_dl: 每个 dataloader 测试前多少个 batch 的数据,-1 为测试所有数据。 | |||
:return: | |||
""" | |||
assert isinstance(num_eval_batch_per_dl, int), "num_eval_batch_per_dl must be of int type." | |||
@@ -80,9 +80,9 @@ class Trainer(TrainerEventTrigger): | |||
您应当使用 ``TorchDDPDriver``,意味着您需要通过 ``python -m torch.distributed.launch`` 的方式来启动训练,此时参数 ``device`` | |||
应当设置为 None(此时我们会忽略该参数),具体见下面对于参数 ``device`` 的更详细的解释。 | |||
:param driver: 训练模型所使用的具体的驱动模式,应当为以下选择中的一个:["torch"],之后我们会加入 jittor、paddle 等 | |||
国产框架的训练模式;其中 "torch" 表示使用 ``TorchSingleDriver`` 或者 ``TorchDDPDriver``,具体使用哪一种取决于参数 ``device`` | |||
的设置; | |||
:param driver: 训练模型所使用的具体的驱动模式,应当为以下选择中的一个:``["torch", "jittor", "paddle"]`` | |||
其中 "torch" 表示使用 ``TorchSingleDriver`` 或者 ``TorchDDPDriver``,具体使用哪一种取决于参数 ``device`` | |||
的设置。 | |||
:param train_dataloader: 训练数据集,注意其必须是单独的一个数据集,不能是 List 或者 Dict; | |||
:param optimizers: 训练所需要的优化器;可以是单独的一个优化器实例,也可以是多个优化器组成的 List; | |||
:param device: 该参数用来指定具体训练时使用的机器;注意当该参数仅当您通过 `torch.distributed.launch/run` 启动时可以为 None, | |||
@@ -135,7 +135,7 @@ class Trainer(TrainerEventTrigger): | |||
: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`; | |||
不需要有返回值;可以参考 :meth:`fastNLP.core.controllers.TrainBatchLoop.batch_step_fn`; | |||
:param train_fn: 用来控制 ``Trainer`` 在训练的前向传播过程中是调用模型的哪一个函数,例如是 ``train_step`` 还是 ``forward``; | |||
默认为 ``None``,如果该值是 ``None``,那么我们会默认使用 ``train_step`` 当做前向传播的函数,如果在模型的定义类中没有找到该方法, | |||
则使用模型默认的前向传播函数,例如对于 pytorch 来说就是 ``forward``。 | |||
@@ -1,6 +1,6 @@ | |||
r""" | |||
instance 模块实现了Instance 类在fastNLP中对应sample。一个sample可以认为是一个Instance类型的对象。 | |||
便于理解的例子可以参考文档 :mod:`fastNLP.core.dataset` 中的表格 | |||
便于理解的例子可以参考文档 :mod:`fastNLP.core.dataset` 。 | |||
""" | |||
@@ -14,10 +14,10 @@ from fastNLP.core.utils.utils import pretty_table_printer | |||
class Instance(Mapping): | |||
r""" | |||
Instance是fastNLP中对应一个sample的类。每个sample在fastNLP中是一个Instance对象。 | |||
Instance一般与 :class:`~fastNLP.DataSet` 一起使用, Instance的初始化如下面的Example所示:: | |||
Instance 是 fastNLP 中对应一个 sample 的类。每个 sample 在 fastNLP 中是一个 Instance 对象。 | |||
Instance 一般与 :class:`~fastNLP.DataSet` 一起使用, Instance 的初始化如下面的 Example 所示:: | |||
instance = Instance() # 请补充完整 | |||
>>> instance = Instance(input="this is a demo sentence", label='good') # 请补充完整 | |||
""" | |||
@@ -44,17 +44,17 @@ class Instance(Mapping): | |||
def keys(self): | |||
r""" | |||
返回一个迭代器,内容是field_name | |||
返回一个迭代器,内容是 field_name | |||
:return: 一个迭代器 | |||
:return: 一个迭代器 | |||
""" | |||
return self.fields.keys() | |||
def values(self): | |||
r""" | |||
返回一个迭代器,内容是field_value | |||
返回一个迭代器,内容是 field_value | |||
:return: 一个迭代器 | |||
:return: 一个迭代器 | |||
""" | |||
return self.fields.values() | |||
@@ -14,14 +14,16 @@ from fastNLP.core.metrics.element import Element | |||
class Metric: | |||
""" | |||
fastNLP 中 Metric 的基类,自定义 Metric 时,请继承该对象。使用该对象,将有助于减少在分布式状态下的 Metric 计算。 | |||
:param backend: 目前支持四种类型的 backend, ``[torch, paddle, jittor, auto]``。其中 ``auto`` 表示根据实际调用 | |||
Metric.update() 函数时传入的参数决定具体的 ``backend`` ,大部分情况下直接使用 ``auto`` 即可。 | |||
:param aggregate_when_get_metric: 在计算 metric 的时候是否自动将各个进程上的相同的 element 的数字聚合后再得到metric, | |||
当 backend 不支持分布式时,该参数无意义。如果为 None ,将在 :class:`fastNLP.Evaluator` 中根据 sampler 是否使用分布式 | |||
进行自动设置。 | |||
""" | |||
def __init__(self, backend: Union[str, Backend, None] = 'auto', aggregate_when_get_metric: bool = None): | |||
""" | |||
:param str backend: 目前支持四种类型的backend, [torch, paddle, jittor, auto]。其中 auto 表示根据实际调用 Metric.update() | |||
函数时传入的参数决定具体的 backend ,大部分情况下直接使用 auto 即可。 | |||
:param bool aggregate_when_get_metric: 在计算 metric 的时候是否自动将各个进程上的相同的 element 的数字聚合后再得到metric, | |||
当 backend 不支持分布式时,该参数无意义。如果为 None ,将在 Evaluator 中根据 sampler 是否使用分布式进行自动设置。 | |||
""" | |||
self.backend = AutoBackend(backend) | |||
self._updated = False | |||
self.get_metric = self._sync_get_metric(self.get_metric) | |||
@@ -39,7 +41,10 @@ class Metric: | |||
""" | |||
注册一个 element 对象,注册之后便可以通过在 Metric 中直接通过 self.{name} 进行调用,可以认为该对象即为对应 backend 的 | |||
tensor 直接进行加减乘除计算即可。 | |||
注意:如果想使得该 metric 可自动扩展到多卡的情况,请一定申明 aggregate_method 。 | |||
..warning:: | |||
如果想使得该 metric 可自动扩展到多卡的情况,请一定申明 aggregate_method 。 | |||
:param name: 当前 element 的名字,注册后,在 Metric 中可以通过 self.{name} 访问该变量。 | |||
:param value: 初始化的值。在调用 Metric.reset() 方法时也将自动设置为该值 | |||
@@ -200,27 +200,27 @@ def _bio_tag_to_spans(tags, ignore_labels=None): | |||
class SpanFPreRecMetric(Metric): | |||
r""" | |||
:param tag_vocab: 标签的 :class:`~fastNLP.Vocabulary` 。支持的标签为"B"(没有label);或"B-xxx"(xxx为某种label,比如POS中的NN), | |||
在解码时,会将相同xxx的认为是同一个label,比如['B-NN', 'E-NN']会被合并为一个'NN'. | |||
:param pred: 用该key在evaluate()时从传入dict中取出prediction数据。 为None,则使用 `pred` 取数据 | |||
:param target: 用该key在evaluate()时从传入dict中取出target数据。 为None,则使用 `target` 取数据 | |||
:param seq_len: 用该key在evaluate()时从传入dict中取出sequence length数据。为None,则使用 `seq_len` 取数据。 | |||
:param encoding_type: 目前支持bio, bmes, bmeso, bioes。默认为None,通过tag_vocab自动判断. | |||
:param ignore_labels: str 组成的list. 这个list中的class不会被用于计算。例如在POS tagging时传入['NN'],则不会计算'NN'个label | |||
:param only_gross: 是否只计算总的f1, precision, recall的值;如果为False,不仅返回总的f1, pre, rec, 还会返回每个label的f1, pre, rec | |||
:param f_type: `micro` 或 `macro` . `micro` :通过先计算总体的TP,FN和FP的数量,再计算f, precision, recall; `macro` : 分布计算每个类别的f, precision, recall,然后做平均(各类别f的权重相同) | |||
:param beta: f_beta分数, :math:`f_{beta} = \frac{(1 + {beta}^{2})*(pre*rec)}{({beta}^{2}*pre + rec)}` . 常用为 `beta=0.5, 1, 2` 若为0.5则精确率的权重高于召回率;若为1,则两者平等;若为2,则召回率权重高于精确率。 | |||
:param backend: 目前支持四种类型的 backend, ``[torch, paddle, jittor, auto]``。其中 ``auto`` 表示根据实际调用 | |||
Metric.update() 函数时传入的参数决定具体的 ``backend`` ,大部分情况下直接使用 ``auto`` 即可。 | |||
:param aggregate_when_get_metric: 在计算 metric 的时候是否自动将各个进程上的相同的 element 的数字聚合后再得到metric, | |||
当 backend 不支持分布式时,该参数无意义。如果为 None ,将在 :class:`fastNLP.Evaluator` 中根据 sampler 是否使用分布式 | |||
进行自动设置。 | |||
""" | |||
def __init__(self, tag_vocab: Vocabulary, encoding_type: str = None, ignore_labels: List[str] = None, | |||
only_gross: bool = True, f_type='micro', | |||
beta=1, backend: Union[str, Backend, None] = 'auto', aggregate_when_get_metric: bool = None) -> None: | |||
r""" | |||
:param tag_vocab: 标签的 :class:`~fastNLP.Vocabulary` 。支持的标签为"B"(没有label);或"B-xxx"(xxx为某种label,比如POS中的NN), | |||
在解码时,会将相同xxx的认为是同一个label,比如['B-NN', 'E-NN']会被合并为一个'NN'. | |||
:param str pred: 用该key在evaluate()时从传入dict中取出prediction数据。 为None,则使用 `pred` 取数据 | |||
:param str target: 用该key在evaluate()时从传入dict中取出target数据。 为None,则使用 `target` 取数据 | |||
:param str seq_len: 用该key在evaluate()时从传入dict中取出sequence length数据。为None,则使用 `seq_len` 取数据。 | |||
:param str encoding_type: 目前支持bio, bmes, bmeso, bioes。默认为None,通过tag_vocab自动判断. | |||
:param list ignore_labels: str 组成的list. 这个list中的class不会被用于计算。例如在POS tagging时传入['NN'],则不会计算'NN'个label | |||
:param bool only_gross: 是否只计算总的f1, precision, recall的值;如果为False,不仅返回总的f1, pre, rec, 还会返回每个label的f1, pre, rec | |||
:param str f_type: `micro` 或 `macro` . `micro` :通过先计算总体的TP,FN和FP的数量,再计算f, precision, recall; `macro` : 分布计算每个类别的f, precision, recall,然后做平均(各类别f的权重相同) | |||
:param float beta: f_beta分数, :math:`f_{beta} = \frac{(1 + {beta}^{2})*(pre*rec)}{({beta}^{2}*pre + rec)}` . 常用为 `beta=0.5, 1, 2` 若为0.5则精确率的权重高于召回率;若为1,则两者平等;若为2,则召回率权重高于精确率。 | |||
:param str backend: 目前支持四种类型的backend, ['auto', 'torch', 'paddle', 'jittor']。其中 auto 表示根据实际调用 Metric.update() | |||
函数时传入的参数决定具体的 backend ,一般情况下直接使用 'auto' 即可。 | |||
:param bool aggregate_when_get_metric: 在计算 metric 的时候是否自动将各个进程上的相同的 element 的数字聚合后再得到metric, | |||
当 backend 不支持分布式时,该参数无意义。如果为 None ,将在 Evaluator 中根据 sampler 是否使用分布式进行自动设置。 | |||
""" | |||
super(SpanFPreRecMetric, self).__init__(backend=backend, aggregate_when_get_metric=aggregate_when_get_metric) | |||
if f_type not in ('micro', 'macro'): | |||
raise ValueError("f_type only supports `micro` or `macro`', got {}.".format(f_type)) | |||
@@ -10,7 +10,7 @@ def conversion_between_reproducible_and_unrepeated_sampler(sampler): | |||
将 sampler 替换成其对应的 reproducible 版本或 unrepeated 版本。如果输入是 UnrepeatedSampler 但是没找到对应的 | |||
ReproducibleSampler, | |||
:param sampler: | |||
:param sampler: 需要转换的 sampler 。 | |||
:return: | |||
""" | |||
assert isinstance(sampler, UnrepeatedSampler) or isinstance(sampler, ReproducibleSampler), \ | |||
@@ -55,16 +55,16 @@ class ReproducibleBatchSampler: | |||
class ReproduceBatchSampler(ReproducibleBatchSampler): | |||
""" | |||
可以使得 batch_sampler 对象状态恢复的 wrapper 。 | |||
:param batch_sampler: 可迭代出 数字 或 数字列表 的可迭代对象。ReproduceBatchSampler 将首先遍历一边该对象,然后将迭代 | |||
出来的序号暂存起来,使用时按照 batch_size 的 batch 大小吐出序号列表。 | |||
:param batch_size: 每个 batch 的大小是多少。 | |||
:param drop_last: 如果最后一个 batch 无法构成 batch_size 那么多个 sample ,是否丢掉。 | |||
:param kwargs: fastNLP 内部使用。 | |||
""" | |||
def __init__(self, batch_sampler, batch_size: int, drop_last: bool, **kwargs): | |||
""" | |||
可以使得 batch_sampler 对象状态恢复的 wrapper 。 | |||
:param batch_sampler: 可迭代出 数字 或 数字列表 的可迭代对象。ReproduceBatchSampler 将首先遍历一边该对象,然后将迭代 | |||
出来的序号暂存起来,使用时按照 batch_size 的 batch 大小吐出序号列表。 | |||
:param batch_size: 每个 batch 的大小是多少。 | |||
:param drop_last: 如果最后一个 batch 无法构成 batch_size 那么多个 sample ,是否丢掉。 | |||
:param kwargs: fastNLP 内部使用。 | |||
""" | |||
super().__init__() | |||
self.batch_sampler = batch_sampler | |||
@@ -158,18 +158,18 @@ class ReproduceBatchSampler(ReproducibleBatchSampler): | |||
class RandomBatchSampler(ReproducibleBatchSampler): | |||
""" | |||
随机分 batch 的 batch_sampler 。 | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param batch_size: 每个 batch 的大小 | |||
:param shuffle: 如果为 True,将不进行 shuffle,实际上数据会以从长到短的方式输出。 | |||
:param drop_last: 如果最后一个 batch 的 sample 数量无法凑齐 batch_size 这么多,是否需要丢掉。 | |||
:param seed: 设置的随机数种子 | |||
:param kwargs: fastNLP 保留使用 | |||
""" | |||
def __init__(self, dataset, batch_size:int = 32, shuffle: bool = True, | |||
drop_last: bool = False, seed: int = 0, **kwargs): | |||
""" | |||
随机分 batch 的 batch_sampler 。 | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param batch_size: 每个 batch 的大小 | |||
:param shuffle: 如果为 True,将不进行 shuffle,实际上数据会以从长到短的方式输出。 | |||
:param drop_last: 如果最后一个 batch 的 sample 数量无法凑齐 batch_size 这么多,是否需要丢掉。 | |||
:param seed: 设置的随机数种子 | |||
:param kwargs: fastNLP 保留使用 | |||
""" | |||
super().__init__() | |||
self.dataset = dataset | |||
@@ -363,28 +363,28 @@ class RandomBatchSampler(ReproducibleBatchSampler): | |||
class BucketedBatchSampler(ReproducibleBatchSampler): | |||
""" | |||
首先按照 ``sample`` 的长度排序,然后按照 batch_size*num_batch_per_bucket 为一个桶的大小,``sample`` 只会在这个桶内进行组 | |||
合,这样每个 ``batch`` 中的 ``padding`` 数量会比较少 (因为桶内的数据的长度都接近)。 | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param length: 每条数据的长度。 | |||
* 为 ``List[int]`` 时 | |||
应当与 dataset 有一样的长度,表示 dataset 中每个元素的数量; | |||
* 为 ``str`` 时 | |||
仅当传入的 ``dataset`` 是 :class:`fastNLP.DataSet` 时,允许传入 `str` ,该 `str` 将被认为是 ``dataset`` 中的 | |||
``field`` 。若 field 中的元素为 ``int``,则认为该值是 sample 的长度;若不为 ``int`` ,则尝试使用 ``len`` 方法 | |||
获取该 ``field`` 中每个元素的长度。 | |||
:param batch_size: 每个 batch 的大小 | |||
:param num_batch_per_bucket: 多少个 ``batch`` 组成一个桶,数据只会在一个桶内进行 ``shuffle`` 。 | |||
:param shuffle: 如果为 True,将不进行 ``shuffle``,实际上数据会以从长到短的方式输出。 | |||
:param drop_last: 如果最后一个 `batch` 的 ``sample`` 数量无法凑齐 ``batch_size`` 这么多,是否需要丢掉。 | |||
:param seed: 设置的随机数种子 | |||
:param kwargs: fastNLP 保留使用 | |||
""" | |||
def __init__(self, dataset, length: Union[List[int], str], batch_size:int = 32, num_batch_per_bucket:int = 10, | |||
shuffle: bool = True, drop_last: bool = False, seed: int = 0, **kwargs): | |||
""" | |||
首先按照 ``sample`` 的长度排序,然后按照 batch_size*num_batch_per_bucket 为一个桶的大小,``sample`` 只会在这个桶内进行组 | |||
合,这样每个 ``batch`` 中的 ``padding`` 数量会比较少 (因为桶内的数据的长度都接近)。 | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param length: 每条数据的长度。 | |||
* 为 ``List[int]`` 时 | |||
应当与 dataset 有一样的长度,表示 dataset 中每个元素的数量; | |||
* 为 ``str`` 时 | |||
仅当传入的 ``dataset`` 是 :class:`fastNLP.DataSet` 时,允许传入 `str` ,该 `str` 将被认为是 ``dataset`` 中的 | |||
``field`` 。若 field 中的元素为 ``int``,则认为该值是 sample 的长度;若不为 ``int`` ,则尝试使用 ``len`` 方法 | |||
获取该 ``field`` 中每个元素的长度。 | |||
:param batch_size: 每个 batch 的大小 | |||
:param num_batch_per_bucket: 多少个 ``batch`` 组成一个桶,数据只会在一个桶内进行 ``shuffle`` 。 | |||
:param shuffle: 如果为 True,将不进行 ``shuffle``,实际上数据会以从长到短的方式输出。 | |||
:param drop_last: 如果最后一个 `batch` 的 ``sample`` 数量无法凑齐 ``batch_size`` 这么多,是否需要丢掉。 | |||
:param seed: 设置的随机数种子 | |||
:param kwargs: fastNLP 保留使用 | |||
""" | |||
super().__init__() | |||
if isinstance(dataset, DataSet) and isinstance(length, str): | |||
length = dataset.get_field(length).content | |||
@@ -53,15 +53,15 @@ class ReproducibleSampler: | |||
class RandomSampler(ReproducibleSampler): | |||
def __init__(self, dataset, shuffle: bool = True, seed: int = 0, **kwargs): | |||
""" | |||
随机顺序的 Sampler 。 | |||
""" | |||
随机顺序的 Sampler 。 | |||
:param dataset: 实现了 __len__ 方法的数据容器 | |||
:param shuffle: 是否在每次 iterate 的时候打乱顺序。 | |||
:param seed: 随机数种子。 | |||
:param kwargs: 用户不需要使用,fastNLP 内部使用 | |||
""" | |||
:param dataset: 实现了 __len__ 方法的数据容器 | |||
:param shuffle: 是否在每次 iterate 的时候打乱顺序。 | |||
:param seed: 随机数种子。 | |||
:param kwargs: 用户不需要使用,fastNLP 内部使用 | |||
""" | |||
def __init__(self, dataset, shuffle: bool = True, seed: int = 0, **kwargs): | |||
super(RandomSampler, self).__init__() | |||
self.dataset = dataset | |||
self.shuffle = shuffle | |||
@@ -213,13 +213,13 @@ class RandomSampler(ReproducibleSampler): | |||
class SequentialSampler(RandomSampler): | |||
def __init__(self, dataset, **kwargs): | |||
""" | |||
按照顺序读取 ``dataset`` 。在多卡情况下,间隔读取,例如,在两卡情况下,卡 0 取 ``[0,2,4,..]``, 卡1取 ``[1,3,5...]`` 。 | |||
""" | |||
按照顺序读取 ``dataset`` 。在多卡情况下,间隔读取,例如,在两卡情况下,卡 0 取 ``[0,2,4,..]``, 卡1取 ``[1,3,5...]`` 。 | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param kwargs: | |||
""" | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param kwargs: | |||
""" | |||
def __init__(self, dataset, **kwargs): | |||
super().__init__(dataset=dataset, **kwargs) | |||
def __iter__(self): | |||
@@ -283,23 +283,23 @@ class SequentialSampler(RandomSampler): | |||
class SortedSampler(SequentialSampler): | |||
""" | |||
将 ``dataset`` 中的数据根据 ``length`` 从长到短进行迭代。在多卡情况下,由于 ``padding`` , 最后一个 ``sample`` 可能是最长 | |||
的那个 ``sample`` 。 | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param length: 每条数据的长度。 | |||
* 为 ``List[int]`` 时 | |||
应当与 dataset 有一样的长度,表示 dataset 中每个元素的数量; | |||
* 为 ``str`` 时 | |||
仅当传入的 ``dataset`` 是 :class:`fastNLP.DataSet` 时,允许传入 `str` ,该 `str` 将被认为是 ``dataset`` 中的 | |||
``field`` 。若 field 中的元素为 ``int``,则认为该值是 sample 的长度;若不为 ``int`` ,则尝试使用 ``len`` 方法 | |||
获取该 ``field`` 中每个元素的长度。 | |||
:param seed: 设置的随机数种子。 | |||
:param kwargs: fastNLP 保留使用。 | |||
""" | |||
def __init__(self, dataset, length:Union[str, List], **kwargs): | |||
""" | |||
将 ``dataset`` 中的数据根据 ``length`` 从长到短进行迭代。在多卡情况下,由于 ``padding`` , 最后一个 ``sample`` 可能是最长 | |||
的那个 ``sample`` 。 | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param length: 每条数据的长度。 | |||
* 为 ``List[int]`` 时 | |||
应当与 dataset 有一样的长度,表示 dataset 中每个元素的数量; | |||
* 为 ``str`` 时 | |||
仅当传入的 ``dataset`` 是 :class:`fastNLP.DataSet` 时,允许传入 `str` ,该 `str` 将被认为是 ``dataset`` 中的 | |||
``field`` 。若 field 中的元素为 ``int``,则认为该值是 sample 的长度;若不为 ``int`` ,则尝试使用 ``len`` 方法 | |||
获取该 ``field`` 中每个元素的长度。 | |||
:param seed: 设置的随机数种子。 | |||
:param kwargs: fastNLP 保留使用。 | |||
""" | |||
super().__init__(dataset=dataset, **kwargs) | |||
if isinstance(dataset, DataSet) and isinstance(length, str): | |||
length = dataset.get_field(length).content | |||
@@ -19,15 +19,15 @@ class UnrepeatedSampler: | |||
class UnrepeatedRandomSampler(UnrepeatedSampler): | |||
def __init__(self, dataset, shuffle: bool = False, seed: int = 0, **kwargs): | |||
""" | |||
考虑在多卡evaluate的场景下,不能重复sample。 | |||
""" | |||
考虑在多卡 evaluate 的场景下,不能重复 sample。 | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param shuffle: 如果为 True,将不进行 shuffle,实际上数据会以从长到短的方式输出。 | |||
:param seed: 设置的随机数种子 | |||
:param kwargs: fastNLP 保留使用 | |||
""" | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param shuffle: 如果为 True,将不进行 shuffle,实际上数据会以从长到短的方式输出。 | |||
:param seed: 设置的随机数种子 | |||
:param kwargs: fastNLP 保留使用 | |||
""" | |||
def __init__(self, dataset, shuffle: bool = False, seed: int = 0, **kwargs): | |||
self.dataset = dataset | |||
self.shuffle = shuffle | |||
self.seed = seed | |||
@@ -96,16 +96,22 @@ class UnrepeatedRandomSampler(UnrepeatedSampler): | |||
class UnrepeatedSortedSampler(UnrepeatedRandomSampler): | |||
""" | |||
将 dataset 中的数据根据 length 从长到短进行迭代,并且保证在多卡场景下数据不重复。本 sampler 可能导致各个机器上的 | |||
batch 数量不完全一致。 | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param length: 每条数据的长度。 | |||
* 为 ``List[int]`` 时 | |||
应当与 dataset 有一样的长度,表示 dataset 中每个元素的数量; | |||
* 为 ``str`` 时 | |||
仅当传入的 ``dataset`` 是 :class:`fastNLP.DataSet` 时,允许传入 `str` ,该 `str` 将被认为是 ``dataset`` 中的 | |||
``field`` 。若 field 中的元素为 ``int``,则认为该值是 sample 的长度;若不为 ``int`` ,则尝试使用 ``len`` 方法 | |||
获取该 ``field`` 中每个元素的长度。 | |||
:param kwargs: fastNLP 保留使用 | |||
""" | |||
def __init__(self, dataset, length:Union[str, List], **kwargs): | |||
""" | |||
将 dataset 中的数据根据 length 从长到短进行迭代,并且保证在多卡场景下数据不重复。本 sampler 可能导致各个机器上的 | |||
batch 数量不完全一致。 | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param length: 如果为 List,应当与 dataset 有一样的长度,表示 dataset 中每个元素的数量;仅当传入的 dataset 为 fastNLP 的 | |||
DataSet 时支持传入 str,会将该str理解为 dataset 的 field 名称,若 field 中的元素为 int,则认为该值是 sample 的长度。 | |||
:param kwargs: fastNLP 保留使用 | |||
""" | |||
super().__init__(dataset=dataset, shuffle=False, seed=0, **kwargs) | |||
if isinstance(dataset, DataSet) and isinstance(length, str): | |||
length = dataset.get_field(length).content | |||
@@ -125,13 +131,13 @@ class UnrepeatedSortedSampler(UnrepeatedRandomSampler): | |||
class UnrepeatedSequentialSampler(UnrepeatedRandomSampler): | |||
def __init__(self, dataset, **kwargs): | |||
""" | |||
按照顺序读取 dataset。在多卡情况下,间隔读取,例如,在两卡情况下,卡0取 [0,2,4,..], 卡1取 [1,3,5...]。 | |||
""" | |||
按照顺序读取 dataset。在多卡情况下,间隔读取,例如,在两卡情况下,卡0取 [0,2,4,..], 卡1取 [1,3,5...]。 | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param kwargs: | |||
""" | |||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||
:param kwargs: | |||
""" | |||
def __init__(self, dataset, **kwargs): | |||
super(UnrepeatedSequentialSampler, self).__init__(dataset, shuffle=False, seed=0, **kwargs) | |||
def __iter__(self): | |||
@@ -44,11 +44,6 @@ class DummyFRichProgress: | |||
return True | |||
class FRichProgress(Progress, metaclass=Singleton): | |||
""" | |||
fastNLP 使用的 progress bar ,新增了 new_progress 函数,通过此函数即可定制 fastNLP 中所有 progress 的样式。 | |||
""" | |||
def new_progess(self, *columns: Union[str, ProgressColumn], | |||
console: Optional[Console] = None, | |||
auto_refresh: bool = True, | |||
@@ -20,13 +20,17 @@ def is_cur_env_distributed() -> bool: | |||
""" | |||
单卡模式该函数一定返回 False; | |||
注意进程 0 在多卡的训练模式下前后的值是不一样的,例如在开启多卡的 driver 之前,在进程 0 上的该函数返回 False;但是在开启后,在进程 0 上 | |||
的该函数返回的值是 True; | |||
多卡模式下除了进程 0 外的其它进程返回的值一定是 True; | |||
的该函数返回的值是 True;多卡模式下除了进程 0 外的其它进程返回的值一定是 True; | |||
""" | |||
return FASTNLP_GLOBAL_RANK in os.environ | |||
def get_global_rank(): | |||
def get_global_rank()->int: | |||
""" | |||
获取当前进程的 global_rank 。 | |||
:return: | |||
""" | |||
return int(os.environ.get(FASTNLP_GLOBAL_RANK, 0)) | |||
@@ -64,7 +68,7 @@ def rank_zero_call(fn: Callable): | |||
@contextmanager | |||
def fastnlp_no_sync_context(level=2): | |||
""" | |||
用于让 fastNLP 的 barrier 以及 gather/broadcast等操作等同于只有1卡的多卡程序。如果为 1 表示 fastNLP 里的barrier 操作失效; | |||
用于让 fastNLP 的 barrier 以及 gather/broadcast等操作等同于只有 1 卡的多卡程序。如果为 1 表示 fastNLP 里的barrier 操作失效; | |||
如果为 2 表示 barrier 与 gather/broadcast 都失效。 | |||
:param int level: 可选 [0, 1, 2] | |||