首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【论文精读 | Locate and Verify: A Two-Stream Network for Improved Deepfake Detection】

【论文精读 | Locate and Verify: A Two-Stream Network for Improved Deepfake Detection】

原创
作者头像
九年义务漏网鲨鱼
发布2025-07-10 21:57:21
发布2025-07-10 21:57:21
8210
举报
文章被收录于专栏:论文精读论文精读

Locate and Verify: A Two-Stream Network for Improved Deepfake Detection

作者在文章中提出了,目前(2023)的伪造检测算法是不能够对浅层伪影特征进行有效提取的,这是因为这些模型存在着在未被篡改区域提取伪影特征,这是不现实。因此,作者提出了一个双流网络,分别提出a Local Forgery Guided Attention (LFGA) module用于定位伪造区域,Multi-scale Patch Feature Fusion (MPFF) modules实现对伪造区域的伪影检测。

模型框架

主要模型框架包含了:

🟢 Spatial Rich Model (SRM):用于提取高频特征信息;

🟢 Cross-Modality Consistency Enhancement (CMCE) module:融合空间和频域信息;

🟢 Local Forgery Guided Attention (LFGA) module:定位伪造特征;

🟢 Multi-scale Patch Feature Fusion (MPFF) modules:检测伪造特征;

Cross-modality Consistency Enhancement

⭐ 通过余弦相似度计算空间、频域特征的相关性,通过余弦相似度和Relu激活函数修正原有特征:F' =ReLIU(F+Cor \odot F_h) ,具体代码如下所示:

代码语言:python
复制
class CMCE(nn.Module):
    def __init__(self, in_channel=3):
        super(CMCE, self).__init__()
        self.relu = nn.ReLU()
        self.bn = nn.BatchNorm2d(in_channel)
        self.stage1 = nn.Sequential(
            nn.Conv2d(in_channel, in_channel, 3, 1, bias=False),
            nn.BatchNorm2d(in_channel),
            nn.ReLU()
        )
        self.stage2 = nn.Sequential(
            nn.Conv2d(in_channel, in_channel, 3, 1, bias=False),
            nn.BatchNorm2d(in_channel),
            nn.ReLU()
        )

    def forward(self, fa, fb):
        (b1, c1, h1, w1), (b2, c2, h2, w2) = fa.size(), fb.size()
        assert c1 == c2
        cos_sim = F.cosine_similarity(fa, fb, dim=1)
        cos_sim = cos_sim.unsqueeze(1)
        fa = fa + fb * cos_sim
        fb = fb + fa * cos_sim
        fa = self.relu(fa)
        fb = self.relu(fb)

        return fa, fb
Local Forgery Guided Attention

⭐ 该模块的主要作用是学习到注意力图的位置特征,防止出现对未篡改区域进行检测。作者提取定位流的中层特征图,并通过线性变换 (论文中是通过一个 1 \times 1 的卷积核实现) 和Softmax激活函数计算注意力值,最后将注意力值作用于分类特征上,具体公式为:F'_c = \text{ReLU(Reshape}(Att \cdot h(F_c))) + F_c ,本质上就是自注意力机制的变体。

具体代码如下所示:

代码语言:python
复制
class LFGA(nn.Module):
    def __init__(self, in_channel=3, ratio=4):
        super(LFGA, self).__init__()
        self.chanel_in = in_channel
        self.query_conv = nn.Conv2d(
            in_channels=in_channel, out_channels=in_channel//ratio, kernel_size=1)
        self.key_conv = nn.Conv2d(
            in_channels=in_channel, out_channels=in_channel//ratio, kernel_size=1)
        self.value_conv = nn.Conv2d(
            in_channels=in_channel, out_channels=in_channel, kernel_size=1)
        self.gamma = nn.Parameter(torch.zeros(1))

        self.softmax = nn.Softmax(dim=-1)
        self.relu = nn.ReLU()
        self.bn = nn.BatchNorm2d(self.chanel_in)

    def forward(self, fa, fb):
        B, C, H, W = fa.size()
        proj_query = self.query_conv(fb).view(
            B, -1, H*W).permute(0, 2, 1)  # B , HW, C
        proj_key = self.key_conv(fb).view(
            B, -1, H*W)  # B X C x (*W*H)
        attention = self.softmax(torch.bmm(proj_query, proj_key))  # BX (N) X (N)
        proj_value = self.value_conv(fa).view(
                B, -1, H*W)  # B , C , HW
        out = torch.bmm(proj_value, attention.permute(0, 2, 1))
        out = out.view(B, C, H, W)
        out = self.gamma*out + fa
        return self.relu(out)
Multi-scale Patch Feature Fusion

分支

是否用了 MPFF

为什么

分割分支(Segmentation)

✅ 使用了 MPFF

要保留局部空间细节、处理多尺度伪造痕迹,MPFF 融合不同尺度的空间信息有助于提升定位精度

分类分支(Classification)

❌ 没有使用 MPFF

更关注整体是否为伪造,所以只需要融合统一尺度下的浅层结构特征 + 高层语义信息,重点是全局信息,MPFF 这种细致的 patch 局部匹配不是必须

⭐ 目前的检测方法难以发挥浅层伪影特征的特点,为了更好的利用伪影的特征,作者提出在定位流中主要关注局部空间细节特征,分类头关注全局语义特征信息。作者在定位流和分类流中都引入了MPFF. 具体来说就是,将流的中间层特征图,将其分为了k个与定位流输出特征图相同分辨率的块(不满一个块用0填充)。为了能够更好的保留局部信息特征不由于感受野的减少而丧失信息,利用所有块和输出特征图计算最后的多尺度局部特征F'_l , 将这个局部特征输入到预测头中。

代码语言:python
复制
class MPFF(nn.Module):
    def __init__(self, size=8):
        super(MPFF, self).__init__()
        # fa: b * c * h1 * w1
        # fb: b * c * h2 * h2
        self.size = size

    def forward(self, fa, fb):
        b1, c1, h1, w1 = fa.size()
        b2, c2, h2, w2 = fb.size()
        assert b1 == b2 and c1 == c2 and self.size == h2
        padding = abs(h1 % self.size - self.size) % self.size
        pad = nn.ReplicationPad2d(padding=(padding // 2, (padding + 1) // 2, padding // 2, (padding + 1) // 2)).to(fa.device)
        fa = pad(fa)
        b1, c1, h1, w1 = fa.size()
        assert h1 % self.size == 0

        window = h1 // self.size
        unfold = nn.Unfold(kernel_size=window, dilation=1, padding=0, stride=window)
        fb = fb.repeat_interleave(window, dim=2)
        fb = fb.repeat_interleave(window, dim=3)
        ff = torch.tanh(fa * fb)
        ff = torch.sum(ff, dim=1, keepdim=True)
        ff = unfold(ff).view(b1, -1, self.size, self.size)

        return ff.to(fa.device)

⭐ 分类流:对于分类头的输出,作者采用了Low-rank Bilinear Pooling来合并这些特征,具体的,通过卷积快得到特征图 F^*_c , 将所有的浅层特征图经过池化层并聚合在一起,得到F_s ,最后的输出为:F'_c=P(UF_s \odot VF_c^*) + B

代码语言:python
复制
y0m = self.pad_max_pool(y0)
y1m = self.pad_max_pool(y1)
y2m = self.pad_max_pool(y2)
y3m = self.pad_max_pool(y3)
y5m = self.pad_max_pool(y5)

# 拼接作为 shallow feature F_s
mul_feas = torch.cat((y0m, y1m, y2m, y3m, y5m), dim=1)

# 与最终语义特征 y6 进行 HBFusion(低秩双线性融合)
cls_feas = self.HBFusion(mul_feas, y6)

⭐ Low-rank Bilinear Pooling 代码:

代码语言:python
复制
class HdmProdBilinearFusion(nn.Module):
    def __init__(self, dim1, dim2,
                 hidden_dim=2048, output_dim=3072, bili_affine=None,
                 bili_dropout=0.5, **kwargs):
        super(HdmProdBilinearFusion, self).__init__()

        self.hidden_dim = hidden_dim
        self.output_dim = output_dim

        self.Trans1 = nn.Linear(dim1, hidden_dim)
        self.Trans2 = nn.Linear(dim2, hidden_dim)
        self.OutTrans = nn.Linear(hidden_dim, output_dim)

        if bili_dropout is None:
            self.Dropout = nn.Identity()
        else:
            self.Dropout = nn.Dropout(bili_dropout)

    def forward(self, features1, features2, **kwargs):
        b1, c1, h1, w1 = features1.size()
        b2, c2, h2, w2 = features2.size()
        assert b1 == b2 and h1 == h2 and w1 == w1
        features1 = features1.view(b1, c1, -1).permute(0, 2, 1).contiguous().view(-1, c1)
        features2 = features2.view(b2, c2, -1).permute(0, 2, 1).contiguous().view(-1, c2)
        prod = self.Trans1(features1) * self.Trans2(features2)
        prod = torch.tanh(prod)
        prod = self.OutTrans(self.Dropout(prod))
        prob = prod.view(b1, -1, self.output_dim).permute(0, 2, 1).contiguous().view(b1, -1, h1, w1)

        return prob

实验

🟢 人脸提取:未提及人脸提取的方法,resize为(2299\times 299),采用了数据增强

🟢 超参数:我们采用用预训练权值初始化的backbone Xception,并使用betas为0.9和0.999,epsilon为1e-8的Adam优化器。初始学习率设置为5𝑒−4,每5个epoch衰减50%。位置流预测的伪造地图的大小设置为19 x 19。MPFF模块的超参数𝑚和𝑛设置为2048和4096。

🟢 跨数据集的实验结果:

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Locate and Verify: A Two-Stream Network for Improved Deepfake Detection
    • 模型框架
      • Cross-modality Consistency Enhancement
      • Local Forgery Guided Attention
      • Multi-scale Patch Feature Fusion
    • 实验
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档