26 | 使用PyTorch完成医疗图像识别大项目:分割模型实训

开始训练模型之前,我们需要先把之前的标注文件清理好。如下是原作给出的代码示例。

#在这个引入部分,有一个新的pylidc包需要安装,使用pip安装即可。这个包是专门用来处理LIDC数据集的,现在用的LUNA数据集就是在这个基础上加工的,关于这个包的说明很简单:A library for working with the LIDC dataset.
import torch
import SimpleITK as sitk
import pandas
import glob, os
import numpy
import tqdm
import pylidc

安装完之后,首先读取原来的标注文件。这个文件里记录了1000多个结节的坐标和直径信息。

annotations = pandas.read_csv('D:/lunadata/annotations.csv')

然后对我们的数据进行扫描,记录恶性数据,是否有缺失数据等等

malignancy_data = []
missing = []
spacing_dict = {}
scans = {s.series_instance_uid:s for s in pylidc.query(pylidc.Scan).all()}
suids = annotations.seriesuid.unique()
for suid in tqdm.tqdm(suids):
    fn = glob.glob('D:/lunadata/subset*/{}.mhd'.format(suid))
    if len(fn) == 0 or '*' in fn[0]:
        missing.append(suid)
        continue
    fn = fn[0]
    x = sitk.ReadImage(fn)
    spacing_dict[suid] = x.GetSpacing()
    s = scans[suid]
    for ann_cluster in s.cluster_annotations():
        is_malignant = len([a.malignancy for a in ann_cluster if a.malignancy >= 4])>=2
        centroid = numpy.mean([a.centroid for a in ann_cluster], 0)
        bbox = numpy.mean([a.bbox_matrix() for a in ann_cluster], 0).T
        coord = x.TransformIndexToPhysicalPoint([int(numpy.round(i)) for i in centroid[[1, 0, 2]]])
        bbox_low = x.TransformIndexToPhysicalPoint([int(numpy.round(i)) for i in bbox[0, [1, 0, 2]]])
        bbox_high = x.TransformIndexToPhysicalPoint([int(numpy.round(i)) for i in bbox[1, [1, 0, 2]]])
        malignancy_data.append((suid, coord[0], coord[1], coord[2], bbox_low[0], bbox_low[1], bbox_low[2], bbox_high[0], bbox_high[1], bbox_high[2], is_malignant, [a.malignancy for a in ann_cluster]))

这里能看到处理的进度条。


image.png

其中的miss用来记录是否有源文件(mhd文件损坏或者缺失),好在我这里是0缺失的。用原始数据信息去匹配我们读取的数据,

df_mal = pandas.DataFrame(malignancy_data, columns=['seriesuid', 'coordX', 'coordY', 'coordZ', 'bboxLowX', 'bboxLowY', 'bboxLowZ', 'bboxHighX', 'bboxHighY', 'bboxHighZ', 'mal_bool', 'mal_details'])

processed_annot = []
annotations['mal_bool'] = float('nan')
annotations['mal_details'] = [[] for _ in annotations.iterrows()]
bbox_keys = ['bboxLowX', 'bboxLowY', 'bboxLowZ', 'bboxHighX', 'bboxHighY', 'bboxHighZ']
for k in bbox_keys:
    annotations[k] = float('nan')
for series_id in tqdm.tqdm(annotations.seriesuid.unique()):
    # series_id = '1.3.6.1.4.1.14519.5.2.1.6279.6001.100225287222365663678666836860'
    c = candidates[candidates.seriesuid == series_id]
    a = annotations[annotations.seriesuid == series_id]
    m = df_mal[df_mal.seriesuid == series_id]
    if len(m) > 0:
        m_ctrs = m[['coordX', 'coordY', 'coordZ']].values
        a_ctrs = a[['coordX', 'coordY', 'coordZ']].values
        #print(m_ctrs.shape, a_ctrs.shape)
        matches = (numpy.linalg.norm(a_ctrs[:, None] - m_ctrs[None], ord=2, axis=-1) / a.diameter_mm.values[:, None] < 0.5)
        has_match = matches.max(-1)
        match_idx = matches.argmax(-1)[has_match]
        a_matched = a[has_match].copy()
        # c_matched['diameter_mm'] = a.diameter_mm.values[match_idx]
        a_matched['mal_bool'] = m.mal_bool.values[match_idx]
        a_matched['mal_details'] = m.mal_details.values[match_idx]
        for k in bbox_keys:
            a_matched[k] = m[k].values[match_idx]
        processed_annot.append(a_matched)
        processed_annot.append(a[~has_match])
    else:
        processed_annot.append(c)
processed_annot = pandas.concat(processed_annot)
processed_annot.sort_values('mal_bool', ascending=False, inplace=True)
processed_annot['len_mal_details'] = processed_annot.mal_details.apply(len)

我这块的输出显示没有需要丢掉的数据,那看起来LUNA数据集里提供的数据已经更新了。


image.png

最后把这个文件保存下来。

df_nona = processed_annot.dropna()
df_nona.to_csv('./data/part2/luna/annotations_with_malignancy.csv', index=False)

已经生成新的标注数据。

image.png

下面开始执行训练代码。首先还是创建缓存,结果这里遇到一个问题,代码接收的参数有问题,
在13章dset.py的49行,isMal_bool = {'False': False, 'True': True}[row[5]]
但实际上我们的文件里这一列存的是0.0和1.0,导致读取异常,把这里改成如下就能正常运行了。
isMal_bool = {'0.0': False, '1.0': True}[row[5]],接着启动缓存建设。

run('test13ch.prepcache.LunaPrepCacheApp')

在shell里面运行训练环节。

> python -m test13ch.training --epoch 20 --augmented final_seg

结果训练了一个epoch就内存溢出了。无奈,把batch size调小一点,从16改成了8个,这次就没问题了,我的设备还是不太行,真想买一台双3090Ti卡的机器。


image.png

这次看看效果。这里列出了第1,5,10,15,20个epoch的结果,可看到第1个epoch不管在训练集还是验证集的精确度很低,召回率还可以,在验证集上的fp(假阳性)达到了2442.7%,这主要是因为训练集使用的是裁剪后的小图片,而验证集使用的是完整的CT切片数据,所以假阳性很高也正常,多给出一些结果再让医生去看总比漏掉要好的多。


image.png

到了第5个epoch,精确度有所提升,训练集的f1达到0.71了
image.png

到了第10个epoch,又提升了一点点,但是验证集上给出的tp有些下降。


image.png

到15个epoch,在训练集上的效果持续提升,但是在验证集上的效果下降明显,tp值以及到了79%,说明这个时候已经出现了过拟合现象。
image.png
image.png

下面去TensorBoard上去看看效果。蓝色是训练集,红色是验证集,首先是损失情况,在训练集上前期损失下降比较快,后面就比较平缓,在验证集上的损失变化不大。


image.png

然后是fn,fp,tp指


image.png

最后是f1 score,精确度,召回率,可以看到在经过了几个epoch之后验证集的召回率开始下降,出现了过拟合现象。
image.png

最后看一看导入TensorBoard的图像效果。带有label_x的表示这是一个标注图像,上面没有颜色的表名这个图像上都是无标注的,在对应的预测结果上,有一些橙色结果是假阳性预测,对于下面带绿色就是阳性标注及阳性预测结果。


image.png

image.png

image.png

image.png

看起来效果还不错,我们的这个模型就先训到这里,基本上可以满足我们的需求了。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容