之前一起打了CISCN 2023初赛,因为周末睡懒觉晚到了两三个小时导致做这个题的时间不充裕了,最后在比赛结束后半个小时出了,主要原因还是因为没有玩过Yolo,当时记得这个题的解题数是很低的(或者好像是零解的?反正截止至我开始写这篇WP的时间[2023-08-07 18:30],我在网上并没有搜到WP),本题在选手机器学习底子坚实的前提下主要考验选手入手新的模型和领域的速度以及将对抗样本训练应用上来的速度。

CISCN2023初赛 AdvDetPatch WriteUp

题干

本次竞赛要求选手针对目标检测模型进行对抗攻击。题目提供了1张样例图片(/home/adv/images/stop.png),要求选手在给定的图片中添加一些对抗性补丁,上传带有补丁的图片和代表补丁位置的mask,要求:

  1. 使得模型无法正确地检测出图片中的所有物体
  2. 并且补丁的面积不能超过图像面积的5%(例如对于640 * 640的图像,patch面积不超过20480 ),就能得到flag。
  3. 图像的尺寸、命名、文件格式(.png)和原始图像保持一致。
  4. mask图像只存在0或255两种像素值,其中255代表patch像素占据的位置。文件名保存为mask.png,mask的白色部分代表补丁部分,黑色是未修改部分。

以下为需要选手攻击的图片

解题过程

初步验证

由于我是第一次玩Yolo,先自己跑一下看看能否得到正确的输出,通过在fool_me.pyprint(pred)得到如下输出

1
2
3
4
5
6
7
tensor([[3.38821e+02, 1.65334e+02, 5.22038e+02, 4.83467e+02, 9.34208e-01, 1.10000e+01],
[4.66704e+02, 2.09673e+02, 6.39832e+02, 5.45919e+02, 9.33491e-01, 1.10000e+01],
[2.00159e+02, 2.04222e+02, 3.79344e+02, 5.11239e+02, 9.25105e-01, 1.10000e+01],
[7.07512e+01, 2.37424e+02, 2.40440e+02, 5.33154e+02, 9.14894e-01, 1.10000e+01],
[7.70359e-01, 2.53848e+02, 1.03618e+02, 5.43242e+02, 9.01769e-01, 1.10000e+01],
[1.41904e+01, 3.93319e+02, 4.42650e+01, 4.40926e+02, 6.95912e-01, 2.40000e+01],
[1.30602e+01, 3.96193e+02, 4.70077e+01, 4.42493e+02, 4.05268e-01, 1.10000e+01]])

对此我们需要确定此Tensor中每个Dimension的含义

我们查阅关于Yolo的资料可知,每个向量中前四个元素代表了Bounding Box的范围,第五维代表了置信度,第六维代表了Class(Label)

据此我们可以写出可视化脚本来可视化模型的输出结果,可视化如下

可见根据出题人给出的代码跑出的输出可以正确地被用于在原始图像上标记出所有Stop Sign的范围

接下来我们需要做的就是攻击这个模型,使得没有物体被检测到了

查找资料

根据题目标题,我们正在做的是Adversarial Patch,对抗性补丁,按照我的理解,这与之前看过的在一个图像上贴一个不透明的同时只覆盖部分原始图像的技术是一个东西

目前这项技术比较流行的是在现实世界中的Adv Patch,如下图,挂有Adv Patch的人无法被模型识别出是人类

根据中英文关键词(Yolo Adversarial Attack; Yolo Adversarial Patch; Yolo 对抗补丁; Yolo 对抗攻击),查找到了一篇题为Sparse Adversarial Attack to Object Detection预印本文章,这篇文章正好提供了我们需要的信息,我们只需要看文章中的两张图就知道如何攻击了

上图介绍了攻击的流程,比如确定Mask的形状,通过反向传播获取增加到图像上的扰动等。其中他有两个Preprocess我没太明白,但这不影响我们理解攻击的方式

上图展示了被攻击成功的图像,请注意图像中类似杂讯的十字,这可能需要对图像进行放大才能清晰地看见。我们可以由此认识到:其实Adv Patch就是在最基础的对抗攻击中限制了可以攻击的图像面积,并且被攻击的像素是替换而不是叠加

Exp

首先需要获取一个Mask,在调试题目的代码后,我发现题目对修改像素的数量其实比较宽松,所以我们采用米字形的Mask以更好地对需要攻击的部分进行扰动,这个Mask可以通过PS绘图后使用Python将灰色像素给去掉(因为题目限制了Mask的像素必须非0即255,所以需要去掉灰色的像素)

有了这个Mask我们就可以开始攻击了,现在移步fool_me.py进行代码修改

首先我们使用随机噪声填充这个Mask

然后我们需要修改原本验证图像是否攻击成功的代码,其实就是对输入图像求个梯度,然后累加这个梯度,直到攻击成功

至此,通常来说题目应该已经被Mark为Solved了,但我们得到的输出并不这么认为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
------------------Initializing detector-------------------
Fusing layers...
Model Summary: 367 layers, 46533693 parameters, 0 gradients
libpng warning: iCCP: known incorrect sRGB profile
---------------------Start detecting----------------------
tensor([[2.31950e+02, 1.63722e+02, 6.37381e+02, 5.43151e+02, 5.21886e-01, 1.10000e+01],
[4.71159e+02, 2.11284e+02, 6.39552e+02, 5.44185e+02, 4.40381e-01, 1.10000e+01],
[1.06963e+00, 2.29302e+02, 2.94475e+02, 5.46162e+02, 4.39895e-01, 1.10000e+01]], grad_fn=<IndexBackward0>)
True True
C:\Users\Kyriota\AppData\Local\Programs\Python\Python39\lib\site-packages\torch\autograd\__init__.py:276: UserWarning: Error detected in struct torch::autograd::CopySlices. Traceback of forward call that caused the error:
File "C:\Users\Kyriota\AppData\Local\Programs\Python\Python39\lib\runpy.py", line 197, in _run_module_as_main
return _run_code(code, main_globals, None,
File "C:\Users\Kyriota\AppData\Local\Programs\Python\Python39\lib\runpy.py", line 87, in _run_code
exec(code, run_globals)
File "c:\Users\Kyriota\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy\__main__.py", line 39, in <module>
cli.main()
File "c:\Users\Kyriota\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy/..\debugpy\server\cli.py", line 430, in main
run()
File "c:\Users\Kyriota\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy/..\debugpy\server\cli.py", line 284, in run_file
runpy.run_path(target, run_name="__main__")
File "c:\Users\Kyriota\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 321, in run_path
return _run_module_code(code, init_globals, run_name,
File "c:\Users\Kyriota\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 135, in _run_module_code
_run_code(code, mod_globals, init_globals,
File "c:\Users\Kyriota\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 124, in _run_code
exec(code, run_globals)
File "C:\Users\Kyriota\Desktop\CISCN\ciscn2023_AdvDetPatch\adv\exp.py", line 181, in <module>
main(opt)
File "C:\Users\Kyriota\Desktop\CISCN\ciscn2023_AdvDetPatch\adv\exp.py", line 176, in main
run(**vars(opt))
File "C:\Users\Kyriota\Desktop\CISCN\ciscn2023_AdvDetPatch\adv\exp.py", line 117, in run
pred = non_max_suppression(pred, 0.2, iou_thres, classes, agnostic_nms, max_det=max_det) # make conf_thres smaller
File "C:\Users\Kyriota\Desktop\CISCN\ciscn2023_AdvDetPatch\adv\utils\general.py", line 697, in non_max_suppression
x[:, 5:] *= x[:, 4:5] # conf = obj_conf * cls_conf
(Triggered internally at C:\actions-runner\_work\pytorch\pytorch\builder\windows\pytorch\torch\csrc\autograd\python_anomaly_mode.cpp:104.)
return Variable._execution_engine.run_backward( # Calls into the C++ engine to run the backward pass
[*]Please remove the object!

在这份报错中,我们关注的是adv\utils\general.py的报错

x[:, 5:] = x[:, 4:5] # conf = obj_conf cls_conf

见到了*=操作符以及[]直接对元素的操作,都表明了这是一个inplace操作,inplace操作是PyTorch为了节省内存,提高运行速度而直接在原始变量上进行修改和计算的一种操作方式,详情可以自行Google:Inplace PyTorch,这种操作方式有一个很大的问题就是无法进行AutoGrad自动求梯度,而此处我们恰好需要进行AutoGrad,所以我们需要将此处的Inplace操作修改为非Inplace的操作,具体需要修改的是adv\utils\general.pyLine 696-739,需要将原本的x进行clone(),在clone出的new_x上进行操作,并避免对new_xx的任意一方进行inplace操作,new_x[:, 5:] = x[:, 5:] * x[:, 4:5]就顺利地避免了这件事

在修改了adv\utils\general.py中的inplace操作后,我们的exp就可以正常运行啦

至此,本题被Mark为Solved

下面的两张图分别为Patch以及被攻击后的图像的可视化检测结果

可以看见被攻击后的图像中检测到的物体置信度都在0.2以下,已经满足题目要求

Comments

⬆︎TOP