深度对抗神经网络(DANN)笔记


一 总体介绍

DANN是一种迁移学习方法,是对抗迁移学习方法的代表方法。基本结构由特征提取层f,分类器部分c和对抗部分d组成,其中f和c其实就是一个标准的分类模型,通过GAN(生成对抗网络)得到迁移对抗模型的灵感。但此时生成的不是假样本,而是假特征,一个足以让目标域和源域区分不开的假特征。

而领域判别器D其实是个标准的二分类分类器,0是源域,1是目标域。它本身的目标是区分源域和目标域,而我们想要的结果是使判别器越来越分不出数据特征来自源域还是目标域,感觉起来这很矛盾。但其实我们引入一个梯度反转层就可以完美避免这个问题。

引入梯度反转层(GRL),分类器c和判别器d朝着优化分类器效果的方向反向传播优化梯度。有了梯度反转层,简单的说就是判别器d反向传播时,梯度更新前引入了一个“ - ”。这样就可以同时满足判别器和我们需求的一致性。

下面是DANN的基本网络图。


二 UDTL代码库中的DANN网络

对抗网络部分代码。

from torch import nn
import numpy as np


def calc_coeff(iter_num, high=1.0, low=0.0, alpha=10.0, max_iter=10000.0):
    return np.float(2.0 * (high - low) / (1.0 + np.exp(-alpha * iter_num / max_iter)) - (high - low) + low)
### 如果self.trade_off_adversarial == 'Step',则调用此函数得到coeff的值,不然self.trade_off_adversarial == 'Cons',则coeff是个固定的值
###coeff——————coeff = self.lam_adversarial 其中trade_off_adverial充当域分类器部分的学习率随着迭代过程会逐渐递减——————学习率

def grl_hook(coeff):#补充连接,因为是引入块,需要连接到model层的梯度:  grad.clone()
    def fun1(grad):
        return -coeff * grad.clone()
    return fun1

class AdversarialNet(nn.Module):
    def __init__(self, in_feature, hidden_size,max_iter=10000.0, trade_off_adversarial='Step', lam_adversarial=1.0):
        super(AdversarialNet, self).__init__()
        self.ad_layer1 = nn.Sequential(
            nn.Linear(in_feature, hidden_size),
            nn.ReLU(inplace=True),
            nn.Dropout(),
        )
        self.ad_layer2 = nn.Sequential(
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(inplace=True),
            nn.Dropout(),
        )
        self.ad_layer3 = nn.Linear(hidden_size, 1)
        self.sigmoid = nn.Sigmoid()
        # parameters
        self.iter_num = 0
        self.alpha = 10
        self.low = 0.0
        self.high = 1.0
        self.max_iter = max_iter
        self.trade_off_adversarial = trade_off_adversarial
        self.lam_adversarial = lam_adversarial
        self.__in_features = 1

    def forward(self, x):
        if self.training:
            self.iter_num += 1
        if self.trade_off_adversarial == 'Cons':
            coeff = self.lam_adversarial
        elif self.trade_off_adversarial == 'Step':
            coeff = calc_coeff(self.iter_num, self.high, self.low, self.alpha, self.max_iter) #学习率
        else:
            raise Exception("loss not implement")
        x = x * 1.0
        x.register_hook(grl_hook(coeff))#register_hook的作用:即对x求导时,对x的导数进行操作,并且register_hook的参数只能以函数的形式传过去,
        #grl_hook(coeff)则返回的是梯度  *  “——”梯度反转层作用
        ##register_hook的作用:对x求导,并将梯度保存下来,这样可以作为参数通过优化器通过反向传播过程进行更新优化,实现DANN所需效果
        x = self.ad_layer1(x)
        x = self.ad_layer2(x)
        y = self.ad_layer3(x)
        y = self.sigmoid(y)
        return y

    def output_num(self):
        return self.__in_features#输出通道是1 代表域判别值0:源域 1:目标域

以上是赵志斌老师UDTL代码中的对抗网络部分的介绍,用于故障诊断数据。

这里要声明的是对于AdversarialNet网络而言,一维的数据和二维数据都可以拿来直接使用,实质上它仅仅是多出了一个二分类判别器和一个梯度反转层而已。
ZhaoZhibin/UDTL: Source codes for the paper "Applications of Unsupervised Deep Transfer Learning to Intelligent Fault Diagnosis: A Survey and Comparative Study" published in TIM (github.com)https://github.com/ZhaoZhibin/UDTL


 三 网络的其他写法

这里博主还找到了另外一种的对抗网络写法。

import torch.nn as nn
from functions import ReverseLayerF#从functions中导入梯度反转层这一类


class CNNModel(nn.Module):

    def __init__(self):
        super(CNNModel, self).__init__()
        self.feature = nn.Sequential()
        self.feature.add_module('f_conv1', nn.Conv2d(3, 64, kernel_size=5))#这里是因为数据是mnist数据所以输入通道为3
        self.feature.add_module('f_bn1', nn.BatchNorm2d(64))
        self.feature.add_module('f_pool1', nn.MaxPool2d(2))
        self.feature.add_module('f_relu1', nn.ReLU(True))
        self.feature.add_module('f_conv2', nn.Conv2d(64, 50, kernel_size=5))
        self.feature.add_module('f_bn2', nn.BatchNorm2d(50))
        self.feature.add_module('f_drop1', nn.Dropout2d())
        self.feature.add_module('f_pool2', nn.MaxPool2d(2))
        self.feature.add_module('f_relu2', nn.ReLU(True))
#上面是backbone部分也是网络的特征提取部分
        self.class_classifier = nn.Sequential()
        self.class_classifier.add_module('c_fc1', nn.Linear(50 * 4 * 4, 100))
        self.class_classifier.add_module('c_bn1', nn.BatchNorm1d(100))
        self.class_classifier.add_module('c_relu1', nn.ReLU(True))
        self.class_classifier.add_module('c_drop1', nn.Dropout())
        self.class_classifier.add_module('c_fc2', nn.Linear(100, 100))
        self.class_classifier.add_module('c_bn2', nn.BatchNorm1d(100))
        self.class_classifier.add_module('c_relu2', nn.ReLU(True))
        self.class_classifier.add_module('c_fc3', nn.Linear(100, 10))
        self.class_classifier.add_module('c_softmax', nn.LogSoftmax(dim=1))
#上面是源域的分类器部分,只要是要对源域数据进行有效的分类
        self.domain_classifier = nn.Sequential()
        self.domain_classifier.add_module('d_fc1', nn.Linear(50 * 4 * 4, 100))
        self.domain_classifier.add_module('d_bn1', nn.BatchNorm1d(100))
        self.domain_classifier.add_module('d_relu1', nn.ReLU(True))
        self.domain_classifier.add_module('d_fc2', nn.Linear(100, 2))
        self.domain_classifier.add_module('d_softmax', nn.LogSoftmax(dim=1))
#上面是领域判别器部分,主要任务是要区分出源域和目标域
    def forward(self, input_data, alpha):
        input_data = input_data.expand(input_data.data.shape[0], 3, 28, 28)
        feature = self.feature(input_data)
        feature = feature.view(-1, 50 * 4 * 4)
        reverse_feature = ReverseLayerF.apply(feature, alpha)
#前向网络中注意到,reverse_feature是通过ReverseLayerF.apply将feature进行反向的梯度计算。
        class_output = self.class_classifier(feature)
        domain_output = self.domain_classifier(reverse_feature)
#并将处理过的reverse_feature特征给domain_classifer进行域判别。
        return class_output, domain_output

对应的ReverseLayerF部分代码: 

from torch.autograd import Function


class ReverseLayerF(Function):

    @staticmethod
    def forward(ctx, x, alpha):
        ctx.alpha = alpha

        return x.view_as(x)

    @staticmethod
    def backward(ctx, grad_output):
        output = grad_output.neg() * ctx.alpha
####grad_output.neg()梯度取负操作,反向内容的核心。

        return output, None

代码地址:https://github.com/fungtion/DANN_py3

通过介绍以上两种不同写法的对抗网络模型,相信你也可以看到对抗网络的核心其实很简单。

仅仅是多出了一个领域判别器和一个梯度反转层。

但采用对抗网络作为迁移网络方法又能很好的解决很多域迁移领域的问题,特别是在域之间的差异较大的情况时,往往要比以MMD(最大均值差异)为代表的度量学习方法效果要好。

以上是我学习过程中对DANN进行的一些总结工作,欢迎评论区讨论交流。

### 使用对抗训练减少源域和目标域间数据分布差异 #### 对抗训练原理 对抗训练是一种有效的方法,用于减小不同数据集或域之间的分布差异。这种方法的核心在于引入一个判别器(Discriminator),该判别器试图区分来自源域的数据和来自目标域的数据。与此同时,另一个组件——生成器(Generator)则尝试欺骗这个判别器,使其无法分辨数据来源于哪个域。 具体来说,在领域适应场景下,模型被设计成不仅能够完成特定的任务(如分类或检测),还能使源域和目标域的特征表示尽可能相似。这种做法有助于提高模型在未见过的目标域上的泛化能力[^1]。 #### 技术实现细节 为了实现这一目的,通常会构建一个多任务学习框架: - **任务网络**:负责处理原始任务,比如图像分类、对象检测等; - **领域鉴别器**:用来判断输入样本属于源域还是目标域; - **共享编码器**:提取可用于两个域的共同特征。 整个系统的优化过程中有两个相互对立的目标: - 让任务网络更好地完成指定任务; - 同时让领域鉴别器难以辨别出给定特征向量的具体来源是源域还是目标域。 当这两个目标同时得到满足时,就意味着已经找到了一种映射方式使得源域和目标域能够很好地对齐,进而减少了两者间的分布差距[^2]。 ```python import torch.nn as nn class DomainAdaptationModel(nn.Module): def __init__(self, num_classes): super(DomainAdaptationModel, self).__init__() # Shared Encoder self.shared_encoder = nn.Sequential( nn.Conv2d(in_channels=3, out_channels=64, kernel_size=5), nn.ReLU(), nn.MaxPool2d(kernel_size=2), nn.Conv2d(in_channels=64, out_channels=128, kernel_size=5), nn.ReLU(), nn.MaxPool2d(kernel_size=2) ) # Task-specific Network (Classifier) self.task_network = nn.Linear(128 * 5 * 5, num_classes) # Domain Discriminator self.domain_discriminator = nn.Sequential( nn.Linear(128 * 5 * 5, 100), nn.ReLU(), nn.Linear(100, 2) # Binary classification between source and target domain ) def forward(self, x, alpha=None): features = self.shared_encoder(x).view(-1, 128*5*5) class_output = F.softmax(self.task_network(features), dim=1) if alpha is not None: reverse_features = ReverseLayerF.apply(features, alpha) domain_output = F.log_softmax(self.domain_discriminator(reverse_features), dim=1) return class_output, domain_output return class_output ``` 在这个例子中,`ReverseLayerF` 是一个特殊的层,它可以在反向传播期间反转梯度的方向,这样就可以确保即使前向传递时不考虑领域信息,但在更新参数时仍然鼓励混淆领域边界的行为。
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值