对抗学习常见方法代码实现篇

原理篇可参考:

对抗学习概念、基本思想、方法综述

FGM

类定义

class FGM():
    def __init__(self, model):
        self.model = model
        self.backup = {}
    def attack(self, epsilon=1., emb_name='emb.'):
        # emb_name这个参数要换成你模型中embedding的参数名
        for name, param in self.model.named_parameters():
            if param.requires_grad and emb_name in name:
                self.backup[name] = param.data.clone()
                norm = torch.norm(param.grad)
                if norm != 0 and not torch.isnan(norm):
                    r_at = epsilon * param.grad / norm
                    param.data.add_(r_at)
    def restore(self, emb_name='emb.'):
        # emb_name这个参数要换成你模型中embedding的参数名
        for name, param in self.model.named_parameters():
            if param.requires_grad and emb_name in name: 
                assert name in self.backup
                param.data = self.backup[name]
        self.backup = {}

应用

# 初始化
fgm = FGM(model)
for batch_input, batch_label in data:
    # 正常训练
    loss = model(batch_input, batch_label)
    loss.backward() # 反向传播,得到正常的grad
    # 对抗训练
    fgm.attack() # 在embedding上添加对抗扰动
    loss_adv = model(batch_input, batch_label)
    loss_adv.backward() # 反向传播,并在正常的grad基础上,累加对抗训练的梯度
    fgm.restore() # 恢复embedding参数
    # 梯度下降,更新参数
    optimizer.step()
    model.zero_grad()

PGD

类定义

class PGD():
    def __init__(self, model, emb_name, epsilon=1., alpha=0.3):
        # emb_name这个参数要换成你模型中embedding的参数名
        self.model = model
        self.emb_name = emb_name
        self.epsilon = epsilon
        self.alpha = alpha
        self.emb_backup = {}
        self.grad_backup = {}
    def attack(self, is_first_attack=False):
        for name, param in self.model.named_parameters():
            if param.requires_grad and self.emb_name in name:
                if is_first_attack:
                    self.emb_backup[name] = param.data.clone()
                norm = torch.norm(param.grad)
                if norm != 0:
                    r_at = self.alpha * param.grad / norm
                    param.data.add_(r_at)
                    param.data = self.project(name, param.data, self.epsilon)
    def restore(self):
        for name, param in self.model.named_parameters():
            if param.requires_grad and self.emb_name in name:
                assert name in self.emb_backup
                param.data = self.emb_backup[name]
        self.emb_backup = {}
    def project(self, param_name, param_data, epsilon):
        r = param_data - self.emb_backup[param_name]
        if torch.norm(r) > epsilon:
            r = epsilon * r / torch.norm(r)
        return self.emb_backup[param_name] + r
    def backup_grad(self):
        for name, param in self.model.named_parameters():
            if param.requires_grad and param.grad is not None:
                self.grad_backup[name] = param.grad.clone()
    def restore_grad(self):
        for name, param in self.model.named_parameters():
            if param.requires_grad and param.grad is not None:
                param.grad = self.grad_backup[name]

应用

pgd = PGD(model)
K = 3
for batch_input, batch_label in data:
    # 正常训练
    loss = model(batch_input, batch_label)
    loss.backward() # 反向传播,得到正常的grad
    pgd.backup_grad()
    # 对抗训练
    for t in range(K):
        pgd.attack(is_first_attack=(t==0)) # 在embedding上添加对抗扰动, first attack时备份param.data
        if t != K-1:
            model.zero_grad()
        else:
            pgd.restore_grad()
        loss_adv = model(batch_input, batch_label)
        loss_adv.backward() # 反向传播,并在正常的grad基础上,累加对抗训练的梯度
    pgd.restore() # 恢复embedding参数
    # 梯度下降,更新参数
    optimizer.step()
    model.zero_grad()

FreeLB

类定义

class FreeLB(object):
    def __init__(self, adv_K, adv_lr, adv_init_mag, adv_max_norm=0., adv_norm_type='l2', base_model='bert'):
        self.adv_K = adv_K
        self.adv_lr = adv_lr
        self.adv_max_norm = adv_max_norm
        self.adv_init_mag = adv_init_mag    # adv-training initialize with what magnitude, 即我们用多大的数值初始化delta
        self.adv_norm_type = adv_norm_type
        self.base_model = base_model
    def attack(self, model, inputs, gradient_accumulation_steps=1):
        input_ids = inputs['input_ids']
        if isinstance(model, torch.nn.DataParallel):
            embeds_init = getattr(model.module, self.base_model).embeddings.word_embeddings(input_ids)
        else:
            embeds_init = getattr(model, self.base_model).embeddings.word_embeddings(input_ids)
        if self.adv_init_mag > 0:   # 影响attack首步是基于原始梯度(delta=0),还是对抗梯度(delta!=0)
            input_mask = inputs['attention_mask'].to(embeds_init)
            input_lengths = torch.sum(input_mask, 1)
            if self.adv_norm_type == "l2":
                delta = torch.zeros_like(embeds_init).uniform_(-1, 1) * input_mask.unsqueeze(2)
                dims = input_lengths * embeds_init.size(-1)
                mag = self.adv_init_mag / torch.sqrt(dims)
                delta = (delta * mag.view(-1, 1, 1)).detach()
            elif self.adv_norm_type == "linf":
                delta = torch.zeros_like(embeds_init).uniform_(-self.adv_init_mag, self.adv_init_mag)
                delta = delta * input_mask.unsqueeze(2)
        else:
            delta = torch.zeros_like(embeds_init)  # 扰动初始化
        loss, logits = None, None
        for astep in range(self.adv_K):
            delta.requires_grad_()
            inputs['inputs_embeds'] = delta + embeds_init  # 累积一次扰动delta
            inputs['input_ids'] = None
            outputs = model(**inputs)
            loss, logits = outputs[:2]  # model outputs are always tuple in transformers (see doc)
            loss = loss.mean()  # mean() to average on multi-gpu parallel training
            loss = loss / gradient_accumulation_steps
            loss.backward()
            delta_grad = delta.grad.clone().detach()  # 备份扰动的grad
            if self.adv_norm_type == "l2":
                denorm = torch.norm(delta_grad.view(delta_grad.size(0), -1), dim=1).view(-1, 1, 1)
                denorm = torch.clamp(denorm, min=1e-8)
                delta = (delta + self.adv_lr * delta_grad / denorm).detach()
                if self.adv_max_norm > 0:
                    delta_norm = torch.norm(delta.view(delta.size(0), -1).float(), p=2, dim=1).detach()
                    exceed_mask = (delta_norm > self.adv_max_norm).to(embeds_init)
                    reweights = (self.adv_max_norm / delta_norm * exceed_mask + (1 - exceed_mask)).view(-1, 1, 1)
                    delta = (delta * reweights).detach()
            elif self.adv_norm_type == "linf":
                denorm = torch.norm(delta_grad.view(delta_grad.size(0), -1), dim=1, p=float("inf")).view(-1, 1, 1)  # p='inf',无穷范数,获取绝对值最大者
                denorm = torch.clamp(denorm, min=1e-8)  # 类似np.clip,将数值夹逼到(min, max)之间
                delta = (delta + self.adv_lr * delta_grad / denorm).detach()  # 计算该步的delta,然后累加到原delta值上(梯度上升)
                if self.adv_max_norm > 0:
                    delta = torch.clamp(delta, -self.adv_max_norm, self.adv_max_norm).detach()
            else:
                raise ValueError("Norm type {} not specified.".format(self.adv_norm_type))
            if isinstance(model, torch.nn.DataParallel):  
                embeds_init = getattr(model.module, self.base_model).embeddings.word_embeddings(input_ids)
            else:
                embeds_init = getattr(model, self.base_model).embeddings.word_embeddings(input_ids)
        return loss, logits

应用

if args.do_adv:
    inputs = {
        "input_ids": input_ids,
        "bbox": layout,
        "token_type_ids": segment_ids,
        "attention_mask": input_mask,
        "masked_lm_labels": lm_label_ids
    }
    loss, prediction_scores = freelb.attack(model, inputs)
loss.backward()
optimizer.step()
scheduler.step()
model.zero_grad()

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
由于生成对抗网络结构复杂,实现过程中需要涉及深度学习框架的基础知识,以下是使用Python和PyTorch实现GAN的基本步骤: 1. 定义生成器和判别器的网络结构 2. 定义损失函数(对比度损失) 3. 定义优化器(Adam优化器、SGD优化器等) 4. 训练模型 下面是一个简单的GAN的代码实现: ```python # 导入依赖库 import torch import torch.nn as nn import torch.optim as optim from torch.autograd import Variable import torchvision import numpy as np import matplotlib.pyplot as plt # 定义随机种子 torch.manual_seed(123) # 定义超参数 num_epochs = 100 batch_size = 16 learning_rate = 0.0002 # 加载数据集 transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor(), torchvision.transforms.Normalize((0.5,), (0.5,))]) train_dataset = torchvision.datasets.MNIST(root='.', train=True, transform=transform, download=True) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True) # 定义生成器的网络结构 class Generator(nn.Module): def __init__(self): super(Generator, self).__init__() self.fc1 = nn.Linear(100, 256) self.fc2 = nn.Linear(256, 512) self.fc3 = nn.Linear(512, 784) self.relu = nn.ReLU() def forward(self, x): x = self.fc1(x) x = self.relu(x) x = self.fc2(x) x = self.relu(x) x = self.fc3(x) x = torch.tanh(x) return x # 定义判别器的网络结构 class Discriminator(nn.Module): def __init__(self): super(Discriminator, self).__init__() self.fc1 = nn.Linear(784, 512) self.fc2 = nn.Linear(512, 256) self.fc3 = nn.Linear(256, 1) self.sigmoid = nn.Sigmoid() def forward(self, x): x = x.view(-1, 784) x = self.fc1(x) x = self.sigmoid(x) x = self.fc2(x) x = self.sigmoid(x) x = self.fc3(x) x = self.sigmoid(x) return x # 创建生成器和判别器实例 generator = Generator() discriminator = Discriminator() # 定义损失函数 criterion = nn.BCELoss() # 定义优化器 generator_optimizer = optim.Adam(generator.parameters(), lr=learning_rate) discriminator_optimizer = optim.Adam(discriminator.parameters(), lr=learning_rate) # 存储损失函数值 generator_loss = [] discriminator_loss = [] # 训练GAN模型 for epoch in range(num_epochs): for i, (images, _) in enumerate(train_loader): batch_size = images.size(0) # 获取真实图片 real_images = Variable(images.view(batch_size, -1)) real_labels = Variable(torch.ones(batch_size, 1)) # 获取生成器的噪声输入 noise = Variable(torch.randn(batch_size, 100)) # 生成假图片 fake_images = generator(noise) fake_labels = Variable(torch.zeros(batch_size, 1)) # 训练判别器 discriminator_optimizer.zero_grad() real_outputs = discriminator(real_images) real_loss = criterion(real_outputs, real_labels) fake_outputs = discriminator(fake_images) fake_loss = criterion(fake_outputs, fake_labels) d_loss = real_loss + fake_loss d_loss.backward() discriminator_optimizer.step() discriminator_loss.append(d_loss.item()) # 训练生成器 generator_optimizer.zero_grad() noise = Variable(torch.randn(batch_size, 100)) fake_images = generator(noise) outputs = discriminator(fake_images) g_loss = criterion(outputs, real_labels) g_loss.backward() generator_optimizer.step() generator_loss.append(g_loss.item()) if (epoch+1) % 10 == 0: print("Epoch [{}/{}], Step [{}/{}], Discriminator Loss: {:.4f}, Generator Loss: {:.4f}" .format(epoch+1, num_epochs, i+1, len(train_loader), d_loss.item(), g_loss.item())) # 生成100张图片并展示 noise = Variable(torch.randn(100, 100)) generated_images = generator(noise).data.numpy() generated_images = generated_images.reshape(100, 28, 28) fig, ax = plt.subplots(10, 10, figsize=(10, 10)) for i in range(10): for j in range(10): ax[i][j].imshow(generated_images[i*10+j], cmap='gray') ax[i][j].axis('off') plt.show() ``` 以上代码实现了基本的GAN,其中指定了数据集为MNIST手写数字数据集,生成器的输入噪声为100维的向量,输出为784维的图片像素值;判别器的输入是784维的图片像素值,输出为一个二元分类结果。在GAN的训练过程中,首先训练判别器来区分真实图片和假图片,再根据判别器的结果训练生成器生成更逼真的假图片,两者交替进行。最后使用生成器生成100张假图片并展示。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NLP_wendi

谢谢您的支持。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值