对抗攻击

  1 # import torch
  2 # import torch.nn as nn
  3 # import torch.optim as optim
  4 # import torchvision.models as models
  5 # import torchvision.transforms as transforms
  6 # from PIL import Image
  7 # import numpy as np
  8 # import matplotlib.pyplot as plt
  9 
 10 # # =================配置区域=================
 11 # IMAGE_PATH = 'panda.jpg'  # 你需要准备一张名为 panda.png 的图片
 12 # EPSILON = 0.05            # 扰动强度 (0.01很小, 0.1很明显)
 13 # # =========================================
 14 
 15 # def fgsm_attack(image, epsilon, data_grad):
 16 #     # 获取梯度的符号 (sign)
 17 #     sign_data_grad = data_grad.sign()
 18 #     # 生成对抗样本:原图 + epsilon * sign(梯度)
 19 #     # 这就像是给图片加了一层专门针对AI的“噪点”
 20 #     perturbed_image = image + epsilon * sign_data_grad
 21 #     # 确保像素值还在 0-1 之间 (截断)
 22 #     perturbed_image = torch.clamp(perturbed_image, 0, 1)
 23 #     return perturbed_image
 24 
 25 # def main():
 26 #     # 1. 加载预训练模型 (ResNet18)
 27 #     print("正在加载 AI 模型...")
 28 #     model = models.resnet18(pretrained=True)
 29 #     model.eval() # 设置为评估模式
 30     
 31 #     # 2. 图片预处理
 32 #     # 把图片变成模型能读懂的 Tensor
 33 #     transform = transforms.Compose([
 34 #         transforms.Resize((224, 224)),
 35 #         transforms.ToTensor(),
 36 #     ])
 37     
 38 #     img = Image.open(IMAGE_PATH).convert("RGB")
 39 #     img_tensor = transform(img).unsqueeze(0) # 增加一个批次维度
 40     
 41 #     # 3. 原始预测
 42 #     output = model(img_tensor)
 43 #     pred = output.argmax(dim=1, keepdim=True)
 44 #     print(f"原始图片识别结果: {pred.item()} (置信度很高)")
 45 
 46 #     # 4. 开始攻击 (计算梯度)
 47 #     # 我们需要计算输入图像相对于损失函数的梯度
 48 #     img_tensor.requires_grad = True # 告诉 PyTorch:我要计算这张图的梯度
 49     
 50 #     output = model(img_tensor)
 51 #     pred = output.argmax(dim=1, keepdim=True)
 52     
 53 #     # 构造损失函数:我们要让“正确类别”的概率变小,或者让“错误类别”的概率变大
 54 #     # 这里简化为:针对当前预测结果计算梯度
 55 #     loss = nn.functional.nll_loss(nn.functional.log_softmax(output, dim=1), pred.squeeze())
 56     
 57 #     # 反向传播,计算梯度
 58 #     model.zero_grad()
 59 #     loss.backward()
 60     
 61 #     # 获取梯度
 62 #     data_grad = img_tensor.grad.data
 63     
 64 #     # 5. 生成对抗样本
 65 #     perturbed_img = fgsm_attack(img_tensor, EPSILON, data_grad)
 66     
 67 #     # 6. 对抗样本预测
 68 #     perturbed_output = model(perturbed_img)
 69 #     perturbed_pred = perturbed_output.argmax(dim=1, keepdim=True)
 70     
 71 #     print(f"对抗样本识别结果: {perturbed_pred.item()} (AI被骗了!)")
 72 
 73 #     # 7. 显示图片对比 (需要 matplotlib)
 74 #     # 把 Tensor 转回图片格式显示
 75 #     img_np = img_tensor.squeeze().permute(1, 2, 0).detach().numpy()
 76 #     perturbed_np = perturbed_img.squeeze().permute(1, 2, 0).detach().numpy()
 77     
 78 #     plt.figure(figsize=(10, 5))
 79     
 80 #     plt.subplot(1, 2, 1)
 81 #     plt.imshow(img_np)
 82 #     plt.title(f"原图: {pred.item()}")
 83 #     plt.axis('off')
 84     
 85 #     plt.subplot(1, 2, 2)
 86 #     plt.imshow(perturbed_np)
 87 #     plt.title(f"对抗图: {perturbed_pred.item()}")
 88 #     plt.axis('off')
 89     
 90 #     plt.show()
 91 
 92 # if __name__ == "__main__":
 93 #     # 注意:运行此脚本需要安装 torch, torchvision, matplotlib, pillow
 94 #     # pip install torch torchvision matplotlib pillow
 95 #     main()
 96 
 97 import torch
 98 import torch.nn as nn
 99 import torchvision.models as models
100 import torchvision.transforms as transforms
101 from PIL import Image
102 import matplotlib.pyplot as plt
103 import os
104 
105 # =================配置区域=================
106 # 请确保图片文件和权重文件都在当前脚本同一目录下,或者填写绝对路径
107 IMAGE_PATH = 'panda.jpg'       
108 WEIGHTS_PATH = 'resnet18-f37072fd.pth'
109 EPSILON = 0.05                 # 扰动强度 (如果攻击失败,可尝试改为 0.1)
110 # =========================================
111 
112 def fgsm_attack(image, epsilon, data_grad):
113     """
114     FGSM 攻击核心函数
115     image: 原始图像 Tensor
116     epsilon: 扰动大小
117     data_grad: 图像相对于损失的梯度
118     """
119     # 获取梯度的符号 (sign)
120     sign_data_grad = data_grad.sign()
121     # 生成对抗样本:原图 + epsilon * sign(梯度)
122     perturbed_image = image + epsilon * sign_data_grad
123     # 确保像素值还在 0-1 之间 (截断)
124     perturbed_image = torch.clamp(perturbed_image, 0, 1)
125     return perturbed_image
126 
127 def main():
128     # 1. 加载预训练模型 (ResNet18)
129     print("正在加载 AI 模型...")
130     
131     # 创建模型架构 (不自动下载权重)
132     model = models.resnet18(pretrained=False)
133     
134     # 检查权重文件是否存在
135     if not os.path.exists(WEIGHTS_PATH):
136         print(f"❌ 错误:找不到权重文件 '{WEIGHTS_PATH}'")
137         print(f"   当前路径: {os.getcwd()}")
138         print(f"   请确保文件存在,或修改 WEIGHTS_PATH 变量。")
139         return
140 
141     # 加载本地权重
142     # map_location='cpu' 保证在没有 GPU 的电脑上也能运行
143     try:
144         state_dict = torch.load(WEIGHTS_PATH, map_location=torch.device('cpu'))
145         model.load_state_dict(state_dict)
146         print("✅ 模型权重加载成功!")
147     except Exception as e:
148         print(f"❌ 加载权重失败: {e}")
149         return
150     
151     model.eval() # 设置为评估模式 (固定参数,不更新权重)
152     
153     # 2. 图片预处理
154     transform = transforms.Compose([
155         transforms.Resize((224, 224)), # ResNet 需要 224x224
156         transforms.ToTensor(),         # 转为 Tensor 并归一化到 0-1
157     ])
158     
159     # 检查图片是否存在
160     if not os.path.exists(IMAGE_PATH):
161         print(f"❌ 错误:找不到图片 '{IMAGE_PATH}'")
162         print(f"   当前路径: {os.getcwd()}")
163         return
164 
165     try:
166         img = Image.open(IMAGE_PATH).convert("RGB")
167         img_tensor = transform(img).unsqueeze(0) # 增加批次维度 [1, 3, 224, 224]
168     except Exception as e:
169         print(f"❌ 读取图片失败: {e}")
170         return
171     
172     # 3. 原始预测 (不使用梯度)
173     with torch.no_grad():
174         output = model(img_tensor)
175         pred = output.argmax(dim=1, keepdim=True)
176     
177     print(f"🐼 原始图片识别结果: 类别ID {pred.item()}")
178 
179     # 4. 开始攻击 (计算梯度)
180     # 关键:设置 requires_grad=True,告诉 PyTorch 我们需要计算输入的梯度
181     img_tensor.requires_grad = True
182     
183     output = model(img_tensor)
184     pred = output.argmax(dim=1, keepdim=True)
185     
186     # 【核心修复点】构造损失函数的目标标签
187     # 错误写法: pred.squeeze() -> 会变成标量,导致 batch_size 不匹配报错
188     # 正确写法: pred.view(-1) -> 保持为 [batch_size] 的一维张量
189     target = pred.view(-1)
190     
191     # 计算损失 (负对数似然损失)
192     loss = nn.functional.nll_loss(nn.functional.log_softmax(output, dim=1), target)
193     
194     # 反向传播
195     model.zero_grad() # 清空模型参数的梯度
196     loss.backward()   # 计算输入图像 (img_tensor) 的梯度
197     
198     # 获取梯度数据
199     data_grad = img_tensor.grad.data
200     
201     # 5. 生成对抗样本
202     perturbed_img = fgsm_attack(img_tensor, EPSILON, data_grad)
203     
204     # 6. 对抗样本预测
205     with torch.no_grad():
206         perturbed_output = model(perturbed_img)
207         perturbed_pred = perturbed_output.argmax(dim=1, keepdim=True)
208     
209     print(f"🤖 对抗样本识别结果: 类别ID {perturbed_pred.item()}")
210     
211     if pred.item() != perturbed_pred.item():
212         print("🎉 攻击成功!AI 被欺骗了,分类发生了改变。")
213     else:
214         print("⚠️ 攻击未生效,AI 依然识别为同一类。")
215         print("💡 建议:尝试增大 EPSILON (例如改为 0.1 或 0.2) 再运行一次。")
216 
217     # 7. 显示图片对比
218     # 将 Tensor 转回 numpy 数组用于绘图 [C, H, W] -> [H, W, C]
219     img_np = img_tensor.squeeze().permute(1, 2, 0).detach().numpy()
220     perturbed_np = perturbed_img.squeeze().permute(1, 2, 0).detach().numpy()
221     
222     plt.figure(figsize=(12, 6))
223     
224     plt.subplot(1, 2, 1)
225     plt.imshow(img_np)
226     plt.title(f"Original image (ID: {pred.item()})", fontsize=14)
227     plt.axis('off')
228     
229     plt.subplot(1, 2, 2)
230     plt.imshow(perturbed_np)
231     plt.title(f"Adversarial example (ID: {perturbed_pred.item()})", fontsize=14, color='red')
232     plt.axis('off')
233     
234     plt.tight_layout()
235     plt.show()
236 
237 if __name__ == "__main__":
238     main()

 

image

 

image

 

image

 

posted @ 2026-03-11 23:43  左耳听风  阅读(17)  评论(0)    收藏  举报