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

2022-07-11 15:51:34 浏览数 (1)

今天又是相对轻松的一节。今天我们来研究一下评估模型的指标问题。前两节我们已经把模型训练完了,并且能够在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,现在我们还不涉及。

今天就先学到这里。

0 人点赞