next
This commit is contained in:
@@ -64,6 +64,59 @@ class TestModel(BaseModel):
|
||||
"""Run forward pass."""
|
||||
self.fake = self.netG(self.real) # G(real)
|
||||
|
||||
def forward_noattack(self):
|
||||
"""Run forward pass."""
|
||||
self.fake_noattack = self.netG(self.real) # G(real)
|
||||
|
||||
def attack(self):
|
||||
image = self.real
|
||||
# Attack
|
||||
pgd_attack = attacks.LinfPGDAttack(model=self.netG)
|
||||
black = np.zeros((1, 3, image.size(2), image.size(3)))
|
||||
black = torch.FloatTensor(black).cuda()
|
||||
input_adv, perturb = pgd_attack.perturb(image, black)
|
||||
|
||||
return input_adv, perturb
|
||||
|
||||
def forward_attack(self, perturb):
|
||||
self.real = torch.clamp(self.real + perturb, min=-1, max=1)
|
||||
self.fake = self.netG(self.real) # G(real)
|
||||
|
||||
def compute_errors(self):
|
||||
generated = self.fake
|
||||
generated_noattack = self.fake_noattack
|
||||
l1 = F.l1_loss(generated, generated_noattack)
|
||||
l2 = F.mse_loss(generated, generated_noattack)
|
||||
l0 = (generated - generated_noattack).norm(0)
|
||||
d = (generated - generated_noattack).norm(float('-inf'))
|
||||
return l1, l2, l0, d
|
||||
|
||||
def attack(self, label, inst, image=None):
|
||||
# Encode Inputs
|
||||
image = Variable(image) if image is not None else None
|
||||
input_label, inst_map, real_image, _ = self.encode_input(Variable(label), Variable(inst), image, infer=True)
|
||||
|
||||
# Fake Generation
|
||||
if self.use_features:
|
||||
if self.opt.use_encoded_image:
|
||||
# encode the real image to get feature map
|
||||
feat_map = self.netE.forward(real_image, inst_map)
|
||||
else:
|
||||
# sample clusters from precomputed features
|
||||
feat_map = self.sample_features(inst_map)
|
||||
input_concat = torch.cat((input_label, feat_map), dim=1)
|
||||
else:
|
||||
input_concat = input_label
|
||||
|
||||
# Attack
|
||||
pgd_attack = attacks.LinfPGDAttack(model=self.netG)
|
||||
black = np.zeros((1, 3, input_concat.size(2), input_concat.size(3)))
|
||||
black = torch.FloatTensor(black).cuda()
|
||||
# print(input_concat.size())
|
||||
input_adv, perturb = pgd_attack.perturb(input_concat, black)
|
||||
|
||||
return input_adv, perturb
|
||||
|
||||
def optimize_parameters(self):
|
||||
"""No optimization for test model."""
|
||||
pass
|
||||
|
||||
@@ -15,7 +15,7 @@ class TestOptions(BaseOptions):
|
||||
parser.add_argument('--phase', type=str, default='test', help='train, val, test, etc')
|
||||
# Dropout and Batchnorm has different behavioir during training and test.
|
||||
parser.add_argument('--eval', action='store_true', help='use eval mode during test time.')
|
||||
parser.add_argument('--num_test', type=int, default=50, help='how many test images to run')
|
||||
parser.add_argument('--num_test', type=int, default=100, help='how many test images to run')
|
||||
# rewrite devalue values
|
||||
parser.set_defaults(model='test')
|
||||
# To avoid cropping, the load_size should be the same as crop_size
|
||||
|
||||
+21
-1
@@ -56,14 +56,34 @@ if __name__ == '__main__':
|
||||
# For [CycleGAN]: It should not affect CycleGAN as CycleGAN uses instancenorm without dropout.
|
||||
if opt.eval:
|
||||
model.eval()
|
||||
|
||||
# Initialize Metrics
|
||||
l1_error, l2_error, min_dist, l0_error, perceptual_error = 0.0, 0.0, 0.0, 0.0, 0.0
|
||||
n_samples = 0
|
||||
|
||||
for i, data in enumerate(dataset):
|
||||
if i >= opt.num_test: # only apply our model to opt.num_test images.
|
||||
break
|
||||
model.set_input(data) # unpack data from data loader
|
||||
model.test() # run inference
|
||||
model.forward_noattack()
|
||||
input_adv, perturb = model.attack()
|
||||
with torch.no_grad():
|
||||
model.forward_attack(perturb)
|
||||
model.compute_visuals()
|
||||
|
||||
# Compute metrics
|
||||
l1, l2, l0, d = model.compute_errors()
|
||||
l1_error += l1
|
||||
l2_error += l2
|
||||
l0_error += l0
|
||||
min_dist += d
|
||||
n_samples += 1
|
||||
# model.test() # run inference
|
||||
visuals = model.get_current_visuals() # get image results
|
||||
img_path = model.get_image_paths() # get image paths
|
||||
if i % 5 == 0: # save images to an HTML file
|
||||
print('processing (%04d)-th image... %s' % (i, img_path))
|
||||
save_images(webpage, visuals, img_path, aspect_ratio=opt.aspect_ratio, width=opt.display_winsize)
|
||||
webpage.save() # save the HTML
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import copy
|
||||
import numpy as np
|
||||
from collections import Iterable
|
||||
from scipy.stats import truncnorm
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
class LinfPGDAttack(object):
|
||||
def __init__(self, model=None, epsilon=0.05, k=1, a=0.05):
|
||||
self.model = model
|
||||
self.epsilon = epsilon
|
||||
self.k = k
|
||||
self.a = a
|
||||
self.loss_fn = nn.MSELoss()
|
||||
|
||||
def perturb(self, X_nat, y):
|
||||
"""
|
||||
Given examples (X_nat, y), returns adversarial
|
||||
examples within epsilon of X_nat in l_infinity norm.
|
||||
"""
|
||||
X = X_nat.clone().detach_()
|
||||
|
||||
for i in range(self.k):
|
||||
print('test', i)
|
||||
X.requires_grad = True
|
||||
output = self.model(X)
|
||||
|
||||
self.model.zero_grad()
|
||||
loss = -self.loss_fn(output, y)
|
||||
loss.backward()
|
||||
grad = X.grad
|
||||
|
||||
X_adv = X + self.a * grad.sign()
|
||||
|
||||
eta = torch.clamp(X_adv - X_nat, min=-self.epsilon, max=self.epsilon)
|
||||
X = torch.clamp(X_nat + eta, min=-1, max=1).detach_()
|
||||
eta = None
|
||||
X_adv = None
|
||||
|
||||
return X, X - X_nat
|
||||
|
||||
def clip_tensor(X, Y, Z):
|
||||
# Clip X with Y min and Z max
|
||||
X_np = X.data.cpu().numpy()
|
||||
Y_np = Y.data.cpu().numpy()
|
||||
Z_np = Z.data.cpu().numpy()
|
||||
X_clipped = np.clip(X_np, Y_np, Z_np)
|
||||
X_res = torch.FloatTensor(X_clipped)
|
||||
return X_res
|
||||
Reference in New Issue
Block a user