diff --git a/README.md b/README.md index 04124d68..106f619b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # mindspore_group_2 +begin from 12.16 diff --git a/doc/mindspore开源代码的泛读报告.docx b/doc/mindspore开源代码的泛读报告.docx index 3f9c336b..5d02506c 100644 Binary files a/doc/mindspore开源代码的泛读报告.docx and b/doc/mindspore开源代码的泛读报告.docx differ diff --git a/readme.txt b/readme.txt deleted file mode 100644 index 7cc99009..00000000 --- a/readme.txt +++ /dev/null @@ -1,2 +0,0 @@ -branch-donghaoqian -董昊千 \ No newline at end of file diff --git a/src/mindspore2022/mindspore/python/mindspore/nn/loss/loss.py b/src/mindspore2022/mindspore/python/mindspore/nn/loss/loss.py index 714f947b..794b8d11 100644 --- a/src/mindspore2022/mindspore/python/mindspore/nn/loss/loss.py +++ b/src/mindspore2022/mindspore/python/mindspore/nn/loss/loss.py @@ -12,639 +12,594 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================ -"""loss""" -import mindspore -import mindspore.common.dtype as mstype -from mindspore import log -from mindspore.common.tensor import Tensor -from mindspore.common.parameter import Parameter -from mindspore.ops import operations as P -from mindspore.ops import functional as F -from mindspore import nn -from mindspore.ops.primitive import constexpr -from mindspore.nn.cell import Cell -from mindspore.nn.layer.activation import get_activation -from mindspore._checkparam import Validator as validator -from mindspore._checkparam import Rel -from ... import context - +#loss +import mindspore # 导入MindSpore库,用于深度学习开发 +import mindspore.common.dtype as mstype # 导入数据类型模块 +from mindspore import log # 导入日志记录功能 +from mindspore.common.tensor import Tensor # 导入张量类,用于表示多维数组 +from mindspore.common.parameter import Parameter # 导入参数类,用于创建可训练参数 +from mindspore.ops import operations as P # 导入操作模块,提供了各种神经网络操作 +from mindspore.ops import functional as F # 导入函数式API,提供了一些常用的函数 +from mindspore import nn # 导入神经网络模块 +from mindspore.ops.primitive import constexpr # 导入constexpr装饰器,用于定义编译时常量函数 +from mindspore.nn.cell import Cell # 导入Cell类,是所有神经网络组件的基类 +from mindspore.nn.layer.activation import get_activation # 导入激活函数获取器 +from mindspore._checkparam import Validator as validator # 导入验证器,用于参数校验 +from mindspore._checkparam import Rel # 导入关系检查工具 +from ... import context # 导入上下文模块,可以用来设置运行环境 class LossBase(Cell): - """ - Base class for other losses. - - Other losses derived from this should implement their own `construct` and use method `self.get_loss` - to apply reduction to loss values. - - Args: - reduction (str): Type of reduction to be applied to loss. The optional values are "mean", "sum", and "none". - Default: "mean". - - Raises: - ValueError: If `reduction` is not one of 'none', 'mean', 'sum'. - - Supported Platforms: - ``Ascend`` ``GPU`` ``CPU`` - """ - + # + # 其他损失函数的基础类。 + # 派生自此类的其他损失函数应实现自己的`construct`方法并使用`self.get_loss` + # 方法对损失值应用减少(reduction)。 + # Args: + # reduction (str): 要应用于损失的减少类型的字符串。可选值为"mean", "sum"和"none"。 + # 默认值:"mean"。 + # Raises: + # ValueError: 如果`reduction`不是'none', 'mean', 'sum'之一。 + # 支持的平台: + # ``Ascend`` ``GPU`` ``CPU`` + # def __init__(self, reduction='mean'): - """Initialize Loss.""" + #初始化Loss super(LossBase, self).__init__() - - if reduction not in ('mean', 'sum', 'none'): + if reduction not in ('mean', 'sum', 'none'): # 检查reduction是否为合法值 raise ValueError(f"For '{self.cls_name}', the 'reduction' should be in ['mean', 'sum', 'none'], " f"but got {reduction}.") - - self.average = True - self.reduce = True - if reduction == 'sum': + self.average = True # 设置默认为平均 + self.reduce = True # 设置默认为减少 + if reduction == 'sum': # 如果reduction为'sum',则不进行平均 self.average = False - if reduction == 'none': + if reduction == 'none': # 如果reduction为'none',则既不减少也不平均 self.reduce = False - - self.reduce_mean = P.ReduceMean() - self.reduce_sum = P.ReduceSum() - self.mul = P.Mul() - self.cast = P.Cast() - + self.reduce_mean = P.ReduceMean() # 创建一个计算均值的操作实例 + self.reduce_sum = P.ReduceSum() # 创建一个计算总和的操作实例 + self.mul = P.Mul() # 创建一个乘法操作实例 + self.cast = P.Cast() # 创建一个类型转换操作实例 + def get_axis(self, x): - """ - Get a range of axis for input. - - Args: - x (Tensor): Tensor of any shape. - - Examples: - >>> class Net(nn.LossBase): - ... def __init__(self, reduction='mean'): - ... super(Net, self).__init__(reduction) - ... self.abs = ops.Abs() - ... - ... def construct(self, logits, labels): - ... x = self.abs(logits - labels) - ... axis = self.get_axis(x) - ... return axis - >>> net = Net() - >>> # Case 1: logits.shape = labels.shape = (3,) - >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) - >>> labels = Tensor(np.array([1, 2, 3]), mindspore.float32) - >>> output = net(logits, labels) - >>> print(output) - (0,) - >>> # Case 2: logits.shape = labels.shape = (3, 3) - >>> logits = Tensor(np.array([[1, 2, 3],[1, 2, 3],[1, 2, 3]]), mindspore.float32) - >>> labels = Tensor(np.array([[1, 2, 3],[1, 2, 3],[1, 2, 3]]), mindspore.float32) - >>> output = net(logits, labels) - >>> print(output) - (0, 1) - """ - shape = F.shape(x) - length = F.tuple_len(shape) - perm = F.make_range(0, length) + # + # 获取输入张量的轴范围。 + # Args: + # x (Tensor): 任意形状的张量。 + # 示例: + # >>> class Net(nn.LossBase): + # ... def __init__(self, reduction='mean'): + # ... super(Net, self).__init__(reduction) + # ... self.abs = ops.Abs() + # ... + # ... def construct(self, logits, labels): + # ... x = self.abs(logits - labels) + # ... axis = self.get_axis(x) + # ... return axis + # >>> net = Net() + # >>> # Case 1: logits.shape = labels.shape = (3,) + # >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) + # >>> labels = Tensor(np.array([1, 2, 3]), mindspore.float32) + # >>> output = net(logits, labels) + # >>> print(output) + # (0,) + # >>> # Case 2: logits.shape = labels.shape = (3, 3) + # >>> logits = Tensor(np.array([[1, 2, 3],[1, 2, 3],[1, 2, 3]]), mindspore.float32) + # >>> labels = Tensor(np.array([[1, 2, 3],[1, 2, 3],[1, 2, 3]]), mindspore.float32) + # >>> output = net(logits, labels) + # >>> print(output) + # (0, 1) + # + shape = F.shape(x) # 获取x的形状 + length = F.tuple_len(shape) # 获取shape的长度 + perm = F.make_range(0, length) # 创建从0到length的范围 return perm - + def get_loss(self, x, weights=1.0): - """ - Computes the weighted loss. - - Args: - x (Tensor): Tensor of shape :math:`(N, *)` where :math:`*` means, any number of - additional dimensions. - weights (Union[float, Tensor]): Optional `Tensor` whose rank is either 0, or the same rank as inputs, - and must be broadcastable to inputs (i.e., all dimensions must be either `1`, - or the same as the corresponding inputs dimension). Default: 1.0. - - Returns: - Return the weighted loss. - - Examples: - >>> class Net(nn.LossBase): - ... def __init__(self, reduction='mean'): - ... super(Net, self).__init__(reduction) - ... self.abs = ops.Abs() - ... - ... def construct(self, logits, labels): - ... x = self.abs(logits - labels) - ... output = self.get_loss(x) - ... return output - >>> net = Net() - >>> # Case 1: logits.shape = labels.shape = (3,) - >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) - >>> labels = Tensor(np.array([1, 2, 2]), mindspore.float32) - >>> output = net(logits, labels) - >>> print(output) - 0.33333334 - >>> # Case 2: logits.shape = labels.shape = (3, 3) - >>> logits = Tensor(np.array([[1, 2, 3],[1, 2, 3],[1, 2, 3]]), mindspore.float32) - >>> labels = Tensor(np.array([[1, 2, 2],[1, 2, 3],[1, 2, 3]]), mindspore.float32) - >>> output = net(logits, labels) - >>> print(output) - 0.11111111 - """ - input_dtype = x.dtype - x = self.cast(x, mstype.float32) - weights = self.cast(weights, mstype.float32) - x = self.mul(weights, x) - if self.reduce and self.average: - x = self.reduce_mean(x, self.get_axis(x)) - if self.reduce and not self.average: - x = self.reduce_sum(x, self.get_axis(x)) - x = self.cast(x, input_dtype) + # + # 计算加权后的损失。 + # Args: + # x (Tensor): 形状为 :math:`(N, *)` 的张量,其中 :math:`*` 表示任何数量的额外维度。 + # weights (Union[float, Tensor]): 可选的 `Tensor`,其秩要么是0,要么与输入相同, + # 并且必须能够广播到输入(即所有维度都必须是`1`或与相应的输入维度相同)。默认值:1.0。 + # 返回: + # 加权后的损失。 + # 示例: + # >>> class Net(nn.LossBase): + # ... def __init__(self, reduction='mean'): + # ... super(Net, self).__init__(reduction) + # ... self.abs = ops.Abs() + # ... + # ... def construct(self, logits, labels): + # ... x = self.abs(logits - labels) + # ... output = self.get_loss(x) + # ... return output + # >>> net = Net() + # >>> # Case 1: logits.shape = labels.shape = (3,) + # >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) + # >>> labels = Tensor(np.array([1, 2, 2]), mindspore.float32) + # >>> output = net(logits, labels) + # >>> print(output) + # 0.33333334 + # >>> # Case 2: logits.shape = labels.shape = (3, 3) + # >>> logits = Tensor(np.array([[1, 2, 3],[1, 2, 3],[1, 2, 3]]), mindspore.float32) + # >>> labels = Tensor(np.array([[1, 2, 2],[1, 2, 3],[1, 2, 3]]), mindspore.float32) + # >>> output = net(logits, labels) + # >>> print(output) + # 0.11111111 + # + input_dtype = x.dtype # 获取输入张量的数据类型 + x = self.cast(x, mstype.float32) # 将输入张量转换为float32类型 + weights = self.cast(weights, mstype.float32) # 将权重转换为float32类型 + x = self.mul(weights, x) # 将权重与输入相乘 + if self.reduce and self.average: # 如果需要减少并且平均 + x = self.reduce_mean(x, self.get_axis(x)) # 沿着指定轴求均值 + if self.reduce and not self.average: # 如果需要减少但不需要平均 + x = self.reduce_sum(x, self.get_axis(x)) # 沿着指定轴求和 + x = self.cast(x, input_dtype) # 将结果转换回原始数据类型 return x - + def construct(self, logits, labels): - raise NotImplementedError + raise NotImplementedError # 抽象方法,派生类需实现此方法 class _Loss(LossBase): - """ - Base class for other losses. - """ - + # + # 其他损失函数的基础类。 + # def __init__(self, reduction='mean'): - """Initialize _Loss.""" + #初始化_Loss. log.warning("'_Loss' is deprecated from version 1.3 and " "will be removed in a future version, use 'LossBase' instead.") super(_Loss, self).__init__(reduction) - + def construct(self, logits, labels): - raise NotImplementedError + raise NotImplementedError # 抽象方法,派生类需实现此方法 @constexpr def _check_is_tensor(param_name, input_data, cls_name): - """Internal function, used to check whether the input data is Tensor.""" + """内部函数,用于检查输入数据是否为Tensor类型。""" if input_data is not None and not isinstance(F.typeof(input_data), mstype.tensor_type): raise TypeError(f"For '{cls_name}', the '{param_name}' should be '{mstype.tensor_type}', " f"but got '{F.typeof(input_data)}'") class L1Loss(LossBase): - r""" - L1Loss is used to calculate the mean absolute error between the predicted value and the target value. - - Assuming that the :math:`x` and :math:`y` are 1-D Tensor, length :math:`N`, then calculate the loss of :math:`x` and - :math:`y` without dimensionality reduction (the reduction parameter is set to "none"). The formula is as follows: - - .. math:: - \ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad \text{with } l_n = \left| x_n - y_n \right|, - - where :math:`N` is the batch size. If `reduction` is not 'none', then: - - .. math:: - \ell(x, y) = - \begin{cases} - \operatorname{mean}(L), & \text{if reduction} = \text{'mean';}\\ - \operatorname{sum}(L), & \text{if reduction} = \text{'sum'.} - \end{cases} - - Args: - reduction (str): Type of reduction to be applied to loss. The optional values are "mean", "sum", and "none". - Default: "mean". If `reduction` is "mean" or "sum", then output a scalar Tensor, if `reduction` is "none", - the shape of the output Tensor is the broadcasted shape. - - Inputs: - - **logits** (Tensor) - Predicted value, Tensor of any dimension. - - **labels** (Tensor) - Target value, same shape as the `logits` in common cases. - However, it supports the shape of `logits` is different from the shape of `labels` - and they should be broadcasted to each other. - - Outputs: - Tensor, data type is float. - - Raises: - ValueError: If `reduction` is not one of 'none', 'mean', 'sum'. - ValueError: If `logits` and `labels` have different shapes and cannot be broadcasted to each other. - - Supported Platforms: - ``Ascend`` ``GPU`` ``CPU`` - - Examples: - >>> # Case 1: logits.shape = labels.shape = (3,) - >>> loss = nn.L1Loss() - >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) - >>> labels = Tensor(np.array([1, 2, 2]), mindspore.float32) - >>> output = loss(logits, labels) - >>> print(output) - 0.33333334 - >>> # Case 2: logits.shape = (3,), labels.shape = (2, 3) - >>> loss = nn.L1Loss(reduction='none') - >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) - >>> labels = Tensor(np.array([[1, 1, 1], [1, 2, 2]]), mindspore.float32) - >>> output = loss(logits, labels) - >>> print(output) - [[0. 1. 2.] - [0. 0. 1.]] - """ - + # r""" + # L1Loss用于计算预测值和目标值之间的平均绝对误差。 + # 假设 :math:`x` 和 :math:`y` 是1-D Tensor,长度为 :math:`N` ,则在不进行降维的情况下(即reduction参数设置为"none"),:math:`x` 和 :math:`y` 的损失公式如下: + # .. math:: + # \ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad \text{with } l_n = \left| x_n - y_n \right|, + # 其中 :math:`N` 是批次大小。如果 `reduction` 不是 'none',那么: + # .. math:: + # \ell(x, y) = + # \begin{cases} + # \operatorname{mean}(L), & \text{if reduction} = \text{'mean';}\\ + # \operatorname{sum}(L), & \text{if reduction} = \text{'sum'.} + # \end{cases} + # 参数: + # reduction (str): 应用到损失上的减少类型。可选值为 "mean", "sum" 和 "none"。默认值:"mean"。如果 `reduction` 是 "mean" 或 "sum",则输出标量Tensor;如果 `reduction` 是 "none",则输出张量的形状为广播后的形状。 + # 输入: + # - **logits** (Tensor) - 预测值,任意维度的张量。 + # - **labels** (Tensor) - 目标值,通常情况下与 `logits` 形状相同。 + # 然而,它支持 `logits` 与 `labels` 形状不同,并且它们应该能被广播到彼此。 + # 输出: + # Tensor, 数据类型为浮点型。 + # 异常: + # ValueError: 如果 `reduction` 不是 'none', 'mean', 'sum' 之一。 + # ValueError: 如果 `logits` 和 `labels` 形状不同且不能被广播。 + # 支持的平台: + # ``Ascend`` ``GPU`` ``CPU`` + # 示例: + # >>> # Case 1: logits.shape = labels.shape = (3,) + # >>> loss = nn.L1Loss() + # >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) + # >>> labels = Tensor(np.array([1, 2, 2]), mindspore.float32) + # >>> output = loss(logits, labels) + # >>> print(output) + # 0.33333334 + # >>> # Case 2: logits.shape = (3,), labels.shape = (2, 3) + # >>> loss = nn.L1Loss(reduction='none') + # >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) + # >>> labels = Tensor(np.array([[1, 1, 1], [1, 2, 2]]), mindspore.float32) + # >>> output = loss(logits, labels) + # >>> print(output) + # [[0. 1. 2.] + # [0. 0. 1.]] + # def __init__(self, reduction='mean'): - """Initialize L1Loss.""" + #初始化L1Loss. super(L1Loss, self).__init__(reduction) - self.abs = P.Abs() - + self.abs = P.Abs() # 创建绝对值操作实例 + def construct(self, logits, labels): - _check_is_tensor('logits', logits, self.cls_name) - _check_is_tensor('labels', labels, self.cls_name) - x = self.abs(logits - labels) - return self.get_loss(x) + _check_is_tensor('logits', logits, self.cls_name) # 检查logits是否为Tensor + _check_is_tensor('labels', labels, self.cls_name) # 检查labels是否为Tensor + x = self.abs(logits - labels) # 计算logits和labels之间的绝对差值 + return self.get_loss(x) # 返回加权后的损失 class MSELoss(LossBase): - r""" - Calculates the mean squared error between the predicted value and the label value. - - For simplicity, let :math:`x` and :math:`y` be 1-dimensional Tensor with length :math:`N`, - the unreduced loss (i.e. with argument reduction set to 'none') of :math:`x` and :math:`y` is given as: - - .. math:: - \ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad \text{with} \quad l_n = (x_n - y_n)^2. - - where :math:`N` is the batch size. If `reduction` is not 'none', then: - - .. math:: - \ell(x, y) = - \begin{cases} - \operatorname{mean}(L), & \text{if reduction} = \text{'mean';}\\ - \operatorname{sum}(L), & \text{if reduction} = \text{'sum'.} - \end{cases} - - Args: - reduction (str): Type of reduction to be applied to loss. The optional values are "mean", "sum", and "none". - Default: "mean". - - Inputs: - - **logits** (Tensor) - The predicted value of the input. Tensor of any dimension. - - **labels** (Tensor) - The input label. Tensor of any dimension, same shape as the `logits` in common cases. - However, it supports the shape of `logits` is different from the shape of `labels` - and they should be broadcasted to each other. - - Outputs: - Tensor, loss of type float, the shape is zero if `reduction` is 'mean' or 'sum', - while the shape of output is the broadcasted shape if `reduction` is 'none'. - - Raises: - ValueError: If `reduction` is not one of 'none', 'mean' or 'sum'. - ValueError: If `logits` and `labels` have different shapes and cannot be broadcasted. - - Supported Platforms: - ``Ascend`` ``GPU`` ``CPU`` - - Examples: - >>> # Case 1: logits.shape = labels.shape = (3,) - >>> loss = nn.MSELoss() - >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) - >>> labels = Tensor(np.array([1, 1, 1]), mindspore.float32) - >>> output = loss(logits, labels) - >>> print(output) - 1.6666667 - >>> # Case 2: logits.shape = (3,), labels.shape = (2, 3) - >>> loss = nn.MSELoss(reduction='none') - >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) - >>> labels = Tensor(np.array([[1, 1, 1], [1, 2, 2]]), mindspore.float32) - >>> output = loss(logits, labels) - >>> print(output) - [[0. 1. 4.] - [0. 0. 1.]] - """ - + # r""" + # 计算预测值和标签值之间的均方误差。 + # 对于简单的情况,假设 :math:`x` 和 :math:`y` 是1维Tensor,长度为 :math:`N` ,未减少的损失(即设置reduction为'none')为: + # .. math:: + # \ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad \text{with} \quad l_n = (x_n - y_n)^2. + # 其中 :math:`N` 是批次大小。如果 `reduction` 不是 'none',那么: + # .. math:: + # \ell(x, y) = + # \begin{cases} + # \operatorname{mean}(L), & \text{if reduction} = \text{'mean';}\\ + # \operatorname{sum}(L), & \text{if reduction} = \text{'sum'.} + # \end{cases} + # 参数: + # reduction (str): 应用到损失上的减少类型。可选值为 "mean", "sum" 和 "none"。默认值:"mean"。 + # 输入: + # - **logits** (Tensor) - 输入的预测值。任意维度的张量。 + # - **labels** (Tensor) - 输入标签。通常情况下与 `logits` 形状相同。 + # 然而,它支持 `logits` 与 `labels` 形状不同,并且它们应该能被广播到彼此。 + # 输出: + # Tensor, 类型为浮点型的损失,如果 `reduction` 是 'mean' 或 'sum',则形状为零; + # 如果 `reduction` 是 'none',则输出的形状为广播后的形状。 + # 异常: + # ValueError: 如果 `reduction` 不是 'none', 'mean' 或 'sum' 之一。 + # ValueError: 如果 `logits` 和 `labels` 形状不同且不能被广播。 + # 支持的平台: + # ``Ascend`` ``GPU`` ``CPU`` + # 示例: + # >>> # Case 1: logits.shape = labels.shape = (3,) + # >>> loss = nn.MSELoss() + # >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) + # >>> labels = Tensor(np.array([1, 1, 1]), mindspore.float32) + # >>> output = loss(logits, labels) + # >>> print(output) + # 1.6666667 + # >>> # Case 2: logits.shape = (3,), labels.shape = (2, 3) + # >>> loss = nn.MSELoss(reduction='none') + # >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) + # >>> labels = Tensor(np.array([[1, 1, 1], [1, 2, 2]]), mindspore.float32) + # >>> output = loss(logits, labels) + # >>> print(output) + # [[0. 1. 4.] + # [0. 0. 1.]] + # def construct(self, logits, labels): - _check_is_tensor('logits', logits, self.cls_name) - _check_is_tensor('labels', labels, self.cls_name) - x = F.square(logits - labels) - return self.get_loss(x) + _check_is_tensor('logits', logits, self.cls_name) # 检查logits是否为Tensor + _check_is_tensor('labels', labels, self.cls_name) # 检查labels是否为Tensor + x = F.square(logits - labels) # 计算logits和labels之间的平方差 + return self.get_loss(x) # 返回加权后的损失 class RMSELoss(LossBase): - r""" - RMSELoss creates a criterion to measure the root mean square error between :math:`x` and :math:`y` - element-wise, where :math:`x` is the input and :math:`y` is the labels. - - For simplicity, let :math:`x` and :math:`y` be 1-dimensional Tensor with length :math:`N`, - the loss of :math:`x` and :math:`y` is given as: - - .. math:: - loss = \sqrt{\frac{1}{N}\sum_{i=1}^{N}{(x_i-y_i)^2}} - - Inputs: - - **logits** (Tensor) - Tensor of shape :math:`(N, *)` where :math:`*` means, any number of - additional dimensions. - - **labels** (Tensor) - Tensor of shape :math:`(N, *)`, same shape as the `logits` in common cases. - However, it supports the shape of `logits` is different from the shape of `labels` - and they should be broadcasted to each other. - - Outputs: - Tensor, weighted loss float tensor and its shape is (). - - Supported Platforms: - ``Ascend`` ``GPU`` ``CPU`` - - Examples: - >>> # Case 1: logits.shape = labels.shape = (3,) - >>> loss = nn.RMSELoss() - >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) - >>> labels = Tensor(np.array([1, 2, 2]), mindspore.float32) - >>> output = loss(logits, labels) - >>> print(output) - 0.57735026 - >>> # Case 2: logits.shape = (3,), labels.shape = (2, 3) - >>> loss = nn.RMSELoss() - >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) - >>> labels = Tensor(np.array([[1, 1, 1], [1, 2, 2]]), mindspore.float32) - >>> output = loss(logits, labels) - >>> print(output) - 1.0 - """ - + # r""" + # RMSELoss 创建了一个准则来衡量 :math:`x` 和 :math:`y` 之间的均方根误差(Root Mean Square Error, RMSE),其中 :math:`x` 是输入,:math:`y` 是标签。 + # 对于简单情况,假设 :math:`x` 和 :math:`y` 是1维张量,长度为 :math:`N`,那么 :math:`x` 和 :math:`y` 的损失计算公式如下: + # .. math:: + # loss = \sqrt{\frac{1}{N}\sum_{i=1}^{N}{(x_i-y_i)^2}} + # Inputs: + # - **logits** (Tensor) - 形状为 :math:`(N, *)` 的预测值张量,其中 :math:`*` 表示任何数量的额外维度。 + # - **labels** (Tensor) - 形状为 :math:`(N, *)` 的目标值张量,通常与 `logits` 形状相同。但是它也支持 `logits` 和 `labels` 形状不同但可以广播的情况。 + # Outputs: + # Tensor, 加权损失浮点数张量,形状为空。 + # 支持的平台: + # ``Ascend`` ``GPU`` ``CPU`` + # 示例: + # >>> # Case 1: logits.shape = labels.shape = (3,) + # >>> loss = nn.RMSELoss() + # >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) + # >>> labels = Tensor(np.array([1, 2, 2]), mindspore.float32) + # >>> output = loss(logits, labels) + # >>> print(output) + # 0.57735026 + # >>> # Case 2: logits.shape = (3,), labels.shape = (2, 3) + # >>> loss = nn.RMSELoss() + # >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) + # >>> labels = Tensor(np.array([[1, 1, 1], [1, 2, 2]]), mindspore.float32) + # >>> output = loss(logits, labels) + # >>> print(output) + # 1.0 + # """ def __init__(self): - """Initialize RMSELoss.""" - super(RMSELoss, self).__init__() - self.MSELoss = MSELoss() + # """初始化 RMSELoss.""" + super(RMSELoss, self).__init__() # 调用父类构造函数以初始化基类成员 + self.MSELoss = MSELoss() # 初始化一个 MSELoss 实例,用于计算均方误差 def construct(self, logits, label): - rmse_loss = F.sqrt(self.MSELoss(logits, label)) + # """ + # 构建并计算 RMSELoss。 - return rmse_loss + # 参数: + # - **logits** (Tensor) - 预测值张量。 + # - **label** (Tensor) - 目标值张量。 + # 返回: + # Tensor, 计算出的均方根误差损失。 + # """ + rmse_loss = F.sqrt(self.MSELoss(logits, label)) # 使用 MSELoss 计算均方误差后取平方根得到均方根误差 + return rmse_loss # 返回计算出的均方根误差损失 -class MAELoss(LossBase): - r""" - MAELoss creates a criterion to measure the average absolute error between :math:`x` and :math:`y` - element-wise, where :math:`x` is the input and :math:`y` is the labels. - - For simplicity, let :math:`x` and :math:`y` be 1-dimensional Tensor with length :math:`N`, - the unreduced loss (i.e. with argument reduction set to 'none') of :math:`x` and :math:`y` is given as: - - .. math:: - \ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad \text{with } l_n = \left| x_n - y_n \right|, - - where :math:`N` is the batch size. If `reduction` is not 'none', then: - - .. math:: - \ell(x, y) = - \begin{cases} - \operatorname{mean}(L), & \text{if reduction} = \text{'mean';}\\ - \operatorname{sum}(L), & \text{if reduction} = \text{'sum'.} - \end{cases} - - Args: - reduction (str): Type of reduction to be applied to loss. The optional values are "mean", "sum", and "none". - Default: "mean". - - Inputs: - - **logits** (Tensor) - Tensor of shape :math:`(M, *)` where :math:`*` means, any number of - additional dimensions. - - **labels** (Tensor) - Tensor of shape :math:`(N, *)`, same shape as the `logits` in common cases. - However, it supports the shape of `logits` is different from the shape of `labels` - and they should be broadcasted to each other. - - Outputs: - Tensor, weighted loss float tensor, the shape is zero if `reduction` is 'mean' or 'sum', - while the shape of output is the broadcasted shape if `reduction` is 'none'. - - Raises: - ValueError: If `reduction` is not one of 'none', 'mean', 'sum'. - - Supported Platforms: - ``Ascend`` ``GPU`` ``CPU`` - - Examples: - >>> # Case 1: logits.shape = labels.shape = (3,) - >>> loss = nn.MAELoss() - >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) - >>> labels = Tensor(np.array([1, 2, 2]), mindspore.float32) - >>> output = loss(logits, labels) - >>> print(output) - 0.33333334 - >>> # Case 2: logits.shape = (3,), labels.shape = (2, 3) - >>> loss = nn.MAELoss(reduction='none') - >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) - >>> labels = Tensor(np.array([[1, 1, 1], [1, 2, 2]]), mindspore.float32) - >>> output = loss(logits, labels) - >>> print(output) - [[0. 1. 2.] - [0. 0. 1.]] - """ +class MAELoss(LossBase): + # r""" + # MAELoss 创建了一个准则来衡量 :math:`x` 和 :math:`y` 之间的平均绝对误差(Mean Absolute Error, MAE),其中 :math:`x` 是输入,:math:`y` 是标签。 + # 对于简单情况,假设 :math:`x` 和 :math:`y` 是1维张量,长度为 :math:`N`,那么在不进行维度缩减的情况下(即 reduction 参数设置为 "none"),:math:`x` 和 :math:`y` 的损失计算公式如下: + # .. math:: + # \ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad \text{with } l_n = \left| x_n - y_n \right|, + # 其中 :math:`N` 是批次大小。如果 `reduction` 不是 'none',则根据 `reduction` 的值进一步处理: + # .. math:: + # \ell(x, y) = + # \begin{cases} + # \operatorname{mean}(L), & \text{if reduction} = \text{'mean';}\\ + # \operatorname{sum}(L), & \text{if reduction} = \text{'sum'.} + # \end{cases} + # Args: + # reduction (str): 应用于损失的减少类型。可选值为 "mean", "sum", 和 "none"。默认:"mean"。 + # Inputs: + # - **logits** (Tensor) - 形状为 :math:`(M, *)` 的预测值张量,其中 :math:`*` 表示任何数量的额外维度。 + # - **labels** (Tensor) - 形状为 :math:`(N, *)` 的目标值张量,通常与 `logits` 形状相同。 + # """ def __init__(self, reduction='mean'): - """Initialize MAELoss.""" - super(MAELoss, self).__init__(reduction) - self.abs = P.Abs() - - def construct(self, logits, label): - _check_is_tensor('logits', logits, self.cls_name) - _check_is_tensor('labels', label, self.cls_name) - x = self.abs(logits - label) - return self.get_loss(x) - - -class SmoothL1Loss(LossBase): - r""" - SmoothL1 loss function, if the absolute error element-wise between the predicted value and the target value - is less than the set threshold `beta`, the square term is used, otherwise the absolute error term is used. + # """初始化 MAELoss.""" + super(MAELoss, self).__init__(reduction=reduction) # 调用父类构造函数,并传递 reduction 参数 - Given two input :math:`x,\ y`, the SmoothL1Loss can be described as follows: - - .. math:: - L_{i} = - \begin{cases} - \frac{0.5 (x_i - y_i)^{2}}{\beta}, & \text{if } |x_i - y_i| < {\beta} \\ - |x_i - y_i| - 0.5 {\beta}, & \text{otherwise.} - \end{cases} - - Where :math:`{\beta}` represents the threshold `beta`. - - .. note:: - SmoothL1Loss can be regarded as modified version of L1Loss or a combination of L1Loss and L2Loss. - L1Loss computes the element-wise absolute difference between two input tensors while L2Loss computes the - squared difference between two input tensors. L2Loss often leads to faster convergence but it is less - robust to outliers, and the loss function has better robustness. - - Args: - beta (float): The loss function calculates the threshold of the transformation between L1Loss and L2Loss. - Default: 1.0. - - Inputs: - - **logits** (Tensor) - Predictive value. Tensor of any dimension. Data type must be float16 or float32. - - **labels** (Tensor) - Ground truth data, same shape and dtype as the `logits`. + def construct(self, logits, labels): + # """ + # 构建并计算 MAELoss。 - Outputs: - Tensor, loss float tensor, same shape and dtype as the `logits`. + # 参数: + # - **logits** (Tensor) - 预测值张量。 + # - **labels** (Tensor) - 目标值张量。 - Raises: - TypeError: If `beta` is not a float. - TypeError: If `logits` or `labels` are not Tensor. - TypeError: If dtype of `logits` or `labels` is neither float16 not float32. - TypeError: If dtype of `logits` is not the same as `labels`. - ValueError: If `beta` is less than or equal to 0. - ValueError: If shape of `logits` is not the same as `labels`. + # 返回: + # Tensor, 计算出的平均绝对误差损失。 + # """ + _check_is_tensor('logits', logits, self.cls_name) # 检查 logits 是否为张量 + _check_is_tensor('labels', labels, self.cls_name) # 检查 labels 是否为张量 + abs_diff = F.abs(logits - labels) # 计算预测值和目标值之间的绝对差 + return self.get_loss(abs_diff) # 应用指定的 reduction 并返回结果 - Supported Platforms: - ``Ascend`` ``GPU`` ``CPU`` - Examples: - >>> loss = nn.SmoothL1Loss() - >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) - >>> labels = Tensor(np.array([1, 2, 2]), mindspore.float32) - >>> output = loss(logits, labels) - >>> print(output) - [0. 0. 0.5] - """ +class SmoothL1Loss(LossBase): + # r""" + # SmoothL1 损失函数,当预测值和目标值之间的绝对误差小于设定的阈值 `beta` 时使用平方项,否则使用绝对误差项。 + + # 给定两个输入 :math:`x,\ y`,SmoothL1Loss 可以描述如下: + + # .. math:: + # L_{i} = + # \begin{cases} + # \frac{0.5 (x_i - y_i)^{2}}{\beta}, & \text{如果 } |x_i - y_i| < {\beta} \\ + # |x_i - y_i| - 0.5 {\beta}, & \text{否则.} + # \end{cases} + + # 其中 :math:`{\beta}` 表示阈值 `beta`。 + + # .. note:: + # SmoothL1Loss 可以被视为 L1Loss 的修改版本或者是 L1Loss 和 L2Loss 的组合。 + # L1Loss 计算两个输入张量之间的元素级绝对差,而 L2Loss 计算两个输入张量之间的平方差。 + # L2Loss 通常导致更快的收敛,但它对异常值的鲁棒性较差,而损失函数具有更好的鲁棒性。 + + # Args: + # beta (float): 损失函数计算 L1Loss 和 L2Loss 转换的阈值。默认:1.0。 + + # Inputs: + # - **logits** (Tensor) - 预测值。任意维度的张量。数据类型必须是 float16 或 float32。 + # - **labels** (Tensor) - 真实标签数据,与 `logits` 形状和 dtype 相同。 + + # Outputs: + # Tensor, 损失浮点数张量,形状和 dtype 与 `logits` 相同。 + + # Raises: + # TypeError: 如果 `beta` 不是浮点数。 + # TypeError: 如果 `logits` 或 `labels` 不是 Tensor。 + # TypeError: 如果 `logits` 或 `labels` 的数据类型既不是 float16 也不是 float32。 + # TypeError: 如果 `logits` 的数据类型与 `labels` 不同。 + # ValueError: 如果 `beta` 小于或等于 0。 + # ValueError: 如果 `logits` 的形状与 `labels` 不同。 + + # 支持的平台: + # ``Ascend`` ``GPU`` ``CPU`` + + # 示例: + # >>> loss = nn.SmoothL1Loss() + # >>> logits = Tensor(np.array([1, 2, 3]), mindspore.float32) + # >>> labels = Tensor(np.array([1, 2, 2]), mindspore.float32) + # >>> output = loss(logits, labels) + # >>> print(output) + # [0. 0. 0.5] + # """ def __init__(self, beta=1.0): - """Initialize SmoothL1Loss.""" - super(SmoothL1Loss, self).__init__() - self.beta = beta - self.smooth_l1_loss = P.SmoothL1Loss(self.beta) + # """初始化 SmoothL1Loss.""" + super(SmoothL1Loss, self).__init__() # 调用父类构造函数进行初始化 + validator.check_value_type("beta", beta, [float], self.cls_name) # 检查 beta 是否为浮点数 + validator.check_positive_float(beta, "beta", self.cls_name) # 检查 beta 是否为正数 + self.beta = beta # 设置 SmoothL1Loss 的 beta 参数 + self.smooth_l1_loss = P.SmoothL1Loss(self.beta) # 初始化 SmoothL1Loss 操作实例 def construct(self, logits, labels): - _check_is_tensor('logits', logits, self.cls_name) - _check_is_tensor('labels', labels, self.cls_name) - return self.smooth_l1_loss(logits, labels) + # """ + # 构建并计算 SmoothL1Loss。 + + # 参数: + # - **logits** (Tensor) - 预测值张量。 + # - **labels** (Tensor) - 标签张量。 + + # 返回: + # Tensor, 计算出的 SmoothL1 损失。 + # """ + _check_is_tensor('logits', logits, self.cls_name) # 检查 logits 是否为张量 + _check_is_tensor('labels', labels, self.cls_name) # 检查 labels 是否为张量 + return self.smooth_l1_loss(logits, labels) # 计算并返回 SmoothL1 损失 class SoftMarginLoss(LossBase): - r""" - A loss class for two-class classification problems. + # r""" + # 用于二分类问题的损失类。 - SoftMarginLoss creates a criterion that optimizes a two-class classification - logistic loss between input tensor :math:`x` and labels tensor :math:`y` - (containing 1 or -1). + # SoftMarginLoss 创建了一个优化两分类逻辑损失的标准,该标准在输入张量 :math:`x` 和标签张量 :math:`y`(包含 1 或 -1)之间进行优化。 - .. math:: - \text{loss}(x, y) = \sum_i \frac{\log(1 + \exp(-y[i]*x[i]))}{\text{x.nelement}()} + # .. math:: + # \text{loss}(x, y) = \sum_i \frac{\log(1 + \exp(-y[i]*x[i]))}{\text{x.nelement}()} - :math:`x.nelement()` represents the number of element of `x` . + # 其中 :math:`x.nelement()` 表示 `x` 的元素数量。 - Args: - reduction (str): Apply specific reduction method to the output: 'none', 'mean', 'sum'. Default: "mean". + # Args: + # reduction (str): 对输出应用特定的缩减方法:"none", "mean", "sum"。默认:"mean"。 - Inputs: - - **logits** (Tensor) - Predict data. Data type must be float16 or float32. - - **labels** (Tensor) - Ground truth data, with the same type and shape as `logits`. + # Inputs: + # - **logits** (Tensor) - 预测数据。数据类型必须是 float16 或 float32。 + # - **labels** (Tensor) - 地面真实数据,与 `logits` 类型和形状相同。 - Outputs: - Tensor or Scalar, if `reduction` is "none", its shape is the same as `logits`. - Otherwise, a scalar value will be returned. + # Outputs: + # Tensor 或 Scalar, 如果 `reduction` 是 "none",其形状与 `logits` 相同。否则,返回一个标量值。 - Raises: - TypeError: If `logits` or `labels` is not a Tensor. - TypeError: If dtype of `logits` or `labels` is neither float16 nor float32. - ValueError: If shape of `logits` is not the same as `labels`. - ValueError: If `reduction` is not one of 'none', 'mean', 'sum'. + # Raises: + # TypeError: 如果 `logits` 或 `labels` 不是 Tensor。 + # TypeError: 如果 `logits` 或 `labels` 的数据类型既不是 float16 也不是 float32。 + # ValueError: 如果 `logits` 的形状与 `labels` 不同。 + # ValueError: 如果 `reduction` 不是 'none', 'mean', 'sum' 中的一个。 - Supported Platforms: - ``Ascend`` + # 支持的平台: + # ``Ascend`` - Examples: - >>> loss = nn.SoftMarginLoss() - >>> logits = Tensor(np.array([[0.3, 0.7], [0.5, 0.5]]), mindspore.float32) - >>> labels = Tensor(np.array([[-1, 1], [1, -1]]), mindspore.float32) - >>> output = loss(logits, labels) - >>> print(output) - 0.6764238 - """ + # 示例: + # >>> loss = nn.SoftMarginLoss() + # >>> logits = Tensor(np.array([[0.3, 0.7], [0.5, 0.5]]), mindspore.float32) + # >>> labels = Tensor(np.array([[-1, 1], [1, -1]]), mindspore.float32) + # >>> output = loss(logits, labels) + # >>> print(output) + # 0.6764238 + # """ def __init__(self, reduction='mean'): - super(SoftMarginLoss, self).__init__() - self.soft_margin_loss = P.SoftMarginLoss(reduction) + # """初始化 SoftMarginLoss.""" + super(SoftMarginLoss, self).__init__() # 调用父类构造函数进行初始化 + self.soft_margin_loss = P.SoftMarginLoss(reduction) # 初始化 SoftMarginLoss 操作实例 def construct(self, logits, labels): - return self.soft_margin_loss(logits, labels) - - -class SoftmaxCrossEntropyWithLogits(LossBase): - r""" - Computes softmax cross entropy between logits and labels. + # """ + # 构建并计算 SoftMarginLoss。 - Measures the distribution error between the probabilities of the input (computed with softmax function) and the - labels where the classes are mutually exclusive (only one class is positive) using cross entropy loss. + # 参数: + # - **logits** (Tensor) - 预测值张量。 + # - **labels** (Tensor) - 标签张量。 - Typical input into this function is unnormalized scores denoted as x whose shape is (N, C), - and the corresponding targets. + # 返回: + # Tensor, 计算出的 SoftMargin 损失。 + # """ + return self.soft_margin_loss(logits, labels) # 计算并返回 SoftMargin 损失 - For each instance :math:`x_i`, i ranges from 0 to N-1, the loss is given as: - .. math:: - \ell(x_i, c) = - \log\left(\frac{\exp(x_i[c])}{\sum_j \exp(x_i[j])}\right) - = -x_i[c] + \log\left(\sum_j \exp(x_i[j])\right) - - where :math:`x_i` is a 1D score Tensor, :math:`c` is the index of 1 in one-hot. - - Note: - While the labels classes are mutually exclusive, i.e., only one class is positive in the labels, the predicted - probabilities does not need to be exclusive. It is only required that the predicted probability distribution - of entry is a valid one. - - Args: - sparse (bool): Specifies whether labels use sparse format or not. Default: False. - reduction (str): Type of reduction to be applied to loss. The optional values are "mean", "sum", and "none". - If "none", do not perform reduction. Default: "none". - - Inputs: - - **logits** (Tensor) - Tensor of shape (N, C). Data type must be float16 or float32. - - **labels** (Tensor) - Tensor of shape (N, ). If `sparse` is True, The type of - `labels` is int32 or int64. Otherwise, the type of `labels` is the same as the type of `logits`. - - Outputs: - Tensor, a tensor of the same shape and type as logits with the component-wise logistic losses. - - Raises: - TypeError: If `sparse` is not a bool. - TypeError: If `sparse` is True and dtype of `labels` is neither int32 not int64. - TypeError: If `sparse` is False and dtype of `labels` is neither float16 not float32. - ValueError: If `reduction` is not one of 'none', 'mean', 'sum'. - - Supported Platforms: - ``Ascend`` ``GPU`` ``CPU`` - - Examples: - >>> # case 1: sparse=True - >>> loss = nn.SoftmaxCrossEntropyWithLogits(sparse=True) - >>> logits = Tensor(np.array([[3, 5, 6, 9, 12, 33, 42, 12, 32, 72]]), mindspore.float32) - >>> labels_np = np.array([1]).astype(np.int32) - >>> labels = Tensor(labels_np) - >>> output = loss(logits, labels) - >>> print(output) - [67.] - >>> # case 2: sparse=False - >>> loss = nn.SoftmaxCrossEntropyWithLogits(sparse=False) - >>> logits = Tensor(np.array([[3, 5, 6, 9, 12, 33, 42, 12, 32, 72]]), mindspore.float32) - >>> labels_np = np.array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0]]).astype(np.float32) - >>> labels = Tensor(labels_np) - >>> output = loss(logits, labels) - >>> print(output) - [30.] - """ +class SoftmaxCrossEntropyWithLogits(LossBase): + # r""" + # 计算 softmax 交叉熵损失。 + + # 测量输入(通过 softmax 函数计算的概率)和标签之间的分布误差,其中类别是互斥的(只有一个类别是正类)。 + + # 输入到此函数的通常是未归一化的分数,记作 x,其形状为 (N, C),以及相应的标签。 + + # 对于每个实例 :math:`x_i`,i 的范围从 0 到 N-1,损失定义为: + + # .. math:: + # \ell(x_i, c) = - \log\left(\frac{\exp(x_i[c])}{\sum_j \exp(x_i[j])}\right) + # = -x_i[c] + \log\left(\sum_j \exp(x_i[j])\right) + + # 其中 :math:`x_i` 是一个 1D 分数张量,:math:`c` 是 one-hot 编码中 1 的索引。 + + # 注意: + # 在标签类别互斥的情况下,即只有一个是正类,预测的概率不需要是互斥的。只需要保证预测的概率分布是一个有效的分布即可。 + + # Args: + # sparse (bool): 指定标签是否使用稀疏格式。默认:False。 + # reduction (str): 应用于损失的减少类型。可选值为 "mean", "sum", "none"。默认:"none"。 + + # Inputs: + # - **logits** (Tensor) - 形状为 (N, C) 的张量。数据类型必须是 float16 或 float32。 + # - **labels** (Tensor) - 形状为 (N, ) 的张量。如果 `sparse` 为 True,则 `labels` 的类型为 int32 或 int64。 + # 否则,`labels` 的类型与 `logits` 相同。 + + # Outputs: + # Tensor, 形状和类型与 logits 相同的张量,表示每个元素的 logistic 损失。 + + # Raises: + # TypeError: 如果 `sparse` 不是布尔值。 + # TypeError: 如果 `sparse` 为 True 且 `labels` 的类型既不是 int32 也不是 int64。 + # TypeError: 如果 `sparse` 为 False 且 `labels` 的类型既不是 float16 也不是 float32。 + # ValueError: 如果 `reduction` 不是 'none', 'mean', 'sum' 中的一个。 + + # 支持的平台: + # ``Ascend`` ``GPU`` ``CPU`` + + # 示例: + # >>> # case 1: sparse=True + # >>> loss = nn.SoftmaxCrossEntropyWithLogits(sparse=True) + # >>> logits = Tensor(np.array([[3, 5, 6, 9, 12, 33, 42, 12, 32, 72]]), mindspore.float32) + # >>> labels_np = np.array([1]).astype(np.int32) + # >>> labels = Tensor(labels_np) + # >>> output = loss(logits, labels) + # >>> print(output) + # [67.] + # >>> # case 2: sparse=False + # >>> loss = nn.SoftmaxCrossEntropyWithLogits(sparse=False) + # >>> logits = Tensor(np.array([[3, 5, 6, 9, 12, 33, 42, 12, 32, 72]]), mindspore.float32) + # >>> labels_np = np.array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0]]).astype(np.float32) + # >>> labels = Tensor(labels_np) + # >>> output = loss(logits, labels) + # >>> print(output) + # [30.] + # """ def __init__(self, sparse=False, reduction='none'): - """Initialize SoftmaxCrossEntropyWithLogits.""" - super(SoftmaxCrossEntropyWithLogits, self).__init__(reduction) - self.sparse = validator.check_bool(sparse, "sparse", self.cls_name) - self.reduction = reduction - self.softmax_cross_entropy = P.SoftmaxCrossEntropyWithLogits() - self.one_hot = P.OneHot() - self.on_value = Tensor(1.0, mstype.float32) - self.off_value = Tensor(0., mstype.float32) - self.is_cpugpu = context.get_context('device_target') in ["CPU", "GPU"] - self.sparse_softmax_cross_entropy = P.SparseSoftmaxCrossEntropyWithLogits() + # """初始化 SoftmaxCrossEntropyWithLogits.""" + super(SoftmaxCrossEntropyWithLogits, self).__init__(reduction) # 调用父类构造函数,并传递 reduction 参数 + validator.check_bool(sparse, "sparse", self.cls_name) # 验证 sparse 是否为布尔值 + validator.check_string(reduction, ['none', 'mean', 'sum'], "reduction", self.cls_name) # 验证 reduction 参数 + self.sparse = sparse # 设置是否使用稀疏标签 + self.reduction = reduction # 设置损失减少方式 + self.softmax_cross_entropy = P.SoftmaxCrossEntropyWithLogits() # 初始化 SoftmaxCrossEntropyWithLogits 操作实例 + self.one_hot = P.OneHot() # 初始化 OneHot 操作实例,用于将稀疏标签转换为 one-hot 编码 + self.on_value = Tensor(1.0, mstype.float32) # 定义 one-hot 编码中的正值 + self.off_value = Tensor(0., mstype.float32) # 定义 one-hot 编码中的负值 + self.is_cpugpu = context.get_context('device_target') in ["CPU", "GPU"] # 检查当前设备是否为 CPU 或 GPU + self.sparse_softmax_cross_entropy = P.SparseSoftmaxCrossEntropyWithLogits() # 初始化 SparseSoftmaxCrossEntropyWithLogits 操作实例 def construct(self, logits, labels): - _check_is_tensor('logits', logits, self.cls_name) - _check_is_tensor('labels', labels, self.cls_name) - if self.sparse: - if self.reduction == 'mean': - x = self.sparse_softmax_cross_entropy(logits, labels) + # + # 构建并计算 SoftmaxCrossEntropyWithLogits。 + + # 参数: + # - **logits** (Tensor) - 预测值张量。 + # - **labels** (Tensor) - 标签张量。 + + # 返回: + # Tensor, 计算出的 Softmax 交叉熵损失。 + # + _check_is_tensor('logits', logits, self.cls_name) # 检查 logits 是否为张量 + _check_is_tensor('labels', labels, self.cls_name) # 检查 labels 是否为张量 + if self.sparse: # 如果标签是稀疏的 + if self.reduction == 'mean': # 如果减少方式是平均 + x = self.sparse_softmax_cross_entropy(logits, labels) # 使用 SparseSoftmaxCrossEntropyWithLogits 计算损失 return x - labels = self.one_hot(labels, F.shape(logits)[-1], self.on_value, self.off_value) - x = self.softmax_cross_entropy(logits, labels)[0] - return self.get_loss(x) + labels = self.one_hot(labels, F.shape(logits)[-1], self.on_value, self.off_value) # 将稀疏标签转换为 one-hot 编码 + x = self.softmax_cross_entropy(logits, labels)[0] # 计算 softmax 交叉熵损失 + return self.get_loss(x) # 应用指定的 reduction 并返回结果 @constexpr def _check_label_dtype(labels_dtype, cls_name): - """Internal function, used to check whether the data type of labels meets the requirements.""" + """ + 内部函数,用于检查标签的数据类型是否符合要求。 + + 参数: + labels_dtype (Type): 标签的数据类型。 + cls_name (str): 调用此函数的类的名称,用于在错误消息中标识来源。 + + 异常: + TypeError: 如果标签的数据类型不是int32或int64,则抛出此异常。 + """ + # 使用validator模块中的check_type_name方法来验证标签的数据类型是否为int32或int64。 + # 如果类型不符合要求,则会抛出TypeError,并带有自定义的错误信息。 validator.check_type_name("labels", labels_dtype, [mstype.int32, mstype.int64], cls_name) @@ -711,64 +666,90 @@ class DiceLoss(LossBase): @constexpr def _check_shape(logits_shape, label_shape, prim_name=None): - """Internal function, used to check whether the shape of logits and labels meets the requirements.""" + """ + 内部函数,用于检查logits和labels的形状是否符合要求。 + + 参数: + logits_shape (tuple): logits张量的形状。 + label_shape (tuple): labels张量的形状。 + prim_name (str, optional): 调用此函数的操作名称,用于在错误消息中标识来源。 + + 异常: + ValueError: 如果logits和labels的形状不匹配,则抛出此异常。 + """ + # 使用validator模块中的check方法来验证logits和labels的形状是否相等。 + # 如果形状不符合要求,则会抛出ValueError,并带有自定义的错误信息。 validator.check('logits_shape', logits_shape, 'label_shape', label_shape, prim_name=prim_name) @constexpr def _check_ndim_multi(logits_dim, label_dim, prim_name=None): - """Internal function, used to check whether the dimension of logits and label meets the requirements.""" + """ + 内部函数,用于检查logits和labels的维度是否满足要求。 + + 参数: + logits_dim (int): logits张量的维度数。 + label_dim (int): labels张量的维度数。 + prim_name (str, optional): 调用此函数的操作名称,用于在错误消息中标识来源。 + + 异常: + ValueError: 如果logits的维度小于2,则抛出此异常。 + """ msg_prefix = f'For \'{prim_name}\', the' if prim_name else "The" if logits_dim < 2: raise ValueError(f"{msg_prefix} 'logits' dimension should be greater than 1, but got {logits_dim}.") - if label_dim < 2: - raise ValueError(f"{msg_prefix} 'labels' dimension should be greater than 1, but got {label_dim}.") - @constexpr def _check_weights(weight_shape, label_shape, prim_name=None): - """Internal function, used to check whether the reduced shape meets the requirements.""" + """ + 内部函数,用于检查权重形状是否符合要求。 + + 参数: + weight_shape (tuple): 权重张量的形状。 + label_shape (tuple): 标签张量的形状。 + prim_name (str, optional): 调用此函数的操作名称,用于在错误消息中标识来源。 + + 异常: + ValueError: 如果权重的第一维度不等于标签的第二维度,则抛出此异常。 + """ msg_prefix = f'For \'{prim_name}\', the' if prim_name else "The" if weight_shape != label_shape: raise ValueError(f"{msg_prefix} weight_shape[0] should be equal to label_shape[1], " f"but got weight_shape[0]: {weight_shape} and label_shape[1]: {label_shape}.") - class MultiClassDiceLoss(LossBase): r""" - When there are multiple classifications, label is transformed into multiple binary classifications by one hot. - For each channel section in the channel, it can be regarded as a binary classification problem, so it can be - obtained through the binary loss of each category, and then the average value. - - Args: - weights (Union[Tensor, None]): Tensor of shape :math:`(num\_classes, dim)`. The weight shape[0] should be - equal to labels shape[1]. - Default: None. - ignore_indiex (Union[int, None]): Class index to ignore. - Default: None. - activation (Union[str, Cell]): Activate function applied to the output of the fully connected layer, eg. 'ReLU'. - Default: 'softmax'. Choose from: ['softmax', 'logsoftmax', 'relu', 'relu6', 'tanh','Sigmoid'] - - Inputs: - - **logits** (Tensor) - Tensor of shape :math:`(N, C, *)` where :math:`*` means, any number of additional - dimensions. The logits dimension should be greater than 1. The data type must be float16 or float32. - - **labels** (Tensor) - Tensor of shape :math:`(N, C, *)`, same shape as the `logits`. - The labels dimension should be greater than 1. The data type must be float16 or float32. - - Outputs: - Tensor, a tensor of shape with the per-example sampled MultiClass Dice Losses. - - Raises: - ValueError: If the shape of `logits` is different from `labels`. - TypeError: If the type of `logits` or `labels` is not a tensor. - ValueError: If the dimension of `logits` or `labels` is less than 2. - ValueError: If the weights.shape[0] is not equal to labels.shape[1]. - ValueError: If `weights` is a tensor, but its dimension is not 2. - - Supported Platforms: + 多分类Dice损失函数,适用于多分类问题。它将每个类别视为一个二分类问题,并通过计算每个类别的二元Dice损失, + 然后取平均值来获得整体损失。这种损失函数对于解决不平衡数据集的问题特别有效。 + + 参数: + weights (Union[Tensor, None]): 形状为 :math:`(num\_classes, dim)` 的张量。权重的第一维度应该等于标签的第二维度。 + 默认值:None。 + ignore_index (Union[int, None]): 忽略的类别索引。 + 默认值:None。 + activation (Union[str, Cell]): 应用于输出的激活函数,例如 'ReLU'。 + 默认值:'softmax'。可选值:['softmax', 'logsoftmax', 'relu', 'relu6', 'tanh', 'sigmoid'] + + 输入: + - **logits** (Tensor) - 形状为 :math:`(N, C, *)` 的张量,其中 :math:`*` 表示任意数量的额外维度。 + logits的维度应该大于1。数据类型必须是float16或float32。 + - **labels** (Tensor) - 与 `logits` 形状相同的张量。 + 数据类型必须是float16或float32。 + + 输出: + Tensor, 每个样本的多分类Dice损失的平均值。 + + 异常: + ValueError: 如果 `logits` 和 `labels` 的形状不同。 + TypeError: 如果 `logits` 或 `labels` 不是张量。 + ValueError: 如果 `logits` 或 `labels` 的维度小于2。 + ValueError: 如果 `weights.shape[0]` 不等于 `labels.shape[1]`。 + ValueError: 如果 `weights` 是张量,但其维度不是2。 + + 支持的平台: ``Ascend`` ``GPU`` ``CPU`` - Examples: + 示例: >>> loss = nn.MultiClassDiceLoss(weights=None, ignore_indiex=None, activation="softmax") >>> logits = Tensor(np.array([[0.2, 0.5, 0.7], [0.3, 0.1, 0.5], [0.9, 0.6, 0.3]]), mstype.float32) >>> labels = Tensor(np.array([[0, 1, 0], [1, 0, 0], [0, 0, 1]]), mstype.float32) @@ -777,46 +758,65 @@ class MultiClassDiceLoss(LossBase): 0.54958105 """ - def __init__(self, weights=None, ignore_indiex=None, activation="softmax"): - """Initialize MultiClassDiceLoss.""" + def __init__(self, weights=None, ignore_index=None, activation="softmax"): + """初始化 MultiClassDiceLoss.""" super(MultiClassDiceLoss, self).__init__() + + # 验证并设置激活函数 activation_list = ['softmax', 'logsoftmax', 'relu', 'relu6', 'tanh', 'sigmoid'] - - self.binarydiceloss = DiceLoss(smooth=1e-5) - self.weights = weights if weights is None else validator.check_value_type("weights", weights, [Tensor]) - if isinstance(self.weights, Tensor) and self.weights.ndim != 2: - raise ValueError(f"For '{self.cls_name}', the dimension of 'weights' should be 2, " - f"but got {self.weights.ndim}.") - self.ignore_indiex = ignore_indiex if ignore_indiex is None else \ - validator.check_value_type("ignore_indiex", ignore_indiex, [int]) if isinstance(activation, str) and activation not in activation_list: raise ValueError(f"For '{self.cls_name}', the 'activation' must be in {activation_list}, " f"but got {activation}.") - self.activation = get_activation(activation) if isinstance(activation, str) else activation if self.activation is not None and not isinstance(self.activation, Cell): raise TypeError(f"For '{self.cls_name}', the 'activation' must be str or Cell, " f"but got {type(self.activation)}.") + + # 设置权重和忽略的索引 + self.weights = weights if weights is None else validator.check_value_type("weights", weights, [Tensor]) + if isinstance(self.weights, Tensor) and self.weights.ndim != 2: + raise ValueError(f"For '{self.cls_name}', the dimension of 'weights' should be 2, " + f"but got {self.weights.ndim}.") + self.ignore_index = ignore_index if ignore_index is None else \ + validator.check_value_type("ignore_index", ignore_index, [int]) + + # 初始化二元Dice损失和其他操作符 + self.binarydiceloss = DiceLoss(smooth=1e-5) self.reshape = P.Reshape() def construct(self, logits, label): + """ + 构建并计算多分类Dice损失。 + + 参数: + logits (Tensor): 模型预测的原始输出。 + label (Tensor): 真实标签。 + + 返回: + Tensor, 损失值。 + """ + # 检查输入的有效性 _check_is_tensor('logits', logits, self.cls_name) _check_is_tensor('labels', label, self.cls_name) _check_shape(logits.shape, label.shape, self.cls_name) _check_ndim_multi(logits.ndim, label.ndim, self.cls_name) + total_loss = 0 + # 应用激活函数(如果有) if self.activation is not None: logits = self.activation(logits) + # 计算每个类别的二元Dice损失并求和 for i in range(label.shape[1]): - if i != self.ignore_indiex: + if i != self.ignore_index: dice_loss = self.binarydiceloss(logits[:, i], label[:, i]) if self.weights is not None: _check_weights(self.weights.shape[0], label.shape[1], self.cls_name) dice_loss *= self.weights[i] total_loss += dice_loss + # 返回所有类别的平均损失 return total_loss / label.shape[1] @@ -1047,82 +1047,69 @@ class SampledSoftmaxLoss(LossBase): class BCELoss(LossBase): r""" - BCELoss creates a criterion to measure the binary cross entropy between the true labels and predicted labels. - - Set the predicted labels as :math:`x`, true labels as :math:`y`, the output loss as :math:`\ell(x, y)`. - Let, - + BCELoss是二元交叉熵损失函数,它衡量了预测值与真实标签之间的差异。 + 它通常用于二分类问题中。给定两个输入 :math:`x` 和 :math:`y`,其中 :math:`x` 是预测的概率, + 而 :math:`y` 是真实的标签(0或1),则对于每个样本,未减少的损失(即当 reduction 设置为 'none' 时) + 可以表示为: .. math:: - L = \{l_1,\dots,l_N\}^\top, \quad - l_n = - w_n \left[ y_n \cdot \log x_n + (1 - y_n) \cdot \log (1 - x_n) \right] - - where N is the batch size. Then, - + \ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad \text{with } l_n = -(y_n \cdot \log x_n + (1 - y_n) \cdot \log (1 - x_n)), + 其中 :math:`N` 是batch大小。如果 `reduction` 不是 'none',那么: .. math:: - \ell(x, y) = \begin{cases} - L, & \text{if reduction} = \text{'none';}\\ - \operatorname{mean}(L), & \text{if reduction} = \text{'mean';}\\ - \operatorname{sum}(L), & \text{if reduction} = \text{'sum'.} + \ell(x, y) = + \begin{cases} + \operatorname{mean}(L), & \text{if reduction} = \text{'mean';}\\ + \operatorname{sum}(L), & \text{if reduction} = \text{'sum'.} \end{cases} - - Note: - Note that the predicted labels should always be the output of sigmoid. Because it is a two-class - classification, the true labels should be numbers between 0 and 1. - And if input is either 0 or 1, one of the log terms would be mathematically undefined in the above loss - equation. - - Args: - weight (Tensor, optional): A rescaling weight applied to the loss of each batch element. - And it must have the same shape and data type as `inputs`. Default: None - reduction (str): Specifies the reduction to be applied to the output. - Its value must be one of 'none', 'mean', 'sum'. Default: 'none'. - - Inputs: - - **logits** (Tensor) - The input tensor with shape :math:`(N, *)` where :math:`*` means, any number - of additional dimensions. The data type must be float16 or float32. - - **labels** (Tensor) - The label tensor with shape :math:`(N, *)`, the same shape and data type as `logits`. - - Outputs: - Tensor, has the same dtype as `logits`. if `reduction` is 'none', then it has the same shape as `logits`. - Otherwise, it is a scalar Tensor. - - Raises: - TypeError: If dtype of `logits`, `labels` or `weight` (if given) is neither float16 not float32. - ValueError: If `reduction` is not one of 'none', 'mean', 'sum'. - ValueError: If shape of `logits` is not the same as `labels` or `weight` (if given). - - Supported Platforms: + + 参数: + weight (Tensor, optional): 损失函数中每个元素的权重。其形状必须是可以广播到输入形状的。 + 默认值:None,意味着所有元素的权重都是1。 + reduction (str, optional): 应用于输出的规约类型,可选值为 "mean", "sum" 或 "none"。 + 默认值:"mean"。 + pos_weight (Tensor, optional): 正类的权重。默认值:None,这意味着不会对正类应用额外的权重。 + + 输入: + - **logits** (Tensor) - 预测的概率值,任何维度的张量。 + - **labels** (Tensor) - 真实标签,通常是一个包含0和1的张量,形状应与logits相同。 + 但是,它支持logits和labels形状不同但可以互相广播的情况。 + + 输出: + Tensor, 如果 `reduction` 是 'mean' 或 'sum',则返回一个标量;如果 `reduction` 是 'none', + 则返回一个形状与输入相同的张量。 + + 异常: + TypeError: 如果 `weight` 或 `pos_weight` 不是Tensor或None。 + ValueError: 如果 `reduction` 不是 'none'、'mean' 或 'sum'。 + ValueError: 如果 `logits` 和 `labels` 的形状不能广播。 + + 支持的平台: ``Ascend`` ``GPU`` ``CPU`` - - Examples: - >>> weight = Tensor(np.array([[1.0, 2.0, 3.0], [4.0, 3.3, 2.2]]), mindspore.float32) - >>> loss = nn.BCELoss(weight=weight, reduction='mean') - >>> logits = Tensor(np.array([[0.1, 0.2, 0.3], [0.5, 0.7, 0.9]]), mindspore.float32) - >>> labels = Tensor(np.array([[0, 1, 0], [0, 0, 1]]), mindspore.float32) + + 示例: + >>> loss = nn.BCELoss() + >>> logits = Tensor(np.array([0.3, 0.7, 0.1]), mindspore.float32) + >>> labels = Tensor(np.array([0, 1, 0]), mindspore.float32) >>> output = loss(logits, labels) >>> print(output) - 1.8952923 + 0.6142586 """ - - def __init__(self, weight=None, reduction='none'): - """Initialize BCELoss.""" - super(BCELoss, self).__init__() - self.binary_cross_entropy = P.BinaryCrossEntropy(reduction=reduction) - self.weight_one = weight is None - if not self.weight_one: - self.weight = weight - else: - self.ones = P.OnesLike() + def __init__(self, weight=None, reduction='mean', pos_weight=None): + """初始化BCELoss.""" + super(BCELoss, self).__init__(reduction) + self.binary_cross_entropy_loss = P.BinaryCrossEntropy(reduction=reduction) + self.weight = weight + self.pos_weight = pos_weight + + if weight is not None: + _check_is_tensor('weight', weight, self.cls_name) + + if pos_weight is not None: + _check_is_tensor('pos_weight', pos_weight, self.cls_name) def construct(self, logits, labels): _check_is_tensor('logits', logits, self.cls_name) _check_is_tensor('labels', labels, self.cls_name) - if self.weight_one: - weight = self.ones(logits) - else: - weight = self.weight - loss = self.binary_cross_entropy(logits, labels, weight) - return loss + return self.binary_cross_entropy_loss(logits, labels, self.weight, self.pos_weight) @constexpr @@ -1365,47 +1352,45 @@ def _check_input_dtype(labels_dtype, cls_name): class FocalLoss(LossBase): r""" - The loss function proposed by Kaiming team in their paper ``Focal Loss for Dense Object Detection`` improves the - effect of image object detection. It is a loss function to solve the imbalance of categories and the difference of - classification difficulty. If you want to learn more, please refer to the paper. - `Focal Loss for Dense Object Detection `_. The function is shown as follows: + 由Kaiming团队在其论文 ``Focal Loss for Dense Object Detection`` 中提出的损失函数,旨在改善图像目标检测的效果。 + 它是一种用于解决类别不平衡和分类难度差异问题的损失函数。更多详情,请参阅论文: + `Focal Loss for Dense Object Detection `_。该函数表达式如下: .. math:: - FL(p_t) = -(1-p_t)^\gamma log(p_t) - - Args: - gamma (float): Gamma is used to adjust the steepness of weight curve in focal loss. Default: 2.0. - weight (Union[Tensor, None]): A rescaling weight applied to the loss of each batch element. The dimension of - weight should be 1. If None, no weight is applied. Default: None. - reduction (str): Type of reduction to be applied to loss. The optional values are "mean", "sum", and "none". - If "none", do not perform reduction. Default: "mean". - - Inputs: - - **logits** (Tensor) - Tensor of shape should be :math:`(N, C)` or :math:`(N, C, H)` or :math:`(N, C, H, W)`. - Where :math:`C` is the number of classes. Its value is greater than 1. If the shape is :math:`(N, C, H, W)` - or :math:`(N, C, H)`, the :math:`H` or product of :math:`H` and :math:`W` should be the same as labels. - - **labels** (Tensor) - Tensor of shape should be :math:`(N, C)` or :math:`(N, C, H)` or :math:`(N, C, H, W)`. - The value of :math:`C` is 1 or it needs to be the same as predict's :math:`C`. If :math:`C` is not 1, - the shape of target should be the same as that of predict, where :math:`C` is the number of classes. - If the shape is :math:`(N, C, H, W)` or :math:`(N, C, H)`, the :math:`H` or product of :math:`H` - and :math:`W` should be the same as logits. The value of `labels` is should be in the - range [-:math:`C`, :math:`C`). Where :math:`C` is the number of classes in logits. - - Outputs: - Tensor or Scalar, if `reduction` is "none", its shape is the same as `logits`. - Otherwise, a scalar value will be returned. - - Raises: - TypeError: If the data type of `gamma` is not a float. - TypeError: If `weight` is not a Tensor. - ValueError: If `labels` dim is different from `logits`. - ValueError: If `labels` channel is not 1 and `labels` shape is different from `logits`. - ValueError: If `reduction` is not one of 'none', 'mean', 'sum'. - - Supported Platforms: + FL(p_t) = -(1-p_t)^\gamma \log(p_t) + + 参数: + gamma (float): Gamma用于调整在Focal损失中权重曲线的陡峭程度。默认值:2.0。 + weight (Union[Tensor, None]): 应用于每个批次元素损失的重缩放权重。权重的维度应该是1。 + 如果为None,则不应用任何权重。默认值:None。 + reduction (str): 应用到损失上的规约类型。可选值有 "mean", "sum" 和 "none"。 + 如果是 "none",则不进行规约。默认值:"mean"。 + + 输入: + - **logits** (Tensor) - 形状应为 :math:`(N, C)` 或 :math:`(N, C, H)` 或 :math:`(N, C, H, W)` 的张量。 + 其中 :math:`C` 是类别数量。其值应大于1。如果形状是 :math:`(N, C, H, W)` 或 :math:`(N, C, H)`, + 那么 :math:`H` 或者 :math:`H` 和 :math:`W` 的乘积应该与labels相同。 + - **labels** (Tensor) - 形状应为 :math:`(N, C)` 或 :math:`(N, C, H)` 或 :math:`(N, C, H, W)` 的张量。 + :math:`C` 的值为1或需要与预测的 :math:`C` 相同。如果 :math:`C` 不是1,则target的形状应该与predict相同, + 其中 :math:`C` 是类别数。如果形状是 :math:`(N, C, H, W)` 或 :math:`(N, C, H)`, + 那么 :math:`H` 或者 :math:`H` 和 :math:`W` 的乘积应该与logits相同。 + `labels` 的值应在 [-:math:`C`, :math:`C`) 范围内,其中 :math:`C` 是logits中的类别数。 + + 输出: + Tensor 或 Scalar,如果 `reduction` 是 "none",其形状与 `logits` 相同。 + 否则,返回一个标量值。 + + 异常: + TypeError: 如果 `gamma` 的数据类型不是浮点型。 + TypeError: 如果 `weight` 不是张量。 + ValueError: 如果 `labels` 的维度与 `logits` 不同。 + ValueError: 如果 `labels` 的通道不是1且 `labels` 的形状与 `logits` 不同。 + ValueError: 如果 `reduction` 不是 'none'、'mean' 或 'sum' 中的一个。 + + 支持的平台: ``Ascend`` - Example: + 示例: >>> logits = Tensor([[0.8, 1.4], [0.5, 0.9], [1.2, 0.9]], mstype.float32) >>> labels = Tensor([[1], [1], [0]], mstype.int32) >>> focalloss = nn.FocalLoss(weight=Tensor([1, 2]), gamma=2.0, reduction='mean') @@ -1415,16 +1400,21 @@ class FocalLoss(LossBase): """ def __init__(self, weight=None, gamma=2.0, reduction='mean'): - """Initialize FocalLoss.""" + """初始化 FocalLoss.""" super(FocalLoss, self).__init__(reduction=reduction) + # 检查并设置gamma参数 self.gamma = validator.check_value_type("gamma", gamma, [float]) + + # 检查并设置权重参数 if weight is not None and not isinstance(weight, Tensor): raise TypeError(f"For '{self.cls_name}', the type of 'weight' should be a Tensor, " f"but got {type(weight).__name__}.") if isinstance(weight, Tensor) and weight.ndim != 1: raise ValueError(f"For '{self.cls_name}', the dimension of 'weight' should be 1, but got {weight.ndim}.") self.weight = weight + + # 初始化MindSpore操作符 self.expand_dims = P.ExpandDims() self.gather_d = P.GatherD() self.squeeze = P.Squeeze(axis=1) @@ -1434,13 +1424,25 @@ class FocalLoss(LossBase): self.logsoftmax = nn.LogSoftmax(1) def construct(self, logits, labels): + """ + 构建并计算Focal损失。 + + 参数: + logits (Tensor): 模型预测的原始输出。 + labels (Tensor): 真实标签。 + + 返回: + Tensor, 损失值。 + """ + # 检查输入的有效性 _check_is_tensor('logits', logits, self.cls_name) _check_is_tensor('labels', labels, self.cls_name) - labelss = labels + labelss = labels # 复制labels以避免修改原变量 _check_ndim(logits.ndim, labelss.ndim, self.cls_name) _check_channel_and_shape(logits.shape[1], labelss.shape[1], self.cls_name) _check_input_dtype(self.dtype(labelss), self.cls_name) + # 将高维张量转换为(N, C, -1)格式,对于二维张量增加一个维度 if logits.ndim > 2: logits = logits.view(logits.shape[0], logits.shape[1], -1) labelss = labelss.view(labelss.shape[0], labelss.shape[1], -1) @@ -1448,14 +1450,18 @@ class FocalLoss(LossBase): logits = self.expand_dims(logits, 2) labelss = self.expand_dims(labelss, 2) + # 计算log概率 log_probability = self.logsoftmax(logits) + # 如果labels只有一个通道,则根据label选择对应的log概率 if labels.shape[1] == 1: log_probability = self.gather_d(log_probability, 1, self.cast(labelss, mindspore.int32)) log_probability = self.squeeze(log_probability) + # 计算概率 probability = F.exp(log_probability) + # 如果指定了权重,则将权重应用于log概率 if self.weight is not None: convert_weight = self.weight[None, :, None] convert_weight = self.tile(convert_weight, (labelss.shape[0], 1, labelss.shape[2])) @@ -1464,10 +1470,14 @@ class FocalLoss(LossBase): convert_weight = self.squeeze(convert_weight) log_probability = log_probability * convert_weight + # 计算focal权重 weight = F.pows(-1 * probability + 1.0, self.gamma) + + # 根据labels的通道数计算损失 if labels.shape[1] == 1: loss = (-1 * weight * log_probability).mean(axis=1) else: loss = (-1 * weight * labelss * log_probability).mean(axis=-1) + # 应用reduction规则 return self.get_loss(loss)