【调制识别】PGD攻击中参数的含义
在PGD(Projected Gradient Descent)对抗攻击中,代码如下:
# 定义PGD对抗样本生成类
class AttackPGD(nn.Module): def __init__(self, model, config):super(AttackPGD, self).__init__()self.model = model # 模型self.rand = config['random_start'] # 是否随机初始化self.step_size = config['step_size'] # 步长self.epsilon = config['epsilon'] # 扰动量self.num_steps = config['num_steps'] # 迭代次数self.attack = config['attack'] # 是否进行对抗攻击def forward(self, inputs, targets):if not self.attack: # 如果不进行对抗攻击,直接返回模型的输出和输入return self.model(inputs), inputsx = inputs.detach() # 返回一个新的张量,从当前计算图中分离出来,即返回的新的张量不会参与梯度计算if self.rand: # 如果随机初始化,则将输入的每个元素随机偏移一个范围在 [-epsilon, epsilon] 之间的值x = x + torch.zeros_like(x).uniform_(-self.epsilon, self.epsilon) x.requires_grad_(True) # 设置 x 需要计算梯度original_training = self.model.training # 保存模型当前训练状态self.model.train() # 将模型设置为训练模式for i in range(self.num_steps): # 迭代 num_steps 次# 清除之前计算的梯度if x.grad is not None: # 如果 x 的梯度不为空,则将其置零,作用是防止梯度累积x.grad.zero_()with torch.enable_grad(): # 启用梯度计算logits = self.model(x).float() # 获取模型的输出loss = F.cross_entropy(logits, targets, reduction='sum') # 计算损失函数grad = torch.autograd.grad(loss, x, create_graph=False)[0] # 计算梯度x = x.detach() + self.step_size * torch.sign(grad) # 更新 xx = torch.min(torch.max(x, inputs - self.epsilon), inputs + self.epsilon)# 投影到inputs的边界[inputs-epsilon,inputs+epsilon]# x = torch.clamp(x, 0, 1) # 将 x 的值限制在 [0, 1] 之间x.requires_grad_(True) # 设置 x 需要计算梯度self.model.train(original_training) # 恢复模型的训练状态return self.model(x), x # 返回模型在对抗样本上的输出和对抗样本本身
在PGD(Projected Gradient Descent)对抗攻击中,参数的含义及代码逻辑解析如下:
参数解析
-
step_size
(扰动步长,示例值0.5)- 作用:每次迭代中沿梯度方向更新扰动的步长。
- 影响:步长越大,单次更新幅度越大,可能更快接近扰动边界,但可能跳过最优解;步长越小,更新更精细,但需要更多迭代次数。
-
epsilon
(扰动范围,示例值2)- 作用:对抗样本与原始输入之间的最大允许扰动(L∞范数约束)。
- 影响:控制扰动的不可察觉性,值越大对抗样本越容易被察觉,但攻击成功率可能更高。
-
num_steps
(迭代次数,示例值4)- 作用:攻击的迭代次数。
- 影响:迭代次数越多,攻击越强(更接近最优扰动),但计算成本更高。
代码逻辑解析
-
初始化
- 保存模型和配置参数(如是否随机初始化、步长、扰动范围等)。
-
生成对抗样本(
forward
方法)- 是否攻击:若关闭攻击(
attack=False
),直接返回原始输入和模型输出。 - 随机初始化:若启用
random_start
,在初始输入上添加均匀分布的随机扰动(范围[-epsilon, epsilon]
),避免陷入局部最优。 - 迭代扰动:
- 计算梯度:通过损失函数(交叉熵)的反向传播获取输入梯度。
- 更新扰动:沿梯度符号方向(
torch.sign(grad)
)以步长step_size
更新输入。 - 投影约束:将扰动后的输入限制在原始输入的邻域
[inputs-epsilon, inputs+epsilon]
内(L∞约束)。 - 循环迭代:重复上述步骤
num_steps
次。
- 是否攻击:若关闭攻击(
-
恢复模型状态
- 确保攻击结束后,模型的训练模式与攻击前一致。
参数设计示例
- 示例配置:
step_size=0.5
,epsilon=2
,num_steps=4
- 合理性:总扰动潜力为
step_size * num_steps = 2
,与epsilon
相等,确保在迭代中能探索整个扰动范围,达到最优攻击效果。
- 合理性:总扰动潜力为
关键细节
- 梯度符号方向:使用
torch.sign(grad)
而非实际梯度值,类似FGSM,但通过多次迭代(PGD)增强攻击效果。 - 投影操作:通过逐像素裁剪(
torch.min
/torch.max
)确保扰动不超过epsilon
,实现L∞约束。 - 数据范围:若输入归一化到
[0,1]
,需额外添加torch.clamp(x, 0, 1)
避免无效像素值(示例代码中未启用)。
总结
PGD通过多次迭代的梯度更新生成对抗样本,三个参数共同控制攻击强度与效率:
epsilon
约束扰动不可察觉性,step_size
和num_steps
平衡搜索速度与精度。- 代码中通过梯度上升最大化损失,同时投影确保扰动合法性,是经典的L∞-PGD实现。