Nassir Navab等人发表在IEEE 3D Vison上的论文V-Net,是U-Net[1]的3D版本,其实U-Net作者自己也发表了3D U-Net[2]。
论文贡献:第一,3D图像分割end2ent模型(基于3D卷积),用于MRI前列腺容积医学图像分割。第二,新的目标函数,基于Dice coefficient。第三,数据扩充方法:random non-linear transformations和histogram matching。第四,加入残差学习提升收敛。
MRI前列腺困难(客套话):第一,不同scans之间较大的外观变异(灰度分布的变换和改变)。第二,主磁场不均匀性引起的伪影和畸变。
方法(V-Net):如图1所示。
图1
整个网络分为压缩路径和非压缩路径,也就是缩小和扩大feature maps,每个stage将特征缩小一半,也就是128-128-64-32-16-8,通道上为1-16-32-64-128-256。每个stage加入残差学习以加速收敛。
图1中的圆圈加交叉代表卷积核为5*5*5,stride为1的卷积,可知padding2*2*2就可以保持特征大小不变。每个stage的末尾使用卷积核为2*2*2,stride为2的卷积,特征大小做小一半(好处为不用保存pooling的switches,所以smaller memory footprint)。整个网络都是使用keiming等人提出的PReLU非线性单元。网络末尾加一个1*1*1的卷积,处理成与输入一样大小的数据,然后接一个softmax。
网络学习U-Net的思想,把缩小端的底层特征送入放大端的相应位置帮助重建高质量图像,并且加速模型收敛。
总结:3D+残差学习+U-Net思想。
Dice损失层:
提出动机为分割区域可能太小,而背景区域太大的话,会bias模型,使得模型不准确。解决方法可以类似U-Net或者SegNet那样对softmax进行加权处理。论文提出新的损失函数,也可以解决这个问题。其实Dice损失层只能用于二分类问题上。多分类的话还是利用softmax加权好吧?Dice系数(Sørensen–Dice coefficient)计算上为,两倍交集除以prediction的大小+gt的大小,参考V-Net代码和3D-Caffe代码。求导很简单,除法求导公式。
实验表明,Dice loss 比 logistic loss好太多,13个Dice点,5mm的豪斯多夫距离。可以尝试这个距离。
下面,我们产生随机数,{0,1}二值binary segmentation或者[0,1]概率图,然后利用TensorLayer里面的函数进行计算。
# -*- coding: utf-8 -*-
"""
Created on Fri Oct 6 11:32:18 2017
@author: shawnyuen
"""
import tensorlayer as tl
import tensorflow as tf
import numpy as np
#outputs = np.float64(np.random.rand(10,10)) # 2-D
outputs = np.float64(np.random.randint(0,2,[1,10,10,10])) # a Tensor
#outputs = np.float64(np.random.rand(1,10,10,10))
y_ = np.float64(np.random.randint(0,2,[1,10,10,10])) # a Tensor
#y_ = np.float64(np.random.rand(1,10,10,10))
print outputs
print '---------------------------------------'
print y_
dice_loss = 1 - tl.cost.dice_coe(outputs, y_, axis=[1,2,3])
#dice_loss = 1 - tl.cost.dice_hard_coe(outputs, y_, axis=[1,2,3])
#print dice_loss
sess = tf.Session()
tl.layers.initialize_global_variables(sess)
print sess.run(dice_loss)
上面有两种Dice,一种是 Soft,一种是 Hard。Hard Dice计算由于存在一个阈值处理,所以是不可导的。用做损失函数用Soft Dice。用tf或者tl就是好,自动求导数,显示写出式子就好。
Soft Dice:
def dice_coe(output, target, loss_type='jaccard', axis=[1,2,3], smooth=1e-5):
inse = tf.reduce_sum(output * target, axis=axis) # compute intersection
if loss_type == 'jaccard': # default loss type, in fact, jaccard and soresen are the same thing
l = tf.reduce_sum(output * output, axis=axis) # number of pixels in output
r = tf.reduce_sum(target * target, axis=axis) # number of pixels in target
elif loss_type == 'sorensen':
l = tf.reduce_sum(output, axis=axis)
r = tf.reduce_sum(target, axis=axis)
else:
raise Exception("Unknow loss_type")
dice = (2. * inse + smooth) / (l + r + smooth) # compute dice coefficient
dice = tf.reduce_mean(dice) # dice coefficient is a scalar between 0 and 1
return dice
Hard Dice:
def dice_hard_coe(output, target, threshold=0.5, axis=[1,2,3], smooth=1e-5):
output = tf.cast(output > threshold, dtype=tf.float32) # thresholding
target = tf.cast(target > threshold, dtype=tf.float32) # thresholding
inse = tf.reduce_sum(tf.multiply(output, target), axis=axis)
l = tf.reduce_sum(output, axis=axis)
r = tf.reduce_sum(target, axis=axis)
hard_dice = (2. * inse + smooth) / (l + r + smooth)
hard_dice = tf.reduce_mean(hard_dice)
return hard_dice
参考资料:
[1] U-Net
[2] 3D U-Net