作者美国纽约州立大学石溪分校 Zhixin Shu
Code: https://github.com/zhixinshu/DeformingAutoencoders-pytorch
关键词:Unsupervised groupwise image alignment 无监督分组图像对齐, disentangle shape and appearance, latent representation
根据代码:https://github.com/zhixinshu/DeformingAutoencoders-pytorch/blob/master/train_DAE_CelebA.py理解该方法:
输入图像到编码器,编码器输出该图像的latent representation(代码中编码器输出的特征的维度 batch_size * channels * 1 * 1,经过维度调整view,得到batch_size * channels),接着 latent represnetation 分别输入到两个全联连接层,得到两个特征通道数量不同的latent representation,这里根据解码器的输出,本人分别称这两个latent representation为 texture/apperance latent representation (shape : 10 * 16), 另一个是shape latent representation (shape: 10 * 128),可以调整参数变换两个latent representation的特征通道数量。
shape LR, texture LR 分别输入到两个解码器,分别得到deformation field,合成appearance。
deformation field 输入到 spatial integration layer 生成网格,网格包含x,y方向的坐标;接着,网格输入到 Hardtanh()激活函数中,坐标取值范围归一化到[-1,1]。最后,grid_sample函数利用坐标,以及合成图像,重新插值生成输出图像。
编码器输出:
# encoders of DAE, using DenseNet architecture
class Dense_Encoders(nn.Module):
def __init__(self, opt):
super(Dense_Encoders, self).__init__()
self.ngpu = opt.ngpu
self.encoder = waspDenseEncoder(opt, ngpu=1, nc=opt.nc, ndf = opt.ndf, ndim = opt.zdim)
self.zImixer = waspMixer(opt, ngpu=1, nin = opt.zdim, nout = opt.idim)
self.zWmixer = waspMixer(opt, ngpu=1, nin = opt.zdim, nout = opt.wdim)
def forward(self, input):
self.z = self.encoder(input) # shape 10 * 128
self.zImg = self.zImixer(self.z)# shape 10 * 16
self.zWarp = self.zWmixer(self.z)# shape 10 * 128
return self.z, self.zImg, self.zWarp
self.z, self.zImg (texture LR), slef.zWarp (shape LR)。
解码器输出:
class Dense_DecodersIntegralWarper2(nn.Module):
def __init__(self, opt):
super(Dense_DecodersIntegralWarper2, self).__init__()
self.imagedimension = opt.imgSize
self.ngpu = opt.ngpu
self.idim = opt.idim
self.wdim = opt.wdim
self.decoderI = waspDenseDecoder(opt, ngpu=self.ngpu, nz=opt.idim, nc=opt.nc, ngf=opt.ngf, lb=0, ub=1)#
self.decoderW = waspDenseDecoder(opt, ngpu=self.ngpu, nz=opt.wdim, nc=2, ngf=opt.ngf, lb=0, ub=1, activation=nn.Tanh, args=[], f_activation=nn.Sigmoid, f_args=[])
self.warper = waspWarper(opt)
self.integrator = waspGridSpatialIntegral(opt)
self.cutter = nn.Hardtanh(-1,1)
def forward(self, zI, zW, basegrid):
self.img = self.decoderI(zI.view(-1,self.idim,1,1))
self.diffentialWarping = self.decoderW(zW.view(-1,self.wdim,1,1))*(5.0/self.imagedimension) # (5.0/self.imagedimension) 对应
self.warping = self.integrator(self.diffentialWarping)-1.2 # 为什么要减去 1.2,为了将取之范围移动到-1~1 附近,而1.2 通过torch.median()估算得到的。
self.warping = self.cutter(self.warping)# 再通过nn.Hardtanh()将self.warping 截断至[-1, 1]内。
self.resWarping = self.warping-basegrid
self.output = self.warper(self.img, self.warping) # 模型重建结果
return self.img, self.resWarping, self.output, self.warping
self.img,self.resWarping, self.output, self.warping
损失函数:
在代码中,作者使用的是L1损失计算重建损失,并不是论文中所述的L2损失。
等式(6)中的Sa 与 So之间的L2损失在代码中也没有实现。在Github的Issues也有人提出相同的问题,但是作者没有反馈。
具体的公式解释,见论文。简单地来说,使用了损失函数3,5,6来约束模型。文章中作者还提到了对抗损失,但是在代码中没有体现出来。:
测试代码与问题:
预训练模型参数加载问题:
使用https://github.com/zhixinshu/DeformingAutoencoders-pytorch/blob/master/train_DAE_CelebA.py代码,以及模型的参数加载预训练模型参数,出现模型参数不匹配问题,将代码中import DAENet_InstanceNorm as DAENet 替换成import DAENet。
使用该模型训练腹部MRI数据集 ,https://chaos.grand-challenge.org/Data/
问题1:模型损失函数不熟练,重建图像显示一团黑。
思考:是不是归一化方法不一样?我自己写了数据扩充,以及归一化:
与作者的对比,发现作者没有使用数据扩充,且输入数据归一化到[0,1],我的输入数据归一化到[-1,1]:
def parseSampledDataPoint(dp0_img, nc):
dp0_img = dp0_img.float()/255 # convert to float and rerange to [0,1]
if nc==1:
dp0_img = dp0_img.unsqueeze(3)
dp0_img = dp0_img.permute(0,3,1,2).contiguous() # reshape to [batch_size, 3, img_H, img_W]
return dp0_img
问题2: 修改归一化后,部分损失能够在模型训练开始阶段逐渐下降,但是随着训练次数的增加,模型的BiasReduce损失值又上升,而且重建图像和生成Texture 几乎一样,不像论文的人脸重建的效果,重建图像与Texture有区别。
思考:
(1)代码BiasReduce损失没有实现 Sa 与 So之间的L2损失 引起的?
(2)batch_size(原代码:100,https://github.com/zhixinshu/DeformingAutoencoders-pytorch/blob/master/train_DAE_CelebA.py)或者 取值(源代码中)超参数影响 ?
分析 推论(2):为了保持不变,将5改成20,进行一次实验。通过对比BiasReduce损失的收敛情况,可以得出取值对BiasReduce损失收敛影响比较重要。但是重建图像和生成Texture 几乎一样,没有改善。
继续分析:
(3)重建损失函数是L1损失,不是L2损失引起 生成的Texture 与 Reconstruction Imags一致?
(4)会不会是保存的时候出现问题?
(5)不更改源代码,将MR图像直接缩放到64输入到模型中,且放弃数据扩充,看看有什么效果?
答:经过两种实验的分析,第一种:将图像缩放到64,有数据扩充,模型输出的Texture,重建图像如下所示:
通过可视化分析,Texture 与重建图像没有显著的差异。但是分析第二种:将图像缩放到64,无数据扩充,生成结果如下所示,发现生成的结果有所改善。