最近在看Unet,记录一下。
论文地址
https://arxiv.org/pdf/1505.04597.pdf
网络结构
图上画的还是很清晰的,但是对于不了解Unet结构的人来说,可能还是有一些不清楚的地方。我这里结合我看的时候的疑问,来讲一下Unet的结构的一些问题。
- 可以看到,输入是572x572的,但是输出变成了388x388,这说明经过网络以后,输出的结果和原图不是完全对应的,这在计算loss和输出结果都可以得到体现。
- 蓝色箭头代表3x3的卷积操作,并且stride是1,padding策略是vaild,因此,每个该操作以后,featuremap的大小会减2。
- 红色箭头代表2x2的maxpooling操作,需要注意的是,此时的padding策略也是vaild(same 策略会在边缘填充0,保证featuremap的每个值都会被取到,vaild会忽略掉不能进行下去的pooling操作,而不是进行填充),这就会导致如果pooling之前featuremap的大小是奇数,那么就会损失一些信息 。
- 绿色箭头代表2x2的反卷积操作,这个只要理解了反卷积操作,就没什么问题,操作会将featuremap的大小乘2。
- 灰色箭头表示复制和剪切操作,可以发现,在同一层左边的最后一层要比右边的第一层要大一些,这就导致了,想要利用浅层的feature,就要进行一些剪切,也导致了最终的输出是输入的中心某个区域。
- 输出的最后一层,使用了1x1的卷积层做了分类。
网络结构代码
纸上得来终觉浅,还是要写个代码,才能了解细节部分。
下面这个代码,我参考了
https://github.com/jakeret/tf_unet
主要把每一层拆开了一步一步写了下来,虽然写的很啰嗦,但是看起来比较容易理解。另外,我忽略了一些对featuremap大小没有影响的操作,比如relu,bn,dropout等。
# conding:utf-8
from __future__ import print_function
import tensorflow as tf
import numpy as np
def weight_variable(shape, stddev=0.1, name="weight"):
initial = tf.truncated_normal(shape, stddev=stddev)
return tf.Variable(initial, name=name)
def bias_variable(shape, name="bias"):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial, name=name)
def get_w_and_b(kernel_size, input_feature_size, output_feature_size, name):
w = weight_variable([kernel_size, kernel_size, input_feature_size, output_feature_size], name=name + '_w')
b = bias_variable([output_feature_size], name=name + '_b')
return w, b
def get_deconv_w_and_b(kernel_size, input_feature_size, output_feature_size, name):
w = weight_variable([kernel_size, kernel_size, input_feature_size, output_feature_size], name=name + '_w')
b = bias_variable([input_feature_size], name=name + '_b')
return w, b
def conv2d(x, W, b):
with tf.name_scope("conv2d"):
conv_2d = tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='VALID')
conv_2d_b = tf.nn.bias_add(conv_2d, b)
return conv_2d_b
def max_pool(x, n):
return tf.nn.max_pool(x, ksize=[1, n, n, 1], strides=[1, n, n, 1], padding='VALID')
def deconv2d(x, W, stride):
with tf.name_scope("deconv2d"):
x_shape = tf.shape(x)
output_shape = tf.stack([x_shape[0], x_shape[1]*2, x_shape[2]*2, x_shape[3]//2])
return tf.nn.co