딥러닝 모델의 내부적 취약점을 이용하여 만든 노이즈(perturbation)값을 활용하여 의도적으로 오분류를 이끌어내는 입력값(적대적 예제; adversarial example)을 만들어 내는 공격
여기서 추가되는 노이즈는 인간의 눈으로는 인지할 수 없을 만큼 작게끔 제약이 걸림
class FGSM(Attacker):
def __init__(self, model, config, target=None):
super(FGSM, self).__init__(model, config)
self.target = target # 오분류할 target class가 정해진 경우
def forward(self, x, y): # x: test input, y: test input의 ground truth class label
x_adv = x.detach().clone()
if self.config['random_init']: # Random start flag
x_adv = self._random_init(x_adv)
x_adv.requires_grad = True
self.model.zero_grad()
logit = self.model(x_adv) # Projection
if self.target is None: # target class가 정해지지 않은 경우 ground truth class에 반대되는 gradient ascent를 수행
cost = -F.cross_entropy(logit, y)
else: # target class가 정해진 경우 target class로 gradient descent를 수행
cost = F.cross_entropy(logit, self.target)
if x_adv.grad is not None:
x_adv.grad.data.fill_(0)
cost.backward()
x_adv.grad.sign_() # Applying sign function
x_adv = x_adv - self.config['eps] * x_adv.grad # x_adv = x + delta
x_adv = torch.clamp(x_adv, *self.clamp) # Clamping the example
return x_adv
class PGD(Attacker):
def __init__(self, model, config, target=None):
super(PGD, self).__init__(model, config)
self.target = target # 오분류할 target class가 정해진 경우
def forward(self, x, y): # x: test input, y: test input의 ground truth class label
x_adv = x.detach().clone()
if self.config['random_init']: # Random start flag
x_adv = self._random_init(x_adv)
for step in range(self.config['attack_steps']):
x_adv.requires_grad = True
self.model.zero_grad()
logit = self.model(x_adv) # Projection
if self.target is None: # gradient ascent
loss = F.cross_entropy(logit, y, reduction='sum')
loss.backward()
grad = x_adv.grad.detach()
grad = grad.sign()
x_adv = x_adv + self.config['attack_lr'] * grad
else: # gradient descent
assert self.target.size() == y.size()
loss = F.cross_entropy(logit, self.target)
loss.backward()
grad = x_adv.grad.detach()
grad = grad.sign()
x_adv = x_adv - self.config['attack_lr'] * grad
# Projection
x_adv = x + torch.clamp(x_adv-x, min=-self.config['eps'], max=self.config['eps'])
x_adv = x_adv.detach()
x_adv = torch.clamp(x_adv, *self.clamp)
return x_adv
본 포스트는 본인의 이해를 돕기 위해 여러 참고 자료를 정리하여 쓰여졌음
https://www.youtube.com/watch?v=TfDO2guk0ug
https://rain-bow.tistory.com/entry/%EC%A0%81%EB%8C%80%EC%A0%81-%EA%B3%B5%EA%B2%A9Adversarial-Attack-FGSMPGD