论文CLIP-Count(基于文本指导的零样本目标计数)详解(PyTorch)

部署运行你感兴趣的模型镜像

论文和代码详解(视频)

论文下载地址:https://arxiv.org/pdf/2305.07304v2.pdf

代码下载地址:https://github.com/songrise/clip-count

论文CrowdCLIP(基于CLIP的无监督人群计数模型)详解(PyTorch,Pytorch_Lighting)

论文CLIP-EBC(基于CLIP的人群统计模型)详解

Reference-less Counting,Zero-shot Counting,Few-shot Counting,单域泛化以及域自适应之间的区别?

        前面我们已经讲过了关于基于CLIP的人群统计两篇文章,也希望读者可以去看前面两篇文章,因为这对于学习CLIP在计数方面具有很好的启发。前面两篇文章主要是从有监督和无监督两方面来进行研究的,而这篇文章也可以说是从无监督的方面来进行研究的,不同的是这篇文章不仅仅是针对人群计数,主要是面对所有的目标,通过文本指定要统计的目标达到效果(零样本计数)。

目录

一 提出目的和方法

提出目的

提出方法

少样本目标统计

无参考计数方法

零样本计数创新

Reference-less Counting​,​​Zero-shot Counting,单元域泛化以及域自适应之间的区别?(非常重要)

CLIP对比语言预训练模型

二 整体模型架构

本文的目标

文本和密集视觉特征

视觉提示微调

分层的文本patch交互模块

密度图回归

综合实验对比

数据介绍

实验细节

FSC-147实验效果

CARPK实验效果

ShanghaiTech crowd counting实验效果

消融实验

可视化效果对比

CLIP-Count局限性


一 提出目的和方法

提出目的

最新研究表明,视觉语言模型(VLMs)虽在零样本图文匹配任务中展现出卓越性能,并可迁移至目标检测与分割等下游任务,但其在物体计数领域的应用仍存在重大挑战

提出方法

       本研究首次探索了视觉语言模型在类别无关物体计数中的迁移方法,提出首个端到端的开放词汇物体密度图预测框架——CLIP-Count,其核心创新包括:

1. 跨模态对齐机制
        通过引入块- 文本对比损失函数,实现文本嵌入与密集视觉特征的对齐,指导模型学习适用于密集预测的块级视觉表征。
2. 层级化语义传播
        设计分层块- 文本交互模块,在多层次视觉特征间传递语义信息,充分挖掘预训练 VLMs 的图文对齐知识。

文本引导零样本计数

  研究突破"示例与查询图像必须具有自相似性"的传统认知,提出文本引导的零样本物体计数新范式(图1-(c)),其核心优势包括:

1. 标注效率提升 :消除训练 / 测试阶段对示例块标注的依赖
2. 描述灵活性 :支持从通用描述(如 " 食物 " )到具体描述(如 " 篮中红苹果 " )的多粒度文本引导

技术挑战与解决方案

文本引导面临三重挑战:

1. 隐含语义歧义 :文本提示仅含隐式描述,缺乏示例标注的显式外观信息
2. 跨模态对齐难题 :需实现文本与图像区域的精准语义对齐
3. 零样本泛化需求 :需适应开放词汇文本提示和未见物体类别

针对上述问题,基于对比语言-图像预训练模型(CLIP)提出CLIP-Count框架:

1. - 文本对比损失 :对齐文本与图像块嵌入空间,实现参数 / 数据高效的 CLIP 迁移学习
2. 分层块 - 文本交互模块 :在多分辨率特征间传播文本信息,增强密集预测 能力

样本目标统计

小样本物体计数算法致力于建立通用模型,通过示例对象作为推理引导实现任意物体的计数

开创性工作

GMN 首次 将类别无关计数构建为匹配问题,利用计数任务中的自相似特性
FamNet 引入 ROI 池化预测密度图,并发布首个类别无关计数数据集 FSC-147[32]

后续技术发展
当前方法主要沿两个方向推进:

1. 视觉主干网络优化
采用视觉 Transformer ViT )等先进架构增强特征提取能力(如 CounTR[19] LOCA
2. 示例匹配机制改进
3. 显式建模示例 - 图像相似度(如 BMNet SAFECount
4. 深度挖掘示例引导信息 [3,18,38]

无参考计数方法

无参考计数技术作为无需人工标注的类别无关计数新方向,近期取得显著进展:

早期探索 :通过区域建议模块替代示例输入提取显著物体
典型方案 :基于预训练视觉 Transformer 隐式提取显著物体,直接回归物体数量估计值
方法扩展值得注意的是,一些近期的少样本计数模型也可以配置为执行无参考计数

核心局限:现有方法虽无需示例输入,但无法在多类共存场景中指定目标对象

零样本计数创新

最新研究提出仅需类别名称即可完成推理的零样本计数:

两阶段训练
使用示例监督训练小样本计数器
通过文本条件变分自编码器在封闭对象集上生成示例原型
本质区别
该方案仍需块级监督训练,而本文方法可实现完全端到端的无监督学习

Reference-less Counting​,​Zero-shot Counting,Few-shot Counting,单元域泛化以及域自适应之间的区别?(非常重要)

​​方法​​

​​核心目标​​

​​数据依赖​​

​​适用场景​​

​Reference-less Counting​

​无需任何参考示例​​(但是训练的时候还是需要标签作为损失更新模型的),直接计数图像中的物体(依赖通用特征或启发式规则)。

❌ 无参考信息
⭕ 可能使用无监督
/跨类别数据

通用物体计数(如人群、车辆),无需特定类别先验。

​Zero-shot Counting (ZSC)​

计数​​训练中未见过的新类别​​(但是训练的时候还是需要标签作为损失更新模型的),依赖语义信息(如类别名称或文本描述)。

❌ 无目标类别标注(不同类别)
⭕ 需预训练模型(如
CLIP)或文本提示

新商品盘点、稀有物种监测等开放世界场景。

​Few-shot Counting​

通过​​极少量参考示例​​(如1~5张标注图像)计数新类别(但是训练的时候还是需要标签作为损失更新模型的)。

⭕ 少量目标类别参考
❌ 无需大规模标注

数据稀缺场景(如医学细胞计数),需快速适应新类别。

​单域泛化 (SDG)​

在​​单一源域​​上训练,泛化到​​未知的多个目标域​​(域偏移)(但是训练的时候还是需要标签作为损失更新模型的)。

❌ 仅单一源域数据(同一类别)
⭕ 需模拟域多样性(如数据增强)

模型需部署到未知环境(如自动驾驶在不同天气下的鲁棒性)。

​域自适应 (DA)​

利用​​源域数据​​和​​无标签(或少量标签)目标域数据​​,对齐分布以减少域偏移(但是训练的时候还是需要标签作为损失更新模型的)。

⭕ 需目标域数据(无监督DA可无标签-同一类别)
❌ 目标域数据不可用时失效

跨设备/场景迁移(如合成数据→真实数据)。

 ❌:表示不包含(不具有);⭕表示包含(或具有)

方法​​

​​典型技术​​

​​挑战​​

​Reference-less​

密度图回归(如MCNN
无监督聚类/分割

难以区分相似背景的物体,依赖手工特征。

​Zero-shot​

多模态对齐(CLIP + 文本提示)
原型网络(如CounTR)

依赖预训练模型的语义泛化能力,对新类别描述敏感。

​Few-shot​

度量学习(如匹配网络)
元学习(如MAML

参考示例过少可能导致计数偏差。

​单域泛化​

对抗生成(如StyleGAN
元学习(模拟域偏移)

DCCUSMPCount方法

仅用单一域数据模拟多样性困难。

​域自适应​

域对齐(MMD/CORAL
对抗训练(DANN
自训练(伪标签)

需目标域数据,无监督DA易受噪声影响。

场景​​

​​适用方法​​

​​原因​​

​商场人流统计​

Reference-less Counting

无需知道行人具体身份,直接估计密度。

​新品上架商品计数​

Zero-shot Counting

新商品无历史数据,但可通过文本描述(如“蓝色保温杯”)定位。

​病理细胞计数​

Few-shot Counting

标注成本高,但可提供少量示例快速适配。

​自动驾驶跨天气泛化​

单域泛化 (SDG)

训练数据仅为晴天,需泛化到雨雪天气。

​合成数据训练→真实测试​

域自适应 (DA)

合成数据与真实数据存在分布差异,需对齐特征。

需求​​

​​推荐方法​​

无先验信息,直接计数

Reference-less Counting

新类别计数,仅有语义描述

Zero-shot Counting

新类别计数,有少量标注示例

Few-shot Counting

单一训练域,需泛化到未知环境

单域泛化 (SDG)

源域与目标域数据分布不同但部分相关

域自适应 (DA)

注:询问了一点模型和参考链接:https://blog.youkuaiyun.com/festaw/article/details/139475358

CLIP对比语言预训练模型

二 整体模型架构

本文的目标

文本和密集视觉特征

class ContrastiveLoss(nn.Module):
    def __init__(self, temperature=0.07, noise_text_ratio=0.0, normalize=False):
        super(ContrastiveLoss, self).__init__()
        self.temperature = temperature
        self.noise_text_ratio = noise_text_ratio
        self.normalize = normalize

    def forward(self,
                patch_embedding,
                img_embedding,
                gt_text_embedding_map,
                noise_text_embeddings,
                gt_density
                ):
        """
        Args:
            patch_embedding: TODO (B, 196, 512) embedding of image patch feature
            img_embedding: (B, 1, 512) embedding of image feature
            text_embedding: (B, 1, 512), ground truth text embedding
            noise_text_embeddings:TODO (N, 1, 512), noise text embeddings
            gt_density: (B, 384, 384), ground truth density map
        """
        #TODO 将GT Density map缩放到指定大小
        gt_density = F.interpolate(gt_density.unsqueeze_(1), size=(224, 224), mode='nearest')
        #TODO 下采样操作,和VIT的conv1一样操作
        density_mask = F.max_pool2d(gt_density, kernel_size=16, stride=16, padding=0) #same as ViT conv1
        #TODO 对于大于0的位置,表示存在人群或者物体
        density_mask = density_mask > 0.
        density_mask = density_mask.permute(0, 2, 3 ,1) # (B, 14, 14, 1)

        #TODO 文本嵌入向量,维度转换为何图像的patch embedding一样大小
        gt_text_embedding_map = gt_text_embedding_map.unsqueeze(1).expand(-1, 14, 14, -1) 

        # TODO [B, 14, 14, 512], contains both gt and noise text embedding
        fused_text_embedding_map = gt_text_embedding_map
        pos_mask = density_mask.squeeze_(-1) # (B, 14, 14, 1)
        #TODO 图像的patch embedding
        patch_embeddings = patch_embedding.reshape(-1, 14, 14, 512)
        #TODO 自动归一化向量并计算余弦相似性 batch cosine similarity, this function automatically normalizes the vectors
        sim_map = F.cosine_similarity(patch_embeddings, fused_text_embedding_map , dim=-1) # (B, 14, 14)
        # TODO 对二值密度图求和 得到正样本数量 sim_global = F.cosine_similarity(img_embedding, fused_text_embedding_map , dim=-1) # (B, 1)
        n_pos = torch.sum(pos_mask, dim=(1, 2)) # TODO (B) how many positive samples in each batch
        # if n_pos == 0, set to 1 to avoid nan TODO 得到正样本
        n_pos = torch.where(n_pos == 0, torch.ones_like(n_pos), n_pos)
        #infoNCE 

        sim_map = torch.exp(sim_map / self.temperature)
        #TODO 分别求解负样本数量和正样本数量
        pos_sum = torch.sum(torch.where(pos_mask, sim_map, torch.zeros_like(sim_map)), dim=(1, 2)) + 1e-5
        neg_sum = torch.sum(torch.where(~pos_mask, sim_map, torch.zeros_like(sim_map)), dim=(1, 2)) + 1e-5

        loss = -torch.log(pos_sum / (pos_sum + neg_sum))
        if self.normalize:
            loss = loss / n_pos            
        return loss.mean()

视觉提示微调

        传统"预训练+微调"范式存在两大局限:

        1. 计算成本过高 :需更新整个模型参数
        2. 数据需求量大 Transformer微调通常需要海量标注数据,这在标注成本高昂的物体计数任务中尤其不现实

        高效迁移方案采用以下策略实现CLIP预训练知识的高效迁移:

        • 参数冻结:保持 CLIP ViT 全部参数固定不变
        • 视觉提示学习 :在各 Transformer 层输入端添加少量可训练参数作为视觉提示

        该方案优势:

        • 仅需少量数据和内存即可完成知识迁移
        • 充分保留 CLIP 预训练获得的丰富视觉表征 能力

关于视觉微调:https://arxiv.org/pdf/2203.12119v2.pdf

分层的文本patch交互模块

MHSA(多头自注意力)和MHCA(多头交叉注意力) 

class CrossAttention(nn.Module):
    def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.):
        super().__init__()
        self.num_heads = num_heads
        head_dim = dim // num_heads
        # NOTE scale factor was wrong in my original version, can set manually to be compat with prev weights
        self.scale = qk_scale or head_dim ** -0.5

        self.wq = nn.Linear(dim, dim, bias=qkv_bias)
        self.wk = nn.Linear(dim, dim, bias=qkv_bias)
        self.wv = nn.Linear(dim, dim, bias=qkv_bias)
        self.attn_drop = nn.Dropout(attn_drop)
        self.proj = nn.Linear(dim, dim)
        self.proj_drop = nn.Dropout(proj_drop)

    def forward(self, x, y):
        B, Nx, C = x.shape
        Ny = y.shape[1]
        # BNxC -> BNxH(C/H) -> BHNx(C/H)
        q = self.wq(x).reshape(B, Nx, self.num_heads, C // self.num_heads).permute(0, 2, 1, 3)
        # BNyC -> BNyH(C/H) -> BHNy(C/H)
        k = self.wk(y).reshape(B, Ny, self.num_heads, C // self.num_heads).permute(0, 2, 1, 3)
        # BNyC -> BNyH(C/H) -> BHNy(C/H)
        v = self.wv(y).reshape(B, Ny, self.num_heads, C // self.num_heads).permute(0, 2, 1, 3)

        attn = (q @ k.transpose(-2, -1)) * self.scale  # BHNx(C/H) @ BH(C/H)Ny -> BHNxNy
        attn = attn.softmax(dim=-1)
        attn = self.attn_drop(attn)

        x = (attn @ v).transpose(1, 2).reshape(B, Nx, C)  # (BHNxNy @ BHNy(C/H)) -> BHNx(C/H) -> BNxH(C/H) -> BNxC
        x = self.proj(x)
        x = self.proj_drop(x)
        return x

class CrossAttentionBlock(nn.Module):

    def __init__(
            self, dim, num_heads, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop=0., attn_drop=0.,
            drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm):
        super().__init__()
        #TODO 注意:自注意力和交叉注意力之间的区别是,交叉注意力的query和key来自不同的输入
        self.norm0 = norm_layer(dim)
        self.selfattn = Attention(
            dim, num_heads=num_heads, qkv_bias=qkv_bias,
            qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop
        )
        self.drop_path0 = DropPath(drop_path) if drop_path > 0. else nn.Identity()
        
        self.norm1 = norm_layer(dim)
        self.attn = CrossAttention(
            dim, num_heads=num_heads, qkv_bias=qkv_bias,
            qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop
        )
        # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here
        self.drop_path1 = DropPath(drop_path) if drop_path > 0. else nn.Identity()

        self.norm2 = norm_layer(dim)
        self.mlp = Mlp(in_features=dim, hidden_features=int(dim * mlp_ratio),
                       act_layer=act_layer, drop=drop)
        self.drop_path2 = DropPath(drop_path) if drop_path > 0. else nn.Identity()

    def forward(self, x, y):
        x = x + self.drop_path0(self.selfattn(self.norm0(x)))
        x = x + self.drop_path1(self.attn(self.norm1(x), y))
        x = x + self.drop_path2(self.mlp(self.norm2(x)))
        return x

密度图回归

综合实验对比

数据介绍

数据集名称

图像数量

主要特征

实验用途

FSC-147

6,135

- 涵盖147个物体类别
- 每类样本独立不重叠
- 提供类别名称、点标注及3个示例边界框

类别无关计数基准测试

CARPK

1,448

- 俯视角停车场图像
- 包含89,777辆汽车标注

跨数据集迁移能力评估

ShanghaiTech

1,198

Part A: 482张(训练400/测试82
Part B: 716张(训练400/测试316
- 两部分采集方法不同

人群计数任务性能验证

实验细节

FSC-147实验效果

CARPK实验效果

ShanghaiTech crowd counting实验效果

消融实验

可视化效果对比

CLIP-Count局限性

附件材料大家自己去看一下。

您可能感兴趣的与本文相关的镜像

PyTorch 2.7

PyTorch 2.7

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值