分类 深度学习 下的文章

这两天在看PyTorch DistributedDataParallel(DDP)相关文章,发现有个系列写的还不错。

虽然讲的是torch.distributed.launch(快被torchrun替代),但是整个思路应该还是有参考意义的。
看的过程中遇到一些问题,顺便补几个知识点。

补充SyncBN里的一个问题:2.1.5 eval部分,在torch 1.13版本里,只要满足eval模式或track_running_stats=True,就会使用统计量(running_mean, running_var)进行计算了。源码如下:

# torch.nn.modules.batchnorm
return F.batch_norm(
            input,
            # If buffers are not to be tracked, ensure that they won't be updated
            self.running_mean
            if not self.training or self.track_running_stats
            else None,
            self.running_var if not self.training or self.track_running_stats else None,
            self.weight,
            self.bias,
            bn_training,
            exponential_average_factor,
            self.eps,)

pix2seq implement by Pytorch

pix2seq - framework

source code : moein-shariatnia/Pix2Seq

paper : http://arxiv.org/abs/2109.10852

这次解析的源码是非官方实现的Pix2Seq,项目地址如上。教程直接看作者的Readme或者Object Detection w/ Transformers Pix2Seq in Pytorch | Towards AI,总体还是比较详细的。

模型即训练源码基本没什么问题,不过推荐先看完原始论文,不然可能在一些细节方面卡住。代码问题主要出现在测试文件中,问题如下。

Issue 1

2023.8.30

Tokenizer类的max_len参数用于限制单张图片的Obejct个数

labels = labels.astype('int')[:self.max_len]

bboxes = self.quantize(bboxes)[:self.max_len]

get_loaders中的collate_fn,把max_len作为输入序列的最大长度,这两处地方出现了矛盾(因为一个Object对应5个token,[x1, y1, x2, y2, class] )

if max_len: # [B,max_seq_len,dim] -> [B,max_len,dim]
        pad = torch.ones(seq_batch.size(0), max_len -
                         seq_batch.size(1)).fill_(pad_idx).long()
        seq_batch = torch.cat([seq_batch, pad], dim=1)

Issue 2

2023.8.31

test.py中的postprocess函数存在问题,没有考虑model未检出object的情况。导致会将一个空序列(即\<EOS\>\<BOS\>)输入tokenizer的decoder方法,从而引发错误

 for i, EOS_idx in enumerate(EOS_idxs.tolist()):
        if EOS_idx == 0:
            all_bboxes.append(None)
            all_labels.append(None)
            all_confs.append(None)
            continue

修正如下,考虑空序列的情况

 for i, EOS_idx in enumerate(EOS_idxs.tolist()):
        if EOS_idx == 0 or EOS_idx ==1: # invalid idx which EOS_idx = 0 or the model detect nothing when EOS_idx = 1 
            all_bboxes.append(None)
            all_labels.append(None)
            all_confs.append(None)
            continue

Issue 3

2023.8.31

test.py 中的第125行,发生类型判断错误。这里是剔除没有检测出物体的图片,但是此时lambda表达式中的x是array类型,所以isinstance函数参数不应该是list。否则过滤后preds_df会是空表

# preds_df = preds_df[preds_df['bbox'].map(lambda x: isinstance(x, list))].reset_index(drop=True)
# fix : list -> np.ndarray
preds_df = preds_df[preds_df['bbox'].map(lambda x: isinstance(x, np.ndarray))].reset_index(drop=True)

Issue 4

2023.9.1 按道理这个很影响结果啊,毕竟图片之间的映射都错了。加上Issue 3,感觉作者写完教程,整理源码后,并没有再测试test.py这个文件了。

test.py第117行开始,保存预测结果。这里的原意应该是把预测结果对应回每张图片的id,但是源码直接对valid_df做了截断,这里很明显导致了部分图片的id被trunc了(这是因为valid_df将同一图片中的不同物体分成了若干行,如果直接按照总的图片数目进行截断,后面的一些图片id就会被截掉)。使用valid_df['id'].unique()进行修改,正好与133行代码对应。能这么做也是因为data loader的shuffle=False

# preds_df = pd.DataFrame()
# valid_df = valid_df.iloc[:len(all_bboxes)]
# preds_df['id'] = valid_df['id'].copy()
# preds_df['bbox'] = all_bboxes
# preds_df['label'] = all_labels
# preds_df['conf'] = all_confs

# I think there is some bug above, because the code \
# do not consider the corresponding id of image, \
# it just trunc the valid_df with len(all_bboxes)!!!

preds_df = pd.DataFrame()
preds_df['id'] = valid_df['id'].unique().copy()
preds_df['bbox'] = all_bboxes
preds_df['label'] = all_labels
preds_df['conf'] = all_confs

# line_133 : valid_df = df[df['id'].isin(preds_df['id'].unique())].reset_index(drop=True)

使用作者提供的权重文件,跑出来的结果确实比原作者给出的结果(mAP=0.264399)高出7%左右,而且由于上述逻辑错误,作者的结果其实只在基本一半数量的box做了mAP的计算(因为测试图片就少了快一半),如果使用全量数据,他的效果应该会更差。

image-20230901001735855

Issue 5

2023.9.1

TODO : 之后把这些问题跟作者反馈一下,下一步考虑使用DDP,方便以后多卡训练。

Issues and PR

https://github.com/moein-shariatnia/Pix2Seq/issues/6

https://github.com/moein-shariatnia/Pix2Seq/issues/7

https://github.com/moein-shariatnia/Pix2Seq/pull/8

项目地址

https://github.com/JJJYmmm/CircleDetection

Readme

本项目使用 Canny + Hough 对图像中的圆形进行检测。参考https://github.com/CV-xueba/A01_cvclass_basic_exercise,不过修复了该项目中canny算法计算亚像素点的bug

文件列表如下:

  • main.py : run detection program
  • my_canny.py : canny算法实现,得到图像的梯度图/梯度方向图
  • my_hough.py : hough算法实现,实现通过参数空间的投票算法进行圆形的数学建模

使用方法:

  • 检测图像放在picture_source文件夹下,命名为picture.jpg(或修改main.py中的Path路径)
  • Canny/Hough检测结果放在picture_result文件夹下

测试结果:

左侧为canny算法结果,右侧为hough检测出的圆(原图上画出)

image-20230706191741408

image-20230706192044307

摘要

本文对现有Deepfake视频进行对抗性修改来绕过对应的检测器,并且进一步证明这种扰动对图像和视频压缩具有鲁棒性

攻击原理

目前关于DeepFake视频的检测可以分为两大类。

第一种是通过手工选取的特征以及自然图片的统计/物理特征进行真假区分,然而视频合成方法通过修改它们的训练目标(例如通过Loss函数指导生成器模拟这些人工/统计特征),从而绕过这种检测。

之前解析的CVPR2023的一篇文章就是采用这种方式绕过检测器

第二种是基于深度神经网络进行检测。首先将视频分解成帧,随后提取帧中的人脸特征并判断。当然现在先进的DeepFake检测器并不是以整张图片帧作为输入,而是先通过面部追踪方法从原始帧中裁剪面部,再经过归一化等变换才输入网络。事实证明这种先验输入可以使检测性能更好。

既然这种视频检测器仍然是通过单帧检测DeepFake,那么如果对视频的每一帧都施加对抗性扰动,理论上就可以欺骗到检测器。

当然现在也有检测器引入时间序列检测deepfake视频,这种检测器使用CNN+RNN架构或3-D CNN模型对帧序列进行处理。文章对这类检测器的代表3-D EfficientNet也进行了攻击。

实验设置

  • 受害者模型:逐帧分析的检测器有XceptionNet和MesoNet,基于时间序列的检测器有3-D EfficientNet
  • 攻击手段:使用基于梯度符号的方法进行扰动,考虑到视频需要处理的帧数较多,使用$L_\infty$对扰动进行限制。
  • 攻击流程:对于任何给定的帧,首先提取脸部区域,并为裁剪后的脸制作一个对抗样本,然后将其放回原始帧中面部裁剪的边界框中。

    image-20230514230933443

攻击类别

白盒攻击

使用基于梯度符号下降的攻击方法(如FGSM/PGD),不过损失函数采用了C&W Attack中的其中一种,即

$$ loss(x')=max(Z(x')_{Fake}-Z(x')_{Real},0) $$

其中$Z(.)$表示模型softmax前一层的输出。那么对抗样本的迭代如下

$$ x_i = x_{i-1} - clip_{\epsilon}(\alpha·sign(\nabla loss(x_{i-1}))) $$

鲁棒的白盒攻击

通常上传到社交网络和其他媒体的视频会被压缩。已知的一些标准操作(如压缩、调整大小)都可能去除图像中的对抗性扰动。为了确保对抗性视频即使在压缩后仍然有效,引入鲁棒的白盒攻击。

首先引入$T$变化操作,这个即刚刚提到的压缩,大小调整等。

那么对于输入x,我们的最终目标是生成

$$ x_{a d v}=a r y m a x_{x}\mathbb{E}_{t\sim T}[F(t(x))_{y}]\operatorname{s.t.}||x-x_{0}||_{\infty}\lt \epsilon $$

其中$F(t(x))_{y}$是指模型将$t(x)$判别成目标类别$y$,即Targeted Attack。

Loss函数如下,其实跟白盒攻击对比只多了一个t变换。

$$ l o s s(x)=\mathbb{E}_{t{\mathord{\sim}}T}\left[m a x(Z(t(x))_{F a k e}-Z(t(x))_{R e a l.0}\right] $$

这里涉及到期望,根据大数定理可以转换成

$$ l o s s(x)=\frac{1}{n}\sum_{t_{i}\sim T}[m a x(Z(t_{i}(x))_{F a k e}-Z(t_{i}(x))_{R e a l},0)] $$

关于T变化,文章提到了以下几种操作

  • 高斯模糊。$t(x)=k*x$,其中k是高斯核,*是卷积算符;
  • 添加高斯噪声。$t(x)=x+\Theta$,其中$\Theta \sim N(0,\sigma)$;
  • 大小转换。在图像的四个边填充0值。$t(x)=x'$且$x^{\prime}[i,j,c]=x[i+t_{x},j+t_{y},c]$
  • 下采样与上采样。首先将图像以因子r下采样,再通过插值上采样回原大小。

黑盒攻击

黑盒攻击采用基于查询的方法,并通过NES进行梯度估计。略去NES梯度估计的理论过程,估计梯度可以表示为

$$ \nabla \mathbb E[F(\theta)]\approx\frac{1}{\sigma n}\sum_{i=1}^{n}\delta_{i}F(\theta+\sigma\delta_{i})_{y} $$

其中$\theta=x+\sigma\delta$且$\delta \sim N(0,I)$,关于NES梯度估计的算法流程见算法1。

image-20230514234820165

估计梯度后,采用基于梯度符号的攻击进行优化。

$$ x_i = x_{i-1} + clip_{\epsilon}(\alpha·sign(\nabla F(x_{i-1})_y)) $$

注意这里是加号,与白盒攻击不同。白盒攻击因为引入了CW的Loss,所以是最小化Loss函数。

鲁棒的黑盒攻击

鲁棒的黑盒攻击同样使用NES进行梯度估计,相比于黑盒攻击同样只多了一个T变换。梯度估计如下

$$ \nabla \mathbb E[F(\theta)]\approx\frac{1}{\sigma n}\sum_{i=1,t_i \sim T}^{n}\delta_{i}F(t_i(\theta+\sigma\delta_{i}))_{y} $$

优化过程与黑盒攻击一致。

实验结果

评估指标

  • 攻击成功率SR:对抗视频中被分类成真实图片的帧的百分比。进一步地,SR-U表示以Raw形式保存的视频的攻击成功率(Raw表示视频未经过压缩),SR-C表示已MJPEG形式保存的视频的攻击成功率。
  • 准确率Accuracy:视频中被检测器分类为假的帧的百分比。ACC-C表示压缩视频上检测器的准确率。
  • 平均失真Mean distortion:通过$L \infty$范数衡量对抗帧和原始帧之间的失真情况

数据集

  • FaceForensics++ HQ Dataset:其中包括DF、F2F、FS、NT四种DeepFake类型的视频

1、对于XceptionNet和MesoNet,它们在原始数据集上的准确率表现

image-20230515000338738

2、白盒攻击的攻击成功率。

image-20230515000627784

3、鲁棒白盒攻击的设置与结果

image-20230515000713965

image-20230515000727580

主要提升在于SR-C,即对于视频压缩的鲁棒性。

4、黑盒攻击与鲁棒黑盒攻击的攻击成功率。

image-20230515000840307

黑盒攻击中,对于每一帧图像,对受害者模型的平均查询次数为985.

image-20230515000852745

鲁棒黑盒攻击中,对于每一帧图像,对受害者模型的平均查询次数为2153.

总结

个人认为文章的亮点有两处:首先是针对deepfake视频施加扰动从而欺骗分类器;其次在白/黑盒攻击场景下考虑了对抗性攻击的鲁棒性,即考虑到物理场景下视频很可能被压缩的情况,针对这种情况对输入进行变化后再执行对抗攻击。

摘要

本篇文章采用蜕变测试(metamorphic testing)的原理来寻找可能影响DeepFake检测模型鲁棒性的潜在因素,并缓解其中的Oracle问题。作者对MesoInception-4和TwoStreamNet两种检测模型进行了评估。通过蜕变测试发现化妆应用程序是一种对抗性攻击,可以欺骗deepfake检测器。实验结果表明,MesoInception-4和TwoStreamNet模型在输入数据被施加化妆扰动时,其性能下降高达30%。

Oracle问题:程序的执行结果不能预知的现象在测试理论中称为“Oracle问题”,即无法知道输入的预期结果,导致测试人员只能选择一些可以预知结果的特殊测试用例进行测试,而不能完整有效地进行测试。例如测试sin函数时,并不知道sin(153°)的预期结果。从而无法验证输入为153°时程序的正确性。

蜕变测试:蜕变测试是软件测试中的概念,是一种特殊的黑盒测试方法。蜕变测试依据被测软件的领域知识和软件的实现方法建立蜕变关系(Metamorphic Relation, MR),利用蜕变关系来生成新的测试用例,通过验证蜕变关系是否被保持来决定测试是否通过。同样以sin函数为例,虽然不知道sin(153°)的预期结果,但是根据数学知识可以得知sin(27°)=sin(153°),这是一种蜕变关系。我们利用程序验证这种关系,如果这个关系不成立可以说明源程序存在问题。

蜕变关系(Metamorphic Relation, MR) :指多次执行目标程序时,输入与输出之间期望遵循的关系。

实验设置

  • 数据集:数据集选择的是FaceForensics++,其中包括四种Deepfake方式形成的图片:Deepfakes (DF),Face2face (F2F), Face Swap (FS),Neural Textures (NT)
  • 受害者模型:MesoInception-4和TwoStreamNet

蜕变测试的应用

本文关于蜕变测试的具体应用如下图。具体来说,首先将数据集中的图片进行Metamorphic Transformation,得到两部分数据集(未扰动与扰动后)。将这两部分数据集分别输入检测器中,通过比较两次的检测结果来判断Metamorphic Transformation这种变化是否是影响模型鲁棒性的潜在因素。

个人感觉就是在对抗样本检测模型鲁棒性的基础上套了一个蜕变测试的壳子

image-20230514153519834

因为输入的扰动数据集是在原始数据集上通过扰动得到,如果模型具有较好的鲁棒性,那么两次测试结果应该一致。这就是蜕变测试中的蜕变关系(MR)。

关于如何衡量MR是否满足,即比较两次测试结果的方式,论文提到的方法是比较两次测试结果的Accuracy,Recall,Specificity

image-20230514160232197

关于选择这三个指标的原因:选择Accuracy是因为它是用于DeepFake检测器的常见评估度量。它展示了模型的正确性和一致性。但是Accuracy受到准确性悖论(accuracy paradox)的影响,高准确性模型可能无法捕获分类任务中的基本信息。

因此作者还考虑了Recall和Specificity,因为FN和FP对于了解模型性能同样重要。高召回率表明模型在识别TP方面做得很好,而低召回率表明高FN。因此,Recall非常适合输出敏感的环境,例如预测deepfake或预测癌症(也就是说宁愿误判,也要找全)。同时,Specificity显示了模型在避免误报方面的表现。Specificity非常适合关注TP和FP的领域,例如推荐引擎(也就是说要减少误报)。

准确度悖论:面对非均衡数据集时,准确度这个评估指标会使模型严重偏向占比更多的类别,导致模型的预测功能失效。

对抗性扰动

本文提到的对抗性扰动(也是之前提到的Metamorphic Transformation)是给图像添加化妆效果。

具体流程如下图,通过Dlib库识别面目的68个面部标志,并得到这些标志的坐标,调用OpenCV的方法在相应坐标绘制RGB色域的多边形,并通过高斯模糊滤镜使多边形与图像更好融合。

image-20230514161200473

实验结果

文章总共展开了两个实验,主要区别在于数据集:一个是子数据集,另一个是完全数据集。参数差异如下图。

image-20230514161906819

image-20230514161918303

分出一个子数据集进行实验的原因是:MesoInception-4模型的主要优势之一是它能够在使用小数据集和最少的训练时间的情况下有效地检测deepfake。所以这里额外测试了小数据集下模型的鲁棒性表现。

1、子数据集上,模型的Accuracy表现

image-20230514162147582

上表也表明Deepfake在跨数据集的表现很差,泛化能力不行

2、子数据集上,模型的recall和specificity表现

image-20230514162429521

image-20230514162504385

低召回率,高特异值表示模型将扰动后的图像同样认为是正常图像。说明化妆这种扰动确实对MesoInception-4和TwoStreamNet造成较大影响。

3、完整数据集上,TwoStreamNet的recall表现

image-20230514162829921

对于F2F这种方式得到的数据集,模型的recall值还保持在48.35%。说明训练数据的大小和质量的变化确实会影响深度学习模型的性能。(这是共识吧)

4、从原始数据集中挑选本来就化妆的DeepFake图像进行测试。发现同样会导致模型的低召回率。说明模型对于自然化妆或后处理化妆都不具备鲁棒性。

image-20230514163157897

总结

文章采用蜕变测试(metamorphic testing)的原理来寻找可能影响DeepFake检测模型鲁棒性的潜在因素。并通过这种测试发现现有的部分DeepFake检测器对于化妆(无论是自然化妆或后续处理)不具备鲁棒性。