- 尝试两种spacing:0.5 & 1.0
- 以病人的为单位统计 mean & std 并进行normlize,此过程忽略值为0的像素点;
- 尝试多通道组合:T2,tumor,lumen,peritumor;
- augmentation:随机选择组合(flip,resize,crop,...)
- 2或3个连续的slice组合为多通道图片或3D图片作为输入;
- 调整stride或增加pooling层,因为spacing后有些slice的分辨率会变得很小;
- 以上操作后,预期4分类acc达到50%+;
- 每个patient的每个slice的预测结果如何得到patient的预测结果;
- 3DModel用于2D slice数据进行尝试;
- 尝试原始图像直接输入模型(512×512);(或直接resize到224×224);
- 调大后两类样本的class weight,尝试设置到极端情况,如原来的1k倍、10k倍等情况;
- 用交叉验证,5-fold;
- 肠壁标记,具体操作待定;
- 加上数据增广,几种方式都加上(trans,crop,flip,scaling,rota);
- 以肿瘤为中心进行box切割,具体操作待定;
- 保存数据增广后的图片,再读取;spacing后,切割出肿瘤box,再resize到250(>224),再切割5个点(4个角和中心)224大小,再flip,就有10个样本。
- 用于normalize的mean和std取自于每个病人,也可以尝试用每个slice的mean和std;
- 350 x 350 或350 x 250;
- 把病变区域tumor也加入到输入作为输入的一部分,具体操作待定。
add by peng
- 将load_dataset_v3改名为laod_dataset;
- models库添加模型alexnet、densenet、inception、mobilenet、resnet、squeezenet、vggnet;
- 尝试用pre-trained模型进行训练;
初始化数据集,统计所需数据
所有的取整操作,都使用
round
- 按病人随机划分训练集和测试集,其中测试集约占 20%,除去没有肿瘤区域的 slices
- 统计均值、标准差。遍历数据集,得到
- 训练集的均值和方差
- 测试集的均值和方差
- 记录每张图片的大小 (shape),记为
shape(512 * 512) - 记录每张图片的 spacing,以及整个数据集 spacing 字段的众数,作为标准 spacing (其他图片都将 resize 到这个 spacing),记为
global_std_spacing_mode - 计算每张图片 resize 到标准 spacing 后的大小 (shape),并记为
shape_spc,并记录 spacing 之后图片大小的最大值,即shape_spc的最大值,记为max_size_spc(615)
- 记录每张图片的肿瘤区域
- 记录每张图片 spacing 之后的肿瘤区域
![]() |
左图,是 spacing 之后的一张最小的图片 (最小的大小是 384 * 384),红色区域是它的肿瘤区域
右图,是 spacing 之后的最大的图片,615 * 615
进行坐标变换,将左图放到右图中,那左图就放在了右图的蓝色正方形处,而黄色区域是肿瘤区域
对所有图片都做这样的坐标变换,放大最大图的中心
现在,找出整个数据集中,包含所有图片肿瘤区域的边框 (右图的青色区域),青色区域在最小图区域里
数据预处理步骤
- 通过
SimpleITK读取图片,数据类型numpy int16,数据格式(h, w, c) - 将
numpy类型的图片转为PIL.Image,数据类型PIL.Image.Image image mode=I;16,数据格式(h, w, c) - 如果图片的原始大小 (
shape) 和 resize 到标准 spacing 后的大小 (shape_spc) 不相等,那么将图片 resize 到标准 spacing,即 resize 到shape_spc - 如果图片 spacing 之后的大小不是最大的,即
shape_spc != max_size_spc,那么将它CenterCrop到最大的图片大小 (这时图片放在中心,周围有一圈黑边) - 包含所有图片肿瘤区域的边框,是一个 h * w 长方形,h 是长边。最后切割出一个 h * h 的正方形区域,这个正方形的中心是长方形的中心。
- 将图片 resize 到 244 * 244
- 数据增广
RandomRotation, degrees=[-10, 10]
- 转成 tensor,数据类型
torch.int16,数据格式(c, h, w) - 类型转换,数据类型
torch.float32,数据格式(c, h, w) - 归一化,训练集和测试集用各自的均值和标准差
训练
- loss function class weight = true
- weighted random sampler = false
- weight_decay = 0.0001
- lr = 0.001
- lr_decay_period = 30 (epoch)
- lr_decay_rate = 2
-
使用的模型
- ResNet34, no pretrain
- ResNet34, pretrain
- ResNet152, pretrain
- DenseNet121, pretrain
结果
尝试多个模型,无论有无预训练模型,多次训练,结果都是这样。训练集都到 100%,若使用预训练模型,几个 epoch 之后训练集就到 100%。
训练集混淆矩阵:
| truth/predict | 0 | 1 | 2 | Acc |
|---|---|---|---|---|
| 0 | 1890 | 0 | 0 | 1890/1890 (100.0000%) |
| 1 | 0 | 269 | 0 | 269/269 (100.0000%) |
| 2 | 0 | 0 | 138 | 138/138 (100.0000%) |
测试集混淆矩阵:
| truth/predict | 0 | 1 | 2 | Acc |
|---|---|---|---|---|
| 0 | 463 | 57 | 3 | 463/523 (88.5277%) |
| 1 | 57 | 6 | 0 | 6/63 (9.5238%) |
| 2 | 30 | 7 | 0 | 0/37 (0.0000%) |
TODO
-
3D CNN
-
多模态
- 以病人为单位分 slices
- 合并数据集 1、2 (CC_ROI and 6_ROI)
- 考虑 2D CNN 的多通道输入(3通道)
- 对于死亡的病人,分别做:
- 去掉其他(非肿瘤)死亡原因的病人
- 不去掉
-
数据角度:
在训练时实时做数据增广,用 pytorch 的函数
不要先增广再训练。
-
算法角度:修改 loss function, 设置 class weight
训练前:
尝试 pytorch 的其他 augmentation
对于每张图,先 rotate,再 crop,rotate 的角度在 之间,每15度 rotate 一次。
注意: 这不是为了解决类别均衡,只是增加了数据量
测试时: 分别对测试图片的原图、rotate 后、crop 后等处理的图片分别预测。然后用 voting 或 probability 的方法集成它们。
K-fold validation
每一类分成 5 个 fold,交叉验证,看是否某一个 fold 效果差。
train : test = 8 : 2
注意: 按病人来分 fold,这里不需要保证每个 fold 的 slices 数量一样多
其他信息有性别、年龄等,性别是二值的(0、1),年龄是多值的(0~100等),无法进行归一化,要考虑这个问题。
加其他医疗信息,在 ResNet 的 global average pooling 层与最后的 fc 层之间,加入一个 fc 层
-
选择 ResNet 模型
-
考虑使用预训练的模型
-
优化器用 SGD
对每个病人,去掉没有肿瘤标记的slice,只选取有肿瘤标记的slice作为数据样本。
考虑是否以某一阈值 P 作为每张slice筛选的条件,即对于某一张slice:
- 肿瘤标记的像素点数量n > P : 则作为数据样本;
- 肿瘤标记的像素点数量n <= P : 则去掉这张slice;
统计不同分辨率的图片分别有多少,不同spacing的图片分别有多少。
统计每个病人每张slice的spacing,2D处理时只考虑xy方向,更具excel表中spacing的倒数这一属性,对每一张slice进行resize (具体操作还不清楚,如何根据spacing进行resize) 。
以小分辨率为基准进行resize,
遍历所有图像后得到肿瘤的最大高度Hmax和宽度Wmax,得到一个切割box的高H和宽W(均略大于Hmax和Wmax,H=W),利用这个box对每张筛选后的slice进行切割,切割出只肿瘤附近的区域。
切割后再进行scale,因为经典ResNet的预训练模型等的输入为24×24。考虑如何裁剪。
pytorch有这一部分crop的函数。
pytorch的norm
均值:统计训练集(或是所有数据集)每一个样本每一个像素值求均值;
方差:同上求方差;
预处理的每一步操作后都将图片打印出来看一看。
- 方法1 (数据角度):
- 测试集与训练集的每种类别的样本数的之间的比例相同,例如:
- 测试集中:30%的类别0、30%类别1、30%的类别2
- 训练集中:70%的类别0、70%类别1、70%的类别2
- 这里的百分比是相对于总数据集的每一种类别的样本数
- 测试集与训练集的每种类别的样本数的之间的比例相同,例如:
- 方法2 (算法角度):
- class-weight:(不是很懂???)
- 例如训练集的每种类别的样本数比例为10-5-2,则测试集的比例为2-5-10;
- class-weight:(不是很懂???)
add by Wu
解决数据类别分布不均的问题。参考网上的博客,ideas:
- 数据角度:
- 数据重采样。随机欠采样,随机过采样
- 类别平衡采样。每个送进去训练的 batch 按类别平衡采样。
- 算法角度:
- 代价敏感学习。当把少数类样本预测错误时,增大惩罚和损失的力度等。
-
统计每一个类别的准确率(如混淆矩阵);
-
记录每一个病人的每一张slice的预测结果(包括标签),记录每一个病人的所有slice的预测结果的三种类别的分数‘/概率 均值、分布等;
-
首先保证训练集上的准确率达到98%+,然后再去保证测试集的准确率达到97%+;
1 Done:
- 写好了加载数据、训练与测试等基本框架
- 使用 VGG11,成功使代码运行起来,并保存模型
2 总结体会:
-
在训练过程中,做好日志输出。
日志记录训练过程中的各种各样的信息,出现问题时也方便解决。而 python 里的
logging模块已经帮我们把这些工作封装好了。 -
要注意保存模型。
训练模型的代码中,应使用
try, catch来捕获异常,当有异常抛出时,保存好模型再退出。特别是,训练过程中按ctrl + C结束,也能保存好模型再退出。也可以每训练完一个 epoch 就保存一次模型,模型文件名写上 accuracy
3 遇到的问题:
- 数据类别分布很不均匀,数据共3类,类别 0 占数据的 80%,而类别 1、2 仅占20%,类别 2 数据稀少。这样训练出来的模型,虽然显示在测试集上的 Accuracy 有 80%,但是显然,这非常不可靠。
4 TODO:
- 解决数据类别分布不均的问题。参考网上的博客,ideas:
- 数据角度:
- 数据重采样。随机欠采样,随机过采样
- 类别平衡采样。每个送进去训练的 batch 按类别平衡采样。
- 算法角度:
- 代价敏感学习。当把少数类样本预测错误时,增大惩罚和损失的力度等。
- 数据角度:
- 完善模型。考虑添加正则项,防止模型过拟合,特别当模型层数较多时。
- 尝试其他 CNN 模型,ResNet等。
- 对 bounding box (抠出病变区域) 的数据进行训练与测试
