@@ -57,7 +57,7 @@ class Trainer(TrainerEventTrigger): | |||||
:param driver: 训练模型所使用的具体的驱动模式,应当为以下选择中的一个:``["auto", "torch", "paddle", "jittor", "fairscale", "deepspeed", "oneflow"]``: | :param driver: 训练模型所使用的具体的驱动模式,应当为以下选择中的一个:``["auto", "torch", "paddle", "jittor", "fairscale", "deepspeed", "oneflow"]``: | ||||
1. 值为 ``"auto"`` 时,**FastNLP** 会根据传入模型的类型自行判断使用哪一种模式; | |||||
1. 值为 ``"auto"`` 时,**fastNLP** 会根据传入模型的类型自行判断使用哪一种模式; | |||||
2. 其值为 ``"torch"`` 时,表示使用 :class:`~fastNLP.core.drivers.TorchSingleDriver` 或者 :class:`~fastNLP.core.drivers.TorchDDPDriver`; | 2. 其值为 ``"torch"`` 时,表示使用 :class:`~fastNLP.core.drivers.TorchSingleDriver` 或者 :class:`~fastNLP.core.drivers.TorchDDPDriver`; | ||||
3. 其值为 ``"paddle"`` 时,表示使用 :class:`~fastNLP.core.drivers.PaddleSingleDriver` 或者 :class:`~fastNLP.core.drivers.PaddleFleetDriver`; | 3. 其值为 ``"paddle"`` 时,表示使用 :class:`~fastNLP.core.drivers.PaddleSingleDriver` 或者 :class:`~fastNLP.core.drivers.PaddleFleetDriver`; | ||||
4. 其值为 ``"jittor"`` 时,表示使用 :class:`~fastNLP.core.drivers.JittorSingleDriver` 或者 :class:`~fastNLP.core.drivers.JittorMPIDriver`; | 4. 其值为 ``"jittor"`` 时,表示使用 :class:`~fastNLP.core.drivers.JittorSingleDriver` 或者 :class:`~fastNLP.core.drivers.JittorMPIDriver`; | ||||
@@ -99,35 +99,24 @@ class _MixCollateFn: | |||||
class MixDataLoader(DataLoader): | class MixDataLoader(DataLoader): | ||||
""" | """ | ||||
针对以下四种情况提供的 ``MixDataLoader``, 目前只支持 ``torch`` 框架的版本, 其中 mode 的取值范围为 ``['sequential', 'mix', 'polling', "Sampler"]``: | |||||
针对以下四种情况提供的 ``MixDataLoader``, 目前只支持 **pytorch** 框架的版本, 其中 mode 的取值范围为 ``['sequential', 'mix', 'polling', 'Sampler']``: | |||||
* 当 mode 为 ``'sequential'`` 时,``MixDataLoader`` 将 ``datasets`` 的序列或者字典视为一个混合大数据集, 按照 datasets 数据集序列或者字典的顺序一个 | * 当 mode 为 ``'sequential'`` 时,``MixDataLoader`` 将 ``datasets`` 的序列或者字典视为一个混合大数据集, 按照 datasets 数据集序列或者字典的顺序一个 | ||||
接一个的 sample 完所有数据。 | 接一个的 sample 完所有数据。 | ||||
* 当 mode 为 ``'mix'`` 时, ``MixDataLoader`` 将 ``datasets`` 的序列或者字典视为一个混合大数据集, 然后根据用户输入的 idx 序列随机 sample | * 当 mode 为 ``'mix'`` 时, ``MixDataLoader`` 将 ``datasets`` 的序列或者字典视为一个混合大数据集, 然后根据用户输入的 idx 序列随机 sample | ||||
混合数据集 datasets 的数据组成一个 batch 序列返回。 | 混合数据集 datasets 的数据组成一个 batch 序列返回。 | ||||
* 当 mode 为 ``'polling'`` 时, ``MixDataLoader`` 按照 ``datasets`` 数据集的顺序, 先从第一个数据集采样一个 batch 的数据返回, | * 当 mode 为 ``'polling'`` 时, ``MixDataLoader`` 按照 ``datasets`` 数据集的顺序, 先从第一个数据集采样一个 batch 的数据返回, | ||||
再从第二数据集采样一个 batch 数据返回, 直至最后一个数据集采样一个 batch 数据返回后再从第一个数据采样第二个 batch 数据返回,直至所有的数据集都被轮询的采样完。 | |||||
* 当 mode 为 ``"Sampler"`` 时, 该 Sampler 是实现 __iter__() 的实例化对象, 其功能是每次 iter 时返回一个 batch 序列, 其类型为 List[int]; | |||||
再从第二数据集采样一个 batch 数据返回, 直至最后一个数据集采样一个 batch 数据返回后再从第一个数据采样第二个 batch 数据返回,直至所有的数据集都被轮询地的采样完。 | |||||
* 当 mode 为 ``'Sampler'`` 时, 该 Sampler 是实现 __iter__() 的实例化对象, 其功能是每次 iter 时返回一个 batch 序列, 其类型为 List[int]; | |||||
且 Sampler 必须将输入的 datasets 视为一个混合大数据集, 其 index 范围为 ``0<idx<len(datasets[0])+...+len(datasets[x])``, 然后参数 | 且 Sampler 必须将输入的 datasets 视为一个混合大数据集, 其 index 范围为 ``0<idx<len(datasets[0])+...+len(datasets[x])``, 然后参数 | ||||
``sampler``, ``drop_last``, ``ds_ratio`` 均无效。 | ``sampler``, ``drop_last``, ``ds_ratio`` 均无效。 | ||||
:param datasets: 实现了 __getitem__() 和 __len__() 对象的序列或者字典。 | :param datasets: 实现了 __getitem__() 和 __len__() 对象的序列或者字典。 | ||||
:param mode: mode 控制 ``MixDataLoader`` 运行模式。 mode 的取值范围为 ``['sequential', 'mix', 'polling', "Sampler"]``: | |||||
* 当 mode 为 ``'sequential'`` 时,``MixDataLoader`` 将 datasets 的序列或者字典视为一个混合大数据集, 按照 datasets 数据集序列或者字典的顺序一个 | |||||
接一个的 sample 完所有数据。 | |||||
* 当 mode 为 ``'mix'`` 时, ``MixDataLoader`` 将 datasets 的序列或者字典视为一个混合大数据集, 然后根据用户输入的 idx 序列随机sample | |||||
混合数据集 datasets 的数据组成一个 batch 序列返回。 | |||||
* 当 mode 为 ``'polling'`` 时, ``MixDataLoader`` 按照 datasets 数据集的顺序, 先从第一个数据集采样一个 batch 的数据返回, | |||||
再从第二数据集采样一个 batch 数据返回, 直至最后一个数据集采样一个 batch 数据返回后再从第一个数据采样第二个 batch 数据返回,直至所有的数据集都被轮询的采样完。 | |||||
* 当 mode 为 ``"Sampler"`` 时, 该 Sampler 是实现 __iter__() 的实例化对象, 其功能是每次 iter 时返回一个 batch 序列, 其类型为 List[int]; | |||||
且 Sampler 必须将输入的 datasets 视为一个混合大数据集, 其 index 范围为 ``0<idx<len(datasets[0])+...+len(datasets[x])``, 然后参数 | |||||
sampler, drop_last, ds_ratio 均无效。 | |||||
:param mode: ``mode`` 控制 ``MixDataLoader`` 运行模式。 ``mode`` 的取值范围为 ``['sequential', 'mix', 'polling', 'Sampler']``,每种模式的详细功能见上文。 | |||||
:param collate_fn: 用于从 dataset 取到的一个 batch 数据进行打包处理的 Callable 函数。 其取值可以为 ``['auto', Callable, List[Callable], Dict[str, Callable]]``: | :param collate_fn: 用于从 dataset 取到的一个 batch 数据进行打包处理的 Callable 函数。 其取值可以为 ``['auto', Callable, List[Callable], Dict[str, Callable]]``: | ||||
* collate_fn 为 ``'auto'`` 时, ``MixDataLoader`` datasets 序列或者dict 初始化一个 :class:`~fastNLP.core.collators.Collator` 作为其默认值, | * collate_fn 为 ``'auto'`` 时, ``MixDataLoader`` datasets 序列或者dict 初始化一个 :class:`~fastNLP.core.collators.Collator` 作为其默认值, | ||||
需要注意的是只有当 datasets 包含的所以 dataset 的数据都为 ``List`` 或者 ``Dict`` 类型时才能使用。否则只能用户自己定义 collate_fn . | |||||
需要注意的是只有当 datasets 包含的所以 dataset 的数据都为 ``List`` 或者 ``Dict`` 类型时才能使用。否则只能用户自己定义 collate_fn 。 | |||||
* collate_fn 为 :class:`Callable` 时, 该 collate_fn 会被 datasets 序列或者dict 的所有数据所共享。该 Callable 函数应当接受一个 batch 参数作为输入, | * collate_fn 为 :class:`Callable` 时, 该 collate_fn 会被 datasets 序列或者dict 的所有数据所共享。该 Callable 函数应当接受一个 batch 参数作为输入, | ||||
batch 是一个 List 对象且 List 中的每一条数据都是 dataset 的一条数据;该 Callable 函数还应当返回一个对象。 | batch 是一个 List 对象且 List 中的每一条数据都是 dataset 的一条数据;该 Callable 函数还应当返回一个对象。 | ||||
* collate_fn 为 ``Dict[str, Callable]`` 时, datasets 的 key 必须和 callable_fn 的 key 一致。 ``MixDataLoader`` 会将 ``collate_fn[key]`` | * collate_fn 为 ``Dict[str, Callable]`` 时, datasets 的 key 必须和 callable_fn 的 key 一致。 ``MixDataLoader`` 会将 ``collate_fn[key]`` | ||||
@@ -135,12 +124,12 @@ class MixDataLoader(DataLoader): | |||||
:param sampler: 实现了 __len__() 和 __iter__() 的实例化对象,其 __iter__() 方法每次都会返回 dataset 的一个下标 index ,其取值范围为 | :param sampler: 实现了 __len__() 和 __iter__() 的实例化对象,其 __iter__() 方法每次都会返回 dataset 的一个下标 index ,其取值范围为 | ||||
``[None, str, Dict[str, "Sampler"]]``: | |||||
``[None, str, Dict[str, Sampler]]``: | |||||
* sampler 为 ``None`` 时, ``MixDataLoader`` 默认初始化 ``torch`` 的 ``SequentialSampler`` 作为默认值。其功能时顺序返回 dataset 的下标。 | |||||
* sampler 为 ``str`` 时, sampler 选择范围为 ``[rand, seq]``。当 sampler 为 ``rand`` 时,``MixDataLoader`` 默认初始化 ``torch`` 的 ``RandomSampler`` | |||||
作为默认值, 其功能时随机采样 dataset 的下标并返回。 当 sampler 为 ``seq`` 时, ``MixDataLoader`` 默认初始化 ``torch`` 的 ``SequentialSampler`` 作为默认值。其功能时顺序返回 dataset 的下标。 | |||||
* sampler 为 ``Dict[str, "Sampler"]`` 时, ``Sampler`` 为用户定义的实现了 __len__() 和 __iter__() 的实例化对象。 其每次 iter 必须返回一个 int 下标。 | |||||
* sampler 为 ``None`` 时, ``MixDataLoader`` 默认初始化 **pytorch** 的 ``SequentialSampler`` 作为默认值。其功能时顺序返回 dataset 的下标。 | |||||
* sampler 为 ``str`` 时, sampler 选择范围为 ``[rand, seq]``。当 sampler 为 ``rand`` 时,``MixDataLoader`` 默认初始化 **pytorch** 的 ``RandomSampler`` | |||||
作为默认值, 其功能时随机采样 dataset 的下标并返回。 当 sampler 为 ``seq`` 时, ``MixDataLoader`` 默认初始化 **pytorch** 的 ``SequentialSampler`` 作为默认值。其功能时顺序返回 dataset 的下标。 | |||||
* sampler 为 ``Dict[str, Sampler]`` 时, ``Sampler`` 为用户定义的实现了 __len__() 和 __iter__() 的实例化对象。 其每次 iter 必须返回一个 int 下标。 | |||||
Dict 的 str 必须和 datasets 的 key 一致。 也即是 ``Dict[str, Sampler]`` 为 datasets 字典的每个 dataset 初始化了一个 Sampler。 | Dict 的 str 必须和 datasets 的 key 一致。 也即是 ``Dict[str, Sampler]`` 为 datasets 字典的每个 dataset 初始化了一个 Sampler。 | ||||
:param num_workers: 当 ``num_workers > 0`` 时, ``MixDataLoader`` 会开启 ``num_workers`` 个子进程来处理数据, 可以加快数据处理速度,但同时 | :param num_workers: 当 ``num_workers > 0`` 时, ``MixDataLoader`` 会开启 ``num_workers`` 个子进程来处理数据, 可以加快数据处理速度,但同时 | ||||
@@ -7,6 +7,7 @@ try: | |||||
except ImportError: | except ImportError: | ||||
tqdm = None | tqdm = None | ||||
__all__ = [] | |||||
if tqdm is not None: | if tqdm is not None: | ||||
class TqdmLoggingHandler(logging.Handler): | class TqdmLoggingHandler(logging.Handler): | ||||
@@ -1,6 +1,6 @@ | |||||
from rich.highlighter import Highlighter | from rich.highlighter import Highlighter | ||||
__all__ = [] | |||||
class ColorHighlighter(Highlighter): | class ColorHighlighter(Highlighter): | ||||
def __init__(self, color='black'): | def __init__(self, color='black'): | ||||
self.color = color | self.color = color | ||||
@@ -1,20 +1,24 @@ | |||||
r""" | r""" | ||||
Logger 是fastNLP中记录日志的模块,logger封装了logging模块的Logger, | |||||
具体使用方式与直接使用logging.Logger相同,同时也新增一些简单好用的API | |||||
:class:`Logger` 是 **fastNLP** 中记录日志的模块,**logger** 封装了 logging 模块的 Logger, | |||||
具体使用方式与直接使用 :class:`logging.Logger` 相同,同时也新增一些简单好用的API | |||||
使用方式:: | 使用方式:: | ||||
from fastNLP import _logger | |||||
# | |||||
# _logger 可以和 logging.Logger 一样使用 | |||||
_logger.info('your msg') | |||||
_logger.error('your msg') | |||||
from fastNLP import logger | |||||
# _logger 新增的API | |||||
# logger 可以和 logging.Logger 一样使用 | |||||
logger.info('your msg') | |||||
logger.error('your msg') | |||||
# logger 新增的API | |||||
# 将日志输出到文件,以及输出的日志等级 | # 将日志输出到文件,以及输出的日志等级 | ||||
_logger.add_file('/path/to/log', level='INFO') | |||||
logger.add_file('/path/to/log', level='INFO') | |||||
# 定义在命令行中的显示格式和日志等级 | # 定义在命令行中的显示格式和日志等级 | ||||
_logger.set_stdout('tqdm', level='WARN') | |||||
logger.set_stdout('tqdm', level='WARN') | |||||
# 仅警告一次 | |||||
logger.warning_once('your msg') | |||||
# 分布式训练下,仅在 rank 0 输出警告 | |||||
logger.rank_zero_warning('your msg') | |||||
""" | """ | ||||
@@ -16,7 +16,7 @@ def print(*args, sep=' ', end='\n', file=None, flush=False): | |||||
:param args: 需要打印的内容 | :param args: 需要打印的内容 | ||||
:param sep: 存在多个输入时,使用的间隔。 | :param sep: 存在多个输入时,使用的间隔。 | ||||
:param end: 该参数在当前设置无意义,因为结尾一定会被加入 '\\\\n' 。 | |||||
:param end: 该参数在当前设置无意义,因为结尾一定会被加入 ``'\\\\n'`` 。 | |||||
:param file: 该参数无意义。 | :param file: 该参数无意义。 | ||||
:param flush: 该参数无意义。 | :param flush: 该参数无意义。 | ||||
:return: | :return: | ||||
@@ -14,24 +14,25 @@ from fastNLP.core.log import logger | |||||
class Accuracy(Metric): | class Accuracy(Metric): | ||||
def __init__(self, backend: Union[str, Backend, None] = 'auto', aggregate_when_get_metric: bool = None): | |||||
""" | |||||
计算 准确率 的 metric 。 | |||||
""" | |||||
计算 准确率 的 metric 。 | |||||
:param backend: 目前支持四种类型的backend, ['auto', 'torch', 'paddle', 'jittor']。其中 auto 表示根据实际调用 Metric.update() | |||||
函数时传入的参数决定具体的 backend ,一般情况下直接使用 'auto' 即可。 | |||||
:param aggregate_when_get_metric: 在计算 metric 的时候是否自动将各个进程上的相同的 element 的数字聚合后再得到 metric, | |||||
当 backend 不支持分布式时,该参数无意义。如果为 None ,将在 Evaluator 中根据 sampler 是否使用分布式进行自动设置。 | |||||
""" | |||||
:param backend: 目前支持五种类型的backend, ``['auto', 'torch', 'paddle', 'jittor', 'oneflow']``。其中 ``'auto'`` 表示根据实际调用 | |||||
:meth:`update` 函数时传入的参数决定具体的 backend ,一般情况下直接使用 ``'auto'`` 即可。 | |||||
:param aggregate_when_get_metric: 在计算 metric 的时候是否自动将各个进程上的相同的 element 的数字聚合后再得到 metric, | |||||
当 ``backend`` 不支持分布式时,该参数无意义。如果为 ``None`` ,将在 :class:`~fastNLP.core.controllers.Evaluator` | |||||
中根据 ``sampler`` 是否使用分布式进行自动设置。 | |||||
""" | |||||
def __init__(self, backend: Union[str, Backend, None] = 'auto', aggregate_when_get_metric: bool = None): | |||||
super(Accuracy, self).__init__(backend=backend, aggregate_when_get_metric=aggregate_when_get_metric) | super(Accuracy, self).__init__(backend=backend, aggregate_when_get_metric=aggregate_when_get_metric) | ||||
self.register_element(name='correct', value=0, aggregate_method='sum', backend=backend) | self.register_element(name='correct', value=0, aggregate_method='sum', backend=backend) | ||||
self.register_element(name='total', value=0, aggregate_method="sum", backend=backend) | self.register_element(name='total', value=0, aggregate_method="sum", backend=backend) | ||||
def get_metric(self) -> dict: | def get_metric(self) -> dict: | ||||
r""" | r""" | ||||
get_metric 函数将根据 update 函数累计的评价指标统计量来计算最终的评价结果. | |||||
:meth:`get_metric` 函数将根据 :meth:`update` 函数累计的评价指标统计量来计算最终的评价结果。 | |||||
:return dict evaluate_result: {"acc": float, 'total': float, 'correct': float} | |||||
:return: 包含以下内容的字典:``{"acc": float, 'total': float, 'correct': float}``; | |||||
""" | """ | ||||
evaluate_result = {'acc': round(self.correct.get_scalar() / (self.total.get_scalar() + 1e-12), 6), | evaluate_result = {'acc': round(self.correct.get_scalar() / (self.total.get_scalar() + 1e-12), 6), | ||||
'total': self.total.item(), 'correct': self.correct.item()} | 'total': self.total.item(), 'correct': self.correct.item()} | ||||
@@ -39,14 +40,14 @@ class Accuracy(Metric): | |||||
def update(self, pred, target, seq_len=None): | def update(self, pred, target, seq_len=None): | ||||
r""" | r""" | ||||
update 函数将针对一个批次的预测结果做评价指标的累计 | |||||
:param pred: 预测的tensor, tensor的形状可以是torch.Size([B,]), torch.Size([B, n_classes]), | |||||
torch.Size([B, max_len]), 或者torch.Size([B, max_len, n_classes]) | |||||
:param target: 真实值的tensor, tensor的形状可以是Element's can be: torch.Size([B,]), | |||||
torch.Size([B,]), torch.Size([B, max_len]), 或者torch.Size([B, max_len]) | |||||
:param seq_len: 序列长度标记, 标记的形状可以是None, None, torch.Size([B]), 或者torch.Size([B]). | |||||
如果mask也被传进来的话seq_len会被忽略. | |||||
:meth:`update` 函数将针对一个批次的预测结果做评价指标的累计。 | |||||
:param pred: 预测的 tensor, tensor 的形状可以是 ``[B,]`` 、``[B, n_classes]`` 、 | |||||
``[B, max_len]`` 或 ``[B, max_len, n_classes]`` | |||||
:param target: 真实值的 tensor, tensor 的形状可以是 ``[B,]`` 、``[B,]`` 、``[B, max_len]`` | |||||
或 ``[B, max_len]`` | |||||
:param seq_len: 序列长度标记, 标记的形状可以是 ``None``, 或者 ``[B]`` 。 | |||||
如果 mask 也被传进来的话 ``seq_len`` 会被忽略 | |||||
""" | """ | ||||
# 为了兼容不同框架,我们将输入变量全部转为numpy类型来进行计算。 | # 为了兼容不同框架,我们将输入变量全部转为numpy类型来进行计算。 | ||||
pred = self.tensor2numpy(pred) | pred = self.tensor2numpy(pred) | ||||
@@ -85,12 +86,11 @@ class Accuracy(Metric): | |||||
class TransformersAccuracy(Accuracy): | class TransformersAccuracy(Accuracy): | ||||
""" | """ | ||||
适配 transformers 中相关模型的 Accuracy metric 。 | |||||
适配 :mod:`transformers` 中相关模型的 Accuracy metric 。 | |||||
""" | """ | ||||
def update(self, logits, labels, attention_mask=None): | def update(self, logits, labels, attention_mask=None): | ||||
r""" | r""" | ||||
update 函数将针对一个批次的预测结果做评价指标的累计 | |||||
:meth:`update` 函数将针对一个批次的预测结果做评价指标的累计。 | |||||
:param logits: 形状为 ``[B, n_classes]`` 或 ``[B, max_len, n_classes]`` 。 | :param logits: 形状为 ``[B, n_classes]`` 或 ``[B, max_len, n_classes]`` 。 | ||||
:param labels: 形状为 ``[B, ]`` 或 ``[B, max_len]`` | :param labels: 形状为 ``[B, ]`` 或 ``[B, max_len]`` | ||||
@@ -2,11 +2,15 @@ __all__ = [ | |||||
'Backend', | 'Backend', | ||||
'AutoBackend', | 'AutoBackend', | ||||
'TorchBackend', | 'TorchBackend', | ||||
'PaddleBackend' | |||||
'PaddleBackend', | |||||
'JittorBackend', | |||||
'OneflowBackend', | |||||
] | ] | ||||
from .backend import Backend | from .backend import Backend | ||||
from .auto_backend import AutoBackend | from .auto_backend import AutoBackend | ||||
from .torch_backend.backend import TorchBackend | |||||
from .paddle_backend.backend import PaddleBackend | |||||
from .torch_backend import TorchBackend | |||||
from .paddle_backend import PaddleBackend | |||||
from .jittor_backend import JittorBackend | |||||
from .oneflow_backend import OneflowBackend |
@@ -10,11 +10,11 @@ from .paddle_backend.backend import PaddleBackend | |||||
from .jittor_backend.backend import JittorBackend | from .jittor_backend.backend import JittorBackend | ||||
from .oneflow_backend.backend import OneflowBackend | from .oneflow_backend.backend import OneflowBackend | ||||
__all__ = [] | |||||
class AutoBackend(Backend): | class AutoBackend(Backend): | ||||
""" | """ | ||||
不需要初始化 backend 的 AutoBackend,能够根据 get_metric 时候判断输入数据类型来选择 backend 是什么类型的 | |||||
不需要初始化 ``backend`` 的 :class:`AutoBackend`,能够根据 :meth:`get_metric` 时候判断输入数据类型来选择 ``backend``。 | |||||
""" | """ | ||||
def __init__(self, backend: Union[str, Backend, None]): | def __init__(self, backend: Union[str, Backend, None]): | ||||
@@ -23,9 +23,9 @@ class AutoBackend(Backend): | |||||
:param backend: 目前支持三种值,为 ``[str, Backend, None]``。 | :param backend: 目前支持三种值,为 ``[str, Backend, None]``。 | ||||
* 当 backend 为 `str` 时, 其只能为 'auto' | |||||
* 当 backend 为 ``Backend`` 对象时, 其直接使用该对象方法覆盖 AutoBackend | |||||
* 当 backend 为 ``None`` 时, 根据 get_metric 时候判断输入数据类型来选择 backend 是什么类型的 | |||||
* 当 backend 为 :class:`str` 时, 其只能为 ``'auto'``; | |||||
* 当 backend 为 ``Backend`` 对象时, 其直接使用该对象方法覆盖 :class:`AutoBackend`; | |||||
* 当 backend 为 ``None`` 时, 根据 :meth:`get_metric` 时候判断输入数据类型来选择 ``backend``; | |||||
""" | """ | ||||
super(AutoBackend, self).__init__() | super(AutoBackend, self).__init__() | ||||
@@ -38,9 +38,10 @@ class AutoBackend(Backend): | |||||
:param backend: 传入的 backend 值。 | :param backend: 传入的 backend 值。 | ||||
* 当 backend 为 `torch` 时, 选择 :class:`~fastNLP.core.metric.TorchBackend` | |||||
* 当 backend 为 `paddle` 时, 选择 :class:`~fastNLP.core.metric.PaddleBackend` | |||||
* 当 backend 为 `jittor` 时, 选择 :class:`~fastNLP.core.metric.JittorBackend` | |||||
* 当 backend 为 ``'torch'`` 时, 选择 :class:`~fastNLP.core.metric.TorchBackend` | |||||
* 当 backend 为 ``'paddle'` 时, 选择 :class:`~fastNLP.core.metric.PaddleBackend` | |||||
* 当 backend 为 ``'jittor'`` 时, 选择 :class:`~fastNLP.core.metric.JittorBackend` | |||||
* 当 backend 为 ``'oneflow'`` 时, 选择 :class:`~fastNLP.core.metric.OneflowBackend` | |||||
* 当 backend 为 ``None`` 时, 直接初始化 | * 当 backend 为 ``None`` 时, 直接初始化 | ||||
""" | """ | ||||
@@ -66,7 +67,7 @@ class AutoBackend(Backend): | |||||
""" | """ | ||||
根据 args 参数类型来选择需要真正初始化的 backend | 根据 args 参数类型来选择需要真正初始化的 backend | ||||
:param args: args 参数, 可能为 ``jittor``, ``torch``, ``paddle``, ``numpy`` 类型, 能够检测并选择真正的 backend。 | |||||
:param args: args 参数, 可能为 ``'jittor'``, ``'torch'``, ``'paddle'``, ``'oneflow'``, ``'numpy'`` 类型, 能够检测并选择真正的 backend。 | |||||
""" | """ | ||||
assert not self.is_specified(), "This method should not be called after backend has been specified. " \ | assert not self.is_specified(), "This method should not be called after backend has been specified. " \ | ||||
@@ -1,10 +1,10 @@ | |||||
from ..utils import AggregateMethodError | from ..utils import AggregateMethodError | ||||
__all__ = [] | |||||
class Backend: | class Backend: | ||||
""" | """ | ||||
Backend 及其子类的所有方法都必须是无状态的。 | |||||
执行评测时使用的 backend,是所有 backend 的父类。Backend 及其子类的所有方法都必须是无状态的。 | |||||
""" | """ | ||||
def __init__(self): | def __init__(self): | ||||
@@ -12,7 +12,8 @@ class Backend: | |||||
def aggregate(self, tensor, method: str): | def aggregate(self, tensor, method: str): | ||||
""" | """ | ||||
聚集结果,并根据 method 计算后,返回结果 | |||||
聚集结果,并根据 ``method 计算后`` ,返回结果。 | |||||
:param tensor: 传入的张量 | :param tensor: 传入的张量 | ||||
:param method: 聚合的方法 | :param method: 聚合的方法 | ||||
""" | """ | ||||
@@ -23,15 +24,15 @@ class Backend: | |||||
def create_tensor(self, value: float): | def create_tensor(self, value: float): | ||||
""" | """ | ||||
创建tensor,并且填入value作为值 | |||||
创建 tensor,并且填入 ``value`` 作为值。 | |||||
:param value: 需要初始化的 value 值 | |||||
:param value: 需要初始化的 ``value`` 值 | |||||
""" | """ | ||||
return value | return value | ||||
def fill_value(self, tensor, value: float): | def fill_value(self, tensor, value: float): | ||||
""" | """ | ||||
将tensor的值设置为value | |||||
将 tensor 的值设置为 ``value`` | |||||
:param tensor: 传进来的张量 | :param tensor: 传进来的张量 | ||||
:param value: 需要填充的值 | :param value: 需要填充的值 | ||||
@@ -40,16 +41,16 @@ class Backend: | |||||
def get_scalar(self, tensor) -> float: | def get_scalar(self, tensor) -> float: | ||||
""" | """ | ||||
tensor的saclar值 | |||||
``tensor`` 的 saclar 值. | |||||
:param tensor: 传入的张量 | |||||
:param tensor: 传入的张量; | |||||
:return: | :return: | ||||
""" | """ | ||||
return tensor | return tensor | ||||
def is_specified(self) -> bool: | def is_specified(self) -> bool: | ||||
""" | """ | ||||
判断是否是某种框架的 backend | |||||
判断是否是某种框架的 backend。 | |||||
:return: | :return: | ||||
""" | """ | ||||
@@ -57,7 +58,7 @@ class Backend: | |||||
def tensor2numpy(self, tensor): | def tensor2numpy(self, tensor): | ||||
""" | """ | ||||
将 tensor 转为 numpy | |||||
将 ``tensor`` 转为 :class:`numpy.array`。 | |||||
:param tensor: 传入的张量 | :param tensor: 传入的张量 | ||||
:return: | :return: | ||||
@@ -66,16 +67,16 @@ class Backend: | |||||
def move_tensor_to_device(self, tensor, device): | def move_tensor_to_device(self, tensor, device): | ||||
""" | """ | ||||
将张量移动到某个设备上 | |||||
将张量移动到某个设备上。 | |||||
:param tensor: 传入的张量 | :param tensor: 传入的张量 | ||||
:param device: 设备号, 一般为 ``'cpu'``, ``'cuda:0'`` 等。 | |||||
:param device: 设备号, 一般为 ``'cpu'``, ``'cuda:0'`` 等 | |||||
""" | """ | ||||
return tensor | return tensor | ||||
def all_gather_object(self, obj, group=None): | def all_gather_object(self, obj, group=None): | ||||
""" | """ | ||||
给定 obj 将各个 rank 上的 obj 汇总到每个 obj 上。返回一个 list 对象,里面依次为各个 rank 对应的 obj 。 | |||||
给定 ``obj`` 将各个 rank 上的 ``obj`` 汇总到每个 ``obj`` 上。返回一个 :class:`list` 对象,里面依次为各个 rank 对应的 ``obj`` 。 | |||||
:param obj: | :param obj: | ||||
:param group: | :param group: | ||||
@@ -1 +1,5 @@ | |||||
__all__ = [ | |||||
"JittorBackend", | |||||
] | |||||
from .backend import JittorBackend |
@@ -0,0 +1,5 @@ | |||||
__all__ = [ | |||||
"OneflowBackend", | |||||
] | |||||
from .backend import OneflowBackend |
@@ -25,12 +25,12 @@ class OneflowBackend(Backend): | |||||
聚集结果,并根据 method 计算后,返回结果 | 聚集结果,并根据 method 计算后,返回结果 | ||||
:param tensor: 需要聚合的张量 | :param tensor: 需要聚合的张量 | ||||
:param method: 聚合的方法, 目前支持 ``['sum', 'mean', 'max', 'mix']``: | |||||
:param method: 聚合的方法, 目前支持 ``['sum', 'mean', 'max', 'min']``: | |||||
* method 为 ``'sum'`` 时, 会将多张卡上聚合结果在维度为 `0` 上 累加起来。 | * method 为 ``'sum'`` 时, 会将多张卡上聚合结果在维度为 `0` 上 累加起来。 | ||||
* method 为 ``'mean'`` 时,会将多张卡上聚合结果在维度为 `0` 上取平均值。 | * method 为 ``'mean'`` 时,会将多张卡上聚合结果在维度为 `0` 上取平均值。 | ||||
* method 为 ``'max'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最大值。 | * method 为 ``'max'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最大值。 | ||||
* method 为 ``'mix'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最小值。 | |||||
* method 为 ``'min'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最小值。 | |||||
""" | """ | ||||
if isinstance(tensor, oneflow.Tensor): | if isinstance(tensor, oneflow.Tensor): | ||||
@@ -2,4 +2,4 @@ __all__ = [ | |||||
'PaddleBackend' | 'PaddleBackend' | ||||
] | ] | ||||
from .backend import Backend as PaddleBackend | |||||
from .backend import PaddleBackend |
@@ -26,12 +26,12 @@ class PaddleBackend(Backend): | |||||
聚集结果,并根据 method 计算后,返回结果 | 聚集结果,并根据 method 计算后,返回结果 | ||||
:param tensor: 需要聚合的张量 | :param tensor: 需要聚合的张量 | ||||
:param method: 聚合的方法, 目前支持 ``['sum', 'mean', 'max', 'mix']``: | |||||
:param method: 聚合的方法, 目前支持 ``['sum', 'mean', 'max', 'min']``: | |||||
* method 为 ``'sum'`` 时, 会将多张卡上聚合结果在维度为 `0` 上 累加起来。 | * method 为 ``'sum'`` 时, 会将多张卡上聚合结果在维度为 `0` 上 累加起来。 | ||||
* method 为 ``'mean'`` 时,会将多张卡上聚合结果在维度为 `0` 上取平均值。 | * method 为 ``'mean'`` 时,会将多张卡上聚合结果在维度为 `0` 上取平均值。 | ||||
* method 为 ``'max'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最大值。 | * method 为 ``'max'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最大值。 | ||||
* method 为 ``'mix'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最小值。 | |||||
* method 为 ``'min'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最小值。 | |||||
""" | """ | ||||
if isinstance(tensor, paddle.Tensor): | if isinstance(tensor, paddle.Tensor): | ||||
@@ -3,4 +3,4 @@ __all__ = [ | |||||
] | ] | ||||
from .backend import Backend as TorchBackend | |||||
from .backend import TorchBackend |
@@ -24,12 +24,12 @@ class TorchBackend(Backend): | |||||
聚集结果,并根据 method 计算后,返回结果 | 聚集结果,并根据 method 计算后,返回结果 | ||||
:param tensor: 需要聚合的张量 | :param tensor: 需要聚合的张量 | ||||
:param method: 聚合的方法, 目前支持 ``['sum', 'mean', 'max', 'mix']``: | |||||
:param method: 聚合的方法, 目前支持 ``['sum', 'mean', 'max', 'min']``: | |||||
* method 为 ``'sum'`` 时, 会将多张卡上聚合结果在维度为 `0` 上 累加起来。 | * method 为 ``'sum'`` 时, 会将多张卡上聚合结果在维度为 `0` 上 累加起来。 | ||||
* method 为 ``'mean'`` 时,会将多张卡上聚合结果在维度为 `0` 上取平均值。 | * method 为 ``'mean'`` 时,会将多张卡上聚合结果在维度为 `0` 上取平均值。 | ||||
* method 为 ``'max'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最大值。 | * method 为 ``'max'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最大值。 | ||||
* method 为 ``'mix'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最小值。 | |||||
* method 为 ``'min'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最小值。 | |||||
""" | """ | ||||
if isinstance(tensor, torch.Tensor): | if isinstance(tensor, torch.Tensor): | ||||
@@ -14,27 +14,36 @@ from .utils import _compute_f_pre_rec | |||||
from fastNLP.core.log import logger | from fastNLP.core.log import logger | ||||
class ClassifyFPreRecMetric(Metric): | class ClassifyFPreRecMetric(Metric): | ||||
def __init__(self, tag_vocab: Vocabulary = 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: | |||||
""" | |||||
""" | |||||
计算分类结果 **F值** 的 **Metric** 。 | |||||
:param tag_vocab: 标签的 :class:`~fastNLP.Vocabulary` . 默认值为 ``None``。若为 ``None`` 则使用数字来作为标签内容, | |||||
否则使用 vocab 来作为标签内容。 | |||||
:param ignore_labels: ``str`` 组成的 ``list``. 这个 ``list``中的 class 不会被用于计算。例如在 POS tagging 时传入 ``['NN']``, | |||||
:param tag_vocab: 标签的 :class:`~fastNLP.core.Vocabulary` 。 默认值为 ``None``。若为 ``None`` 则使用数字来作为标签内容, | |||||
否则使用 vocab 来作为标签内容 | |||||
:param ignore_labels: :class:`str` 组成的 :class:`list`. 这个 :class:`list` 中的 class 不会被用于计算。例如在 POS tagging 时传入 ``['NN']``, | |||||
则不会计算 'NN' 个 label | 则不会计算 'NN' 个 label | ||||
:param only_gross: 是否只计算总的 ``f1``, ``precision``, ``recall``的值;如果为 ``False``,不仅返回总的 ``f1``, ``pre``, | |||||
:param only_gross: 是否只计算总的 ``f1``, ``precision``, ``recall``的值;如果为 ``False``,不仅返回总的 ``f1``, ``pre``, | |||||
``rec``, 还会返回每个 label 的 ``f1``, ``pre``, ``rec`` | ``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)}` . | |||||
:param backend: 目前支持四种类型的 backend, ``[torch, paddle, jittor, 'auto']``。其中 ``'auto'`` 表示根据实际调用 Metric.update() | |||||
函数时传入的参数决定具体的 backend ,大部分情况下直接使用 ``'auto'`` 即可。 | |||||
:param aggregate_when_get_metric: 在计算 metric 的时候是否自动将各个进程上的相同的 element 的数字聚合后再得到metric, | |||||
当 backend 不支持分布式时,该参数无意义。如果为 ``None`` ,将在 Evaluator 中根据 sampler 是否使用分布式进行自动设置。 | |||||
:param f_type: `micro` 或 `macro` 。 | |||||
""" | |||||
* `micro` : 通过先计算总体的 TP,FN 和 FP 的数量,再计算 f, precision, recall; | |||||
* `macro` : 分布计算每个类别的 f, precision, recall,然后做平均(各类别 f 的权重相同) | |||||
:param beta: **f_beta** 分数中的 ``beta`` 值。 常用为 ``beta=0.5, 1, 2`` 若为 0.5 则 **精确率** 的权重高于 **召回率** ;若为1,则两者平等;若为2,则 | |||||
**召回率** 权重高于 **精确率** 。**f_beta** 分数的计算公式为: | |||||
.. math:: | |||||
f_{beta} = \\frac{(1 + {beta}^{2})*(pre*rec)}{({beta}^{2}*pre + rec)} | |||||
:param backend: 目前支持五种类型的 backend, ``['torch', 'paddle', 'jittor', 'oneflow', 'auto']``。其中 ``'auto'`` 表示根据实际调用 :meth:`update` | |||||
函数时传入的参数决定具体的 backend ,大部分情况下直接使用 ``'auto'`` 即可。 | |||||
:param aggregate_when_get_metric: 在计算 metric 的时候是否自动将各个进程上的相同的 element 的数字聚合后再得到 metric, | |||||
当 backend 不支持分布式时,该参数无意义。如果为 ``None`` ,将在 :class:`~fastNLP.core.controllers.Evaluator` 中根据 | |||||
sampler 是否使用分布式进行自动设置。 | |||||
""" | |||||
def __init__(self, tag_vocab: Vocabulary = 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: | |||||
super(ClassifyFPreRecMetric, self).__init__(backend=backend, | super(ClassifyFPreRecMetric, self).__init__(backend=backend, | ||||
aggregate_when_get_metric=aggregate_when_get_metric) | aggregate_when_get_metric=aggregate_when_get_metric) | ||||
if f_type not in ('micro', 'macro'): | if f_type not in ('micro', 'macro'): | ||||
@@ -56,8 +65,7 @@ class ClassifyFPreRecMetric(Metric): | |||||
def reset(self): | def reset(self): | ||||
""" | """ | ||||
重置 tp, fp, fn 的值 | |||||
重置 ``tp``, ``fp``, ``fn`` 的值 | |||||
""" | """ | ||||
# 由于不是 element 了,需要自己手动清零一下 | # 由于不是 element 了,需要自己手动清零一下 | ||||
self._tp.clear() | self._tp.clear() | ||||
@@ -66,9 +74,9 @@ class ClassifyFPreRecMetric(Metric): | |||||
def get_metric(self) -> dict: | def get_metric(self) -> dict: | ||||
r""" | r""" | ||||
get_metric 函数将根据 update 函数累计的评价指标统计量来计算最终的评价结果. | |||||
:meth:`get_metric` 函数将根据 :meth:`update` 函数累计的评价指标统计量来计算最终的评价结果。 | |||||
:return evaluate_result: {"acc": float} | |||||
:return: 包含以下内容的字典:``{"acc": float}`` | |||||
""" | """ | ||||
evaluate_result = {} | evaluate_result = {} | ||||
@@ -127,13 +135,13 @@ class ClassifyFPreRecMetric(Metric): | |||||
def update(self, pred, target, seq_len=None): | def update(self, pred, target, seq_len=None): | ||||
r""" | r""" | ||||
update 函数将针对一个批次的预测结果做评价指标的累计 | |||||
:meth:`update` 函数将针对一个批次的预测结果做评价指标的累计。 | |||||
:param pred: 预测的 tensor, tensor 的形状可以是 [B,], [B, n_classes]) | |||||
[B, max_len], 或者 [B, max_len, n_classes] | |||||
:param target: 真实值的 tensor, tensor 的形状可以是 [B,], | |||||
[B,], [B, max_len], 或者 [B, max_len] | |||||
:param seq_len: 序列长度标记, 标记的形状可以是 None, [B]. | |||||
:param pred: 预测的 tensor, tensor 的形状可以是 ``[B,]`` 、``[B, n_classes]`` 、 | |||||
``[B, max_len]`` 或 ``[B, max_len, n_classes]`` | |||||
:param target: 真实值的 tensor, tensor 的形状可以是 ``[B,]`` 、``[B,]`` 、``[B, max_len]`` | |||||
或 ``[B, max_len]`` | |||||
:param seq_len: 序列长度标记, 标记的形状可以是 ``None``, 或者 ``[B]`` | |||||
""" | """ | ||||
pred = self.tensor2numpy(pred) | pred = self.tensor2numpy(pred) | ||||
@@ -23,27 +23,27 @@ def _wrap_cal_value(func): | |||||
class Element: | class Element: | ||||
""" | |||||
保存 :class:`~fastNLP.core.metrics.Metric` 中计算的元素值的对象 | |||||
:param name: 名称 | |||||
:param value: 元素的值 | |||||
:param aggregate_method: 聚合的方法, 目前支持 ``['sum', 'mean', 'max', 'min']``: | |||||
* method 为 ``'sum'`` 时, 会将多张卡上聚合结果在维度为 `0` 上 累加起来。 | |||||
* method 为 ``'mean'`` 时,会将多张卡上聚合结果在维度为 `0` 上取平均值。 | |||||
* method 为 ``'max'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最大值。 | |||||
* method 为 ``'min'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最小值。 | |||||
:param backend: 使用的 backend 。Element 的类型会根据 ``backend`` 进行实际的初始化。例如 ``backend`` 为 ``'torch'`` 则该对象为 | |||||
:class:`torch.Tensor` ; 如果 ``'backend'`` 为 ``'paddle'`` 则该对象为 :class:`paddle.Tensor` ;如果 ``backend`` 为 | |||||
``'jittor'`` , 则该对象为 :class:`jittor.Var` 。一般情况下直接默认为 ``'auto'`` 就行了, **fastNLP** 会根据实际调用 :meth`Metric.update` | |||||
函数时传入的参数进行合理的初始化,例如当传入的参数中只包含 :class:`torch.Tensor` 这一种 tensor 时(可以有其它非 tensor 类型的输入) | |||||
则认为 ``backend`` 为 ``'torch'`` ;只包含 :class:`jittor.Var` 这一种 tensor 时(可以有其它非 tensor 类型的输入)则认为 ``backend`` | |||||
为 ``'jittor'`` 。如果没有检测到任何一种 tensor ,就默认使用 :class:`float` 类型作为 element 。 | |||||
""" | |||||
def __init__(self, name, value: float, aggregate_method, backend: Backend): | def __init__(self, name, value: float, aggregate_method, backend: Backend): | ||||
""" | |||||
保存 Metric 中计算的元素值的对象 | |||||
:param name: 名称 | |||||
:param value: 元素的值 | |||||
:param aggregate_method: 聚合的方法, 目前支持 ``['sum', 'mean', 'max', 'mix']``: | |||||
* method 为 ``'sum'`` 时, 会将多张卡上聚合结果在维度为 `0` 上 累加起来。 | |||||
* method 为 ``'mean'`` 时,会将多张卡上聚合结果在维度为 `0` 上取平均值。 | |||||
* method 为 ``'max'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最大值。 | |||||
* method 为 ``'mix'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最小值。 | |||||
:param backend: 使用的 backend 。Element 的类型会根据 backend 进行实际的初始化。例如 backend 为 torch 则该对象为 | |||||
Torch.tensor ; 如果backend 为 paddle 则该对象为 paddle.tensor ;如果 backend 为 jittor , 则该对象为 jittor.Var 。 | |||||
一般情况下直接默认为 auto 就行了,fastNLP 会根据实际调用 Metric.update() 函数时传入的参数进行合理的初始化,例如当传入 | |||||
的参数中只包含 torch.Tensor 这一种 tensor 时(可以有其它非 tensor 类型的输入)则认为 backend 为 torch ;只包含 | |||||
jittor.Var 则认为 backend 这一种 tensor 时(可以有其它非 tensor 类型的输入)则认为 backend 为 jittor 。如果没有检测 | |||||
到任何一种 tensor ,就默认使用 float 类型作为 element 。 | |||||
""" | |||||
self.name = name | self.name = name | ||||
self.init_value = value | self.init_value = value | ||||
self.aggregate_method = aggregate_method | self.aggregate_method = aggregate_method | ||||
@@ -64,7 +64,6 @@ class Element: | |||||
def aggregate(self): | def aggregate(self): | ||||
""" | """ | ||||
自动 aggregate 对应的元素 | 自动 aggregate 对应的元素 | ||||
""" | """ | ||||
self._check_value_initialized() | self._check_value_initialized() | ||||
if self.aggregate_method is None: # 如果没有 aggregate 则不进行聚合。 | if self.aggregate_method is None: # 如果没有 aggregate 则不进行聚合。 | ||||
@@ -116,7 +115,7 @@ class Element: | |||||
def fill_value(self, value): | def fill_value(self, value): | ||||
""" | """ | ||||
对元素进行 fill_value, 会执行队友 backend 的 fill_value 方法 | |||||
对元素进行 :meth:`fill_value` , 会执行对应 backend 的 :meth:`fill_value` 方法 | |||||
""" | """ | ||||
self._check_value_initialized() | self._check_value_initialized() | ||||
@@ -136,7 +135,6 @@ class Element: | |||||
def _check_value_initialized(self): | def _check_value_initialized(self): | ||||
""" | """ | ||||
检查 Element 的 value 是否初始化了 | 检查 Element 的 value 是否初始化了 | ||||
""" | """ | ||||
if self._value is None: | if self._value is None: | ||||
assert self.backend.is_specified(), f"Backend is not specified, please specify backend in the Metric " \ | assert self.backend.is_specified(), f"Backend is not specified, please specify backend in the Metric " \ | ||||
@@ -302,7 +300,7 @@ class Element: | |||||
def __getattr__(self, item): | def __getattr__(self, item): | ||||
""" | """ | ||||
为FDataLoader提供dataset的方法和属性,实现该方法后,用户可以在FDataLoader实例化后使用apply等dataset的方法 | |||||
为 FDataLoader 提供 dataset 的方法和属性,实现该方法后,用户可以在 FDataLoader 实例化后使用 apply 等 dataset 的方法 | |||||
:param item: | :param item: | ||||
:return: | :return: | ||||
""" | """ | ||||
@@ -15,13 +15,13 @@ from fastNLP.core.metrics.element import Element | |||||
class Metric: | class Metric: | ||||
""" | """ | ||||
fastNLP 中 Metric 的基类,自定义 Metric 时,请继承该对象。使用该对象,将有助于减少在分布式状态下的 Metric 计算。 | |||||
**fastNLP** 中 :class:`Metric` 的基类,自定义 :class:`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.core.controllers.Evaluator` 中根据 sampler 是否使用分布式 | |||||
进行自动设置。 | |||||
:param backend: 目前支持五种类型的 backend, ``['torch', 'paddle', 'jittor', 'oneflow', 'auto']``。其中 ``'auto'`` 表示根据实际调用 :meth:`update` | |||||
函数时传入的参数决定具体的 backend ,大部分情况下直接使用 ``'auto'`` 即可。 | |||||
:param aggregate_when_get_metric: 在计算 metric 的时候是否自动将各个进程上的相同的 element 的数字聚合后再得到 metric, | |||||
当 backend 不支持分布式时,该参数无意义。如果为 ``None`` ,将在 :class:`~fastNLP.core.controllers.Evaluator` 中根据 | |||||
sampler 是否使用分布式进行自动设置。 | |||||
""" | """ | ||||
def __init__(self, backend: Union[str, Backend, None] = 'auto', aggregate_when_get_metric: bool = None): | def __init__(self, backend: Union[str, Backend, None] = 'auto', aggregate_when_get_metric: bool = None): | ||||
self.backend = AutoBackend(backend) | self.backend = AutoBackend(backend) | ||||
@@ -39,22 +39,22 @@ class Metric: | |||||
def register_element(self, name, value: float = 0, aggregate_method=None, backend='auto') -> Element: | def register_element(self, name, value: float = 0, aggregate_method=None, backend='auto') -> Element: | ||||
""" | """ | ||||
注册一个 element 对象,注册之后便可以通过在 Metric 中直接通过 self.{name} 进行调用,可以认为该对象即为对应 backend 的 | |||||
注册一个 element 对象,注册之后便可以通过在 Metric 中直接通过 ``self.{name}`` 进行调用,可以认为该对象即为对应 backend 的 | |||||
tensor 直接进行加减乘除计算即可。 | tensor 直接进行加减乘除计算即可。 | ||||
..warning:: | |||||
.. warning:: | |||||
如果想使得该 metric 可自动扩展到多卡的情况,请一定申明 aggregate_method 。 | |||||
如果想使得该 metric 可自动扩展到多卡的情况,请一定申明 ``aggregate_method`` 。 | |||||
:param name: 当前 element 的名字,注册后,在 Metric 中可以通过 self.{name} 访问该变量。 | |||||
:param value: 初始化的值。在调用 Metric.reset() 方法时也将自动设置为该值 | |||||
:param name: 当前 element 的名字,注册后,在 Metric 中可以通过 ``self.{name}`` 访问该变量。 | |||||
:param value: 初始化的值。在调用 :meth:`Metric.reset` 方法时也将自动设置为该值 | |||||
:param aggregate_method: 如何聚合多卡上的结果,如果为单卡执行,该值无意义。如果设置为 None 则表示该 element 不进行聚合。 | :param aggregate_method: 如何聚合多卡上的结果,如果为单卡执行,该值无意义。如果设置为 None 则表示该 element 不进行聚合。 | ||||
:param backend: 使用的 backend 。Element 的类型会根据 backend 进行实际的初始化。例如 backend 为 torch 则该对象为 | |||||
Torch.tensor ; 如果backend 为 paddle 则该对象为 paddle.tensor ;如果 backend 为 jittor , 则该对象为 jittor.Var 。 | |||||
一般情况下直接默认为 auto 就行了,fastNLP 会根据实际调用 Metric.update() 函数时传入的参数进行合理的初始化,例如当传入 | |||||
的参数中只包含 torch.Tensor 这一种 tensor 时(可以有其它非 tensor 类型的输入)则认为 backend 为 torch ;只包含 | |||||
jittor.Var 则认为 backend 这一种 tensor 时(可以有其它非 tensor 类型的输入)则认为 backend 为 jittor 。如果没有检测 | |||||
到任何一种 tensor ,就默认使用 float 类型作为 element 。 | |||||
:param backend: 使用的 backend 。Element 的类型会根据 ``backend`` 进行实际的初始化。例如 ``backend`` 为 ``'torch'`` 则该对象为 | |||||
:class:`torch.Tensor` ; 如果 ``'backend'`` 为 ``'paddle'`` 则该对象为 :class:`paddle.Tensor` ;如果 ``backend`` 为 | |||||
``'jittor'`` , 则该对象为 :class:`jittor.Var` 。一般情况下直接默认为 ``'auto'`` 就行了, **fastNLP** 会根据实际调用 :meth`Metric.update` | |||||
函数时传入的参数进行合理的初始化,例如当传入的参数中只包含 :class:`torch.Tensor` 这一种 tensor 时(可以有其它非 tensor 类型的输入) | |||||
则认为 ``backend`` 为 ``'torch'`` ;只包含 :class:`jittor.Var` 这一种 tensor 时(可以有其它非 tensor 类型的输入)则认为 ``backend`` | |||||
为 ``'jittor'`` 。如果没有检测到任何一种 tensor ,就默认使用 :class:`float` 类型作为 element 。 | |||||
:return: 注册的 Element 对象 | :return: 注册的 Element 对象 | ||||
""" | """ | ||||
if backend == 'auto': | if backend == 'auto': | ||||
@@ -71,8 +71,8 @@ class Metric: | |||||
def reset(self): | def reset(self): | ||||
""" | """ | ||||
如果有非 element 的对象需要 reset 的时候,在本方法中写下非 element 的reset 方式。注册的 element 对象会自动 reset 为初始值。 | |||||
在对每个 ``evaluate_dataloaders`` 遍历进行验证之前,:meth:`reset` 函数会被调用来重置每个非 element 对象; | |||||
如果有非 element 的对象需要重置的时候,在本方法中写下非 element 的重置方式。注册的 element 对象则会自动 reset 为初始值。 | |||||
""" | """ | ||||
pass | pass | ||||
@@ -142,9 +142,8 @@ class Metric: | |||||
@contextmanager | @contextmanager | ||||
def sync(self, recover=True, aggregate=False): | def sync(self, recover=True, aggregate=False): | ||||
""" | """ | ||||
在这个上下文下, metric 会自动先同步需要同步操作的 element 。当 recover 为 True 时,在退出环境的时候,会重新将 element 的 | |||||
值恢复到计算前的值。 | |||||
在这个上下文下, :meth:`Metric` 会自动先同步需要同步操作的 element 。当 ``recover`` 为 ``True`` 时,在退出环境的时候,会重新将 element 的 | |||||
值恢复到计算前的值。 | |||||
""" | """ | ||||
keep_value = {} | keep_value = {} | ||||
if aggregate: | if aggregate: | ||||
@@ -172,14 +171,14 @@ class Metric: | |||||
def set_auto_aggregate_when_get_metric(self, flag: bool): | def set_auto_aggregate_when_get_metric(self, flag: bool): | ||||
""" | """ | ||||
设置是否在 get_metric 的时候自动 aggregate | |||||
设置是否在 :meth:`get_metric` 的时候自动 aggregate | |||||
""" | """ | ||||
self.aggregate_when_get_metric = flag | self.aggregate_when_get_metric = flag | ||||
def tensor2numpy(self, tensor) -> np.array: | def tensor2numpy(self, tensor) -> np.array: | ||||
""" | """ | ||||
将tensor向量转为numpy类型变量 | |||||
将 ``tensor`` 向量转为 :class:`numpy.array` 类型变量。 | |||||
:param tensor: | :param tensor: | ||||
:return: | :return: | ||||
@@ -188,7 +187,7 @@ class Metric: | |||||
def to(self, device): | def to(self, device): | ||||
""" | """ | ||||
将所有的 element 变量移动到 device 设备上 | |||||
将所有的 element 变量移动到 ``device`` 设备上 | |||||
:param device: | :param device: | ||||
:return: | :return: | ||||
@@ -198,7 +197,7 @@ class Metric: | |||||
def all_gather_object(self, obj, group=None)->List: | def all_gather_object(self, obj, group=None)->List: | ||||
""" | """ | ||||
给定 obj 将各个 rank 上的 obj 汇总到每个 obj 上。返回一个 list 对象,里面依次为各个 rank 对应的 obj 。 | |||||
给定 ``obj`` 将各个 rank 上的 ``obj`` 汇总到每个 ``obj`` 上。返回一个 list 对象,里面依次为各个 rank 对应的 ``obj`` 。 | |||||
:param obj: 需要汇总的对象,必须是个 pickable 的对象。 | :param obj: 需要汇总的对象,必须是个 pickable 的对象。 | ||||
:param group: | :param group: | ||||
@@ -201,19 +201,31 @@ def _bio_tag_to_spans(tags, ignore_labels=None): | |||||
class SpanFPreRecMetric(Metric): | class SpanFPreRecMetric(Metric): | ||||
r""" | r""" | ||||
:param tag_vocab: 标签的 :class:`~fastNLP.Vocabulary` 。支持的标签为"B"(没有label);或"B-xxx"(xxx为某种label,比如POS中的NN), | |||||
在解码时,会将相同xxx的认为是同一个label,比如['B-NN', 'E-NN']会被合并为一个'NN'. | |||||
: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.core.controllers.Evaluator` 中根据 sampler 是否使用分布式 | |||||
进行自动设置。 | |||||
在 **序列标注** 任务中评估抽取结果匹配度的 **Metric** 。 | |||||
:param tag_vocab: 标签的 :class:`~fastNLP.core.Vocabulary` 。支持的标签有 ``"B"`` (没有label);或 ``"B-xxx"`` ( ``xxx`` 为某种 label ,比如 POS 中的 NN), | |||||
在解码时,会将相同 ``xxx`` 的认为是同一个 label ,比如 ['B-NN', 'E-NN'] 会被合并为一个 'NN' 。 | |||||
:param encoding_type: 目前支持 ``['bio', 'bmes', 'bmeso', 'bioes', None]`` 。默认为 ``None`` ,通过 ``tag_vocab`` 自动判断 | |||||
:param ignore_labels: 字符串组成的列表,这个列表中包含的内容不会被用于计算。例如在 *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** 分数中的 ``beta`` 值。 常用为 ``beta=0.5, 1, 2`` 若为 0.5 则 **精确率** 的权重高于 **召回率** ;若为1,则两者平等;若为2,则 | |||||
**召回率** 权重高于 **精确率** 。**f_beta** 分数的计算公式为: | |||||
.. math:: | |||||
f_{beta} = \\frac{(1 + {beta}^{2})*(pre*rec)}{({beta}^{2}*pre + rec)} | |||||
:param backend: 目前支持五种类型的 backend, ``['torch', 'paddle', 'jittor', 'oneflow', 'auto']``。其中 ``'auto'`` 表示根据实际调用 :meth:`update` | |||||
函数时传入的参数决定具体的 backend ,大部分情况下直接使用 ``'auto'`` 即可。 | |||||
:param aggregate_when_get_metric: 在计算 metric 的时候是否自动将各个进程上的相同的 element 的数字聚合后再得到 metric, | |||||
当 backend 不支持分布式时,该参数无意义。如果为 ``None`` ,将在 :class:`~fastNLP.core.controllers.Evaluator` 中根据 | |||||
sampler 是否使用分布式进行自动设置。 | |||||
""" | """ | ||||
def __init__(self, tag_vocab: Vocabulary, encoding_type: str = None, ignore_labels: List[str] = None, | def __init__(self, tag_vocab: Vocabulary, encoding_type: str = None, ignore_labels: List[str] = None, | ||||
only_gross: bool = True, f_type='micro', | only_gross: bool = True, f_type='micro', | ||||
@@ -262,7 +274,7 @@ class SpanFPreRecMetric(Metric): | |||||
def get_metric(self) -> dict: | def get_metric(self) -> dict: | ||||
""" | """ | ||||
get_metric 函数将根据 update 函数累计的评价指标统计量来计算最终的评价结果. | |||||
:meth:`get_metric` 函数将根据 :meth:`update` 函数累计的评价指标统计量来计算最终的评价结果。 | |||||
""" | """ | ||||
evaluate_result = {} | evaluate_result = {} | ||||
@@ -317,12 +329,12 @@ class SpanFPreRecMetric(Metric): | |||||
return evaluate_result | return evaluate_result | ||||
def update(self, pred, target, seq_len: Optional[List] = None) -> None: | def update(self, pred, target, seq_len: Optional[List] = None) -> None: | ||||
r"""u | |||||
pdate函数将针对一个批次的预测结果做评价指标的累计 | |||||
r""" | |||||
:meth:`update` 函数将针对一个批次的预测结果做评价指标的累计。 | |||||
:param pred: [batch, seq_len] 或者 [batch, seq_len, len(tag_vocab)], 预测的结果 | |||||
:param target: [batch, seq_len], 真实值 | |||||
:param seq_len: [batch] 文本长度标记 | |||||
:param pred: 预测的结果,大小为 ``[batch, seq_len]`` 或者 ``[batch, seq_len, len(tag_vocab)]`` | |||||
:param target: 真实值,大小为 ``[batch, seq_len]`` | |||||
:param seq_len: 文本长度标记,大小为 ``[batch]`` | |||||
:return: | :return: | ||||
""" | """ | ||||
pred = self.tensor2numpy(pred) | pred = self.tensor2numpy(pred) | ||||
@@ -7,10 +7,10 @@ from fastNLP.core.samplers.unrepeated_sampler import UnrepeatedSampler, Unrepeat | |||||
def conversion_between_reproducible_and_unrepeated_sampler(sampler): | def conversion_between_reproducible_and_unrepeated_sampler(sampler): | ||||
""" | """ | ||||
将 sampler 替换成其对应的 reproducible 版本或 unrepeated 版本。如果输入是 UnrepeatedSampler 但是没找到对应的 | |||||
ReproducibleSampler, | |||||
将 ``sampler`` 替换成其对应的 reproducible 版本或 unrepeated 版本。如果输入是 :class:`~fastNLP.core.samplers.unrepeated_sampler.UnrepeatedSampler` | |||||
但是没找到对应的 :class:`~fastNLP.core.samplers.reproducible_sampler.ReproducibleSampler` 则会报错。 | |||||
:param sampler: 需要转换的 sampler 。 | |||||
:param sampler: 需要转换的 ``sampler`` ; | |||||
:return: | :return: | ||||
""" | """ | ||||
assert isinstance(sampler, UnrepeatedSampler) or isinstance(sampler, ReproducibleSampler), \ | assert isinstance(sampler, UnrepeatedSampler) or isinstance(sampler, ReproducibleSampler), \ | ||||
@@ -18,20 +18,30 @@ if _NEED_IMPORT_TORCH: | |||||
class MixSampler: | class MixSampler: | ||||
""" | """ | ||||
mix_sampler的基类 | |||||
所有 mix_sampler 的基类。 | |||||
:param dataset: 一个字典,每个元素都是一个实现了 __getitem__ 和 __len__ 的数据容器 | |||||
:param batch_size: ``dataset`` 的批次大小,所有 ``dataset`` 均采用该 ``batch_size`` 作为批次大小 | |||||
:param sampler: 实例化好的 ``sampler`` ,每个 ``dataset`` 对应一个 ``sampler`` 对象 | |||||
:param ds_ratio: ``ds_ratio`` 是控制 datasets 怎么组成一个混合大数据集的重要参数, 其取值为 ``[None, 'truncate_to_least', 'pad_to_most', List[float], Dict[str, float]]``: | |||||
* ds_ratio 为 ``None``, datasets 数据集序列或字典不进行数据扩充处理; | |||||
* ds_ratio 为 ``'truncate_to_least'``, datasets 数据集序列或字典会计算得到 datasets序列中 dataset 最断长度 ``mix_len``, 其他数据集会被切断 | |||||
到最短长度 ``mix_len``。这种切断不是物理上切断,``MixDataLoader`` 会根据 sampler 不同来采样数据集到指定的最短长度 ``mix_len``; | |||||
* ds_ratio 为 ``'pad_to_most'``, datasets 数据集序列或字典会计算得到 datasets序列中 dataset 最大长度 ``max_len``, 其他其他数据集会扩充 | |||||
到最大长度 ``mix_len``。这种扩充不是物理上扩充, ``MixDataLoader`` 会根据 sampler 不同来重采样 dataset 到指定的最大长度 ``max_len``; | |||||
* ds_ratio 为 ``Dict[str, float]`` 时, datasets 类型也必须为 ``Dict[str, DataSet]``, 其 key 一一对应。 ``ds_ratio`` 的 value 是任意大于 0 的浮点数, | |||||
代表着 datasets 的 value 数据进行扩充或者缩减的倍数; | |||||
:param drop_last: 当最后一个 batch 长度小于 ``batch_size`` 时是否丢弃 | |||||
:param rank: 分布式训练中当前进程的 ``global_rank`` | |||||
:param world_size: 分布式训练中进程的总数 **world_size** | |||||
""" | """ | ||||
def __init__(self, dataset: Dict, batch_size: int = None, | def __init__(self, dataset: Dict, batch_size: int = None, | ||||
sampler: Union[Dict[str, "Sampler"], None, str] = None, | sampler: Union[Dict[str, "Sampler"], None, str] = None, | ||||
ds_ratio: Union[str, Dict[str, float]] = None, | ds_ratio: Union[str, Dict[str, float]] = None, | ||||
drop_last: bool = False, rank: int = -1, word_size: int = -1) -> None: | drop_last: bool = False, rank: int = -1, word_size: int = -1) -> None: | ||||
""" | |||||
:param dataset: 实现了__getitem__和__len__的数据容器列表 | |||||
:param batch_size: 对应dataset的批次大小,可以为list或者为int,当为int时默认所有dataset | |||||
:param sampler: 实例化好的sampler,每个dataset对应一个sampler对象 | |||||
:param drop_last: 是否去掉最后一个batch的数据,其长度小于batch_size | |||||
""" | |||||
# sampler 为 dict,则判断是否与 datasets 的 key 相同 | # sampler 为 dict,则判断是否与 datasets 的 key 相同 | ||||
if isinstance(sampler, Dict): | if isinstance(sampler, Dict): | ||||
for key in dataset.keys(): | for key in dataset.keys(): | ||||
@@ -119,7 +129,7 @@ class MixSampler: | |||||
class InnerSampler: | class InnerSampler: | ||||
""" | """ | ||||
提供多卡情况下使用的内部sampler | |||||
提供多卡情况下使用的内部 sampler | |||||
""" | """ | ||||
def __init__(self, ds_ind_list: List) -> None: | def __init__(self, ds_ind_list: List) -> None: | ||||
self.ds_ind_list = ds_ind_list | self.ds_ind_list = ds_ind_list | ||||
@@ -134,7 +144,8 @@ class InnerSampler: | |||||
class DopedSampler(MixSampler): | class DopedSampler(MixSampler): | ||||
""" | """ | ||||
定制给MixDataLoader的BatchSampler,其功能是将传入的datasets的list列表混合采样组成一个个batch返回。 | |||||
定制给 :class:`~fastNLP.core.dataloaders.MixDataLoader` 的 ``BatchSampler``,其功能是将传入的 ``datasets`` | |||||
字典混合采样组成一个个 batch 返回。 | |||||
""" | """ | ||||
def __init__(self, dataset: Dict, batch_size: int = None, | def __init__(self, dataset: Dict, batch_size: int = None, | ||||
sampler: Union[Dict[str, "Sampler"], str] = None, | sampler: Union[Dict[str, "Sampler"], str] = None, | ||||
@@ -256,27 +267,21 @@ class DopedSampler(MixSampler): | |||||
class MixSequentialSampler(MixSampler): | class MixSequentialSampler(MixSampler): | ||||
""" | """ | ||||
定制给MixDataLoader的BatchSampler,其功能是将传入的datasets的list列表顺序采样并返回index,只有处理了上一个dataset才会处理下一个。 | |||||
定制给 :class:`~fastNLP.core.dataloaders.MixDataLoader` 的 ``BatchSampler``,其功能是将传入的 ``datasets`` 按顺序采样并返回 index, | |||||
只有上一个 dataset 处理结束后才会处理下一个。 | |||||
""" | """ | ||||
def __init__(self, dataset: Union[List, Dict], batch_size: int = None, | |||||
def __init__(self, dataset: Dict, batch_size: int = None, | |||||
sampler: Union[List["Sampler"], Dict[str, "Sampler"], None, str] = None, | sampler: Union[List["Sampler"], Dict[str, "Sampler"], None, str] = None, | ||||
ds_ratio: Union[str, List[float], Dict[str, float]] = None, | ds_ratio: Union[str, List[float], Dict[str, float]] = None, | ||||
drop_last: bool = False, rank: int = -1, word_size: int = -1) -> None: | drop_last: bool = False, rank: int = -1, word_size: int = -1) -> None: | ||||
""" | |||||
:param dataset: 实现了__getitem__和__len__的数据容器列表 | |||||
:param batch_size: 对应dataset的批次大小,可以为list或者为int,当为int时默认所有dataset | |||||
:param sampler: 实例化好的sampler,每个dataset对应一个sampler对象 | |||||
:param drop_last: 是否去掉最后一个batch的数据,其长度小于batch_size | |||||
""" | |||||
super(MixSequentialSampler, self).__init__(dataset=dataset, batch_size=batch_size, | super(MixSequentialSampler, self).__init__(dataset=dataset, batch_size=batch_size, | ||||
sampler=sampler, ds_ratio=ds_ratio, | sampler=sampler, ds_ratio=ds_ratio, | ||||
drop_last=drop_last, rank=rank, word_size=word_size) | drop_last=drop_last, rank=rank, word_size=word_size) | ||||
def __iter__(self) -> Iterable[List[int]]: | def __iter__(self) -> Iterable[List[int]]: | ||||
""" | """ | ||||
按照dataset的顺序采样,打包成一个batch后返回 | |||||
按照 ``dataset`` 的顺序采样,打包成一个 batch 后返回。 | |||||
:return: | :return: | ||||
""" | """ | ||||
@@ -384,22 +389,14 @@ class MixSequentialSampler(MixSampler): | |||||
class PollingSampler(MixSampler): | class PollingSampler(MixSampler): | ||||
""" | """ | ||||
定制给MixDataLoader的BatchSampler,其功能是将传入的datasets的list列表轮流采样并返回index,处理了上个dataset的一个batch后会处理下一个。 | |||||
定制给 :class:`~fastNLP.core.dataloaders.MixDataLoader` 的 ``BatchSampler``,其功能是将传入的 ``datasets`` 轮流采样并返回 index, | |||||
处理结束上个 dataset 的一个 batch 后会处理下一个。 | |||||
""" | """ | ||||
def __init__(self, dataset: Union[List, Dict], batch_size: int = 16, | def __init__(self, dataset: Union[List, Dict], batch_size: int = 16, | ||||
sampler: Union[List["Sampler"], Dict[str, "Sampler"], str] = None, | sampler: Union[List["Sampler"], Dict[str, "Sampler"], str] = None, | ||||
drop_last: bool = False, ds_ratio="pad_to_most", rank: int = -1, | drop_last: bool = False, ds_ratio="pad_to_most", rank: int = -1, | ||||
word_size: int = -1) -> None: | word_size: int = -1) -> None: | ||||
""" | |||||
:param dataset: 实现了__getitem__和__len__的数据容器列表 | |||||
:param batch_size: 对应dataset的批次大小,可以为list或者为int,当为int时默认所有dataset | |||||
:param sampler: 实例化好的sampler,每个dataset对应一个sampler对象 | |||||
:param drop_last: 是否去掉最后一个batch的数据,其长度小于batch_size | |||||
:param ds_ratio: 当ds_ratio=None时候, 轮流采样dataset列表直至所有的数据集采样完;当ds_ratio='truncate_to_least'时, | |||||
以dataset列表最短的ds为基准,长的数据集会被截断;当ds_ratio='pad_to_most'时,以dataset列表最长ds为基准,短的数据集会被重采样 | |||||
""" | |||||
super(PollingSampler, self).__init__(dataset=dataset, batch_size=batch_size, | super(PollingSampler, self).__init__(dataset=dataset, batch_size=batch_size, | ||||
sampler=sampler, ds_ratio=ds_ratio, | sampler=sampler, ds_ratio=ds_ratio, | ||||
drop_last=drop_last, rank=rank, word_size=word_size) | drop_last=drop_last, rank=rank, word_size=word_size) | ||||
@@ -1,3 +1,14 @@ | |||||
""" | |||||
:class:`ReproducibleBatchSampler` 是 **fastNLP** 提供的一种特殊 BatchSampler,它可以记录采样过程中每一次采样和 epoch 的信息, | |||||
方便在保存-加载后能够从上一次采样结束的地方继续进行采样,实现 **断点重训** 。 | |||||
.. note:: | |||||
DataLoader 中只要存在 :class:`~fastNLP.core.samplers.reproducible_sampler.ReproducibleSampler` 或 :class:`ReproducibleBatchSampler` | |||||
中的一个便可以实现断点重训复现的功能。 | |||||
""" | |||||
__all__ = [ | __all__ = [ | ||||
'BucketedBatchSampler', | 'BucketedBatchSampler', | ||||
"ReproduceBatchSampler", | "ReproduceBatchSampler", | ||||
@@ -19,6 +30,12 @@ from abc import abstractmethod | |||||
class ReproducibleBatchSampler: | class ReproducibleBatchSampler: | ||||
""" | |||||
**可复现**的 BatchSampler 对象。 | |||||
注意所有继承 :class:`ReproducibleBatchSampler` 的类的 :meth:`__init__` 方法中都需要加入参数 `**kwargs`,用来使我们再断点重训时重新实例化这个 BatchSampler | |||||
注意,所有 :meth:`__init__` 中初始化的变量,都不能含有 ``_`` 下横线作为开头;所有不在 :meth:`__init__` 中设置的变量都必须以下横线开头。 | |||||
""" | |||||
def __init__(self, **kwargs): | def __init__(self, **kwargs): | ||||
self.num_replicas = 1 | self.num_replicas = 1 | ||||
@@ -57,13 +74,13 @@ class ReproducibleBatchSampler: | |||||
class ReproduceBatchSampler(ReproducibleBatchSampler): | class ReproduceBatchSampler(ReproducibleBatchSampler): | ||||
""" | """ | ||||
可以使得 batch_sampler 对象状态恢复的 wrapper 。 | |||||
可以使得 ``batch_sampler`` 对象状态恢复的 wrapper 。 | |||||
:param batch_sampler: 可迭代出 数字 或 数字列表 的可迭代对象。ReproduceBatchSampler 将首先遍历一边该对象,然后将迭代 | |||||
出来的序号暂存起来,使用时按照 batch_size 的 batch 大小吐出序号列表。 | |||||
:param batch_size: 每个 batch 的大小是多少。 | |||||
:param drop_last: 如果最后一个 batch 无法构成 batch_size 那么多个 sample ,是否丢掉。 | |||||
:param kwargs: fastNLP 内部使用。 | |||||
:param batch_sampler: 可迭代出 **数字** 或 **数字列表** 的可迭代对象。:class:`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): | def __init__(self, batch_sampler, batch_size: int, drop_last: bool, **kwargs): | ||||
super().__init__() | super().__init__() | ||||
@@ -162,12 +179,12 @@ class RandomBatchSampler(ReproducibleBatchSampler): | |||||
""" | """ | ||||
随机分 batch 的 batch_sampler 。 | 随机分 batch 的 batch_sampler 。 | ||||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||||
:param dataset: 实现了 __len__ 方法的数据容器 | |||||
:param batch_size: 每个 batch 的大小 | :param batch_size: 每个 batch 的大小 | ||||
:param shuffle: 如果为 True,将不进行 shuffle,实际上数据会以从长到短的方式输出。 | |||||
:param drop_last: 如果最后一个 batch 的 sample 数量无法凑齐 batch_size 这么多,是否需要丢掉。 | |||||
:param shuffle: 如果为 ``True``,将不进行打乱操作,实际上数据会以从长到短的方式输出 | |||||
:param drop_last: 如果最后一个 batch 无法构成 batch_size 个 sample ,是否丢掉 | |||||
:param seed: 设置的随机数种子 | :param seed: 设置的随机数种子 | ||||
:param kwargs: fastNLP 保留使用 | |||||
:param kwargs: fastNLP 内部使用的参数 | |||||
""" | """ | ||||
def __init__(self, dataset, batch_size:int = 32, shuffle: bool = True, | def __init__(self, dataset, batch_size:int = 32, shuffle: bool = True, | ||||
drop_last: bool = False, seed: int = 0, **kwargs): | drop_last: bool = False, seed: int = 0, **kwargs): | ||||
@@ -195,6 +212,15 @@ class RandomBatchSampler(ReproducibleBatchSampler): | |||||
self.old_batch_size = kwargs.get('old_batch_size', self.batch_size) | self.old_batch_size = kwargs.get('old_batch_size', self.batch_size) | ||||
def set_distributed(self, num_replicas, rank, pad=True): | def set_distributed(self, num_replicas, rank, pad=True): | ||||
""" | |||||
进行分布式的相关设置,应当在初始化该 BatchSampler 本身后立即被调用。 | |||||
:param num_replicas: 分布式训练中的进程总数 | |||||
:param rank: 当前进程的 ``global_rank`` | |||||
:param pad: 如果 sample 数量不整除 ``num_replicas`` 的时候,要不要 pad 一下,使得最终使得每个进程上 | |||||
的 sample 数量是完全一致的 | |||||
:return: 自身 | |||||
""" | |||||
assert self.during_iter is False, "Cannot set the sampler to be distributed when it is " \ | assert self.during_iter is False, "Cannot set the sampler to be distributed when it is " \ | ||||
"during an unfinished iteration." | "during an unfinished iteration." | ||||
assert num_replicas > 0 and isinstance(num_replicas, int) | assert num_replicas > 0 and isinstance(num_replicas, int) | ||||
@@ -266,14 +292,14 @@ class RandomBatchSampler(ReproducibleBatchSampler): | |||||
if self.epoch < 0: # 防止用户没有修改epoch,导致每个epoch都一样了 | if self.epoch < 0: # 防止用户没有修改epoch,导致每个epoch都一样了 | ||||
self.epoch -= 1 | self.epoch -= 1 | ||||
def batchify(self, indices, batch_size, seed): | |||||
def batchify(self, indices, batch_size, seed) -> List[List[int]]: | |||||
""" | """ | ||||
将 indices 分为 batches | |||||
将 ``indices`` 分为 batches | |||||
:param sorted_indices: List[int] | |||||
:param indices: List[int] | |||||
:param batch_size: int | :param batch_size: int | ||||
:param seed: int | :param seed: int | ||||
:return: List[List[int]] | |||||
:return: | |||||
""" | """ | ||||
# 实际的 bucket 大小 | # 实际的 bucket 大小 | ||||
rng = np.random.default_rng(abs(seed)) | rng = np.random.default_rng(abs(seed)) | ||||
@@ -299,19 +325,15 @@ class RandomBatchSampler(ReproducibleBatchSampler): | |||||
@property | @property | ||||
def total_size(self): | def total_size(self): | ||||
""" | """ | ||||
这个变量代表的含义是当前这个sampler会最终产生出的index数量(包括了其它rank的),因为replica和pad的原因,这个值可能等于、 | |||||
大于或者小于len(dataset) | |||||
:return: | |||||
当前 BatchSampler 会最终产生出的 index 数量(包括了其它 rank 的),因为 ``replica`` 和 ``pad`` 的原因,这个值可能等于、 | |||||
大于或者小于 ``len(dataset)``。 | |||||
""" | """ | ||||
return self.num_consumed_samples + self.num_replicas*self.num_left_samples | return self.num_consumed_samples + self.num_replicas*self.num_left_samples | ||||
@property | @property | ||||
def num_left_samples(self): | def num_left_samples(self): | ||||
""" | """ | ||||
返回当前 iteration 还有多少个 sample 结束,表示的是当前 rank 的还剩多少。 | |||||
:return: | |||||
当前迭代还有多少个 sample 结束,表示的是 **当前 rank** 的还剩多少。 | |||||
""" | """ | ||||
num_consumed_samples = self.num_consumed_samples | num_consumed_samples = self.num_consumed_samples | ||||
return math.ceil((self.num_samples - num_consumed_samples) / self.num_replicas) if \ | return math.ceil((self.num_samples - num_consumed_samples) / self.num_replicas) if \ | ||||
@@ -320,9 +342,7 @@ class RandomBatchSampler(ReproducibleBatchSampler): | |||||
@property | @property | ||||
def num_samples(self): | def num_samples(self): | ||||
""" | """ | ||||
返回样本的总数 | |||||
:return: | |||||
样本的总数 | |||||
""" | """ | ||||
total_len = getattr(self.dataset, 'total_len', None) | total_len = getattr(self.dataset, 'total_len', None) | ||||
if not isinstance(total_len, int): | if not isinstance(total_len, int): | ||||
@@ -377,18 +397,19 @@ class RandomBatchSampler(ReproducibleBatchSampler): | |||||
class BucketedBatchSampler(ReproducibleBatchSampler): | class BucketedBatchSampler(ReproducibleBatchSampler): | ||||
""" | """ | ||||
首先按照 ``sample`` 的长度排序,然后按照 batch_size*num_batch_per_bucket 为一个桶的大小,``sample`` 只会在这个桶内进行组 | |||||
首先按照 ``sample`` 的长度排序,然后按照 *batch_size*num_batch_per_bucket* 为一个桶的大小,``sample`` 只会在这个桶内进行组 | |||||
合,这样每个 ``batch`` 中的 ``padding`` 数量会比较少 (因为桶内的数据的长度都接近)。 | 合,这样每个 ``batch`` 中的 ``padding`` 数量会比较少 (因为桶内的数据的长度都接近)。 | ||||
:param dataset: 实现了 __len__ 方法的数据容器。 | :param dataset: 实现了 __len__ 方法的数据容器。 | ||||
:param length: 每条数据的长度。 | :param length: 每条数据的长度。 | ||||
* 为 ``List[int]`` 时 | * 为 ``List[int]`` 时 | ||||
应当与 dataset 有一样的长度,表示 dataset 中每个元素的数量; | |||||
应当与 dataset 有一样的长度,表示 dataset 中每个元素的数量; | |||||
* 为 ``str`` 时 | * 为 ``str`` 时 | ||||
仅当传入的 ``dataset`` 是 :class:`~fastNLP.DataSet` 时,允许传入 `str` ,该 `str` 将被认为是 ``dataset`` 中的 | |||||
仅当传入的 ``dataset`` 是 :class:`~fastNLP.DataSet` 时,允许传入 `str` ,该 `str` 将被认为是 ``dataset`` 中的 | |||||
``field`` 。若 field 中的元素为 ``int``,则认为该值是 sample 的长度;若不为 ``int`` ,则尝试使用 ``len`` 方法 | ``field`` 。若 field 中的元素为 ``int``,则认为该值是 sample 的长度;若不为 ``int`` ,则尝试使用 ``len`` 方法 | ||||
获取该 ``field`` 中每个元素的长度。 | 获取该 ``field`` 中每个元素的长度。 | ||||
:param batch_size: 每个 batch 的大小 | :param batch_size: 每个 batch 的大小 | ||||
:param num_batch_per_bucket: 多少个 ``batch`` 组成一个桶,数据只会在一个桶内进行 ``shuffle`` 。 | :param num_batch_per_bucket: 多少个 ``batch`` 组成一个桶,数据只会在一个桶内进行 ``shuffle`` 。 | ||||
:param shuffle: 如果为 True,将不进行 ``shuffle``,实际上数据会以从长到短的方式输出。 | :param shuffle: 如果为 True,将不进行 ``shuffle``,实际上数据会以从长到短的方式输出。 | ||||
@@ -440,6 +461,15 @@ class BucketedBatchSampler(ReproducibleBatchSampler): | |||||
self.old_num_batch_per_bucket = kwargs.get('old_num_batch_per_bucket', self.num_batch_per_bucket) | self.old_num_batch_per_bucket = kwargs.get('old_num_batch_per_bucket', self.num_batch_per_bucket) | ||||
def set_distributed(self, num_replicas, rank, pad=True): | def set_distributed(self, num_replicas, rank, pad=True): | ||||
""" | |||||
进行分布式的相关设置,应当在初始化该 BatchSampler 本身后立即被调用。 | |||||
:param num_replicas: 分布式训练中的进程总数 | |||||
:param rank: 当前进程的 ``global_rank`` | |||||
:param pad: 如果 sample 数量不整除 ``num_replicas`` 的时候,要不要 pad 一下,使得最终使得每个进程上 | |||||
的 sample 数量是完全一致的 | |||||
:return: | |||||
""" | |||||
assert self.during_iter is False, "Cannot set the sampler to be distributed when it is " \ | assert self.during_iter is False, "Cannot set the sampler to be distributed when it is " \ | ||||
"during an unfinished iteration." | "during an unfinished iteration." | ||||
assert num_replicas > 0 and isinstance(num_replicas, int) | assert num_replicas > 0 and isinstance(num_replicas, int) | ||||
@@ -462,19 +492,15 @@ class BucketedBatchSampler(ReproducibleBatchSampler): | |||||
@property | @property | ||||
def total_size(self): | def total_size(self): | ||||
""" | """ | ||||
这个变量代表的含义是当前这个sampler会最终产生出的index数量(包括了其它rank的),因为replica和pad的原因,这个值可能等于、 | |||||
大于或者小于len(dataset) | |||||
:return: | |||||
当前 BatchSampler 会最终产生出的 index 数量(包括了其它 rank 的),因为 ``replica`` 和 ``pad`` 的原因,这个值可能等于、 | |||||
大于或者小于 ``len(dataset)``。 | |||||
""" | """ | ||||
return self.num_consumed_samples + self.num_replicas*self.num_left_samples | return self.num_consumed_samples + self.num_replicas*self.num_left_samples | ||||
@property | @property | ||||
def num_left_samples(self): | def num_left_samples(self): | ||||
""" | """ | ||||
返回当前 iteration 还有多少个 sample 结束,表示的是当前 rank 的还剩多少。 | |||||
:return: | |||||
当前迭代还有多少个 sample 结束,表示的是 **当前 rank** 的还剩多少。 | |||||
""" | """ | ||||
num_consumed_samples = self.num_consumed_samples | num_consumed_samples = self.num_consumed_samples | ||||
return math.ceil((self.num_samples - num_consumed_samples) / self.num_replicas) if \ | return math.ceil((self.num_samples - num_consumed_samples) / self.num_replicas) if \ | ||||
@@ -483,9 +509,7 @@ class BucketedBatchSampler(ReproducibleBatchSampler): | |||||
@property | @property | ||||
def num_samples(self): | def num_samples(self): | ||||
""" | """ | ||||
返回样本的总数 | |||||
:return: | |||||
样本的总数 | |||||
""" | """ | ||||
total_len = getattr(self.dataset, 'total_len', None) | total_len = getattr(self.dataset, 'total_len', None) | ||||
if not isinstance(total_len, int): | if not isinstance(total_len, int): | ||||
@@ -572,15 +596,15 @@ class BucketedBatchSampler(ReproducibleBatchSampler): | |||||
if self.epoch < 0: # 防止用户没有修改epoch,导致每个epoch都一样了 | if self.epoch < 0: # 防止用户没有修改epoch,导致每个epoch都一样了 | ||||
self.epoch -= 1 | self.epoch -= 1 | ||||
def bucketerize(self, sorted_indices, batch_size, num_batch_per_bucket, seed): | |||||
def bucketerize(self, sorted_indices, batch_size, num_batch_per_bucket, seed) -> List[List[int]]: | |||||
""" | """ | ||||
将 indices 分桶 | |||||
将 ``indices`` 分桶 | |||||
:param sorted_indices: List[int] | :param sorted_indices: List[int] | ||||
:param batch_size: int | :param batch_size: int | ||||
:param num_batch_per_bucket: int | :param num_batch_per_bucket: int | ||||
:param seed: int | :param seed: int | ||||
:return: List[List[int]] | |||||
:return: | |||||
""" | """ | ||||
# 实际的 bucket 大小 | # 实际的 bucket 大小 | ||||
bucket_size = min(len(sorted_indices), batch_size * num_batch_per_bucket) | bucket_size = min(len(sorted_indices), batch_size * num_batch_per_bucket) | ||||
@@ -1,3 +1,14 @@ | |||||
""" | |||||
:class:`ReproducibleSampler` 是 **fastNLP** 提供的一种特殊 Sampler,它可以记录采样过程中每一次采样和 epoch 的信息, | |||||
方便在保存-加载后能够从上一次采样结束的地方继续进行采样,实现 **断点重训** 。 | |||||
.. note:: | |||||
DataLoader 中只要存在 :class:`ReproducibleSampler` 或 :class:`~fastNLP.core.samplers.reproducible_batch_sampler.ReproducibleBatchSampler` | |||||
中的一个便可以实现断点重训复现的功能。 | |||||
""" | |||||
__all__ = [ | __all__ = [ | ||||
'ReproducibleSampler', | 'ReproducibleSampler', | ||||
'RandomSampler', | 'RandomSampler', | ||||
@@ -16,10 +27,10 @@ from fastNLP.core.dataset import DataSet | |||||
class ReproducibleSampler: | class ReproducibleSampler: | ||||
""" | """ | ||||
可复现的 Sampler 对象。 | |||||
**可复现** 的 Sampler 对象。 | |||||
注意所有继承 `ReproducibleSampler` 的类的 `__init__` 方法中都需要加入参数 `**kwargs`,用来使我们再断点重训时重新实例化这个 sampler | |||||
或者 batch_sampler;注意,所有在 init 中初始化的变量,都不能含有 _ 下横线作为开头;所有不在 init 中设置的变量都必须以下横线开头。 | |||||
注意所有继承 :class:`ReproducibleSampler` 的类的 :meth:`__init__` 方法中都需要加入参数 `**kwargs`,用来使我们再断点重训时重新实例化这个 Sampler | |||||
注意,所有 :meth:`__init__` 中初始化的变量,都不能含有 ``_`` 下横线作为开头;所有不在 :meth:`__init__` 中设置的变量都必须以下横线开头。 | |||||
""" | """ | ||||
def __init__(self, **kwargs): | def __init__(self, **kwargs): | ||||
@@ -61,9 +72,9 @@ class RandomSampler(ReproducibleSampler): | |||||
随机顺序的 Sampler 。 | 随机顺序的 Sampler 。 | ||||
:param dataset: 实现了 __len__ 方法的数据容器 | :param dataset: 实现了 __len__ 方法的数据容器 | ||||
:param shuffle: 是否在每次 iterate 的时候打乱顺序。 | |||||
:param seed: 随机数种子。 | |||||
:param kwargs: 用户不需要使用,fastNLP 内部使用 | |||||
:param shuffle: 是否在每次 iterate 的时候打乱顺序 | |||||
:param seed: 随机数种子 | |||||
:param kwargs: fastNLP 内部使用的参数 | |||||
""" | """ | ||||
def __init__(self, dataset, shuffle: bool = True, seed: int = 0, **kwargs): | def __init__(self, dataset, shuffle: bool = True, seed: int = 0, **kwargs): | ||||
super(RandomSampler, self).__init__() | super(RandomSampler, self).__init__() | ||||
@@ -84,15 +95,16 @@ class RandomSampler(ReproducibleSampler): | |||||
def __len__(self): | def __len__(self): | ||||
""" | """ | ||||
返回 sampler 一次完整的迭代过程会产生多少个index。多卡的情况下,只考虑当前rank; | |||||
:return: | |||||
返回 sampler 一次完整的迭代过程会产生多少个index。多卡的情况下,只考虑当前rank。 | |||||
""" | """ | ||||
return self.total_size//self.num_replicas | return self.total_size//self.num_replicas | ||||
def __iter__(self): | def __iter__(self): | ||||
r""" | r""" | ||||
当前使用num_consumed_samples做法会在交替使用的时候遇到问题; | |||||
当前使用 num_consumed_samples 做法会在交替使用的时候遇到问题。 | |||||
Example:: | Example:: | ||||
>>> sampler = RandomSampler() | >>> sampler = RandomSampler() | ||||
>>> iter1 = iter(sampler) | >>> iter1 = iter(sampler) | ||||
>>> iter2 = iter(sampler) | >>> iter2 = iter(sampler) | ||||
@@ -131,8 +143,6 @@ class RandomSampler(ReproducibleSampler): | |||||
def generate_indices(self) -> List[int]: | def generate_indices(self) -> List[int]: | ||||
""" | """ | ||||
生成随机序列 | 生成随机序列 | ||||
:return: | |||||
""" | """ | ||||
if self.shuffle: | if self.shuffle: | ||||
indices = list(range(self.num_samples)) | indices = list(range(self.num_samples)) | ||||
@@ -176,12 +186,13 @@ class RandomSampler(ReproducibleSampler): | |||||
def set_distributed(self, num_replicas:int, rank:int, pad:bool=True): | def set_distributed(self, num_replicas:int, rank:int, pad:bool=True): | ||||
""" | """ | ||||
进行分布式的相关设置,应当在初始化该 Sampler 本身后立即被调用。 | |||||
:param num_replicas: | |||||
:param rank: | |||||
:param pad: 这个 pad 的意思是指如果 sample 数量不整除 num_replicas 的时候,要不要 pad 一下,使得最终使得 replica 上 | |||||
的 sample 数量是完全一致的。 | |||||
:return: | |||||
:param num_replicas: 分布式训练中的进程总数 | |||||
:param rank: 当前进程的 ``global_rank``。 | |||||
:param pad: 如果 sample 数量不整除 ``num_replicas`` 的时候,要不要 pad 一下,使得最终使得每个进程上 | |||||
的 sample 数量是完全一致的 | |||||
:return: 自身 | |||||
""" | """ | ||||
assert self.during_iter is False, "Cannot set the sampler to be distributed when it is " \ | assert self.during_iter is False, "Cannot set the sampler to be distributed when it is " \ | ||||
@@ -198,18 +209,15 @@ class RandomSampler(ReproducibleSampler): | |||||
@property | @property | ||||
def total_size(self): | def total_size(self): | ||||
""" | """ | ||||
这个变量代表的含义是当前这个sampler会最终产生出的index数量,因为replica和pad的原因,这个值可能等于、大于或者小于len(dataset) | |||||
:return: | |||||
当前 sampler 会最终产生出的 index 数量(包括了其它 rank 的),因为 ``replica`` 和 ``pad`` 的原因,这个值可能等于、 | |||||
大于或者小于 ``len(dataset)``。 | |||||
""" | """ | ||||
return self.num_consumed_samples + self.num_replicas*self.num_left_samples | return self.num_consumed_samples + self.num_replicas*self.num_left_samples | ||||
@property | @property | ||||
def num_left_samples(self): | def num_left_samples(self): | ||||
""" | """ | ||||
返回当前 iteration 还有多少个 sample 结束。表示的是当前 rank 的还剩多少 | |||||
:return: | |||||
当前迭代还有多少个 sample 结束。表示的是 **当前 rank** 的还剩多少 | |||||
""" | """ | ||||
num_consumed_samples = self.num_consumed_samples | num_consumed_samples = self.num_consumed_samples | ||||
return math.ceil((self.num_samples - num_consumed_samples) / self.num_replicas) if \ | return math.ceil((self.num_samples - num_consumed_samples) / self.num_replicas) if \ | ||||
@@ -218,9 +226,7 @@ class RandomSampler(ReproducibleSampler): | |||||
@property | @property | ||||
def num_samples(self): | def num_samples(self): | ||||
""" | """ | ||||
返回样本的总数 | |||||
:return: | |||||
样本的总数 | |||||
""" | """ | ||||
total_len = getattr(self.dataset, 'total_len', None) | total_len = getattr(self.dataset, 'total_len', None) | ||||
if not isinstance(total_len, int): | if not isinstance(total_len, int): | ||||
@@ -229,7 +235,7 @@ class RandomSampler(ReproducibleSampler): | |||||
class SequentialSampler(RandomSampler): | class SequentialSampler(RandomSampler): | ||||
""" | """ | ||||
按照顺序读取 ``dataset`` 。在多卡情况下,间隔读取,例如,在两卡情况下,卡 0 取 ``[0,2,4,..]``, 卡1取 ``[1,3,5...]`` 。 | |||||
按照顺序读取 ``dataset`` 。在多卡情况下,间隔读取,例如,在两卡情况下,卡 0 取 ``[0,2,4,..]``, 卡 1 取 ``[1,3,5...]`` 。 | |||||
:param dataset: 实现了 __len__ 方法的数据容器。 | :param dataset: 实现了 __len__ 方法的数据容器。 | ||||
:param kwargs: | :param kwargs: | ||||
@@ -300,17 +306,18 @@ class SortedSampler(SequentialSampler): | |||||
将 ``dataset`` 中的数据根据 ``length`` 从长到短进行迭代。在多卡情况下,由于 ``padding`` , 最后一个 ``sample`` 可能是最长 | 将 ``dataset`` 中的数据根据 ``length`` 从长到短进行迭代。在多卡情况下,由于 ``padding`` , 最后一个 ``sample`` 可能是最长 | ||||
的那个 ``sample`` 。 | 的那个 ``sample`` 。 | ||||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||||
:param length: 每条数据的长度。 | |||||
:param dataset: 实现了 __len__ 方法的数据容器 | |||||
:param length: 每条数据的长度: | |||||
* 为 ``List[int]`` 时 | * 为 ``List[int]`` 时 | ||||
应当与 dataset 有一样的长度,表示 dataset 中每个元素的数量; | |||||
应当与 dataset 有一样的长度,表示 dataset 中每个元素的数量; | |||||
* 为 ``str`` 时 | * 为 ``str`` 时 | ||||
仅当传入的 ``dataset`` 是 :class:`~fastNLP.DataSet` 时,允许传入 `str` ,该 `str` 将被认为是 ``dataset`` 中的 | |||||
仅当传入的 ``dataset`` 是 :class:`~fastNLP.DataSet` 时,允许传入 `str` ,该 `str` 将被认为是 ``dataset`` 中的 | |||||
``field`` 。若 field 中的元素为 ``int``,则认为该值是 sample 的长度;若不为 ``int`` ,则尝试使用 ``len`` 方法 | ``field`` 。若 field 中的元素为 ``int``,则认为该值是 sample 的长度;若不为 ``int`` ,则尝试使用 ``len`` 方法 | ||||
获取该 ``field`` 中每个元素的长度。 | |||||
:param seed: 设置的随机数种子。 | |||||
:param kwargs: fastNLP 保留使用。 | |||||
获取该 ``field`` 中每个元素的长度; | |||||
:param seed: 设置的随机数种子 | |||||
:param kwargs: fastNLP 内部使用的参数 | |||||
""" | """ | ||||
def __init__(self, dataset, length:Union[str, List], **kwargs): | def __init__(self, dataset, length:Union[str, List], **kwargs): | ||||
super().__init__(dataset=dataset, **kwargs) | super().__init__(dataset=dataset, **kwargs) | ||||
@@ -13,19 +13,19 @@ import numpy as np | |||||
class UnrepeatedSampler: | class UnrepeatedSampler: | ||||
""" | """ | ||||
在多卡场景下保证 indice 不重复的 sampler | |||||
在多卡场景下保证 indice 不重复的 Sampler。 | |||||
""" | """ | ||||
pass | pass | ||||
class UnrepeatedRandomSampler(UnrepeatedSampler): | class UnrepeatedRandomSampler(UnrepeatedSampler): | ||||
""" | """ | ||||
考虑在多卡 evaluate 的场景下,不能重复 sample。 | |||||
考虑在多卡 evaluate 的场景下,不能重复采样。 | |||||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||||
:param shuffle: 如果为 True,将不进行 shuffle,实际上数据会以从长到短的方式输出。 | |||||
:param dataset: 实现了 __len__ 方法的数据容器 | |||||
:param shuffle: 如果为 ``True``,将不进行 shuffle,实际上数据会以从长到短的方式输出 | |||||
:param seed: 设置的随机数种子 | :param seed: 设置的随机数种子 | ||||
:param kwargs: fastNLP 保留使用 | |||||
:param kwargs: fastNLP 内部使用的参数 | |||||
""" | """ | ||||
def __init__(self, dataset, shuffle: bool = False, seed: int = 0, **kwargs): | def __init__(self, dataset, shuffle: bool = False, seed: int = 0, **kwargs): | ||||
self.dataset = dataset | self.dataset = dataset | ||||
@@ -39,7 +39,7 @@ class UnrepeatedRandomSampler(UnrepeatedSampler): | |||||
def __len__(self): | def __len__(self): | ||||
""" | """ | ||||
返回 sampler 一次完整的迭代过程会产生多少个index。多卡的情况下,只考虑当前rank; | |||||
返回 ``Sampler`` 一次完整的迭代过程会产生多少个 index 。多卡的情况下,只考虑 **当前rank** 。 | |||||
:return: | :return: | ||||
""" | """ | ||||
num_common = self.num_samples//self.num_replicas | num_common = self.num_samples//self.num_replicas | ||||
@@ -78,11 +78,11 @@ class UnrepeatedRandomSampler(UnrepeatedSampler): | |||||
def set_distributed(self, num_replicas, rank): | def set_distributed(self, num_replicas, rank): | ||||
""" | """ | ||||
该方法本质上等同于 ddp 情形下的没有完成的初始化,应当在初始化该 sampler 本身后立即被调用; | |||||
该方法本质上等同于 ddp 情形下的没有完成的初始化,应当在初始化该 Sampler 本身后立即被调用。 | |||||
:param num_replicas: | |||||
:param rank: | |||||
:return: | |||||
:param num_replicas: 分布式训练中的进程总数 | |||||
:param rank: 当前进程的 ``global_rank`` | |||||
:return: 自身 | |||||
""" | """ | ||||
assert num_replicas<=self.num_samples, f"The number of replicas({num_replicas}) should be lesser than the " \ | assert num_replicas<=self.num_samples, f"The number of replicas({num_replicas}) should be lesser than the " \ | ||||
f"number of samples({self.num_samples})." | f"number of samples({self.num_samples})." | ||||
@@ -97,28 +97,30 @@ class UnrepeatedRandomSampler(UnrepeatedSampler): | |||||
@property | @property | ||||
def num_samples(self): | def num_samples(self): | ||||
""" | """ | ||||
返回样本的总数 | |||||
:return: | |||||
样本的总数 | |||||
""" | """ | ||||
return getattr(self.dataset, 'total_len', len(self.dataset)) | return getattr(self.dataset, 'total_len', len(self.dataset)) | ||||
class UnrepeatedSortedSampler(UnrepeatedRandomSampler): | class UnrepeatedSortedSampler(UnrepeatedRandomSampler): | ||||
""" | """ | ||||
将 dataset 中的数据根据 length 从长到短进行迭代,并且保证在多卡场景下数据不重复。本 sampler 可能导致各个机器上的 | |||||
batch 数量不完全一致。 | |||||
将 ``dataset`` 中的数据根据 ``length`` 从长到短进行迭代,并且保证在多卡场景下数据不重复。 | |||||
.. note:: | |||||
本 Sampler 可能导致各个机器上的batch 数量不完全一致。 | |||||
:param dataset: 实现了 __len__ 方法的数据容器。 | |||||
:param length: 每条数据的长度。 | |||||
:param dataset: 实现了 __len__ 方法的数据容器 | |||||
:param length: 每条数据的长度 | |||||
* 为 ``List[int]`` 时 | * 为 ``List[int]`` 时 | ||||
应当与 dataset 有一样的长度,表示 dataset 中每个元素的数量; | 应当与 dataset 有一样的长度,表示 dataset 中每个元素的数量; | ||||
* 为 ``str`` 时 | * 为 ``str`` 时 | ||||
仅当传入的 ``dataset`` 是 :class:`~fastNLP.DataSet` 时,允许传入 `str` ,该 `str` 将被认为是 ``dataset`` 中的 | |||||
仅当传入的 ``dataset`` 是 :class:`~fastNLP.DataSet` 时,允许传入 `str` ,该 `str` 将被认为是 ``dataset`` 中的 | |||||
``field`` 。若 field 中的元素为 ``int``,则认为该值是 sample 的长度;若不为 ``int`` ,则尝试使用 ``len`` 方法 | ``field`` 。若 field 中的元素为 ``int``,则认为该值是 sample 的长度;若不为 ``int`` ,则尝试使用 ``len`` 方法 | ||||
获取该 ``field`` 中每个元素的长度。 | |||||
:param kwargs: fastNLP 保留使用 | |||||
获取该 ``field`` 中每个元素的长度; | |||||
:param kwargs: fastNLP 内部使用的参数 | |||||
""" | """ | ||||
def __init__(self, dataset, length:Union[str, List], **kwargs): | def __init__(self, dataset, length:Union[str, List], **kwargs): | ||||
kwargs['shuffle'] = False | kwargs['shuffle'] = False | ||||
@@ -146,9 +148,9 @@ class UnrepeatedSequentialSampler(UnrepeatedRandomSampler): | |||||
按照顺序读取 dataset。 | 按照顺序读取 dataset。 | ||||
:param dataset: 实现了 __len__ 方法的数据容器。 | :param dataset: 实现了 __len__ 方法的数据容器。 | ||||
:param chunk_dist: 如果为 True ,当多卡时,将不间隔索取数据;为 False ,间隔取数据。例如,假设 dataset 有 10 个 sample ,使用 | |||||
2 卡,如果为 True ,卡 0 拿 [0, 1, 2, 3, 4], 卡 1 拿 [5, 6, 7, 8, 9] ; 如果为 False ,则卡 0 拿 [0, 2, 4, 8, 8], 卡 | |||||
1 拿 [1, 3, 5, 7, 9] 。 | |||||
:param chunk_dist: 如果为 ``True`` ,当多卡时将不间隔索取数据;为 ``False`` 时则会间隔取数据。假设 dataset 有 10 个 sample ,使用 | |||||
2 卡,如果为 ``True`` ,卡 **0** 拿 [0, 1, 2, 3, 4], 卡 **1** 拿 [5, 6, 7, 8, 9] ; 如果为 ``False`` ,则卡 **0** 拿 [0, 2, 4, 8, 8], | |||||
卡 **1** 拿 [1, 3, 5, 7, 9] 。 | |||||
:param kwargs: | :param kwargs: | ||||
""" | """ | ||||
def __init__(self, dataset, chunk_dist=False, **kwargs): | def __init__(self, dataset, chunk_dist=False, **kwargs): | ||||
@@ -183,9 +183,9 @@ def cal_fn_hash_code(fn: Optional[Callable] = None, fn_kwargs: Optional[dict] = | |||||
return hasher.hexdigest() | return hasher.hexdigest() | ||||
def cache_results(_cache_fp, _hash_param=True, _refresh=False, _verbose=1, _check_hash=True): | |||||
def cache_results(_cache_fp: str, _hash_param: bool = True, _refresh: bool = False, _verbose: int = 1, _check_hash: bool = True): | |||||
r""" | r""" | ||||
cache_results是fastNLP中用于cache数据的装饰器。通过下面的例子看一下如何使用:: | |||||
:func:`cache_results` 是 **fastNLP** 中用于缓存数据的装饰器。通过下面的例子看一下如何使用:: | |||||
import time | import time | ||||
import numpy as np | import numpy as np | ||||
@@ -220,19 +220,20 @@ def cache_results(_cache_fp, _hash_param=True, _refresh=False, _verbose=1, _chec | |||||
# res = [1 8 2 5 1] | # res = [1 8 2 5 1] | ||||
# 2.0086121559143066 | # 2.0086121559143066 | ||||
可以看到第二次运行的时候,只用了0.0001s左右,是由于第二次运行将直接从cache.pkl这个文件读取数据,而不会经过再次预处理。 | |||||
如果在函数加上了装饰器@cache_results(),则函数会增加五个参数[_cache_fp, _hash_param, _refresh, _verbose, | |||||
_check_hash]。上面的例子即为使用_cache_fp的情况,这五个参数不会传入到被装饰函数中,当然被装饰函数参数名也不能包含这五个名称。 | |||||
可以看到第二次运行的时候,只用了 0.0001s 左右,这是由于第二次运行将直接从cache.pkl这个文件读取数据,而不会经过再次预处理。 | |||||
如果在函数加上了装饰器 ``@cache_results()``,则函数会增加五个参数 ``[_cache_fp, _hash_param, _refresh, _verbose, | |||||
_check_hash]``。上面的例子即为使用_cache_fp的情况,这五个参数不会传入到被装饰函数中,当然被装饰函数参数名也不能包含这五个名称。 | |||||
:param str _cache_fp: 将返回结果缓存到什么位置;或从什么位置读取缓存。如果为None,cache_results没有任何效用,除非在 | |||||
函数调用的时候传入 _cache_fp 这个参数。保存文件的名称会受到 | |||||
:param bool _hash_param: 是否将传入给被装饰函数的 parameter 进行 str 之后的 hash 结果加入到 _cache_fp 中,这样每次函数的 | |||||
:param _cache_fp: 将返回结果缓存到什么位置;或从什么位置读取缓存。如果为 ``None`` ,cache_results 没有任何效用,除非在 | |||||
函数调用的时候传入 _cache_fp 这个参数。实际保存的文件名会受到 ``_hash_param`` 参数的影响,例如传入的名称是 **"caches/cache.pkl"**, | |||||
实际保存的文件名会是 **"caches/{hash_param_result}_cache.pkl"**。 | |||||
:param _hash_param: 是否将传入给被装饰函数的 parameter 进行 :func:`str` 之后的 hash 结果加入到 ``_cache_fp`` 中,这样每次函数的 | |||||
parameter 改变的时候,cache 文件就自动改变了。 | parameter 改变的时候,cache 文件就自动改变了。 | ||||
:param bool _refresh: 强制重新生成新的 cache 。 | |||||
:param int _verbose: 是否打印cache的信息。 | |||||
:param bool _check_hash: 如果为 True 将尝试对比修饰的函数的源码以及该函数内部调用的函数的源码的hash值。如果发现保存时的hash值 | |||||
与当前的hash值有差异,会报warning。但该warning可能出现实质上并不影响结果的误报(例如增删空白行);且在修改不涉及源码时,虽然 | |||||
该修改对结果有影响,但无法做出warning。 | |||||
:param _refresh: 强制重新生成新的 cache 。 | |||||
:param _verbose: 是否打印 cache 的信息。 | |||||
:param _check_hash: 如果为 ``True`` 将尝试对比修饰的函数的源码以及该函数内部调用的函数的源码的 hash 值。如果发现保存时的 hash 值 | |||||
与当前的 hash 值有差异,会报 warning 。但该 warning 可能出现实质上并不影响结果的误报(例如增删空白行);且在修改不涉及源码时,虽然 | |||||
该修改对结果有影响,但无法做出 warning。 | |||||
:return: | :return: | ||||
""" | """ | ||||
@@ -1,7 +1,7 @@ | |||||
class EarlyStopException(BaseException): | class EarlyStopException(BaseException): | ||||
r""" | r""" | ||||
用于EarlyStop时从Trainer训练循环中跳出。 | |||||
用于 EarlyStop 时从 Trainer 训练循环中跳出。 | |||||
""" | """ | ||||
@@ -16,10 +16,10 @@ from fastNLP.core.dataset import Instance | |||||
def is_jittor_module(model) -> bool: | def is_jittor_module(model) -> bool: | ||||
""" | """ | ||||
判断传入的 ``model`` 是否是 :class:`jittor.Module` 类型 | |||||
判断传入的 ``model`` 是否是 :class:`jittor.Module` 类型。 | |||||
:param model: 模型; | |||||
:return: 当前模型是否为 ``jittor`` 的模型; | |||||
:param model: | |||||
:return: 当前模型是否为 ``jittor`` 的模型 | |||||
""" | """ | ||||
try: | try: | ||||
return isinstance(model, jt.Module) | return isinstance(model, jt.Module) | ||||
@@ -28,10 +28,10 @@ def is_jittor_module(model) -> bool: | |||||
def is_jittor_dataset(dataset) -> bool: | def is_jittor_dataset(dataset) -> bool: | ||||
""" | """ | ||||
判断传入的 ``dataset`` 是否是 :class:`jittor.dataset.Dataset` 类型 | |||||
判断传入的 ``dataset`` 是否是 :class:`jittor.dataset.Dataset` 类型。 | |||||
:param dataset: 数据集; | |||||
:return: 当前 ``dataset`` 是否为 ``jittor`` 的数据集类型; | |||||
:param dataset: | |||||
:return: 当前 ``dataset`` 是否为 ``jittor`` 的数据集类型 | |||||
""" | """ | ||||
try: | try: | ||||
if isinstance(dataset, jt.dataset.Dataset): | if isinstance(dataset, jt.dataset.Dataset): | ||||
@@ -44,7 +44,7 @@ def is_jittor_dataset(dataset) -> bool: | |||||
def jittor_collate_wraps(func, auto_collator: Callable): | def jittor_collate_wraps(func, auto_collator: Callable): | ||||
""" | """ | ||||
对 ``jittor`` 的 ``collate_fn`` 进行 ``wrap`` 封装,。如果数据集为 ``mapping`` 类型,那么采用 ``auto_collator`` , | |||||
对 ``jittor`` 的 ``collate_fn`` 进行 wrap 封装,。如果数据集为 :class:`Mapping` 类型,那么采用 ``auto_collator`` , | |||||
否则还是采用 ``jittor`` 的 ``collate_batch``。 | 否则还是采用 ``jittor`` 的 ``collate_batch``。 | ||||
:param func: | :param func: | ||||
@@ -7,7 +7,7 @@ if _NEED_IMPORT_ONEFLOW: | |||||
import oneflow | import oneflow | ||||
__all__ = [ | __all__ = [ | ||||
'get_oneflow_device' | |||||
'get_oneflow_device', | |||||
'oneflow_move_data_to_device', | 'oneflow_move_data_to_device', | ||||
'is_oneflow_module', | 'is_oneflow_module', | ||||
'is_in_oneflow_dist', | 'is_in_oneflow_dist', | ||||
@@ -32,11 +32,11 @@ def get_oneflow_device(device): | |||||
def oneflow_move_data_to_device(batch: Any, device: Optional[Union[str, "oneflow.device"]] = None) -> Any: | def oneflow_move_data_to_device(batch: Any, device: Optional[Union[str, "oneflow.device"]] = None) -> Any: | ||||
r""" | r""" | ||||
在 **oneflow** 中将数据集合 ``batch`` 传输到给定设备。任何定义方法 ``to(device)`` 的对象都将被移动并且集合中的所有其他对象将保持不变; | |||||
在 **oneflow** 中将数据集合 ``batch`` 传输到给定设备。 | |||||
:param batch: 需要迁移的数据; | |||||
:param device: 数据应当迁移到的设备;当该参数的值为 ``None`` 时则不执行任何操作; | |||||
:return: 迁移到新设备上的数据集合; | |||||
:param batch: 需要迁移的数据 | |||||
:param device: 数据应当迁移到的设备;当该参数的值为 ``None`` 时则不执行任何操作 | |||||
:return: 迁移到新设备上的数据集合 | |||||
""" | """ | ||||
if device is None: | if device is None: | ||||
return batch | return batch | ||||
@@ -52,10 +52,10 @@ def oneflow_move_data_to_device(batch: Any, device: Optional[Union[str, "oneflow | |||||
def is_oneflow_module(model) -> bool: | def is_oneflow_module(model) -> bool: | ||||
""" | """ | ||||
判断传入的 ``model`` 是否是 :class:`oneflow.nn.Module` 类型 | |||||
判断传入的 ``model`` 是否是 :class:`oneflow.nn.Module` 类型。 | |||||
:param model: 模型; | |||||
:return: 当前模型是否为 ``oneflow`` 的模型; | |||||
:param model: | |||||
:return: 当前模型是否为 ``oneflow`` 的模型 | |||||
""" | """ | ||||
try: | try: | ||||
return isinstance(model, oneflow.nn.Module) | return isinstance(model, oneflow.nn.Module) | ||||
@@ -42,8 +42,8 @@ def _convert_data_device(device: Union[str, int]) -> str: | |||||
在分布式单进程仅支持单卡的情况下中,这个函数实际等同于直接转换为 ``gpu:0`` 返回。 | 在分布式单进程仅支持单卡的情况下中,这个函数实际等同于直接转换为 ``gpu:0`` 返回。 | ||||
:param device: 未转化的设备; | |||||
:return: 转化后的设备,格式为 ``gpu:x``; | |||||
:param device: 未转化的设备 | |||||
:return: 转化后的设备,格式为 ``gpu:x`` | |||||
""" | """ | ||||
try: | try: | ||||
user_visible_devices = os.getenv(USER_CUDA_VISIBLE_DEVICES) | user_visible_devices = os.getenv(USER_CUDA_VISIBLE_DEVICES) | ||||
@@ -65,8 +65,8 @@ def _convert_data_device(device: Union[str, int]) -> str: | |||||
def paddle_to(data: "paddle.Tensor", device: Union[str, int, 'paddle.fluid.core_avx.Place', | def paddle_to(data: "paddle.Tensor", device: Union[str, int, 'paddle.fluid.core_avx.Place', | ||||
'paddle.CPUPlace', 'paddle.CUDAPlace']) -> "paddle.Tensor": | 'paddle.CPUPlace', 'paddle.CUDAPlace']) -> "paddle.Tensor": | ||||
""" | """ | ||||
将 ``data`` 迁移到指定的 ``device`` 上。``paddle.Tensor`` 没有类似 ``torch.Tensor`` 的 ``to`` 函数, | |||||
该函数只是集成了 :func:`paddle.Tensor.cpu` 和 :func:`paddle.Tensor.cuda` 两个函数。 | |||||
将 ``data`` 迁移到指定的 ``device`` 上。:class:`paddle.Tensor` 没有类似 :meth:`torch.Tensor.to` 的函数来迁移张量, | |||||
因此该函数只是集成了 :func:`paddle.Tensor.cpu` 和 :func:`paddle.Tensor.cuda` 两个函数。 | |||||
:param data: 要迁移的张量; | :param data: 要迁移的张量; | ||||
:param device: 目标设备,可以是 ``str`` 或 ``int`` 及 **paddle** 自己的 :class:`paddle.fluid.core_avx.Place`、 | :param device: 目标设备,可以是 ``str`` 或 ``int`` 及 **paddle** 自己的 :class:`paddle.fluid.core_avx.Place`、 | ||||
@@ -1,5 +1,5 @@ | |||||
""" | """ | ||||
该文件用于为 **fastNLP** 提供一个统一的 ``progress bar`` 管理,通过共用一个``Task`` 对象, :class:`~fastNLP.core.Trainer` | |||||
该文件用于为 **fastNLP** 提供一个统一的 ``progress bar`` 管理,通过共用一个 ``Task`` 对象, :class:`~fastNLP.core.Trainer` | |||||
中的 ``progress bar`` 和 :class:`~fastNLP.core.Evaluator` 中的 ``progress bar`` 才能不冲突 | 中的 ``progress bar`` 和 :class:`~fastNLP.core.Evaluator` 中的 ``progress bar`` 才能不冲突 | ||||
""" | """ | ||||
import sys | import sys | ||||
@@ -87,23 +87,18 @@ class Vocabulary(object): | |||||
vocab["word"] # str to int | vocab["word"] # str to int | ||||
vocab.to_word(5) # int to str | vocab.to_word(5) # int to str | ||||
:param max_size: `Vocabulary` 的最大大小, 即能存储词的最大数量 | |||||
若为 ``None`` , 则不限制大小。 | |||||
:param min_freq: 能被记录下的词在文本中的最小出现频率, 应大于或等于 1。 | |||||
若小于该频率, 词语将被视为 `unknown`. 若为 ``None`` , 所有文本中的词都被记录。 | |||||
:param padding: padding的字符. 如果设置为 ``None`` , | |||||
则vocabulary中不考虑padding, 也不计入词表大小,为 ``None`` 的情况多在为 label 建立 Vocabulary 的情况。 | |||||
:param unknown: unknown的字符,所有未被记录的词在转为 :class:`int` 时将被视为 `unknown` 。 | |||||
如果设置为 ``None`` ,则 vocabulary 中不考虑 `unknown`, 也不计入词表大小。 | |||||
为 ``None`` 的情况多在为 labe l建立 Vocabulary 的情况 | |||||
""" | """ | ||||
def __init__(self, max_size:int=None, min_freq:int=None, padding:str='<pad>', unknown:str='<unk>'): | |||||
r""" | |||||
:param max_size: `Vocabulary` 的最大大小, 即能存储词的最大数量 | |||||
若为 ``None`` , 则不限制大小. Default: ``None`` | |||||
:param min_freq: 能被记录下的词在文本中的最小出现频率, 应大于或等于 1. | |||||
若小于该频率, 词语将被视为 `unknown`. 若为 ``None`` , 所有文本中的词都被记录. Default: ``None`` | |||||
:param padding: padding的字符. 如果设置为 ``None`` , | |||||
则vocabulary中不考虑padding, 也不计入词表大小,为 ``None`` 的情况多在为label建立Vocabulary的情况. | |||||
Default: '<pad>' | |||||
:param unknown: unknown的字符,所有未被记录的词在转为 `int` 时将被视为unknown. | |||||
如果设置为 ``None`` ,则vocabulary中不考虑unknow, 也不计入词表大小. | |||||
为 ``None`` 的情况多在为label建立Vocabulary的情况. | |||||
Default: '<unk>' | |||||
""" | |||||
def __init__(self, max_size:int=None, min_freq:int=None, padding:str='<pad>', unknown:str='<unk>'): | |||||
self.max_size = max_size | self.max_size = max_size | ||||
self.min_freq = min_freq | self.min_freq = min_freq | ||||
self.word_count = Counter() | self.word_count = Counter() | ||||
@@ -138,13 +133,16 @@ class Vocabulary(object): | |||||
r""" | r""" | ||||
依次增加序列中词在词典中的出现频率 | 依次增加序列中词在词典中的出现频率 | ||||
:param word_lst: 列表形式的词语,如word_list=['I', 'am', 'a', 'Chinese'],列表中的每个词会计算出现频率并加入到词典中。 | |||||
:param no_create_entry: 如果词语来自于非训练集建议设置为True。 | |||||
如果为True,则不会有这个词语创建一个单独的entry,它将一直被指向unk的表示; 如果为False,则为这个词创建一个单独 | |||||
的entry。如果这个word来自于dev或者test,一般设置为True,如果来自与train一般设置为False。以下两种情况: 如果新 | |||||
加入一个word,且no_create_entry为True,但这个词之前已经在Vocabulary中且并不是no_create_entry的,则还是会为这 | |||||
个词创建一个单独的vector; 如果no_create_entry为False,但这个词之前已经在Vocabulary中且并不是no_create_entry的, | |||||
则这个词将认为是需要创建单独的vector的。 | |||||
:param word_lst: 列表形式的词语,如 word_list=['I', 'am', 'a', 'Chinese'],列表中的每个词会计算出现频率并加入到词典中。 | |||||
:param no_create_entry: 如果词语来自于非训练集建议设置为 ``True`` 。 | |||||
* 如果为 ``True`` -- 则不会有这个词语创建一个单独的 entry ,它将一直被指向 ``<UNK>`` 的表示; | |||||
* 如果为 ``False`` -- 为这个词创建一个单独的 entry。如果这个词来自于验证集或训练集,一般设置为True,如果来自于训练集一 | |||||
般设置为``False``; | |||||
有以下两种情况: 如果新加入一个 word ,且 ``no_create_entry`` 为 ``True``,但这个词之前已经在 Vocabulary 中且并不是 | |||||
``no_create_entry`` 的,则还是会为这个词创建一个单独的 vector ; 如果 ``no_create_entry`` 为 ``False`` ,但这个词之 | |||||
前已经在 Vocabulary 中且并不是 ``no_create_entry的`` ,则这个词将认为是需要创建单独的 vector 的。 | |||||
""" | """ | ||||
self._add_no_create_entry(word_lst, no_create_entry) | self._add_no_create_entry(word_lst, no_create_entry) | ||||
@@ -156,13 +154,16 @@ class Vocabulary(object): | |||||
r""" | r""" | ||||
增加一个新词在词典中的出现频率 | 增加一个新词在词典中的出现频率 | ||||
:param word: 要添加进字典的新词, word为一个字符串 | |||||
:param no_create_entry: 如果词语来自于非训练集建议设置为True。 | |||||
如果为True,则不会有这个词语创建一个单独的entry,它将一直被指向unk的表示; 如果为False,则为这个词创建一个单独 | |||||
的entry。如果这个word来自于dev或者test,一般设置为True,如果来自与train一般设置为False。以下两种情况: 如果新 | |||||
加入一个word,且no_create_entry为True,但这个词之前已经在Vocabulary中且并不是no_create_entry的,则还是会为这 | |||||
个词创建一个单独的vector; 如果no_create_entry为False,但这个词之前已经在Vocabulary中且并不是no_create_entry的, | |||||
则这个词将认为是需要创建单独的vector的。 | |||||
:param word: 要添加进字典的新词, ``word`` 为一个字符串 | |||||
:param no_create_entry: 如果词语来自于非训练集建议设置为 ``True`` 。 | |||||
* 如果为 ``True`` -- 则不会有这个词语创建一个单独的 entry ,它将一直被指向 ``<UNK>`` 的表示; | |||||
* 如果为 ``False`` -- 为这个词创建一个单独的 entry。如果这个词来自于验证集或训练集,一般设置为 ``True`` ,如果来自于训练集一 | |||||
般设置为 ``False``; | |||||
有以下两种情况: 如果新加入一个 word ,且 ``no_create_entry`` 为 ``True``,但这个词之前已经在 Vocabulary 中且并不是 | |||||
``no_create_entry`` 的,则还是会为这个词创建一个单独的 vector ; 如果 ``no_create_entry`` 为 ``False`` ,但这个词之 | |||||
前已经在 Vocabulary 中且并不是 ``no_create_entry的`` ,则这个词将认为是需要创建单独的 vector 的。 | |||||
""" | """ | ||||
self._add_no_create_entry(word, no_create_entry) | self._add_no_create_entry(word, no_create_entry) | ||||
@@ -173,9 +174,13 @@ class Vocabulary(object): | |||||
r""" | r""" | ||||
在新加入word时,检查_no_create_word的设置。 | 在新加入word时,检查_no_create_word的设置。 | ||||
:param word: 要添加的新词或者是List类型的新词,如word='I'或者word=['I', 'am', 'a', 'Chinese']均可 | |||||
:param no_create_entry: 如果词语来自于非训练集建议设置为True。如果为True,则不会有这个词语创建一个单独的entry, | |||||
它将一直被指向unk的表示; 如果为False,则为这个词创建一个单独的entry | |||||
:param word: 要添加的新词或者是 :class:`List`类型的新词,如 word='I' 或者 word=['I', 'am', 'a', 'Chinese'] 均可 | |||||
:param no_create_entry: 如果词语来自于非训练集建议设置为 ``True`` 。 | |||||
* 如果为 ``True`` -- 则不会有这个词语创建一个单独的 entry ,它将一直被指向 ``<UNK>`` 的表示; | |||||
* 如果为 ``False`` -- 为这个词创建一个单独的 entry。如果这个词来自于验证集或训练集,一般设置为 ``True`` ,如果来自于训练集一 | |||||
般设置为 ``False``; | |||||
:return: | :return: | ||||
""" | """ | ||||
@@ -192,12 +197,16 @@ class Vocabulary(object): | |||||
r""" | r""" | ||||
增加一个新词在词典中的出现频率 | 增加一个新词在词典中的出现频率 | ||||
:param word: 要添加进字典的新词, word为一个字符串 | |||||
:param no_create_entry: 如果词语来自于非训练集建议设置为True。如果为True,则不会有这个词语创建一个单独的entry, | |||||
它将一直被指向unk的表示; 如果为False,则为这个词创建一个单独的entry。如果这个word来自于dev或者test,一般设置为True, | |||||
如果来自与train一般设置为False。以下两种情况: 如果新加入一个word,且no_create_entry为True,但这个词之前已经在Vocabulary | |||||
中且并不是no_create_entry的,则还是会为这词创建一个单独的vector; 如果no_create_entry为False,但这个词之前已经在Vocabulary | |||||
中且并不是no_create_entry的,则这个词将认为是需要创建单独的vector的。 | |||||
:param word: 要添加进字典的新词, ``word`` 为一个字符串 | |||||
:param no_create_entry: 如果词语来自于非训练集建议设置为 ``True`` 。 | |||||
* 如果为 ``True`` -- 则不会有这个词语创建一个单独的 entry ,它将一直被指向 ``<UNK>`` 的表示; | |||||
* 如果为 ``False`` -- 为这个词创建一个单独的 entry。如果这个词来自于验证集或训练集,一般设置为 ``True`` ,如果来自于训练集一 | |||||
般设置为 ``False``; | |||||
有以下两种情况: 如果新加入一个 word ,且 ``no_create_entry`` 为 ``True``,但这个词之前已经在 Vocabulary 中且并不是 | |||||
``no_create_entry`` 的,则还是会为这个词创建一个单独的 vector ; 如果 ``no_create_entry`` 为 ``False`` ,但这个词之 | |||||
前已经在 Vocabulary 中且并不是 ``no_create_entry的`` ,则这个词将认为是需要创建单独的 vector 的。 | |||||
""" | """ | ||||
self.add(word, no_create_entry=no_create_entry) | self.add(word, no_create_entry=no_create_entry) | ||||
@@ -207,12 +216,16 @@ class Vocabulary(object): | |||||
r""" | r""" | ||||
依次增加序列中词在词典中的出现频率 | 依次增加序列中词在词典中的出现频率 | ||||
:param word_lst: 需要添加的新词的list序列,如word_lst=['I', 'am', 'a', 'Chinese'] | |||||
:param no_create_entry: 如果词语来自于非训练集建议设置为True。如果为True,则不会有这个词语创建一个单独的entry, | |||||
它将一直被指向unk的表示; 如果为False,则为这个词创建一个单独的entry。如果这个word来自于dev或者test,一般设置为True, | |||||
如果来自与train一般设置为False。以下两种情况: 如果新加入一个word,且no_create_entry为True,但这个词之前已经在Vocabulary | |||||
中且并不是no_create_entry的,则还是会为这词创建一个单独的vector; 如果no_create_entry为False,但这个词之前已经在Vocabulary | |||||
中且并不是no_create_entry的,则这个词将认为是需要创建单独的vector的。 | |||||
:param word_lst: 需要添加的新词的 list 序列,如 word_lst=['I', 'am', 'a', 'Chinese'] 。 | |||||
:param no_create_entry: 如果词语来自于非训练集建议设置为 ``True`` 。 | |||||
* 如果为 ``True`` -- 则不会有这个词语创建一个单独的 entry ,它将一直被指向 ``<UNK>`` 的表示; | |||||
* 如果为 ``False`` -- 为这个词创建一个单独的 entry。如果这个词来自于验证集或训练集,一般设置为 ``True`` ,如果来自于训练集一 | |||||
般设置为 ``False``; | |||||
有以下两种情况: 如果新加入一个 word ,且 ``no_create_entry`` 为 ``True``,但这个词之前已经在 Vocabulary 中且并不是 | |||||
``no_create_entry`` 的,则还是会为这个词创建一个单独的 vector ; 如果 ``no_create_entry`` 为 ``False`` ,但这个词之 | |||||
前已经在 Vocabulary 中且并不是 ``no_create_entry的`` ,则这个词将认为是需要创建单独的 vector 的。 | |||||
""" | """ | ||||
self.update(word_lst, no_create_entry=no_create_entry) | self.update(word_lst, no_create_entry=no_create_entry) | ||||
@@ -220,9 +233,8 @@ class Vocabulary(object): | |||||
def build_vocab(self): | def build_vocab(self): | ||||
r""" | r""" | ||||
根据已经出现的词和出现频率构建词典. 注意: 重复构建可能会改变词典的大小, | |||||
但已经记录在词典中的词, 不会改变对应的 `int` | |||||
根据已经出现的词和出现频率构建词典。注意:重复构建可能会改变词典的大小, | |||||
但已经记录在词典中的词,不会改变对应的 :class:`int` | |||||
""" | """ | ||||
if self._word2idx is None: | if self._word2idx is None: | ||||
self._word2idx = {} | self._word2idx = {} | ||||
@@ -295,16 +307,16 @@ class Vocabulary(object): | |||||
@_check_build_vocab | @_check_build_vocab | ||||
def index_dataset(self, *datasets, field_name:Union[List, str], new_field_name:Union[List, str, None]=None): | def index_dataset(self, *datasets, field_name:Union[List, str], new_field_name:Union[List, str, None]=None): | ||||
r""" | r""" | ||||
将DataSet中对应field的词转为数字,Example:: | |||||
将 ``DataSet`` 中对应 field 的词转为数字,例如:: | |||||
# remember to use `field_name` | # remember to use `field_name` | ||||
vocab.index_dataset(train_data, dev_data, test_data, field_name='words') | vocab.index_dataset(train_data, dev_data, test_data, field_name='words') | ||||
:param datasets: 其类型为:~fastNLP.core.Dataset或者List[~fastNLP.core.Dataset] 需要转index的一个或多个数据集 | |||||
:param field_name: 需要转index的field, 若有多个 DataSet, 每个DataSet都必须有此 field. | |||||
目前支持 ``str`` , ``List[str]`` | |||||
:param list,str new_field_name: 保存结果的field_name. 若为 ``None`` , 将覆盖原field. | |||||
Default: ``None``. | |||||
:param datasets: 其类型为 :class:`~fastNLP.core.dataset.DataSet` 或者 :class:`List` [ :class:`~fastNLP.core.dataset.DataSet` ], | |||||
即需要处理的一个或多个数据集 | |||||
:param field_name: 需要转为 index 的 field, 若有多个 DataSet, 每个 DataSet 都必须有此 field. | |||||
目前支持 :class:`str` , :class:`List` [ :class:`str` ] | |||||
:param new_field_name: 保存结果的 field_name。 若为 ``None`` , 将覆盖原 field。 | |||||
""" | """ | ||||
def index_instance(field): | def index_instance(field): | ||||
@@ -359,17 +371,18 @@ class Vocabulary(object): | |||||
# remember to use `field_name` | # remember to use `field_name` | ||||
vocab.from_dataset(train_data1, train_data2, field_name='words', no_create_entry_dataset=[test_data1, test_data2]) | vocab.from_dataset(train_data1, train_data2, field_name='words', no_create_entry_dataset=[test_data1, test_data2]) | ||||
:param 其类型为:~fastNLP.core.Dataset或者List[~fastNLP.core.Dataset] 需要转index的一个或多个数据集 | |||||
:param field_name: 构建词典所使用的 field(s), 支持一个或多个field,若有多个 DataSet, 每个DataSet都必须有这些field. | |||||
:param datasets: 其类型为 :class:`~fastNLP.core.dataset.DataSet` 或者 List[:class:`~fastNLP.core.dataset.DataSet`]。 | |||||
:param field_name: 构建词典所使用的 field(s), 支持一个或多个 field,若有多个 DataSet, 每个 DataSet 都必须有这些 field. | |||||
目前支持的field结构: ``str`` , ``List[str]`` | 目前支持的field结构: ``str`` , ``List[str]`` | ||||
:param no_create_entry_dataset: 可以传入DataSet, List[DataSet]或者None(默认), 建议直接将非训练数据都传入到这个参数。该选项用在接下来的模型会使用pretrain | |||||
的embedding(包括glove, word2vec, elmo与bert)且会finetune的情况。如果仅使用来自于train的数据建立vocabulary,会导致test与dev | |||||
中的数据无法充分利用到来自于预训练embedding的信息,所以在建立词表的时候将test与dev考虑进来会使得最终的结果更好。 | |||||
如果一个词出现在了train中,但是没在预训练模型中,embedding会为它用unk初始化,但它是单独的一个vector,如果 | |||||
finetune embedding的话,这个词在更新之后可能会有更好的表示; 而如果这个词仅出现在了dev或test中,那么就不能为它们单独建立vector, | |||||
而应该让它指向unk这个vector的值。所以只位于no_create_entry_dataset中的token,将首先从预训练的词表中寻找它的表示, | |||||
如果找到了,就使用该表示; 如果没有找到,则认为该词的表示应该为unk的表示。 | |||||
:return Vocabulary自身 | |||||
:param no_create_entry_dataset: 可以传入 :class:`~fastNLP.core.dataset.DataSet`, :class:`List` [ :class:`~fastNLP.core.dataset.DataSet` ] 或者 | |||||
``None`` (默认),建议直接将非训练数据都传入到这个参数。该选项用于接下来的模型会使用预训练的 embedding (包括 ``glove``, ``word2vec`` , | |||||
``elmo`` 与 ``bert`` )且会 finetune 的情况。如果仅使用来自于训练集的数据建立词表,会导致测试集与验证集中的数据无法充分利用到来自于预训练 | |||||
embedding 的信息,所以在建立词表的时候将测试集与验证集考虑进来会使得最终的结果更好。 | |||||
如果一个词出现在了训练集中,但是没在预训练模型中, embedding 会为它用 ``<UNK>`` 初始化;但如果它是单独的一个 vector ,并且 finetune embedding | |||||
的话,这个词在更新之后可能会有更好的表示;而如果这个词仅出现在了验证集或者测试集中,那么就不能为它们单独建立 vector,而应该让它指向 ``<UNK>`` 这个 | |||||
vector 的值。所以只位于 ``no_create_entry_dataset`` 中的 token 将首先从预训练的词表中寻找它的表示,如果找到了,就使用该表示; 如果没有找到,则认 | |||||
为该词的表示应该为 ``<UNK>`` 的表示。 | |||||
:return: Vocabulary 自身 | |||||
""" | """ | ||||
if isinstance(field_name, str): | if isinstance(field_name, str): | ||||
@@ -425,14 +438,14 @@ class Vocabulary(object): | |||||
def to_index(self, w:str): | def to_index(self, w:str): | ||||
r""" | r""" | ||||
将词转为数字. 若词不再词典中被记录, 将视为 unknown, 若 ``unknown=None`` , 将抛出 ``ValueError`` :: | |||||
将词转为数字。 若词不在词典中被记录, 将视为 `unknown`, 若 ``unknown=None`` , 将抛出 ``ValueError`` :: | |||||
index = vocab.to_index('abc') | index = vocab.to_index('abc') | ||||
# equals to | # equals to | ||||
index = vocab['abc'] | index = vocab['abc'] | ||||
:param w: 需要输入的词语 | :param w: 需要输入的词语 | ||||
:return 词语w对应的int类型的index | |||||
:return: 词语 ``w`` 对应的 :class:`int`类型的 index | |||||
""" | """ | ||||
return self.__getitem__(w) | return self.__getitem__(w) | ||||
@@ -440,7 +453,7 @@ class Vocabulary(object): | |||||
@_check_build_vocab | @_check_build_vocab | ||||
def unknown_idx(self): | def unknown_idx(self): | ||||
r""" | r""" | ||||
获得unknown 对应的数字. | |||||
获得 ``unknown`` 对应的数字. | |||||
""" | """ | ||||
if self.unknown is None: | if self.unknown is None: | ||||
return None | return None | ||||
@@ -450,7 +463,7 @@ class Vocabulary(object): | |||||
@_check_build_vocab | @_check_build_vocab | ||||
def padding_idx(self): | def padding_idx(self): | ||||
r""" | r""" | ||||
获得padding 对应的数字 | |||||
获得 ``padding`` 对应的数字 | |||||
""" | """ | ||||
if self.padding is None: | if self.padding is None: | ||||
return None | return None | ||||
@@ -461,16 +474,16 @@ class Vocabulary(object): | |||||
r""" | r""" | ||||
给定一个数字, 将其转为对应的词. | 给定一个数字, 将其转为对应的词. | ||||
:param int idx: the index | |||||
:return str word: the word | |||||
:param idx: | |||||
:return: ``idx`` 对应的词 | |||||
""" | """ | ||||
return self._idx2word[idx] | return self._idx2word[idx] | ||||
def clear(self): | def clear(self): | ||||
r""" | r""" | ||||
删除Vocabulary中的词表数据。相当于重新初始化一下。 | |||||
删除 :class:Vocabulary`` 中的词表数据。相当于重新初始化一下。 | |||||
:return: | |||||
:return: 自身 | |||||
""" | """ | ||||
self.word_count.clear() | self.word_count.clear() | ||||
self._word2idx = None | self._word2idx = None | ||||
@@ -481,7 +494,7 @@ class Vocabulary(object): | |||||
def __getstate__(self): | def __getstate__(self): | ||||
r""" | r""" | ||||
用来从pickle中加载data | |||||
用来从 pickle 中加载 data | |||||
""" | """ | ||||
len(self) # make sure vocab has been built | len(self) # make sure vocab has been built | ||||
@@ -492,7 +505,7 @@ class Vocabulary(object): | |||||
def __setstate__(self, state): | def __setstate__(self, state): | ||||
r""" | r""" | ||||
支持pickle的保存,保存到pickle的data state | |||||
支持 pickle 的保存,保存到 pickle 的 data state | |||||
""" | """ | ||||
self.__dict__.update(state) | self.__dict__.update(state) | ||||
@@ -507,11 +520,11 @@ class Vocabulary(object): | |||||
for index in range(len(self._word2idx)): | for index in range(len(self._word2idx)): | ||||
yield self.to_word(index), index | yield self.to_word(index), index | ||||
def save(self, filepath: [str, io.StringIO]): | |||||
def save(self, filepath: Union[str, io.StringIO]): | |||||
r""" | r""" | ||||
:param filepath: Vocabulary的储存路径 | |||||
:return: | |||||
保存当前词表。 | |||||
:param filepath: 词表储存路径 | |||||
""" | """ | ||||
if isinstance(filepath, io.IOBase): | if isinstance(filepath, io.IOBase): | ||||
assert filepath.writable() | assert filepath.writable() | ||||
@@ -547,8 +560,8 @@ class Vocabulary(object): | |||||
r""" | r""" | ||||
从文件路径中加载数据 | 从文件路径中加载数据 | ||||
:param filepath: Vocabulary的读取路径 | |||||
:return: Vocabulary | |||||
:param filepath: 词表的读取路径 | |||||
:return: 读取的 :class:`Vocabulary` | |||||
""" | """ | ||||
if isinstance(filepath, io.IOBase): | if isinstance(filepath, io.IOBase): | ||||
assert filepath.writable() | assert filepath.writable() | ||||