• 基于MindSpore高效完成图像分割,实现Dice!


    Dice 系数的介绍及实现

    Dice系数原理

    Dice是医学图像比赛中使用频率最高的度量指标,它是一种集合相似度度量指标,通常用于计算两个样本的相似度,值阈为[0, 1]。在医学图像中经常用于图像分割,分割的最好结果是1,最差时候结果为0.

    Dice系数计算公式如下:

    当然Dice也有另一个表达方式,是利用混淆矩阵中的TP,FP,FN来表达:

    该公式原理如下图:

    MindSpore代码实现

    先简单介绍一下MindSpore——新一代AI开源计算框架。创新编程范式,AI科学家和工程师更易使用,便于开放式创新;该计算框架可满足终端边缘计算云全场景需求,能更好保护数据隐私;可开源,形成广阔应用生态

    2020年3月28日,华为在开发者大会2020上宣布,全场景AI计算框架MindSpore在码云正式开源。MindSpore着重提升易用性并降低AI开发者的开发门槛,MindSpore原生适应每个场景包括端、边缘和云,并能够在按需协同的基础上,通过实现AI算法即代码,使开发态变得更加友好显著减少模型开发时间降低模型开发门槛

    通过MindSpore自身的技术创新及MindSpore与华为昇腾AI处理器的协同优化,实现了运行态的高效,大大提高了计算性能;MindSpore也支持GPU、CPU等其它处理器。

    1. """Dice"""
    2. import numpy as np
    3. from mindspore._checkparam import Validator as validator
    4. from .metric import Metric
    5. class Dice(Metric):
    6. def __init__(self, smooth=1e-5):
    7. super(Dice, self).__init__()
    8. self.smooth = validator.check_positive_float(smooth, "smooth")
    9. self._dice_coeff_sum = 0
    10. self._samples_num = 0
    11. self.clear()
    12. def clear(self):
    13.         # 是来清除历史数据
    14. self._dice_coeff_sum = 0
    15. self._samples_num = 0
    16. def update(self, *inputs):
    17. # 更新输入数据,y_pred和y,数据输入类型可以是Tensor,lisy或numpy,维度必须相等
    18. if len(inputs) != 2:
    19. raise ValueError('Dice need 2 inputs (y_pred, y), but got {}'.format(len(inputs)))
    20. # 将数据进行转换,统一转换为numpy
    21. y_pred = self._convert_data(inputs[0])
    22. y = self._convert_data(inputs[1])
    23. self._samples_num += y.shape[0]
    24. if y_pred.shape != y.shape:
    25. raise RuntimeError('y_pred and y should have same the dimension, but the shape of y_pred is{}, '
    26. 'the shape of y is {}.'.format(y_pred.shape, y.shape))
    27. # 先求交集,利用dot对应点相乘再相加
    28. intersection = np.dot(y_pred.flatten(), y.flatten())
    29. # 求并集,先将输入shape都拉到一维,然后分别进行点乘,再将两个输入进行相加
    30. unionset = np.dot(y_pred.flatten(), y_pred.flatten()) + np.dot(y.flatten(), y.flatten())
    31. # 利用公式进行计算,加smooth是为了防止分母为0,避免当pred和true都为0时,分子被0除的问题,同时减少过拟合
    32. single_dice_coeff = 2 * float(intersection) / float(unionset + self.smooth)
    33. # 对每一批次的系数进行累加
    34. self._dice_coeff_sum += single_dice_coeff
    35. def eval(self):
    36. # 进行计算
    37. if self._samples_num == 0:
    38. raise RuntimeError('Total samples num must not be 0.')
    39. return self._dice_coeff_sum / float(self._samples_num)

    使用方法如下:

    1. import numpy as np
    2. from mindspore import Tensor
    3. from mindspore.nn.metrics Dice
    4. metric = Dice(smooth=1e-5)
    5. metric.clear()
    6. x = Tensor(np.array([[0.2, 0.5], [0.3, 0.1], [0.9, 0.6]]))
    7. y = Tensor(np.array([[0, 1], [1, 0], [0, 1]]))
    8. metric.update(x, y)
    9. dice = metric.eval()
    10. print(dice)
    11. 0.20467791371802546

    每个batch(两组数据)进行计算的时候如下:

    1. import numpy as np
    2. from mindspore import Tensor
    3. from mindspore.nn.metrics Dice
    4. metric = Dice(smooth=1e-5)
    5. metric.clear()
    6. x = Tensor(np.array([[0.2, 0.5], [0.3, 0.1], [0.9, 0.6]]))
    7. y = Tensor(np.array([[0, 1], [1, 0], [0, 1]]))
    8. metric.update(x, y)
    9. x1= Tensor(np.array([[0.2, 0.5], [0.3, 0.1], [0.9, 0.6]]))
    10. y1 = Tensor(np.array([[1, 0], [1, 1], [1, 0]]))
    11. metric.update(x1, y1)
    12. avg_dice = metric.eval()
    13. print(dice)

    Dice Loss 介绍及实现

    Dice Loss原理

    Dice Loss 原理是在 Dice 系数的基础上进行计算,用1去减Dice系数

    这种是在二分类一个批次只有一张图的情况,当一个批次有N张图片时,可以将图片压缩为一维向量,如下图:

    对应的label也会相应变化,最后一起计算N张图片的Dice系数和Dice Loss。

    MindSpore 二分类 DiceLoss 代码实现

    1. class DiceLoss(_Loss):
    2. def __init__(self, smooth=1e-5):
    3. super(DiceLoss, self).__init__()
    4. self.smooth = validator.check_positive_float(smooth, "smooth")
    5. self.reshape = P.Reshape()
    6. def construct(self, logits, label):
    7. # 进行维度校验,维度必须相等。(输入必须是tensor)
    8. _check_shape(logits.shape, label.shape)
    9. # 求交集,和dice系数一样的方式
    10. intersection = self.reduce_sum(self.mul(logits.view(-1), label.view(-1)))
    11. # 求并集,和dice系数一样的方式
    12. unionset = self.reduce_sum(self.mul(logits.view(-1), logits.view(-1))) + \
    13. self.reduce_sum(self.mul(label.view(-1), label.view(-1)))
    14. # 利用公式进行计算
    15. single_dice_coeff = (2 * intersection) / (unionset + self.smooth)
    16. dice_loss = 1 - single_dice_coeff / label.shape[0]
    17. return dice_loss.mean()
    18. @constexpr
    19. def _check_shape(logits_shape, label_shape):
    20. validator.check('logits_shape', logits_shape, 'label_shape', label_shape)

    使用方法如下:

    1. import numpy as np
    2. import mindspore.common.dtype as mstype
    3. import mindspore.nn as nn
    4. from mindspore import Tensor
    5. loss = nn.DiceLoss(smooth=1e-5)
    6. y_pred = Tensor(np.array([[0.2, 0.5], [0.3, 0.1], [0.9, 0.6]]), mstype.float32)
    7. y = Tensor(np.array([[0, 1], [1, 0], [0, 1]]), mstype.float32)
    8. output = loss(y_pred, y)
    9. print(output)
    10. [0.7953220862819745]

    MindSpore 多分类 MultiClassDiceLoss 代码实现

    在MindSpore中支持在语义分割中有多种损失函数可以选择,不过最常用的还是用交叉熵来做损失函数。

    1. class MultiClassDiceLoss(_Loss):
    2. def __init__(self, weights=None, ignore_indiex=None, activation=A.Softmax(axis=1)):
    3. super(MultiClassDiceLoss, self).__init__()
    4. # 利用Dice系数
    5. self.binarydiceloss = DiceLoss(smooth=1e-5)
    6. # 权重是一个Tensor,应该和分类数的维度一样:Tensor of shape `[num_classes, dim]`。
    7. self.weights = weights if weights is None else validator.check_value_type("weights", weights, [Tensor])
    8. # 要忽略的类别序号
    9. self.ignore_indiex = ignore_indiex if ignore_indiex is None else \
    10. validator.check_value_type("ignore_indiex", ignore_indiex, [int])
    11. # 使用激活函数
    12. self.activation = A.get_activation(activation) if isinstance(activation, str) else activation
    13. if activation is not None and not isinstance(self.activation, Cell):
    14. raise TypeError("The activation must be str or Cell, but got {}.".format(activation))
    15. self.activation_flag = self.activation is not None
    16. self.reshape = P.Reshape()
    17. def construct(self, logits, label):
    18. # 进行维度校验,维度必须相等。(输入必须是tensor)
    19. _check_shape(logits.shape, label.shape)
    20. # 先定义一个loss,初始值为0
    21. total_loss = 0
    22. # 如果使用激活函数
    23. if self.activation_flag:
    24. logits = self.activation(logits)
    25. # 按照标签的维度的第一个数进行遍历
    26. for i in range(label.shape[1]):
    27. if i != self.ignore_indiex:
    28. dice_loss = self.binarydiceloss(logits[:, i], label[:, i])
    29. if self.weights is not None:
    30. _check_weights(self.weights, label)
    31. dice_loss *= self.weights[i]
    32. total_loss += dice_loss
    33. return total_loss/label.shape[1]

    使用方法如下:

    1. import numpy as np
    2. import mindspore.common.dtype as mstype
    3. import mindspore.nn as nn
    4. from mindspore import Tensor
    5. loss = nn.MultiClassDiceLoss(weights=None, ignore_indiex=None, activation="softmax")
    6. y_pred = Tensor(np.array([[0.2, 0.5], [0.3, 0.1], [0.9, 0.6]]), mstype.float32)
    7. y = Tensor(np.array([[0, 1], [1, 0], [0, 1]]), mstype.float32)
    8. output = loss(y_pred, y)
    9. print(output)
    10. [0.7761003]

    Dice Loss 存在的问题

    训练误差曲线非常混乱,很难看出关于收敛的信息。尽管可以检查在验证集上的误差来避开此问题。

  • 相关阅读:
    直播进入新风口:XR虚拟直播市场火爆,未来发展势不可挡
    mysql8 新特性注入
    C++基础——友元函数和内部类讲解
    一点一点学习C++之笔记005
    束搜索-binsearch
    基于springboot实现二手交易平台管理系统演示【项目源码】分享
    oracle常见报错问题处理
    微服务统一认证方案
    一个注解@LoadBalanced就能让RestTemplate拥有负载均衡的能力?「扩展点实战系列」- 第443篇
    shell 脚本部署 helm
  • 原文地址:https://blog.csdn.net/Kenji_Shinji/article/details/125895212