前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >22 | 使用PyTorch完成医疗图像识别大项目:模型指标

22 | 使用PyTorch完成医疗图像识别大项目:模型指标

作者头像
机器学习之禅
发布2022-07-11 15:51:34
7790
发布2022-07-11 15:51:34
举报
文章被收录于专栏:机器学习之禅机器学习之禅

今天又是相对轻松的一节。今天我们来研究一下评估模型的指标问题。前两节我们已经把模型训练完了,并且能够在TensorBoard上面查看我们的迭代效果。但是模型的效果实在是不如人意,哪怕我已经把全部的数据都加进去了,但是模型也只能学会把类别都归为非节点。

然而我们用准确率去评估模型的时候,貌似效果还不错,都可以到达99.8%的准确率。这就好像期末考试的时候老师出了100判断题道题,其中只有一道选错,其他的都选对。一个天天逃课的学生只要知道了这个逻辑,他就全都选对,也能得99分,但是这根本不能说明他学到 了什么知识。 今天我们就学习一些新的与评估效果相关的概念。这里先来说一个例子。假设我们有两只看门狗roxie和preston(可以认为是不同的模型)。他们的任务就是当发生危险情况的时候汪汪叫来提醒主人,这里面最危险的情况就是有小偷潜入。

对于小狗roxie来说,它不知道什么情况才是最紧急的,但凡有点风吹草动就会叫唤,比如说有路人经过的时候,有小鸟飞进来的时候,快递员来送快递的时候,如果每次小狗roxie叫唤的时候我们都去看看,肯定能预防小偷,但是那能把我们累死,而且啥事都干不了了。 老狗preston呢能够很好地分辨小偷和其他的事情,但是它年纪大了喜欢睡觉,万一小偷来的时候它睡着了那就被小偷溜进去了。

阳性与阴性

听到阳性和阴性我想大家内心都有应激反应了。最近北京疫情比较严重,又恢复了天天做核酸的状态。对于一个人做核酸的结果有两种情况,一种是阳性一种是阴性。对于我们的狗狗报警也是一样的情况,会分成危险和不危险两种情况。但是我们都知道,不管什么样的狗狗报警都有一定的错误,这个错误也会分成两种情况,那就是把本来是小偷的误报成不危险,把一只小鸟误报成危险。作为我们的核酸检测也是一样的,比如我们经常看到新闻说某某病人前几次核酸都是阴性,但是最后一次检测阳了,或者是某某小区有核酸检测阳性但是经过排查确认是误报。

这两种情况就是假阴性和假阳性。我来看看下面这张图,下面的灰色区域是实际的危险样例,上面的白色区域是实际的安全区域。但是模型给出的预测则是左侧的是安全样例,右侧的是危险样例。当然一般来说被划分错的部分不会有那么大,这里只是为了方便观察。左下角的老鼠本来应该是危险的,但是狗狗却认为它安全,这些老鼠就是假阴性案例,右上角的猫猫没有危险,狗狗却认为它危险,这些猫猫就是假阳性案例。对应的,左上角的小鸟是真阴性案例,右下角的小偷是真阳性案例。

召回率和精确度

由上面的几种情况构成了一个混淆矩阵。其中T和F分布是True和False,N和P表示Negative和Positive。

image.png

有了混淆矩阵,接下来要看两个指标,召回率和精确度。 召回率是真阳性同真阳性与假阴性和的比值。从公式上来说就是

从公式上可以看出来如果想提高召回率,那就要降低假阴性的数量。对于我们的小狗roxie,不管遇到什么都叫唤,它的召回率就很高。因为绝大部分的情况都被它分成了阳性,留给FN的空间不多了。

再来看精确度。公式定义如下,是真阳性同真阳性与假阳性和的比值。

对于小狗roxie来说,它的精确度是很低的,但是对于老狗preston来说,它的精确度就很高,因为它几乎不叫,只有看到小偷的时候才会叫,甚至很多时候它都睡过去了,看不到小偷。

把指标加入日志

召回率和精确度都是我们需要观察的指标,我们当前期望这两个指标都很高,但是现实往往是一个高另外一个就会低。下面把这两个指标加入到我们的日志指标中。前半部分代码我们已经看过了,是就算TN,TP,FN,FP的具体数值。这些代码加在training.py中,LunaTrainigApp的logMetrics的方法里。

代码语言:javascript
复制
        neg_count = int(negLabel_mask.sum())
        pos_count = int(posLabel_mask.sum())

        trueNeg_count = neg_correct = int((negLabel_mask & negPred_mask).sum())
        truePos_count = pos_correct = int((posLabel_mask & posPred_mask).sum())

        falsePos_count = neg_count - neg_correct
        falseNeg_count = pos_count - pos_correct

然后是计算召回率和精确度的计算公式

代码语言:javascript
复制
        precision = metrics_dict['pr/precision'] = \
            truePos_count / np.float32(truePos_count + falsePos_count)
        recall    = metrics_dict['pr/recall'] = \
            truePos_count / np.float32(truePos_count + falseNeg_count)

F1 Score

到了这里还没有完,我们还需要介绍一个F1 Score。通过召回率和精确度可以观察模型的效果,但是要用这两个指标去衡量不同的模型这时候就有点难度。比如说一个召回率高,一个精确度高,没办法对比,所以这里就把它俩结合一下,才有了F1分数。

F1分数的取值范围是0-1,当得分为0的时候表明模型没有分类能力,得分为1时认为模型超级优秀。对比一下F1得分与取召回和精确度均值或者最小值的区别。颜色越深的区域代表分值越接近0,颜色越浅的区域代表分值越接近1。比起平均值来说,F1对某个单项值过小的得分较低,这样可以避免模型出现前面两只狗的情况,要么全都叫,要么基本不叫。而对于取最小值,F1又友好一点,对于两个分数都还差不多的情况,F1有一个更加平滑的结果。假设我们在召回率固定的情况下,提升了精确度,对于取最小值来说这个结果不会发生任何变化,显然这不合理。

最后我们把F1得分也加入日志中。

代码语言:javascript
复制
#计算F1 score
        metrics_dict['pr/f1_score'] = \
            2 * (precision * recall) / (precision + recall)
#记录整体日志
        log.info(
            ("E{} {:8} {loss/all:.4f} loss, "
                 + "{correct/all:-5.1f}% correct, "
                 + "{pr/precision:.4f} precision, "
                 + "{pr/recall:.4f} recall, "
                 + "{pr/f1_score:.4f} f1 score"
            ).format(
                epoch_ndx,
                mode_str,
                **metrics_dict,
            )
        )
#记录负样本日志
        log.info(
            ("E{} {:8} {loss/neg:.4f} loss, "
                 + "{correct/neg:-5.1f}% correct ({neg_correct:} of {neg_count:})"
            ).format(
                epoch_ndx,
                mode_str + '_neg',
                neg_correct=neg_correct,
                neg_count=neg_count,
                **metrics_dict,
            )
        )
#记录正样本日志
        log.info(
            ("E{} {:8} {loss/pos:.4f} loss, "
                 + "{correct/pos:-5.1f}% correct ({pos_correct:} of {pos_count:})"
            ).format(
                epoch_ndx,
                mode_str + '_pos',
                pos_correct=pos_correct,
                pos_count=pos_count,
                **metrics_dict,
            )
        )

你可以按照上面的代码对上一个模型训练的代码进行修改,我这里等不及训练了,实在是有点慢,直接贴了书里的结果。可以看到虽然模型运行完了,但是我们的F1 score貌似没有计算出来,得到了一个nan的结果。这主要是因为有被0除的情况。没有任何样本被标记为阳性,因此精确度计算的分母是0。

不过我们还是已经了解了一些模型指标的计算,当然F1 score也不是全部,还有更多的指标可以用来作为评估标准,比如AUC,现在我们还不涉及。

今天就先学到这里。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 机器学习之禅 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 阳性与阴性
  • 召回率和精确度
  • 把指标加入日志
  • F1 Score
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档