Lliutongtongcode check for master # Conflicts: # mindspore/common/initializer.py # mindspore/nn/cell.py # # 似乎您正在做一个拣选提交。如果不对,请删除文件 # .git/CHERRY_PICK_HEAD # 然后重试。 # 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交 # 说明将会终止提交。 # # 日期: Fri Aug 13 18:40:19 2021 +0800 # # 位于分支 code_review_r1.3 # 您的分支与上游分支 'ma/r1.3' 一致。 # # 您在执行拣选提交 ffda6be35c 的操作。 # # 要提交的变更: # 修改: mindspore/common/__init__.py # 修改: mindspore/common/_register_for_tensor.py # 修改: mindspore/common/api.py # 修改: mindspore/common/dtype.py # 修改: mindspore/common/initializer.py # 修改: mindspore/common/monad.py # 修改: mindspore/common/parameter.py # 修改: mindspore/common/seed.py # 修改: mindspore/common/tensor.py # 修改: mindspore/nn/cell.py # 修改: mindspore/nn/metrics/__init__.py # 修改: mindspore/nn/metrics/confusion_matrix.py # 修改: mindspore/nn/metrics/error.py # 修改: mindspore/nn/metrics/fbeta.py # 修改: mindspore/nn/metrics/loss.py # 修改: mindspore/nn/metrics/metric.py # 修改: mindspore/nn/metrics/precision.py # 修改: mindspore/nn/metrics/recall.py # 修改: mindspore/nn/metrics/topk.py # 修改: mindspore/train/callback/_checkpoint.py # 修改: mindspore/train/model.py # 修改: mindspore/train/serialization.py # # Conflicts: # mindspore/common/api.py # mindspore/common/initializer.py # mindspore/nn/metrics/confusion_matrix.py # # 似乎您正在做一个拣选提交。如果不对,请删除文件 # .git/CHERRY_PICK_HEAD # 然后重试。 # 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交 # 说明将会终止提交。 # # 日期: Fri Aug 13 18:40:19 2021 +0800 # # 位于分支 code_review_master # 您的分支与上游分支 'ma/master' 一致。 # # 您在执行拣选提交 743f9fbff3 的操作。 # # 要提交的变更: # 修改: mindspore/common/__init__.py # 修改: mindspore/common/_monad.py # 修改: mindspore/common/_register_for_tensor.py # 修改: mindspore/common/api.py # 修改: mindspore/common/dtype.py # 修改: mindspore/common/initializer.py # 修改: mindspore/common/parameter.py # 修改: mindspore/common/seed.py # 修改: mindspore/common/tensor.py # 修改: mindspore/nn/cell.py # 修改: mindspore/nn/metrics/__init__.py # 修改: mindspore/nn/metrics/confusion_matrix.py # 修改: mindspore/nn/metrics/error.py # 修改: mindspore/nn/metrics/fbeta.py # 修改: mindspore/nn/metrics/loss.py # 修改: mindspore/nn/metrics/metric.py # 修改: mindspore/nn/metrics/precision.py # 修改: mindspore/nn/metrics/recall.py # 修改: mindspore/nn/metrics/topk.py # 修改: mindspore/train/callback/_checkpoint.py # 修改: mindspore/train/model.py # 修改: mindspore/train/serialization.py #
077f10b7创建于 2021年8月30日历史提交
# Copyright 2020-2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Precision."""
import sys

import numpy as np

from mindspore._checkparam import Validator as validator
from .metric import EvaluationBase, rearrange_inputs


class Precision(EvaluationBase):
    r"""
    Calculates precision for classification and multilabel data.

    The precision function creates two local variables, :math:`\text{true_positive}` and
    :math:`\text{false_positive}`, that are used to compute the precision. This value is
    ultimately returned as the precision, an idempotent operation that simply divides
    :math:`\text{true_positive}` by the sum of :math:`\text{true_positive}` and :math:`\text{false_positive}`.

    .. math::
        \text{precision} = \frac{\text{true_positive}}{\text{true_positive} + \text{false_positive}}

    Note:
        In the multi-label cases, the elements of :math:`y` and :math:`y_{pred}` must be 0 or 1.

    Args:
        eval_type (str): Metric to calculate accuracy over a dataset, for classification or
                         multilabel. Default: 'classification'.

    Examples:
        >>> import numpy as np
        >>> from mindspore import nn, Tensor
        >>>
        >>> x = Tensor(np.array([[0.2, 0.5], [0.3, 0.1], [0.9, 0.6]]))
        >>> y = Tensor(np.array([1, 0, 1]))
        >>> metric = nn.Precision('classification')
        >>> metric.clear()
        >>> metric.update(x, y)
        >>> precision = metric.eval()
        >>> print(precision)
        [0.5 1. ]

    """
    def __init__(self, eval_type='classification'):
        super(Precision, self).__init__(eval_type)
        self.eps = sys.float_info.min
        self.clear()

    def clear(self):
        """Clears the internal evaluation result."""
        self._class_num = 0
        if self._type == "multilabel":
            self._true_positives = np.empty(0)
            self._positives = np.empty(0)
            self._true_positives_average = 0
            self._positives_average = 0
        else:
            self._true_positives = 0
            self._positives = 0

    @rearrange_inputs
    def update(self, *inputs):
        """
        Updates the internal evaluation result with `y_pred` and `y`.

        Args:
            inputs: Input `y_pred` and `y`. `y_pred` and `y` are Tensor, list or numpy.ndarray.
                For 'classification' evaluation type, `y_pred` is in most cases (not strictly) a list
                of floating numbers in range :math:`[0, 1]`
                and the shape is :math:`(N, C)`, where :math:`N` is the number of cases and :math:`C`
                is the number of categories. Shape of `y` can be :math:`(N, C)` with values 0 and 1 if one-hot
                encoding is used or the shape is :math:`(N,)` with integer values if index of category is used.
                For 'multilabel' evaluation type, `y_pred` and `y` can only be one-hot encoding with
                values 0 or 1. Indices with 1 indicate positive category. The shape of `y_pred` and `y`
                are both :math:`(N, C)`.

        Raises:
            ValueError: If the number of input is not 2.
        """
        if len(inputs) != 2:
            raise ValueError('The precision needs 2 inputs (y_pred, y), but got {}'.format(len(inputs)))
        y_pred = self._convert_data(inputs[0])
        y = self._convert_data(inputs[1])
        if self._type == 'classification' and y_pred.ndim == y.ndim and self._check_onehot_data(y):
            y = y.argmax(axis=1)
        self._check_shape(y_pred, y)
        self._check_value(y_pred, y)

        if self._class_num == 0:
            self._class_num = y_pred.shape[1]
        elif y_pred.shape[1] != self._class_num:
            raise ValueError('Class number not match, last input data contain {} classes, but current data contain {} '
                             'classes'.format(self._class_num, y_pred.shape[1]))

        class_num = self._class_num
        if self._type == "classification":
            if y.max() + 1 > class_num:
                raise ValueError('y_pred contains {} classes less than y contains {} classes.'.
                                 format(class_num, y.max() + 1))
            y = np.eye(class_num)[y.reshape(-1)]
            indices = y_pred.argmax(axis=1).reshape(-1)
            y_pred = np.eye(class_num)[indices]
        elif self._type == "multilabel":
            y_pred = y_pred.swapaxes(1, 0).reshape(class_num, -1)
            y = y.swapaxes(1, 0).reshape(class_num, -1)

        positives = y_pred.sum(axis=0)
        true_positives = (y * y_pred).sum(axis=0)

        if self._type == "multilabel":
            self._true_positives_average += np.sum(true_positives / (positives + self.eps))
            self._positives_average += len(positives)
            self._true_positives = np.concatenate((self._true_positives, true_positives), axis=0)
            self._positives = np.concatenate((self._positives, positives), axis=0)

        else:
            self._true_positives += true_positives
            self._positives += positives

    def eval(self, average=False):
        """
        Computes the precision.

        Args:
            average (bool): Specify whether calculate the average precision. Default value is False.

        Returns:
            Float, the computed result.
        """
        if self._class_num == 0:
            raise RuntimeError('The input number of samples can not be 0.')

        validator.check_value_type("average", average, [bool], self.__class__.__name__)
        result = self._true_positives / (self._positives + self.eps)

        if average:
            if self._type == "multilabel":
                result = self._true_positives_average / (self._positives_average + self.eps)
            return result.mean()
        return result