源域真实数据分类损失最小化: 使 F 学习到更适合源域真实数据的特征表示
源域生成数据分类损失最小化: 使 F 能够理解并学习到源域生成数据的特征
让D误以为目标域生成数据为真: 使生成的数据更加适应目标域,混淆鉴别器D
源域生成数据标签分类损失:
目标域生成数据域分类损失(为了混淆,所以域标签为1):
这些损失函数的设计和优化目的是通过调整特征提取器 F 的参数,使其能够更好地适应源域数据和目标域数据,从而实现更好的域自适应和迁移学习效果,只有直接使用源域数据的源域真实数据域分类损失是与F无关的。
通过训练判定不同类型数据的真实性和分类信息。这些数据包括源域中的真实数据、生成器G生成的假数据以及目标域生成的假数据。
通过对抗训练与鉴别器D竞争,并促进特征提取器F的学习。
源域真实数据分类误差最小化:源域真实数据分类损失(上面提到的鉴别器中的分类损失与分类器C的参数是不共享的,也就是说是两个意义相同但参数不同的分类器,鉴别器中有一个小分类器用于自己使用而不是使用C来计算源域真实数据分类损失)
从上面对特征提取器的介绍可以看出,这篇论文中的各个组件的反向传播是独立的,很多loss都涉及到了F,但是F的反向传播只与上述三个损失有关,其他损失更新参数的时候不会更新F的参数。也就是说我们在写代码设计模型的时候要把上述四个组件分开写,分别进行反向传播。
通过F提取的特征图使源域和目标域的同类数据特征分布越相似,从而提高测试精度。F(src)和F(tar)对G来说是类似的,这意味着参数调整是偏向于使得F(src)和F(tar)相近,这是模型优化的目标。
import torch
import torch.nn as nn
from torch.autograd import Variable
"""
生成器G
"""
class _netG(nn.Module):
def __init__(self, opt, nclasses):
super(_netG, self).__init__()
self.ndim = 2 * opt.ndf
self.ngf = opt.ngf
self.nz = opt.nz
self.gpu = opt.gpu
self.nclasses = nclasses
# 定义生成器的主要神经网络结构
self.main = nn.Sequential(
nn.ConvTranspose2d(self.nz + self.ndim + nclasses + 1, self.ngf * 8, 2, 1, 0, bias=False),
nn.BatchNorm2d(self.ngf * 8),
nn.ReLU(True),
nn.ConvTranspose2d(self.ngf * 8, self.ngf * 4, 4, 2, 1, bias=False),
nn.BatchNorm2d(self.ngf * 4),
nn.ReLU(True),
nn.ConvTranspose2d(self.ngf * 4, self.ngf * 2, 4, 2, 1, bias=False),
nn.BatchNorm2d(self.ngf * 2),
nn.ReLU(True),
nn.ConvTranspose2d(self.ngf * 2, self.ngf, 4, 2, 1, bias=False),
nn.BatchNorm2d(self.ngf),
nn.ReLU(True),
nn.ConvTranspose2d(self.ngf, 3, 4, 2, 1, bias=False),
nn.Tanh()
)
def forward(self, input):
batchSize = input.size()[0]
input = input.view(-1, self.ndim + self.nclasses + 1, 1, 1)
noise = torch.FloatTensor(batchSize, self.nz, 1, 1).normal_(0, 1)
if self.gpu >= 0:
noise = noise.cuda()
noisev = Variable(noise)
output = self.main(torch.cat((input, noisev), 1))
return output
"""
鉴别器D
"""
class _netD(nn.Module):
def __init__(self, opt, nclasses):
super(_netD, self).__init__()
self.ndf = opt.ndf
self.feature = nn.Sequential(
nn.Conv2d(3, self.ndf, 3, 1, 1),
nn.BatchNorm2d(self.ndf),
nn.LeakyReLU(0.2, inplace=True),
nn.MaxPool2d(2, 2),
nn.Conv2d(self.ndf, self.ndf * 2, 3, 1, 1),
nn.BatchNorm2d(self.ndf * 2),
nn.LeakyReLU(0.2, inplace=True),
nn.MaxPool2d(2, 2),
nn.Conv2d(self.ndf * 2, self.ndf * 4, 3, 1, 1),
nn.BatchNorm2d(self.ndf * 4),
nn.LeakyReLU(0.2, inplace=True),
nn.MaxPool2d(2, 2),
nn.Conv2d(self.ndf * 4, self.ndf * 2, 3, 1, 1),
nn.BatchNorm2d(self.ndf * 2),
nn.LeakyReLU(0.2, inplace=True),
nn.MaxPool2d(4, 4)
)
self.classifier_c = nn.Sequential(nn.Linear(self.ndf * 2, nclasses))
self.classifier_s = nn.Sequential(
nn.Linear(self.ndf * 2, 1),
nn.Sigmoid())
def forward(self, input):
output = self.feature(input)
output_s = self.classifier_s(output.view(-1, self.ndf * 2))
output_s = output_s.view(-1)
output_c = self.classifier_c(output.view(-1, self.ndf * 2))
return output_s, output_c
"""
特征提取器F
"""
class _netF(nn.Module):
def __init__(self, opt):
super(_netF, self).__init__()
self.ndf = opt.ndf
self.feature = nn.Sequential(
nn.Conv2d(3, self.ndf, 5, 1, 0),
nn.ReLU(inplace=True),
nn.MaxPool2d(2, 2),
nn.Conv2d(self.ndf, self.ndf, 5, 1, 0),
nn.ReLU(inplace=True),
nn.MaxPool2d(2, 2),
nn.Conv2d(self.ndf, self.ndf * 2, 5, 1, 0),
nn.ReLU(inplace=True)
)
def forward(self, input):
output = self.feature(input)
return output.view(-1, 2 * self.ndf)
"""
分类器C
"""
class _netC(nn.Module):
def __init__(self, opt, nclasses):
super(_netC, self).__init__()
self.ndf = opt.ndf
self.main = nn.Sequential(
nn.Linear(2 * self.ndf, 2 * self.ndf),
nn.ReLU(inplace=True),
nn.Linear(2 * self.ndf, nclasses),
)
def forward(self, input):
output = self.main(input)
return output