文章目录
- 简介
- 一、unet3
- 二、完整代码(keras)
-
- 1.引入库
- 2.辅助函数
- 3.搭建网络
- 4.创建模型
简介
很久以前就看到了unet3 医学图像分割的论文,本来想直接去github找keras/Tensorflow为了实现,似乎所有找到的东西都与源代码不同,所以我根据论文和源代码写了它们,但我不能保证它们与源代码完全一致,并发送它们来吸引玉石。很多讲unet3 博客写得很好。如果你想知道全文,你可以看看这个翻译【UNet3 (UNet )论文解读 九零猴,本文还简单介绍了自己的理解。
unet3 论文 源码(Pytorch)
一、unet3
简单来说,unet3 有三个特点: 1 跨尺度连接,防止语义在下采样/上采样之间损失 2 全尺度深监督,学习深层次特征 3 为了消除医学图像中噪声引起的假阳性分割,提出了分类指导模块 4 新的混合损失函数(TODO)
呃,前三点其实各有槽点,后面再说
unet3 如上图所示,网络结构一般都很容易理解。作者认为unet和unet 没有跨尺度的特征图连接,所以想到不同尺度的信息传输到解码器,解码器中的信息也跨层传输,以减少信息丢失(真的很简单和粗糙=_=)。 以解码器3为例,解码器3集成了解码器1、2、3和4、5的特性,通过最大池(来自编码器的特性)或采样(来自解码器的特性)调整到与解码器3相同的特性图大小,并通过卷积层(源代码为卷积 BN ReLu)将特征数调整到一致。这些拼接的特征图再经过卷积 BN ReLu块输出特性OK。 这张图解释了另外两个特点,一个是全尺度深度监督,另一个是分类指导模块(CGM)。 对所有解码器各层的输出进行全尺度深度监督,计算损失函数。 作者提出了分类指导模块,以防止噪声引起的假阳性分割。在网络瓶颈层(编码器底层)添加分类指导模块,En5)模块,这层网络最深,特征图数量最多,特征图最小,可能会过滤掉一定的噪音。作者在这一层后面加了一个小分类头(Dropout Conv1x1 Pooling Sigmoid),该分类头输出概率表示输入图像中是否有目标器官,将该分类结果与分类头相乘,可消除假阳性。
比较结果,直接看图:
说完特点,说说槽点: 1 全尺度连接好,作者特别提到,unet3 参数小于unet和unet 但事实上,训练似乎需要更多的时间和内存,这似乎是因为unet3 使用更多的卷积操作(例如,unet解码器每层只需卷积2次,但看上面Fig.2,unet3 每层解码器需要6次卷积) 2 还没想好 3 CGM这只是一个简单的模块,即使在我自己的实验中添加了它Dropout拟合很快就过去了,图像分割头的验证集损失仍在减少,CGM这里的损失函数已经不降反升了。
二、完整代码(keras)
注:孩子不懂事,代码写着玩,不一定正确,如有问题欢迎指出和讨论,转载请注明出处。 CGM输出这块的实现还是有待商榷的,我的代码里CGM分割掩膜分别输出,后面要手动乘以。
1.引入库
import tensorflow as tf import numpy as np from keras.models import Model from keras.layers import Conv2D, Input, concatenate, MaxPooling2D, UpSampling2D, Activation, BatchNormalization, LayerNormalization, Dropout, GlobalMaxPooling2D
2.辅助函数
# helper function to build unet3 def normalization(input_tensor, normalization): if normalization=='batch': return(BatchNormalization()(input_tensor)) elif normalization=='layer': return(LayerNormalization()(input_tensor)) elif normalization == None: return input_tensor else: raise ValueError('Invalid normalization') def conv2d_block(input_tensor, filters, kernel_size, norm_type, use_residual, act_type='relu', double_features = False, dilation=[1, 1]): x = Conv2D(filters, kernel_size, padding='same', dilation_rate=dilation[0], use_bias=False, kernel_initializer='he_normal')(input_tensor) x = normalization(x, norm_type) x = Activation(act_type)(x) if double_features: filters *= 2 x = Conv2D(filters, kernel_size, padding='same', dilation_rate=dilation[1], use_bias=False, kernel_initializer='he_normal')(x) x = normalization(x, norm_type) if use_residual: if K.int_shape(input_tensor)[-1] != K.int_shape(x)[-1]: shortcut = Conv2D(filters, kernel_size=1, padding='same', use_bias=False, kernel_initializer='he_normal')(input_tensor) shortcut = normalization(shortcut, norm_type) x = add([x, shortcut]) else: x = add([x, input_tensor]) x = Activation(act_type)(x) return x def down_layer_2d(input_tensor, down_pattern, filters, norm_type=None): if down_pattern == 'maxpooling': x = MaxPooling2D(pool_size=(2, 2))(input_tensor) elif down_pattern == 'avgpooling': x = AveragePooling2D(pool_size=(2, 2))(input_tensor) elif down_pattern == 'conv': x = Conv2D(filters, kernel_size=(2, 2), strides=(2, 2), padding='same', use_bias=False if norm_type is None else True, kernel_initializer='he_normal')(input_tensor) normalization(x, norm_type) elif down_pattern == 'normconv': x = normalization(input_tensor, norm_type) x = Conv2D(filters, kernel_size=(2, 2), strides=(2, 2), padding='same', kernel_initializer='he_normal')(x) else: raise ValueError('Invalid down_pattern') return x def conv_norm_act(input_tensor, filters, kernel_size , norm_type='batch', act_type='relu', dilation=1): output_tensor = Conv2D(filters, kernel_size, padding='same', dilation_rate=(dilation, dilation), use_bias=False if norm_type is not None else True, kernel_initializer='he_normal')(input_tensor) output_tensor = normalization(output_tensor, normalization=norm_type) output_tensor = Activation(act_type)(output_tensor) return output_tensor def aggregate(l1, l2, l3, l4, l5, filters, kernel_size, norm_type='batch', act_type='relu'): out = concatenate([l1, l2, l3, l4, l5], axis = -1) out = Conv2D(filters * 5, kernel_size, padding = 'same', use_bias=False if norm_type is not None else True, kernel_initializer = 'he_normal')(out) out = normalization(out, norm_type) out = Activation(act_type)(out) return out def cgm_block(input_tensor, class_num, dropout_rate = 0.): x = Dropout(rate = dropout_rate)(input_tensor) x = Conv2D(class_num, 1, padding='same', kernel_initializer='he_normal')(x) # x = BatchNormalization()(x) x = GlobalMaxPooling2D()(x) # 用全局最大池化代替原文中的自适应最大池化,这里的效果应该是一样的 x = Activation('sigmoid', name='cgm_output')(x) # x = Lambda(lambda x: K.expand_dims(x, axis=1))(x) # x = Lambda(lambda x: K.expand_dims(x, axis=1), name = 'cgm_output')(x) # x = Reshape((batch_size, 1, 1, class_num))(x) return x
3.搭建网络
# build unet3+ model def unet3p_2d(input_shape, initial_features=32, kernel_size=3, class_num=1, norm_type='batch', double_features=False, use_residual=False, down_pattern='maxpooling', using_deep_supervision=True, using_cgm=False, cgm_drop_rate=0.5, show_summary=True): ''' input_shape: (height, width, channel) initial_features: int, 初始特征图数量,每次下采样特征图数量加倍, unet3+原文中用的是64 kernel_size: int, 卷积核大小 class_num: int, 图像分割的类别数 norm_type: str, 标准化方式, 'batch' 或 'layer', unet3+使用的是BatchNormalization double_features: bool, 在conv2d_block模块中是否在第二个卷积中将特征图数量翻倍,3dunet论文中提出该方法可以避免瓶颈问题,通常可以设为False use_residual: bool, 编码器部分是否使用残差连接 down_pattern: str, 下采样方式, 'maxpooling' 或 'avgpooling' 或 'conv' 或 'normconv', unet3+使用的是MaxPooling using_deep_supervision: bool, 是否使用全尺度深度监督 using_cgm: bool, 是否使用分类指导模块(CGM) cgm_drop_rate: float, CGM模块中Dropout比率 show_summary: bool, 是否显示模型概况 ''' if class_num == 1: last_layer_activation = 'sigmoid' else: last_layer_activation = 'softmax' inputs = Input(input_shape) xe1 = conv2d_block(input_tensor=inputs, filters=initial_features, kernel_size=kernel_size, norm_type=norm_type, double_features=double_features, use_residual=use_residual) xe1_pool = down_layer_2d(input_tensor=xe1, down_pattern=down_pattern, filters=initial_features) xe2 = conv2d_block(input_tensor=xe1_pool, filters=initial_features * 2, kernel_size=kernel_size, norm_type=norm_type, double_features=double_features, use_residual=use_residual) xe2_pool = down_layer_2d(input_tensor=xe2, down_pattern=down_pattern, filters=initial_features * 2) xe3 = conv2d_block(input_tensor=xe2_pool, filters=initial_features * 4, kernel_size=kernel_size, norm_type=norm_type, double_features=double_features, use_residual=use_residual) xe3_pool = down_layer_2d(input_tensor=xe3, down_pattern=down_pattern, filters=initial_features * 4) xe4 = conv2d_block(input_tensor=xe3_pool, filters=initial_features * 8, kernel_size=kernel_size, norm_type=norm_type, double_features=double_features, use_residual=use_residual) xe4_pool = down_layer_2d(input_tensor=xe4, down_pattern=down_pattern, filters=initial_features * 8) xe5 = conv2d_block(input_tensor=xe4_pool, filters=initial_features * 16, kernel_size=kernel_size, norm_type=norm_type, double_features=double_features, use_residual=use_residual) if using_cgm: cgm = cgm_block(input_tensor = xe5 , class_num = class_num ,dropout_rate = cgm_drop_rate) xd4_from_xe5 = UpSampling2D(size=(2,2), interpolation='bilinear')(xe5) xd4_from_xe5 = conv_norm_act(input_tensor=xd4_from_xe5, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd4_from_xe4 = conv_norm_act(input_tensor=xe4, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd4_from_xe3 = MaxPooling2D(pool_size = (2, 2))(xe3) xd4_from_xe3 = conv_norm_act(input_tensor=xd4_from_xe3, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd4_from_xe2 = MaxPooling2D(pool_size = (4, 4))(xe2) xd4_from_xe2 = conv_norm_act(input_tensor=xd4_from_xe2, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd4_from_xe1 = MaxPooling2D(pool_size = (8, 8))(xe1) xd4_from_xe1 = conv_norm_act(input_tensor=xd4_from_xe1, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd4 = aggregate(xd4_from_xe5, xd4_from_xe4, xd4_from_xe3, xd4_from_xe2, xd4_from_xe1, filters=initial_features, kernel_size=kernel_size, norm_type=norm_type) xd3_from_xe5 = UpSampling2D(size=(4, 4), interpolation='bilinear')(xe5) xd3_from_xe5 = conv_norm_act(input_tensor=xd3_from_xe5, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd3_from_xd4 = UpSampling2D(size=(2, 2), interpolation='bilinear')(xd4) xd3_from_xd4 = conv_norm_act(input_tensor=xd3_from_xd4, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd3_from_xe3 = conv_norm_act(input_tensor=xe3, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd3_from_xe2 = MaxPooling2D(pool_size = (2, 2))(xe2) xd3_from_xe2 = conv_norm_act(input_tensor=xd3_from_xe2, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd3_from_xe1 = MaxPooling2D(pool_size = (4, 4))(xe1) xd3_from_xe1 = conv_norm_act(input_tensor=xd3_from_xe1, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd3 = aggregate(xd3_from_xe5, xd3_from_xd4, xd3_from_xe3, xd3_from_xe2, xd3_from_xe1, filters=initial_features, kernel_size=kernel_size, norm_type=norm_type) xd2_from_xe5 = UpSampling2D(size=(8, 8), interpolation='bilinear')(xe5) xd2_from_xe5 = conv_norm_act(input_tensor=xd2_from_xe5, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd2_from_xd4 = UpSampling2D(size=(4, 4), interpolation='bilinear')(xd4) xd2_from_xd4 = conv_norm_act(input_tensor=xd2_from_xd4, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd2_from_xd3 = UpSampling2D(size=(2, 2), interpolation='bilinear')(xd3) xd2_from_xd3 = conv_norm_act(input_tensor=xd2_from_xd3, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd2_from_xe2 = conv_norm_act(input_tensor=xe2, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd2_from_xe1 = MaxPooling2D(pool_size = (2, 2))(xe1) xd2_from_xe1 = conv_norm_act(input_tensor=xd2_from_xe1, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd2 = aggregate(xd2_from_xe5, xd2_from_xd4, xd2_from_xd3, xd2_from_xe2, xd2_from_xe1, filters=initial_features, kernel_size=kernel_size, norm_type=norm_type) xd1_from_xe5 = UpSampling2D(size=(16, 16), interpolation='bilinear')(xe5) xd1_from_xe5 = conv_norm_act(input_tensor=xd1_from_xe5, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd1_from_xd4 = UpSampling2D(size=(8, 8), interpolation='bilinear')(xd4) xd1_from_xd4 = conv_norm_act(input_tensor=xd1_from_xd4, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd1_from_xd3 = UpSampling2D(size=(4, 4), interpolation='bilinear')(xd3) xd1_from_xd3 = conv_norm_act(input_tensor=xd1_from_xd3, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd1_from_xd2 = UpSampling2D(size=(2, 2), interpolation='bilinear')(xd2) xd1_from_xd2 = conv_norm_act(input_tensor=xd1_from_xd2, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd1_from_xe1 = conv_norm_act(input_tensor=xe1, filters=initial_features, kernel_size=kernel_size ,norm_type=norm_type) xd1 = aggregate(xd1_from_xe5, xd1_from_xd4, xd1_from_xd3, xd1_from_xd2, xd1_from_xe1, filters=initial_features, kernel_size=kernel_size, norm_type=norm_type) if using_deep_supervision: xd55 = Conv2D(class_num, kernel_size, activation=None, padding='same')(xe5) xd55 = UpSampling2D(size=(16, 16))(xd55) xd55 = Activation(last_layer_activation, name='output_de5')(xd55) xd44 = Conv2D(class_num, kernel_size, activation=None, padding='same')(xd4) xd44 = UpSampling2D(size=(8, 8))(xd44) xd44 = Activation(last_layer_activation, name='output_de4')(xd44) xd33 = Conv2D(class_num, kernel_size, activation=None, padding='same')(xd3) xd33 = UpSampling2D(size=(4, 4))(xd33) xd33 = Activation(last_layer_activation, name='output_de3')(xd33) xd22 = Conv2D(class_num, kernel_size, activation=None, padding='same')(xd2) xd22 = UpSampling2D(size=(2, 2))(xd22) xd22 = Activation(last_layer_activation, name='output_de2')(xd22) xd11 = Conv2D(class_num, kernel_size, activation=None, padding='same')(xd1) xd11 = Activation(last_layer_activation, name='output_de1')(xd11) if using_cgm: outputs=[xd11, xd22, xd33, xd44, xd55, cgm] else: outputs=[xd11, xd22, xd33, xd44, xd55] else: conv_output = Conv2D(class_num, 1, activation=last_layer_activation, name='output')(xd1) if using_cgm: outputs=[conv_output, cgm 标签:
低压并联电容器串接xd1电抗器电力电容电抗器限流电抗器xd1