@@ -12,18 +12,35 @@ from .jittor_backend.backend import JittorBackend | |||||
class AutoBackend(Backend): | class AutoBackend(Backend): | ||||
""" | """ | ||||
不需要初始化backend的AutoBackend,能够根据get_metric时候判断输入数据类型来选择backend是什么类型的 | |||||
不需要初始化 backend 的 AutoBackend,能够根据 get_metric 时候判断输入数据类型来选择 backend 是什么类型的 | |||||
""" | """ | ||||
def __init__(self, backend: Union[str, Backend, None]): | def __init__(self, backend: Union[str, Backend, None]): | ||||
""" | |||||
初始化 backend. | |||||
:param backend: 目前支持三种值,为 ``[str, Backend, None]``。 | |||||
* 当 backend 为 `str` 时, 其只能为 'auto' | |||||
* 当 backend 为 ``Backend`` 对象时, 其直接使用该对象方法覆盖 AutoBackend | |||||
* 当 backend 为 ``None`` 时, 根据 get_metric 时候判断输入数据类型来选择 backend 是什么类型的 | |||||
""" | |||||
super(AutoBackend, self).__init__() | super(AutoBackend, self).__init__() | ||||
if backend != 'auto': | if backend != 'auto': | ||||
self._convert_backend(backend) | self._convert_backend(backend) | ||||
def _convert_backend(self, backend): | def _convert_backend(self, backend): | ||||
""" | """ | ||||
将AutoBackend转换为合适的Backend对象 | |||||
将 AutoBackend 转换为合适的 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 为 ``None`` 时, 直接初始化 | |||||
""" | """ | ||||
if isinstance(backend, Backend): | if isinstance(backend, Backend): | ||||
@@ -43,6 +60,12 @@ class AutoBackend(Backend): | |||||
self._specified = True | self._specified = True | ||||
def choose_real_backend(self, args): | def choose_real_backend(self, args): | ||||
""" | |||||
根据 args 参数类型来选择需要真正初始化的 backend | |||||
:param args: args 参数, 可能为 ``jittor``, ``torch``, ``paddle``, ``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. " \ | ||||
"This must be a bug, please report." | "This must be a bug, please report." | ||||
types = [] | types = [] | ||||
@@ -12,7 +12,9 @@ class Backend: | |||||
def aggregate(self, tensor, method: str): | def aggregate(self, tensor, method: str): | ||||
""" | """ | ||||
聚集结果,并根据method计算后,返回结果 | |||||
聚集结果,并根据 method 计算后,返回结果 | |||||
:param tensor: 传入的张量 | |||||
:param method: 聚合的方法 | |||||
""" | """ | ||||
if method is not None: | if method is not None: | ||||
return AggregateMethodError(should_have_aggregate_method=False, only_warn=True) | return AggregateMethodError(should_have_aggregate_method=False, only_warn=True) | ||||
@@ -22,6 +24,8 @@ class Backend: | |||||
def create_tensor(self, value: float): | def create_tensor(self, value: float): | ||||
""" | """ | ||||
创建tensor,并且填入value作为值 | 创建tensor,并且填入value作为值 | ||||
:param value: 需要初始化的 value 值 | |||||
""" | """ | ||||
return value | return value | ||||
@@ -29,6 +33,8 @@ class Backend: | |||||
""" | """ | ||||
将tensor的值设置为value | 将tensor的值设置为value | ||||
:param tensor: 传进来的张量 | |||||
:param value: 需要填充的值 | |||||
""" | """ | ||||
return value | return value | ||||
@@ -36,14 +42,14 @@ class Backend: | |||||
""" | """ | ||||
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: | ||||
""" | """ | ||||
@@ -51,15 +57,19 @@ class Backend: | |||||
def tensor2numpy(self, tensor): | def tensor2numpy(self, tensor): | ||||
""" | """ | ||||
将tensor转为numpy | |||||
将 tensor 转为 numpy | |||||
:param tensor: | |||||
:param tensor: 传入的张量 | |||||
:return: | :return: | ||||
""" | """ | ||||
return tensor | return tensor | ||||
def move_tensor_to_device(self, tensor, device): | def move_tensor_to_device(self, tensor, device): | ||||
""" | """ | ||||
将张量移动到某个设备上 | |||||
:param tensor: 传入的张量 | |||||
:param device: 设备号, 一般为 ``'cpu'``, ``'cuda:0'`` 等。 | |||||
""" | """ | ||||
return tensor | return tensor | ||||
@@ -16,20 +16,20 @@ class JittorBackend(Backend): | |||||
def aggregate(self, tensor, method: str): | def aggregate(self, tensor, method: str): | ||||
""" | """ | ||||
聚集结果,并根据method计算后,返回结果 | |||||
聚集结果,并根据 method 计算后,返回结果 | |||||
""" | """ | ||||
return tensor | return tensor | ||||
def create_tensor(self, value: float): | def create_tensor(self, value: float): | ||||
""" | """ | ||||
创建tensor,并且填入value作为值 | |||||
创建 tensor,并且填入 value 作为值 | |||||
""" | """ | ||||
value = jittor.Var(value) | value = jittor.Var(value) | ||||
return value | return value | ||||
def fill_value(self, tensor, value: float): | def fill_value(self, tensor, value: float): | ||||
""" | """ | ||||
将tensor的值设置为value | |||||
将 tensor 的值设置为 value | |||||
""" | """ | ||||
value = jittor.full_like(tensor, value) | value = jittor.full_like(tensor, value) | ||||
@@ -37,7 +37,7 @@ class JittorBackend(Backend): | |||||
def get_scalar(self, tensor) -> float: | def get_scalar(self, tensor) -> float: | ||||
""" | """ | ||||
tensor的saclar值 | |||||
tensor 的 saclar 值 | |||||
:param tensor: | :param tensor: | ||||
:return: | :return: | ||||
@@ -46,7 +46,7 @@ class JittorBackend(Backend): | |||||
def is_specified(self) -> bool: | def is_specified(self) -> bool: | ||||
""" | """ | ||||
判断是否是某种框架的backend | |||||
判断是否是某种框架的 backend | |||||
:return: | :return: | ||||
""" | """ | ||||
@@ -54,7 +54,7 @@ class JittorBackend(Backend): | |||||
def tensor2numpy(self, tensor): | def tensor2numpy(self, tensor): | ||||
""" | """ | ||||
将tensor转为numpy | |||||
将 tensor 转为 numpy | |||||
:param tensor: | :param tensor: | ||||
:return: | :return: | ||||
@@ -68,6 +68,6 @@ class JittorBackend(Backend): | |||||
def move_tensor_to_device(self, tensor, device): | def move_tensor_to_device(self, tensor, device): | ||||
""" | """ | ||||
jittor的没有转移设备的函数,因此该函数实际上无效 | |||||
jittor 的没有转移设备的函数,因此该函数实际上无效 | |||||
""" | """ | ||||
return tensor | return tensor |
@@ -23,7 +23,16 @@ class PaddleBackend(Backend): | |||||
def aggregate(self, tensor, method: str): | def aggregate(self, tensor, method: str): | ||||
""" | """ | ||||
聚集结果,并根据method计算后,返回结果 | |||||
聚集结果,并根据 method 计算后,返回结果 | |||||
:param tensor: 需要聚合的张量 | |||||
:param method: 聚合的方法, 目前支持 ``['sum', 'mean', 'max', 'mix']``: | |||||
* method 为 ``'sum'`` 时, 会将多张卡上聚合结果在维度为 `0` 上 累加起来。 | |||||
* method 为 ``'mean'`` 时,会将多张卡上聚合结果在维度为 `0` 上取平均值。 | |||||
* method 为 ``'max'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最大值。 | |||||
* method 为 ``'mix'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最小值。 | |||||
""" | """ | ||||
if isinstance(tensor, paddle.Tensor): | if isinstance(tensor, paddle.Tensor): | ||||
if parallel_helper._is_parallel_ctx_initialized(): | if parallel_helper._is_parallel_ctx_initialized(): | ||||
@@ -48,23 +57,37 @@ class PaddleBackend(Backend): | |||||
def create_tensor(self, value: float): | def create_tensor(self, value: float): | ||||
""" | """ | ||||
创建tensor,并且填入value作为值 | |||||
创建 tensor,并且填入 value 作为值 | |||||
:param value: 创建张量的初始值 | |||||
""" | """ | ||||
tensor = paddle.ones((1,)).fill_(value) | tensor = paddle.ones((1,)).fill_(value) | ||||
return tensor | return tensor | ||||
def fill_value(self, tensor, value: float): | def fill_value(self, tensor, value: float): | ||||
""" | """ | ||||
将tensor的值设置为value | |||||
将 tensor 的值设置为 value | |||||
:param tensor: 传入的张量 | |||||
:param value: 需要 fill 的值。 | |||||
""" | """ | ||||
tensor.fill_(value) | tensor.fill_(value) | ||||
return tensor | return tensor | ||||
def get_scalar(self, tensor) -> float: | def get_scalar(self, tensor) -> float: | ||||
""" | |||||
获取 tensor 的 scalar 值 | |||||
:param tensor: 传入的张量 | |||||
""" | |||||
return tensor.item() | return tensor.item() | ||||
def tensor2numpy(self, tensor) -> np.array: | def tensor2numpy(self, tensor) -> np.array: | ||||
""" | |||||
将 tensor 转为 numpy 值, 主要是在 metric 计算中使用 | |||||
:param tensor: 传入的张量 | |||||
""" | |||||
if isinstance(tensor, paddle.Tensor): | if isinstance(tensor, paddle.Tensor): | ||||
return tensor.cpu().detach().numpy() | return tensor.cpu().detach().numpy() | ||||
elif isinstance(tensor, np.array): | elif isinstance(tensor, np.array): | ||||
@@ -77,15 +100,29 @@ class PaddleBackend(Backend): | |||||
@staticmethod | @staticmethod | ||||
def is_distributed() -> bool: | def is_distributed() -> bool: | ||||
""" | """ | ||||
判断是否为 ddp 状态 | |||||
:return: | :return: | ||||
""" | """ | ||||
return is_in_paddle_dist() | return is_in_paddle_dist() | ||||
def move_tensor_to_device(self, tensor, device): | def move_tensor_to_device(self, tensor, device): | ||||
""" | |||||
将张量移到设备上 | |||||
:param tensor: 需要移动的张量 | |||||
:param device: 设备名, 一般为 "cpu", "cuda:0"等字符串 | |||||
""" | |||||
device = _convert_data_device(device) | device = _convert_data_device(device) | ||||
return paddle_to(tensor, device) | return paddle_to(tensor, device) | ||||
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 。 | |||||
:param obj: | |||||
:param group: | |||||
""" | |||||
if self.is_distributed(): | if self.is_distributed(): | ||||
obj_list = fastnlp_paddle_all_gather(obj, group=group) | obj_list = fastnlp_paddle_all_gather(obj, group=group) | ||||
return obj_list | return obj_list | ||||
@@ -21,7 +21,16 @@ class TorchBackend(Backend): | |||||
def aggregate(self, tensor, method: str): | def aggregate(self, tensor, method: str): | ||||
""" | """ | ||||
聚集结果,并根据method计算后,返回结果。 | |||||
聚集结果,并根据 method 计算后,返回结果 | |||||
:param tensor: 需要聚合的张量 | |||||
:param method: 聚合的方法, 目前支持 ``['sum', 'mean', 'max', 'mix']``: | |||||
* method 为 ``'sum'`` 时, 会将多张卡上聚合结果在维度为 `0` 上 累加起来。 | |||||
* method 为 ``'mean'`` 时,会将多张卡上聚合结果在维度为 `0` 上取平均值。 | |||||
* method 为 ``'max'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最大值。 | |||||
* method 为 ``'mix'`` 时,会将多张卡上聚合结果在维度为 `0` 上取最小值。 | |||||
""" | """ | ||||
if isinstance(tensor, torch.Tensor): | if isinstance(tensor, torch.Tensor): | ||||
if dist.is_initialized(): | if dist.is_initialized(): | ||||
@@ -46,26 +55,36 @@ class TorchBackend(Backend): | |||||
def create_tensor(self, value: float): | def create_tensor(self, value: float): | ||||
""" | """ | ||||
创建tensor,并且填入value作为值 | |||||
创建 tensor,并且填入 value 作为值 | |||||
:param value: 创建张量的初始值 | |||||
""" | """ | ||||
tensor = torch.ones(1).fill_(value) | tensor = torch.ones(1).fill_(value) | ||||
return tensor | return tensor | ||||
def fill_value(self, tensor, value: float): | def fill_value(self, tensor, value: float): | ||||
""" | """ | ||||
将tensor的值设置为value | |||||
将 tensor 的值设置为 value | |||||
:param tensor: 传入的张量 | |||||
:param value: 需要 fill 的值。 | |||||
""" | """ | ||||
tensor.fill_(value) | tensor.fill_(value) | ||||
return tensor | return tensor | ||||
def get_scalar(self, tensor) -> float: | def get_scalar(self, tensor) -> float: | ||||
""" | |||||
获取 tensor 的 scalar 值 | |||||
:param tensor: 传入的张量 | |||||
""" | |||||
return tensor.item() | return tensor.item() | ||||
def tensor2numpy(self, tensor) -> np.array: | def tensor2numpy(self, tensor) -> np.array: | ||||
""" | """ | ||||
将对应的tensor转为numpy对象 | |||||
将 tensor 转为 numpy 值, 主要是在 metric 计算中使用 | |||||
:param tensor: 传入的张量 | |||||
""" | """ | ||||
if isinstance(tensor, torch.Tensor): | if isinstance(tensor, torch.Tensor): | ||||
@@ -80,14 +99,28 @@ class TorchBackend(Backend): | |||||
@staticmethod | @staticmethod | ||||
def is_distributed() -> bool: | def is_distributed() -> bool: | ||||
""" | """ | ||||
判断是否为 ddp 状态 | |||||
:return: | :return: | ||||
""" | """ | ||||
return dist.is_available() and dist.is_initialized() | return dist.is_available() and dist.is_initialized() | ||||
def move_tensor_to_device(self, tensor, device): | def move_tensor_to_device(self, tensor, device): | ||||
""" | |||||
将张量移到设备上 | |||||
:param tensor: 需要移动的张量 | |||||
:param device: 设备名, 一般为 "cpu", "cuda:0"等字符串 | |||||
""" | |||||
return tensor.to(device) | return tensor.to(device) | ||||
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 。 | |||||
:param obj: | |||||
:param group: | |||||
""" | |||||
if self.is_distributed(): | if self.is_distributed(): | ||||
obj_list = fastnlp_torch_all_gather(obj, group=group) | obj_list = fastnlp_torch_all_gather(obj, group=group) | ||||
return obj_list | return obj_list | ||||
@@ -20,15 +20,21 @@ class ClassifyFPreRecMetric(Metric): | |||||
aggregate_when_get_metric: bool = None) -> None: | aggregate_when_get_metric: bool = None) -> None: | ||||
""" | """ | ||||
:param tag_vocab: | |||||
:param ignore_labels: | |||||
:param only_gross: | |||||
:param f_type: | |||||
:param beta: | |||||
:param str backend: 目前支持四种类型的backend, [torch, paddle, jittor, auto]。其中 auto 表示根据实际调用 Metric.update() | |||||
函数时传入的参数决定具体的 backend ,大部分情况下直接使用 auto 即可。 | |||||
:param bool aggregate_when_get_metric: 在计算 metric 的时候是否自动将各个进程上的相同的 element 的数字聚合后再得到metric, | |||||
当 backend 不支持分布式时,该参数无意义。如果为 None ,将在 Evaluator 中根据 sampler 是否使用分布式进行自动设置。 | |||||
:param tag_vocab: 标签的 :class:`~fastNLP.Vocabulary` . 默认值为 ``None``。若为 ``None`` 则使用数字来作为标签内容, | |||||
否则使用 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)}` . | |||||
:param backend: 目前支持四种类型的 backend, ``[torch, paddle, jittor, 'auto']``。其中 ``'auto'`` 表示根据实际调用 Metric.update() | |||||
函数时传入的参数决定具体的 backend ,大部分情况下直接使用 ``'auto'`` 即可。 | |||||
:param aggregate_when_get_metric: 在计算 metric 的时候是否自动将各个进程上的相同的 element 的数字聚合后再得到metric, | |||||
当 backend 不支持分布式时,该参数无意义。如果为 ``None`` ,将在 Evaluator 中根据 sampler 是否使用分布式进行自动设置。 | |||||
""" | """ | ||||
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) | ||||
@@ -50,6 +56,10 @@ class ClassifyFPreRecMetric(Metric): | |||||
self._fn = Counter() | self._fn = Counter() | ||||
def reset(self): | def reset(self): | ||||
""" | |||||
重置 tp, fp, fn 的值 | |||||
""" | |||||
# 由于不是 element 了,需要自己手动清零一下 | # 由于不是 element 了,需要自己手动清零一下 | ||||
self._tp.clear() | self._tp.clear() | ||||
self._fp.clear() | self._fp.clear() | ||||
@@ -57,9 +67,9 @@ class ClassifyFPreRecMetric(Metric): | |||||
def get_metric(self) -> dict: | def get_metric(self) -> dict: | ||||
r""" | r""" | ||||
get_metric函数将根据update函数累计的评价指标统计量来计算最终的评价结果. | |||||
get_metric 函数将根据 update 函数累计的评价指标统计量来计算最终的评价结果. | |||||
:return dict evaluate_result: {"acc": float} | |||||
:return evaluate_result: {"acc": float} | |||||
""" | """ | ||||
evaluate_result = {} | evaluate_result = {} | ||||
@@ -120,12 +130,12 @@ class ClassifyFPreRecMetric(Metric): | |||||
r""" | r""" | ||||
update 函数将针对一个批次的预测结果做评价指标的累计 | update 函数将针对一个批次的预测结果做评价指标的累计 | ||||
:param torch.Tensor pred: 预测的tensor, tensor的形状可以是torch.Size([B,]), torch.Size([B, n_classes]), | |||||
torch.Size([B, max_len]), 或者torch.Size([B, max_len, n_classes]) | |||||
:param torch.Tensor target: 真实值的tensor, tensor的形状可以是Element's can be: torch.Size([B,]), | |||||
torch.Size([B,]), torch.Size([B, max_len]), 或者torch.Size([B, max_len]) | |||||
:param torch.Tensor seq_len: 序列长度标记, 标记的形状可以是None, None, torch.Size([B]), 或者torch.Size([B]). | |||||
如果mask也被传进来的话seq_len会被忽略. | |||||
: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) | ||||
target = self.tensor2numpy(target) | target = self.tensor2numpy(target) | ||||
@@ -12,6 +12,26 @@ from fastNLP.envs.env import FASTNLP_GLOBAL_RANK | |||||
class Element: | class 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 | ||||
@@ -31,7 +51,7 @@ class Element: | |||||
def aggregate(self): | def aggregate(self): | ||||
""" | """ | ||||
自动aggregate对应的元素 | |||||
自动 aggregate 对应的元素 | |||||
""" | """ | ||||
self._check_value_initialized() | self._check_value_initialized() | ||||
@@ -54,6 +74,9 @@ class Element: | |||||
raise RuntimeError(msg) | raise RuntimeError(msg) | ||||
def reset(self): | def reset(self): | ||||
""" | |||||
重置 value | |||||
""" | |||||
if self.backend.is_specified(): | if self.backend.is_specified(): | ||||
self._value = self.backend.fill_value(self._value, self.init_value) | self._value = self.backend.fill_value(self._value, self.init_value) | ||||
@@ -72,19 +95,36 @@ class Element: | |||||
return self._value | return self._value | ||||
def get_scalar(self) -> float: | def get_scalar(self) -> float: | ||||
""" | |||||
获取元素的 scalar 值 | |||||
""" | |||||
self._check_value_initialized() | self._check_value_initialized() | ||||
return self.backend.get_scalar(self._value) | return self.backend.get_scalar(self._value) | ||||
def fill_value(self, value): | def fill_value(self, value): | ||||
""" | |||||
对元素进行 fill_value, 会执行队友 backend 的 fill_value 方法 | |||||
""" | |||||
self._value = self.backend.fill_value(self._value, value) | self._value = self.backend.fill_value(self._value, value) | ||||
def to(self, device): | def to(self, device): | ||||
""" | |||||
将元素移到某个设备上 | |||||
:param device: 设备名, 一般为 ``"cpu"``, ``"cuda:0"`` 等 | |||||
""" | |||||
# device这里如何处理呢? | # device这里如何处理呢? | ||||
if self._value is not None: | if self._value is not None: | ||||
self._value = self.backend.move_tensor_to_device(self._value, device) | self._value = self.backend.move_tensor_to_device(self._value, device) | ||||
self.device = device | self.device = device | ||||
def _check_value_initialized(self): | def _check_value_initialized(self): | ||||
""" | |||||
检查 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 " \ | ||||
f"initialization." | f"initialization." | ||||
@@ -114,6 +114,9 @@ class Metric: | |||||
return _wrap_update | return _wrap_update | ||||
def check_backend(self, *args, **kwargs): | def check_backend(self, *args, **kwargs): | ||||
""" | |||||
根据传入的参数的类型选择当前需要的 backend | |||||
""" | |||||
if not self.backend.is_specified(): | if not self.backend.is_specified(): | ||||
_args = [] | _args = [] | ||||
for arg in args: | for arg in args: | ||||
@@ -45,9 +45,9 @@ def _check_tag_vocab_and_encoding_type(tag_vocab: Union[Vocabulary, dict], encod | |||||
def _get_encoding_type_from_tag_vocab(tag_vocab: Union[Vocabulary, dict]) -> str: | def _get_encoding_type_from_tag_vocab(tag_vocab: Union[Vocabulary, dict]) -> str: | ||||
r""" | r""" | ||||
给定Vocabulary自动判断是哪种类型的encoding, 支持判断bmes, bioes, bmeso, bio | |||||
给定 Vocabular y自动判断是哪种类型的 encoding, 支持判断 bmes, bioes, bmeso, bio | |||||
:param tag_vocab: 支持传入tag Vocabulary; 或者传入形如{0:"O", 1:"B-tag1"},即index在前,tag在后的dict。 | |||||
:param tag_vocab: 支持传入 tag Vocabulary; 或者传入形如 {0:"O", 1:"B-tag1"},即 index 在前,tag 在后的 dict。 | |||||
:return: | :return: | ||||
""" | """ | ||||
tag_set = set() | tag_set = set() | ||||
@@ -81,9 +81,9 @@ def _get_encoding_type_from_tag_vocab(tag_vocab: Union[Vocabulary, dict]) -> str | |||||
def _bmes_tag_to_spans(tags, ignore_labels=None): | def _bmes_tag_to_spans(tags, ignore_labels=None): | ||||
r""" | r""" | ||||
给定一个tags的lis,比如['S-song', 'B-singer', 'M-singer', 'E-singer', 'S-moive', 'S-actor']。 | |||||
返回[('song', (0, 1)), ('singer', (1, 4)), ('moive', (4, 5)), ('actor', (5, 6))] (左闭右开区间) | |||||
也可以是单纯的['S', 'B', 'M', 'E', 'B', 'M', 'M',...]序列 | |||||
给定一个 tags 的 lis,比如 ['S-song', 'B-singer', 'M-singer', 'E-singer', 'S-moive', 'S-actor']。 | |||||
返回 [('song', (0, 1)), ('singer', (1, 4)), ('moive', (4, 5)), ('actor', (5, 6))] (左闭右开区间) | |||||
也可以是单纯的 ['S', 'B', 'M', 'E', 'B', 'M', 'M',...]序列 | |||||
:param tags: List[str], | :param tags: List[str], | ||||
:param ignore_labels: List[str], 在该list中的label将被忽略 | :param ignore_labels: List[str], 在该list中的label将被忽略 | ||||
@@ -111,8 +111,8 @@ def _bmes_tag_to_spans(tags, ignore_labels=None): | |||||
def _bmeso_tag_to_spans(tags, ignore_labels=None): | def _bmeso_tag_to_spans(tags, ignore_labels=None): | ||||
r""" | r""" | ||||
给定一个tags的lis,比如['O', 'B-singer', 'M-singer', 'E-singer', 'O', 'O']。 | |||||
返回[('singer', (1, 4))] (左闭右开区间) | |||||
给定一个 tag s的 lis,比如 ['O', 'B-singer', 'M-singer', 'E-singer', 'O', 'O']。 | |||||
返回 [('singer', (1, 4))] (左闭右开区间) | |||||
:param tags: List[str], | :param tags: List[str], | ||||
:param ignore_labels: List[str], 在该list中的label将被忽略 | :param ignore_labels: List[str], 在该list中的label将被忽略 | ||||
@@ -142,8 +142,8 @@ def _bmeso_tag_to_spans(tags, ignore_labels=None): | |||||
def _bioes_tag_to_spans(tags, ignore_labels=None): | def _bioes_tag_to_spans(tags, ignore_labels=None): | ||||
r""" | r""" | ||||
给定一个tags的lis,比如['O', 'B-singer', 'I-singer', 'E-singer', 'O', 'O']。 | |||||
返回[('singer', (1, 4))] (左闭右开区间) | |||||
给定一个 tags 的 lis,比如 ['O', 'B-singer', 'I-singer', 'E-singer', 'O', 'O']。 | |||||
返回 [('singer', (1, 4))] (左闭右开区间) | |||||
:param tags: List[str], | :param tags: List[str], | ||||
:param ignore_labels: List[str], 在该list中的label将被忽略 | :param ignore_labels: List[str], 在该list中的label将被忽略 | ||||
@@ -173,8 +173,8 @@ def _bioes_tag_to_spans(tags, ignore_labels=None): | |||||
def _bio_tag_to_spans(tags, ignore_labels=None): | def _bio_tag_to_spans(tags, ignore_labels=None): | ||||
r""" | r""" | ||||
给定一个tags的lis,比如['O', 'B-singer', 'I-singer', 'I-singer', 'O', 'O']。 | |||||
返回[('singer', (1, 4))] (左闭右开区间) | |||||
给定一个 tags 的 lis,比如 ['O', 'B-singer', 'I-singer', 'I-singer', 'O', 'O']。 | |||||
返回 [('singer', (1, 4))] (左闭右开区间) | |||||
:param tags: List[str], | :param tags: List[str], | ||||
:param ignore_labels: List[str], 在该list中的label将被忽略 | :param ignore_labels: List[str], 在该list中的label将被忽略 | ||||
@@ -204,9 +204,6 @@ class SpanFPreRecMetric(Metric): | |||||
:param tag_vocab: 标签的 :class:`~fastNLP.Vocabulary` 。支持的标签为"B"(没有label);或"B-xxx"(xxx为某种label,比如POS中的NN), | :param tag_vocab: 标签的 :class:`~fastNLP.Vocabulary` 。支持的标签为"B"(没有label);或"B-xxx"(xxx为某种label,比如POS中的NN), | ||||
在解码时,会将相同xxx的认为是同一个label,比如['B-NN', 'E-NN']会被合并为一个'NN'. | 在解码时,会将相同xxx的认为是同一个label,比如['B-NN', 'E-NN']会被合并为一个'NN'. | ||||
:param pred: 用该key在evaluate()时从传入dict中取出prediction数据。 为None,则使用 `pred` 取数据 | |||||
:param target: 用该key在evaluate()时从传入dict中取出target数据。 为None,则使用 `target` 取数据 | |||||
:param seq_len: 用该key在evaluate()时从传入dict中取出sequence length数据。为None,则使用 `seq_len` 取数据。 | |||||
:param encoding_type: 目前支持bio, bmes, bmeso, bioes。默认为None,通过tag_vocab自动判断. | :param encoding_type: 目前支持bio, bmes, bmeso, bioes。默认为None,通过tag_vocab自动判断. | ||||
:param ignore_labels: str 组成的list. 这个list中的class不会被用于计算。例如在POS tagging时传入['NN'],则不会计算'NN'个label | :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 only_gross: 是否只计算总的f1, precision, recall的值;如果为False,不仅返回总的f1, pre, rec, 还会返回每个label的f1, pre, rec | ||||
@@ -256,11 +253,17 @@ class SpanFPreRecMetric(Metric): | |||||
self._fn = Counter() | self._fn = Counter() | ||||
def reset(self): | def reset(self): | ||||
""" | |||||
重置所有元素 | |||||
""" | |||||
self._tp.clear() | self._tp.clear() | ||||
self._fp.clear() | self._fp.clear() | ||||
self._fn.clear() | self._fn.clear() | ||||
def get_metric(self) -> dict: | def get_metric(self) -> dict: | ||||
""" | |||||
get_metric 函数将根据 update 函数累计的评价指标统计量来计算最终的评价结果. | |||||
""" | |||||
evaluate_result = {} | evaluate_result = {} | ||||
# 通过 all_gather_object 将各个卡上的结果收集过来,并加和。 | # 通过 all_gather_object 将各个卡上的结果收集过来,并加和。 | ||||
@@ -314,7 +317,8 @@ 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"""update函数将针对一个批次的预测结果做评价指标的累计 | |||||
r"""u | |||||
pdate函数将针对一个批次的预测结果做评价指标的累计 | |||||
:param pred: [batch, seq_len] 或者 [batch, seq_len, len(tag_vocab)], 预测的结果 | :param pred: [batch, seq_len] 或者 [batch, seq_len, len(tag_vocab)], 预测的结果 | ||||
:param target: [batch, seq_len], 真实值 | :param target: [batch, seq_len], 真实值 | ||||