ugatit也是基于cyclegan,需要使用gan loss, cycle loss和idt loss。除此之外,论文还提出了在G和D中使用一个分类器,训练分类器也需要一个loss,叫做cam loss,受类激活映射启发。
论文的两个创新点:
- adaptive Layer Instance normalization(adaLIN)
- 类似于SENet的通道注意力。只不过exciting权重是来自CAM分类器。
方法
G_A代表从A域往B域转换,D_A代表判别真实的B域和假的B域样本G_A(X)。另外的一个方向BtoA就不多重复说明。
Note: 实际上tensorflow的源代码,指明作者用了两个不同感受野的D(一个方向有两个D,一共4个D),但用法一样。这里我统一用D表示。
将生成器G分解为4部分:E,M,A,U。
E包含:
- 前两层是stride为2的下采样。
- 后x层是stride为1的resblock。使用Instance norm
M: 一个MLP,有几层全连接层组成。(light 版本的区别就在于此)
A: 论文创新其一,注意力模块,后续接了x层adaLIn的resblock。
U是上采样模块: 两层两倍上采样(upsample, conv , LN, relu),以及一个conv+tanh作为head。这里使用的LN而不是IN,有原因,见下文
生成器的部分着重在M和A的作用上。
判别器D就是patch-base的D,只不过作者在一个方向上使用了local和global两个尺度的D。同时D中也是用了CAM。使用方式了G中的CAM一致,但作用不一样。
details
A的部分
样本经过G中的E之后,得到一个tensor,其shape为
c
×
h
×
w
c \times h\times w
c×h×w(省略batchsize),这个tensor用T表示
然后经过GAP和GMP(均值池化和最大池化,全局的),得到两个向量,记作
t
m
∈
R
c
,
t
a
∈
R
c
t_m \in R^{c}, t_a \in R^c
tm∈Rc,ta∈Rc。然后使用一个全连接层得到对应的标量:
s
c
a
l
a
r
i
=
m
a
t
m
u
l
(
W
,
t
i
)
;
i
∈
{
a
,
m
}
scalar_i = matmul(W, t_i); i \in \{a,m\}
scalari=matmul(W,ti);i∈{a,m}
这个标量会用于计算cam loss。同时这个W(全连接层的权重),会作为weight重新激活T(类似senet的通道重激活,一个值重新加权整个通道)。
这两个特征经过cat以及1x1的conv降维之后,会送入M中。
M的部分
M是一个MLP。其作用是预测gamm和beta,用于后面需要的adalin的norm之后的denorm 部分。gamm和beta都是c维的向量。
X层使用AdaLIN的resblock
def adaptive_ins_layer_resblock(x_init, channels, gamma, beta, use_bias=True, smoothing=True, scope='adaptive_resblock') :
with tf.variable_scope(scope):
with tf.variable_scope('res1'):
x = conv(x_init, channels, kernel=3, stride=1, pad=1, pad_type='reflect', use_bias=use_bias)
x = adaptive_instance_layer_norm(x, gamma, beta, smoothing)
x = relu(x)
with tf.variable_scope('res2'):
x = conv(x, channels, kernel=3, stride=1, pad=1, pad_type='reflect', use_bias=use_bias)
x = adaptive_instance_layer_norm(x, gamma, beta, smoothing)
return x + x_init
进入adaptive_instance_layer_norm中看一下:
def adaptive_instance_layer_norm(x, gamma, beta, smoothing=True, scope='instance_layer_norm') :
with tf.variable_scope(scope):
ch = x.shape[-1]
eps = 1e-5
ins_mean, ins_sigma = tf.nn.moments(x, axes=[1, 2], keep_dims=True)
x_ins = (x - ins_mean) / (tf.sqrt(ins_sigma + eps))
ln_mean, ln_sigma = tf.nn.moments(x, axes=[1, 2, 3], keep_dims=True)
x_ln = (x - ln_mean) / (tf.sqrt(ln_sigma + eps))
rho = tf.get_variable("rho", [ch], initializer=tf.constant_initializer(1.0), constraint=lambda x: tf.clip_by_value(x, clip_value_min=0.0, clip_value_max=1.0))
if smoothing :
rho = tf.clip_by_value(rho - tf.constant(0.1), 0.0, 1.0)
x_hat = rho * x_ins + (1 - rho) * x_ln
x_hat = x_hat * gamma + beta
return x_hat
AdaLIN的操作和论文描述一致,代码也很直观。rho是一个trainable parameter,限制在0到1,用来融合LN和IN。最后使用MLP预测的beta和gamma重新denorm。
两个创新点的作用
- CAM机制的引入:在G和D中都用了CAM,但其作用不一样。G中的CAM的作用是让生成器感知到什么区域是两个域(A,B)差别很大的。D中的CAM的目的是让D了解真假样本哪个位置是差别很大的,以此grident能让G生成更好的样本。
- AdaLIN:作者认为IN和LN在翻译任务上各有好处。IN有注意更好保留源域的细节,这是由于IN是channel-wise的norm,但风格特性就不够。而LN因为是layer-wise的norm,更好的学到风格的统计量。因此在特征表征上,更希望使用IN保留源域的细节,在解码(decoder)上,更希望利用这些源域的细节特征,转换到风格上,这就是为啥上采样的部分使用的是LN。在上采样前,使用adaLIN,将两种norm的优点吸收,根据两个域的数据分布自己决定偏向IN还是LN,更适合unpair的图像翻译任务。