596. SenNet + HOA - Hacking the Human Vasculature in 3D | blood-vessel-segmentation
该解决方案是作为由Common Fund细胞衰老网络(SenNet)计划与人类器官图谱(HOA)合作组织的血管分割竞赛的一部分而实现的。
竞赛概览页面:SenNet + HOA - Hacking the Human Vasculature in 3D
竞赛数据集:数据集链接
非常感谢组织者提供的这次机会!
框架 — TensorFlow
数据管道 — 2D, ROI, 调整尺寸 (1024x704), tfrecord
模型 — 几乎是经典的U-net(详情见下文)
解决方案在两个notebook中呈现:
该解决方案开发于2023年12月,在圣诞老人新年礼物之前,最终帮助700多名参赛者跃升至0.8以上分数。我带着0.567分排在第238位开始了假期。一周后当我再次打开排行榜时,我已经下降了超过150个名次!而这仅仅是个开始!;)我又在这个方法上花了一周时间。我必须承认,由于提高分数让我在排名中越来越靠后,我并没有太大热情继续。
最终,通过增加图像尺寸和进行少量架构改进,我达到了0.636的结果,我开始寻找其他方法(见下文失败的尝试章节)。
所有数据(除了kidney_3_dense标签)都用作训练数据。图像包含大量无用的信息区域。为了减少这些区域,使用统计方法对图像进行预处理以提取ROI。
def apply_roi(image, label=None):
"""
排除无用的图像区域
"""
# 移除标准差低的行和列
row_mask = image.std(axis=1) > 0.22
clmn_mask = image.std(axis=0) > 0.22
# 清理这种方法产生的噪声并获取可靠的区域
row_mask = cleaning_mask(row_mask)
clmn_mask = cleaning_mask(clmn_mask)
image = image[row_mask, :][:, clmn_mask]
label = label[row_mask, :][:, clmn_mask] if isinstance(label, np.ndarray) else None
# 记录填充区域的大小以便后续正确恢复
row_pad = (row_mask.argmax(), row_mask[::-1].argmax())
clmn_pad = (clmn_mask.argmax(), clmn_mask[::-1].argmax())
return image, label, (row_pad, clmn_pad)
def cleaning_mask(mask):
"""
从噪声掩码中选择可靠区域
"""
# 如果边框从第一个元素开始或在最后一个元素结束
mask[0] = False
mask[-1] = False
# 获取边框边缘
frames = np.nonzero(mask[:-1] != mask[1:])[0]
# 获取边框长度
delta = frames[1:] - frames[:-1]
# 获取最大长度边框的索引
max_solid_block_begin = np.argmax(delta)
# 其他都是垃圾数据
garbage = np.delete(frames, [max_solid_block_begin, max_solid_block_begin + 1])
# 清理掩码
for a, b in zip(garbage[::2], garbage[1::2]):
mask[a + 1:b + 1] = False
return mask
接下来,所有图像都被调整为1024x704的统一尺寸。实验从384x256尺寸开始,随着尺寸增加,结果如预期般改善。1024x704是未导致OOM错误的最大尺寸。处理后的图像示例如下。
每25张图像(4%)用于验证,因为标签密度沿z轴变化很大。
最终的图像和标签被打包到tfrecord文件中,以组织多线程管道(总文件数 - 92个,每个162 MB)。对于1024x704形状的图像,最大可能的批量大小为32。未使用数据增强——我只是没来得及做!
使用或多或少的经典U-net架构作为模型。
使用二元交叉熵评估损失。使用Adam优化器进行优化。学习率按照带有warmup的余弦衰减计划进行调整。
由于存在严重的类别不平衡,因此使用了权重。想法是在实例级别设置权重,因为当我们从肾脏中心向边缘移动时(沿z轴),类别比例变化很大。但首先我硬编码了权重,效果还可以接受。后来我没有回到这个问题,所以还有改进空间。
模型从头开始训练,共60个epoch。预测时选择验证损失最小的epoch。
预测结果大致如下:
这里有很多FP(假阳性)… 然而,对样本权重进行调整似乎很有意义 🤔
显然,考虑到大量小细节,任何缩放都会损害结果。我尝试通过将图像分割成片段(256x256大小的相交瓦片)来解决这个问题。我使用了相同的模型架构。但标签只出现在瓦片重叠的地方,当从瓦片组装掩码时,我得到了空白!我还没来得及弄清楚这个问题。
第二。我尝试通过改变架构来解决标签缩放问题——我在解码器末尾添加了另一个"类U-net"结构——2个卷积层和两个转置卷积层。这里做得也不太好,但在私有排行榜上本可以排到第67名 😉
是的,是的… 发生了一场大地震… 不知为何,大多数解决方案都以可疑的相似方式失败了 ;) 在约100-600名的区间内,这一点很明显。
我的主要解决方案,与获胜方案在网络架构和图像尺寸上相似,在公共和私有数据集上都给出了稳定的结果。在私有数据集上——甚至更好一点!
公共和私有数据集之间的差异为0.012分。在第一千名中,如此稳定的结果屈指可数。总的来说,方差已经正常,剩下的就是处理偏差了 😁
感谢所有致力于该问题的人!与你们一起工作很有趣!祝好运! ✋