第一篇博客首先感谢我的女朋友姜旭艳小姐的大力支持 😊
在语义分割问题中经常出现的操作就是反卷积,目的是将图片从较小的分辨率放大到更大的分辨率。反卷积层的参数初始化在训练过程中有很大的作用,如果采用随机初始化可能导致训练时间更长或者是效果不好,所以往往采用的是bilinear kernel初始化。
bilinear kernel实现代码
def upsample_filt(size):
"""
size: Size of the filter
"""
factor = (size + 1)
if size % 2 == 1:
center = factor - 1
else:
center = factor - 0.5
og = np.ogrid[:size, :size]
return (1 - abs(og[0] - center) / factor) * (1 - abs(og[1] - center) / factor)
# 以 size = 4 为例
factor = 5
center = 1.5
og =
[array([[0],
[1],
[2],
[3]]), array([[0, 1, 2, 3]])]
abs(og[0] - center) =
[[ 1.5]
[ 0.5]
[ 0.5]
[ 1.5]]
# 显然 abs(og[0] - center) 表示的是图像上每一个像素点到中心点(center)的距离
# 然后再利用核长度 factor 进行归一化
# 之所以 factor = size + 1 是因为将中心点也考虑进来了 所以总共有 size + 1 个像素
# 然后 (1 - abs(og[0] - center) / factor) 表示的就是在x轴方向上每个像素点的权重
# 距离越远权重越小
# 最后将x和y轴上的值相乘得到整个图像上每个像素点的权重
1 - abs(og[0] - center) / factor) * (1 - abs(og[1] - center) / factor =
[[ 0.0625 0.1875 0.1875 0.0625]
[ 0.1875 0.5625 0.5625 0.1875]
[ 0.1875 0.5625 0.5625 0.1875]
[ 0.0625 0.1875 0.1875 0.0625]]
在反卷积核中卷积核是一个四维变量[in_channels, out_channels, H, W],所以需要将卷积核的每一层都用上述的双线性插值核来赋值。
因此完整的初始化代码为
def bilinear_kernel(in_channels, out_channels, kernel_size):
'''
return a bilinear filter tensor
'''
factor = (kernel_size + 1) // 2
if kernel_size % 2 == 1:
center = factor - 1
else:
center = factor - 0.5
og = np.ogrid[:kernel_size, :kernel_size]
filt = (1 - abs(og[0] - center) / factor) * (1 - abs(og[1] - center) / factor)
# 上半部分是生成一层双线性插值核
# 下面是拉伸
weight = np.zeros((in_channels, out_channels, kernel_size, kernel_size), dtype='float32')
# 赋值到每一层
weight[range(in_channels), range(out_channels), :, :] = filt
return torch.from_numpy(weight)
conv_trans = nn.ConvTranspose2d(3, 3, 4, 2, 1)
# 将其定义为 bilinear kernel
conv_trans.weight.data = bilinear_kernel(3, 3, 4)
网友评论