딥러닝 모델의 내부적 취약점을 이용하여 만든 노이즈(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