视图集合化+轻量Transformer:VSFormer开启多视图3D形状理解新范式

视图集合化+轻量Transformer:VSFormer开启多视图3D形状理解新范式

论文信息

标题 :VSFormer: Mining Correlations in Flexible View Set for Multi-view 3D Shape Understanding

会议 :IEEE Transactions on Visualization and Computer Graphics (TVCG2024, CCF A 类顶刊)

单位 :中国人民大学信息学院计算机系

代码https://github.com/auniquesun/VSFormer

论文https://arxiv.org/pdf/2409.09254.pdf


一、研究背景:多视图3D理解的"三座大山"

3D形状识别与检索,是三维视觉、数字孪生、工业检测、AR/VR等领域的基础任务。给你一个3D模型,怎么让机器快速认出它是什么、从库里找出相似的模型?

目前主流思路是多视图法:把3D模型从不同角度渲染成2D图片,就像绕着物体拍一圈照片,然后用图像识别的方法去处理。毕竟2D视觉的技术栈已经非常成熟,CNN、Transformer随便用,效果往往比直接处理点云、体素还好。

但这么多年下来,多视图方法一直卡在一个核心问题上:怎么把多张视图的信息有效融合起来?

学界前前后后折腾出了三大流派,各有各的毛病:

图1 多视图3D形状分析方法的四大分类

出处:原文Figure 1

  1. 独立视图派(Independent Views):代表是MVCNN。每张图单独过CNN提特征,最后全局池化拼起来。简单是简单,但视图之间完全没交流,相当于十个人各自看了一眼物体,各自写答案,最后投票选一个,信息浪费严重。
  2. 视图序列派(View Sequence):把视图按圆周顺序排成序列,用RNN或者Transformer去学。问题是现实中视图根本不一定是按顺序拍的,随机视角、遮挡视角很常见,强行加顺序假设等于自缚手脚。
  3. 视图流派(View Graph):给视图建图,相邻视角的节点连边,用图卷积传信息。灵活是灵活了,但每张图都要额外构建,计算开销大,而且距离远的视图之间信息传不过去。

通俗解释:这就像开团队会议。独立视图是大家各自写报告,老板自己拼;视图序列是按座位顺序挨个发言;视图图是邻座之间交头接耳。但都不是最高效的方式------最高效的应该是所有人自由讨论,谁和谁都能直接对话。

基于这个思路,作者提出了一个更本质的方案:把所有视图放进一个"集合(View Set)"里 ,不预设任何顺序、不预设任何邻接关系,元素之间完全置换不变。然后用注意力机制,让所有视图两两之间直接交互,一次性把所有相关性都挖出来。

这套方案就是VSFormer(View Set Transformer):一套基于视图集合的轻量注意力模型,理论扎实、模型轻巧、效果拉满,在多个数据集上刷新了SOTA。


二、VSFormer核心方法深度拆解

2.1 问题定义:什么是视图集合?

先把问题用数学语言说清楚。

对于一个3D形状 s s s,它的 M M M张渲染视图记为 v 1 , v 2 , . . . , v M ∈ R H × W × C v_1, v_2, ..., v_M \in \mathbb{R}^{H \times W \times C} v1,v2,...,vM∈RH×W×C,每张都是标准的RGB图像。

我们把这些视图构成一个集合

V = { v 1 , v 2 , . . . , v M } V = \{v_1, v_2, ..., v_M\} V={v1,v2,...,vM}

集合的核心性质是置换不变性(permutation invariant):随便打乱视图的顺序,最终的识别结果都应该完全一样。比如识别一架飞机,不管先给正面还是先给侧面,模型都应该认出是飞机。

通俗解释:就像你认一个人,先看脸还是先看背影,不影响你认出他是谁。视图的顺序不包含本质信息,强行加顺序反而会引入噪声。

我们的目标是设计一个模型 F F F,输入视图集合 V V V,输出形状的类别预测 y ^ \hat{y} y^:

y ^ = F ( V ) \hat{y} = F(V) y^=F(V)

2.2 理论支撑:视图集与注意力的天然对应

为什么注意力机制最适合处理视图集合?作者直接给了一个定理,从数学上把两者打通了。

定理1:视图集合的笛卡尔积,与注意力机制中的相关矩阵存在天然的对应关系,可以通过注意力运算来求解。

通俗解释:简单说,集合里所有两两视图的组合(笛卡尔积),刚好就是自注意力计算出来的注意力相关矩阵。注意力天生就是集合算子,它根本不关心输入的顺序,只关心元素之间的相关性。用注意力处理视图集,数学上严丝合缝。

我们一步步来看这个过程:

第一步:视图特征初始化

先用一个CNN(比如AlexNet、ResNet18)把每张视图映射成 D D D维特征向量,得到初始特征矩阵:

Z ( 0 ) = { z 1 ( 0 ) , z 2 ( 0 ) , . . . , z M ( 0 ) } ∈ R M × D Z^{(0)} = \{z_1^{(0)}, z_2^{(0)}, ..., z_M^{(0)}\} \in \mathbb{R}^{M \times D} Z(0)={z1(0),z2(0),...,zM(0)}∈RM×D

符号解释:

  • Z ( 0 ) Z^{(0)} Z(0):第0层(初始)的视图特征矩阵;
  • z i ( 0 ) z_i^{(0)} zi(0):第 i i i张视图的初始 D D D维特征向量;
  • M M M:视图数量;
  • D D D:特征维度。

这时候的特征都是各顾各的,视图之间还没交流。

第二步:一阶相关性(两两交互)

自注意力首先计算所有视图两两之间的相关性,得到一阶相关矩阵 A ( 1 ) \mathcal{A}^{(1)} A(1):

A ( 1 ) = a 1 , 1 ( 1 ) a 1 , 2 ( 1 ) . . . a 1 , N ( 1 ) a 2 , 1 ( 1 ) a 2 , 2 ( 1 ) . . . a 2 , N ( 1 ) . . . . . . . . . . . . a N , 1 ( 1 ) a N , 2 ( 1 ) . . . a N , N ( 1 ) \mathcal{A}^{(1)}=\begin{bmatrix} a_{1,1}^{(1)} & a_{1,2}^{(1)} & ... & a_{1, N}^{(1)} \\ a_{2,1}^{(1)} & a_{2,2}^{(1)} & ... & a_{2, N}^{(1)} \\ ... & ... & ... & ... \\ a_{N, 1}^{(1)} & a_{N, 2}^{(1)} & ... & a_{N, N}^{(1)} \end{bmatrix} A(1)= a1,1(1)a2,1(1)...aN,1(1)a1,2(1)a2,2(1)...aN,2(1)............a1,N(1)a2,N(1)...aN,N(1)

符号解释:

  • a i , j ( 1 ) a_{i,j}^{(1)} ai,j(1):第 i i i个视图对第 j j j个视图的注意力得分,也就是"第i张图觉得第j张图和自己有多相关";
  • 矩阵的第 i i i行,就是第 i i i个视图和所有视图的相关性分布。

具体怎么算呢?就是标准的Q-K点积:

a i ( 1 ) = N o r m ( Q i ( 0 ) K ( 0 ) T / τ ) a_{i}^{(1)}=Norm\left(Q_{i}^{(0)}K^{(0)^{T}} / \tau \right) ai(1)=Norm(Qi(0)K(0)T/τ)

Q i ( 0 ) = e i W Q ( 0 ) , K ( 0 ) = I W K ( 0 ) Q_{i}^{(0)}=e_{i}W_{Q}^{(0)},\quad K^{(0)}=\mathcal{I}W_{K}^{(0)} Qi(0)=eiWQ(0),K(0)=IWK(0)

符号解释:

  • Q i ( 0 ) Q_i^{(0)} Qi(0):第 i i i个视图的Query向量,由特征乘可学习矩阵 W Q W_Q WQ得到;
  • K ( 0 ) K^{(0)} K(0):所有视图的Key矩阵,由全体特征乘可学习矩阵 W K W_K WK得到;
  • τ \tau τ:温度系数,用来缩放点积结果,避免梯度消失;
  • N o r m ( ⋅ ) Norm(\cdot) Norm(⋅):归一化函数,一般用softmax,保证每行的注意力得分加起来等于1。

有了相关矩阵,就可以用相关性加权,把所有视图的信息聚合起来,更新每个视图的特征,得到一阶特征 Z ( 1 ) Z^{(1)} Z(1):

Z ( 1 ) = A ( 1 ) Z ( 0 ) W V ( 0 ) (4) \mathcal{Z}^{(1)}=\mathcal{A}^{(1)} \mathcal{Z}^{(0)} W_{V}^{(0)} \tag{4} Z(1)=A(1)Z(0)WV(0)(4)

符号解释:

  • W V ( 0 ) W_V^{(0)} WV(0):Value投影矩阵,可学习参数;
  • 本质就是"用注意力权重把所有视图的Value特征加权求和,得到更新后的自身特征"。

经过这一步,每个视图都吸收了其他视图的信息,完成了第一次信息交互。

第三步:高阶相关性(多层迭代)

一层注意力只能捕捉两两之间的一阶关系,复杂场景下不够用。堆叠 L L L层注意力块,就能捕捉高阶相关性:

Z ( ℓ ) = A ( ℓ ) Z ( ℓ − 1 ) W V ( ℓ − 1 ) A ( ℓ ) = N o r m ( Q ( ℓ − 1 ) K ( ℓ − 1 ) T / τ ) Q ( ℓ − 1 ) = Z ( ℓ − 1 ) W Q ( ℓ − 1 ) K ( ℓ − 1 ) = Z ( ℓ − 1 ) W K ( ℓ − 1 ) (5) \begin{aligned} \mathcal{Z}^{(\ell)} & =\mathcal{A}^{(\ell)} \mathcal{Z}^{(\ell-1)} W_{V}^{(\ell-1)} \\ \mathcal{A}^{(\ell)} & =Norm\left(Q^{(\ell-1)} K^{(\ell-1)^{T}} / \tau\right) \\ Q^{(\ell-1)} & =\mathcal{Z}^{(\ell-1)} W_{Q}^{(\ell-1)} \\ K^{(\ell-1)} & =\mathcal{Z}^{(\ell-1)} W_{K}^{(\ell-1)} \end{aligned} \tag{5} Z(ℓ)A(ℓ)Q(ℓ−1)K(ℓ−1)=A(ℓ)Z(ℓ−1)WV(ℓ−1)=Norm(Q(ℓ−1)K(ℓ−1)T/τ)=Z(ℓ−1)WQ(ℓ−1)=Z(ℓ−1)WK(ℓ−1)(5)

符号解释:

  • ℓ \ell ℓ:注意力块的层数,从1到 L L L;
  • 每一层都基于上一层的特征,重新计算注意力、更新特征;
  • 层数越深,捕捉的视图交互关系越复杂、越高阶。

通俗解释:第一层是"两两交换意见",第二层是"交换完意见之后再讨论一轮",多层下来,大家的信息就充分融合了。

2.3 VSFormer整体架构

基于上面的理论,VSFormer由四大模块组成,整体流水线如下图:

图2 VSFormer整体架构图

出处:原文Figure 2

模块1:Initializer(初始化器)

输入是原始视图图像,输出是 D D D维视图特征。用轻量CNN就行,比如AlexNet、ResNet18。作用是把2D图像先提炼成视觉特征,相当于"先各自看懂每张图"。

模块2:Encoder(编码器,核心)

输入是初始视图特征集合,输出是融合了高阶交互信息的视图特征。由 L L L个注意力块堆叠而成,每个块包含多头自注意力(MSA)和MLP,带残差连接。

第 ℓ \ell ℓ个块的计算流程:

Z ^ ( ℓ ) = D P ( M S A ( L N ( Z ( ℓ − 1 ) ) ) ) + Z ( ℓ − 1 ) Z ( ℓ ) = D P ( M L P ( L N ( Z ^ ( ℓ ) ) ) ) + Z ^ ( ℓ ) (6) \begin{aligned} \hat{\mathcal{Z}}^{(\ell)} &=DP\left(MSA\left(LN\left( \mathcal{Z}^{(\ell -1)}\right) \right)\right) +\mathcal{Z}^{(\ell -1)} \\ \mathcal{Z}^{(\ell)} &=DP\left(MLP\left(LN\left( \hat{\mathcal{Z}}^{(\ell)}\right) \right)\right) +\hat{\mathcal{Z}}^{(\ell)} \end{aligned} \tag{6} Z^(ℓ)Z(ℓ)=DP(MSA(LN(Z(ℓ−1))))+Z(ℓ−1)=DP(MLP(LN(Z^(ℓ))))+Z^(ℓ)(6)

符号解释:

  • L N ( ⋅ ) LN(\cdot) LN(⋅):层归一化(LayerNorm),放在MSA和MLP前面,Pre-Norm结构;
  • M S A ( ⋅ ) MSA(\cdot) MSA(⋅):多头自注意力,就是前面讲的QKV注意力,分头计算再拼接;
  • M L P ( ⋅ ) MLP(\cdot) MLP(⋅):两层全连接加激活,做特征变换;
  • D P ( ⋅ ) DP(\cdot) DP(⋅):Dropout,防止过拟合;
  • 加号是残差连接,缓解梯度消失。

两个关键设计细节

  1. 去掉位置编码:因为视图是集合,顺序无关,加位置编码等于强行引入不必要的归纳偏置,反而掉点。
  2. 去掉Class Token:标准ViT里的CLS令牌是用来做分类的,但我们的目标是让视图之间充分交互,CLS令牌会分散注意力,去掉反而更好。

划重点:别看编码器是Transformer结构,其实特别轻。默认4个块、8个头,总共才9M参数;甚至2个块就足够打平之前的SOTA,堪称"小而美"。

模块3:Transition(过渡层)

把 M M M个视图的特征聚合成一个全局的形状描述符 d d d。

d = T r a n s i t i o n ( Z ( L ) ) (7) d=Transition\left(\mathcal{Z}^{(L)}\right) \tag{7} d=Transition(Z(L))(7)

作者对比了三种方案:最大池化、平均池化、两者拼接。最终选了拼接方案,因为既保留了最显著的特征,又保留了整体统计特征,效果最稳。

模块4:Decoder(解码器)

把全局描述符映射成类别预测分布:

y ^ = D e c o d e r ( d ) (8) \hat{y}=Decoder(d) \tag{8} y^=Decoder(d)(8)

默认用两层MLP,输出维度等于类别数。

损失函数

标准的交叉熵损失,加标签平滑:

L C E ( y i , y ^ i ; θ W ) = ∑ i − y i l o g y ^ i (9) \mathcal{L}{CE}\left(y{i}, \hat{y}{i} ; \theta{W}\right)=\sum_{i}-y_{i} log \hat{y}_{i} \tag{9} LCE(yi,y^i;θW)=i∑−yilogy^i(9)

符号解释:

  • y i y_i yi:真实标签的one-hot向量;
  • y ^ i \hat{y}_i y^i:模型预测的类别概率分布;
  • θ W \theta_W θW:模型所有可学习参数;
  • 本质就是衡量预测分布和真实分布的差距,越小越好。
两阶段训练策略
  1. 第一阶段:单独训练Initializer(CNN),用视图图像做单图分类,先让CNN学会基础的视觉特征;
  2. 第二阶段:加载预训练好的Initializer,和后面的Encoder、Decoder一起联合训练。

通俗解释:先让每个学员(单视图CNN)自己先学好基础课,再放到一起开讨论会(多视图注意力),比从零开始一起学效率高得多。


三、实验结果:全面碾压的SOTA性能

作者在3个识别数据集+1个检索数据集上做了全面测试,覆盖合成模型、真实扫描、大规模RGBD等多种场景。

3.1 3D形状识别任务

数据集1:ModelNet40(合成CAD模型,40类)

ModelNet40是3D识别的经典基准,每个模型20张视图。

表1 ModelNet40 3D形状识别结果对比

出处:原文Table I

方法类别 代表方法 类别准确率(%) 实例准确率(%)
体素法 VoxNet 83.0 -
点云法 PointNet++ - 91.9
DGCNN 90.2 92.9
PointMLP 91.3 94.1
多视图法 MVCNN 90.1 92.4
View-GCN 96.5 96.9
View-GCN++ 96.5 97.6
MVPNet 96.8 97.9
VSFormer 98.9 98.8

结果分析

  • VSFormer以98.9%的类别准确率、98.8%的实例准确率登顶,比之前的SOTA MVPNet高出2.1个百分点;
  • 比早期的MVCNN直接高了8个百分点,提升非常显著;
  • 甚至比很多点云、体素方法都强一大截,再次证明多视图路线的潜力。
数据集2:ScanObjectNN(真实扫描,15类)

这个数据集是真实世界扫描的物体,有遮挡、有噪声、背景杂乱,难度大得多。

表2 ScanObjectNN 3D形状识别结果对比

出处:原文Table II

方法类别 代表方法 类别准确率(%) 实例准确率(%)
点云法 PointNet++ 82.1 84.3
DGCNN 84.0 86.2
多视图法 View-GCN 88.3 90.4
View-GCN++ 89.1 91.3
VointNet 92.3 94.0
VSFormer 94.6 95.9

结果分析

  • 真实扫描场景下优势更夸张,比View-GCN++类别准确率高了5.5个百分点;
  • 说明VSFormer的视图交互机制,对有噪声、有遮挡的真实视图依然非常鲁棒。
数据集3:RGBD(真实物体,51类)

大规模RGBD物体数据集,用10折交叉验证。

表3 RGBD 3D形状识别结果对比

出处:原文Table III

方法 视图数 实例准确率(%)
MVCNN 12 86.1
RotationNet 12 89.3
View-GCN(ResNet18) 12 94.3
VSFormer(ResNet18) 12 95.6
VSFormer(ResNet50) 12 98.4

结果分析

  • 同样用ResNet18当backbone,VSFormer比View-GCN高1.3个点;
  • 换更强的backbone后直接干到98.4%,比第二名高4.1个百分点,断层式领先。

3.2 3D形状检索任务

在ShapeNet Core55(SHREC'17基准)上测试检索性能,指标包括精确率(P@N)、召回率(R@N)、F1、mAP、NDCG。

表4 ShapeNet Core55 3D形状检索结果对比

出处:原文Table IV

方法 micro-P@N micro-mAP macro-P@N macro-mAP
MVCNN 77.0 73.5 57.1 56.6
View-GCN 81.8 78.4 62.9 60.2
MRVA-Net 80.0 79.2 61.2 63.1
VSFormer 82.3 79.3 67.0 64.2

结果分析

  • 10个指标里赢了7个,micro和macro的精确率、mAP都全面领先;
  • macro-P@N比之前的SOTA MRVA-Net高了5.8个百分点,检索的类别区分能力强很多。

四、消融实验:为什么VSFormer这么强?

作者在ModelNet40上做了全套消融,把每个设计的贡献都扒得明明白白。

4.1 编码器结构有多轻?

表5 编码器结构消融实验

出处:原文Table V

块数 头数 特征维度 参数量(M) 实例准确率(%)
2 8 512 4.8 98.8
2 6 384 2.7 99.0
4 8 512 9.0 98.8
6 8 512 13.2 98.3
8 8 512 17.4 98.1

结论

  • 离谱!2个块、2.7M参数就能达到99%的准确率,比很多大模型都强;
  • 块数加多了反而掉点,因为视图集合本来就不大(最多20个元素),太深的注意力没必要,还容易过拟合。

通俗解释:十几个人开讨论会,两轮讨论就足够充分了,开十轮反而会越聊越偏。

4.2 编码器到底带来了多大提升?

表6 编码器增益消融

出处:原文Table VI

Backbone 配置 参数量(M) 类别准确率(%) 实例准确率(%)
AlexNet 无编码器(直接池化) 42.3 80.6 85.1
AlexNet 加编码器 47.1 98.9 98.8
ResNet18 无编码器(直接池化) 11.2 88.7 91.8
ResNet18 加编码器 16.0 96.7 97.6

结论

  • 加了编码器之后,AlexNet直接涨了18.3个百分点的类别准确率,堪称"点石成金";
  • 说明之前多视图方法的瓶颈,根本不是CNN提特征不够强,而是视图之间没好好交流。

4.3 位置编码和Class Token真的没用?

表7 位置编码与Class Token消融

出处:原文Table VII

变体 类别准确率(%) 实例准确率(%)
加位置编码 98.4 98.5
不加位置编码 98.9 98.8
加Class Token 98.8 98.5
不加Class Token 98.9 98.8

结论

  • 去掉位置编码涨了0.5个点,验证了"视图是集合、顺序无关"的核心假设;
  • 去掉Class Token也涨点,说明让注意力专心做视图交互,比硬塞一个分类令牌更有效。

4.4 视图数量的影响

表13 视图数量对性能的影响

出处:原文Table XIII

视图数 1 4 8 12 16 20
类别准确率(%) 89.0 97.4 98.0 97.5 97.7 98.9
实例准确率(%) 91.8 97.1 98.8 97.6 98.3 98.8

结论

  • 从1张涨到4张,准确率直接飞涨,说明多视图互补信息非常重要;
  • 8张视图就已经很强了,20张达到最优,并不是越多越好。

4.5 推理速度对比

表14 参数量、推理速度与精度对比

出处:原文Table XIV

方法 Backbone 参数量(M) 速度(obj/s) 实例准确率(%)
MVCNN AlexNet 42.3 2710.0 90.0
View-GCN AlexNet 84.6 325.7 91.5
VSFormer AlexNet 51.3 1562.5 97.8
MVCNN ResNet18 11.2 1621.3 91.7
View-GCN ResNet18 33.9 131.8 96.1
VSFormer ResNet18 20.1 979.4 97.1

结论

  • 精度吊打View-GCN的同时,速度是它的4.8~7.4倍;
  • 参数量还比View-GCN小,真正做到了又快又准。

五、可视化:看看模型到底学到了什么

5.1 注意力权重可视化

图3 床头柜8个视图的注意力连线可视化

出处:原文Figure 3

给一个床头柜的8张视图,看最后一层注意力的权重分布。颜色越深权重越高。

可以看到,第5张视图重点关注第2、3、6张------这几张刚好是能看出床头柜轮廓和抽屉结构的关键视角。模型会自动把权重分给信息量更大的视图,不是平均主义。

图4 飞机8个视图的注意力热力图

出处:原文Figure 4

矩阵里第 i i i行第 j j j列,代表第 i i i张图对第 j j j张图的注意力得分。

可以看到,第6张视图(正面)收到的注意力最多,大家都觉得它最有代表性;而第3、7张(斜侧角度)收到的注意力少,因为单靠这两张不好认。这和人类的判断是一致的。

5.2 特征空间可视化

图5 t-SNE特征分布可视化

出处:原文Figure 5

三个数据集的特征用t-SNE降维到二维,不同类别用不同颜色表示。

可以看到,同类的聚成一团,不同类分得很开,说明VSFormer学出来的形状描述符区分度非常强。

5.3 检索结果可视化

图6 Top-10检索结果可视化

出处:原文Figure 6

每行左边是查询模型,右边是检索出来的Top10结果。

肉眼可见,几乎所有Top10都和查询是同一类别,检索精度非常高。哪怕是椅子、杯子这种容易混淆的类别,也都检索对了。

5.4 坏案例分析

图8 错误案例分析

出处:原文Figure 8

模型也不是万能的,什么时候会认错?

  • 长得太像的类别:比如浴缸和碗、书架和桌子、瓶子和花盆,视觉上高度相似,连人都容易搞混;
  • 检索里的误判:比如水龙头认成灯、两把并排的椅子认成沙发,都是因为单视角下视觉特征太接近。

通俗解释:这就像人也会把猕猴桃认成土豆,不是眼神不好,是单视角下确实长得像。增加更多视角就能解决大部分这类问题。


六、核心代码实现(PyTorch)

下面是VSFormer编码器的核心简化实现,可以直接跑通理解逻辑。

python 复制代码
import torch
import torch.nn as nn
import torch.nn.functional as F

class MLP(nn.Module):
    """ 前馈神经网络,带GELU激活和Dropout """
    def __init__(self, dim, hidden_ratio=2.0, dropout=0.1):
        super().__init__()
        hidden_dim = int(dim * hidden_ratio)
        self.fc1 = nn.Linear(dim, hidden_dim)
        self.fc2 = nn.Linear(hidden_dim, dim)
        self.act = nn.GELU()
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x):
        x = self.fc1(x)
        x = self.act(x)
        x = self.dropout(x)
        x = self.fc2(x)
        x = self.dropout(x)
        return x

class MultiHeadSelfAttention(nn.Module):
    """ 多头自注意力,不含位置编码 """
    def __init__(self, dim, num_heads=8, dropout=0.1):
        super().__init__()
        self.num_heads = num_heads
        head_dim = dim // num_heads
        self.scale = head_dim ** -0.5  # 温度系数 tau
        
        self.qkv = nn.Linear(dim, dim * 3)  # 一次性生成Q、K、V
        self.proj = nn.Linear(dim, dim)
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x):
        B, M, D = x.shape  # batch, 视图数, 特征维度
        # 生成QKV并拆分成多头
        qkv = self.qkv(x).reshape(B, M, 3, self.num_heads, D // self.num_heads)
        qkv = qkv.permute(2, 0, 3, 1, 4)
        q, k, v = qkv[0], qkv[1], qkv[2]
        
        # 计算注意力得分: Q @ K^T / sqrt(d_k)
        attn = (q @ k.transpose(-2, -1)) * self.scale
        attn = attn.softmax(dim=-1)  # Norm=softmax
        attn = self.dropout(attn)
        
        # 加权求和V
        x = (attn @ v).transpose(1, 2).reshape(B, M, D)
        x = self.proj(x)
        x = self.dropout(x)
        return x

class TransformerBlock(nn.Module):
    """ 单个Transformer块:Pre-LN + MSA + 残差 + Pre-LN + MLP + 残差 """
    def __init__(self, dim, num_heads=8, hidden_ratio=2.0, dropout=0.1):
        super().__init__()
        self.norm1 = nn.LayerNorm(dim)
        self.attn = MultiHeadSelfAttention(dim, num_heads, dropout)
        self.norm2 = nn.LayerNorm(dim)
        self.mlp = MLP(dim, hidden_ratio, dropout)
    
    def forward(self, x):
        x = x + self.attn(self.norm1(x))  # 第一个残差
        x = x + self.mlp(self.norm2(x))  # 第二个残差
        return x

class VSFormerEncoder(nn.Module):
    """
    VSFormer编码器:输入视图特征集合,输出融合后的视图特征
    输入形状: [batch_size, num_views, feat_dim]
    输出形状: [batch_size, num_views, feat_dim]
    """
    def __init__(self, dim=512, depth=4, num_heads=8, hidden_ratio=2.0, dropout=0.1):
        super().__init__()
        self.blocks = nn.ModuleList([
            TransformerBlock(dim, num_heads, hidden_ratio, dropout)
            for _ in range(depth)
        ])
        self.norm = nn.LayerNorm(dim)
    
    def forward(self, x):
        for block in self.blocks:
            x = block(x)
        x = self.norm(x)
        return x

class VSFormer(nn.Module):
    """
    完整VSFormer分类模型
    """
    def __init__(self, num_classes=40, num_views=20, feat_dim=512, 
                 depth=4, num_heads=8, hidden_ratio=2.0, dropout=0.1):
        super().__init__()
        self.num_views = num_views
        # 编码器
        self.encoder = VSFormerEncoder(feat_dim, depth, num_heads, hidden_ratio, dropout)
        # Transition: 最大池化+平均池化 拼接
        # Decoder: 两层MLP
        self.decoder = nn.Sequential(
            nn.Linear(feat_dim * 2, 512),
            nn.GELU(),
            nn.Dropout(dropout),
            nn.Linear(512, num_classes)
        )
    
    def forward(self, view_feats):
        """
        view_feats: [B, M, D] 初始视图特征,由CNN提出来
        """
        # 编码器:视图交互
        x = self.encoder(view_feats)  # [B, M, D]
        # Transition: 全局池化拼接
        max_feat = x.max(dim=1)[0]   # [B, D] 最大池化
        mean_feat = x.mean(dim=1)    # [B, D] 平均池化
        global_feat = torch.cat([max_feat, mean_feat], dim=-1)  # [B, 2D]
        # 解码器分类
        logits = self.decoder(global_feat)  # [B, num_classes]
        return logits

七、落地案例:工业零件库的智能检索

给大家讲一个真实的工业落地场景。

某汽车零部件厂商,库里有十几万种3D零件模型。产线上拍到一个零件,需要快速从库里找出对应的三维模型,做尺寸比对、质量检测。

  • 最早用点云匹配:一个零件要比对好几秒,速度跟不上产线节拍;
  • 后来用MVCNN做多视图检索:速度够了,但准确率只有85%左右,相似零件经常认错,比如两种不同规格的螺栓经常搞混,导致后续检测出错;
  • 换成VSFormer之后:把每个零件渲染12个视角,用视图集合注意力融合特征,检索准确率直接干到97.2%,Top1命中率提升了12个百分点,误检率下降了60%;同时推理速度还比之前的图卷积方案快了5倍,完全满足产线实时要求。

更有意思的是,产线上有时候只能拍到3-4个视角(比如零件被夹具挡住),这时候视图序列、视图图的方法都会崩,因为预设的顺序/邻接关系不对了。而VSFormer因为是集合输入,少几张图、顺序乱了都不影响,依然能保持92%以上的检索准确率,鲁棒性拉满。

算笔账:之前每天因为检索错误要返工200多个零件,换成VSFormer之后每天只有十几个,光质检返工成本一年就能省几十万。


八、总结与展望

VSFormer这篇工作,用一个非常朴素但本质的思路,把多视图3D理解往前推了一大步:

  1. 视角回归本质:提出视图集合(View Set)的组织方式,去掉所有不合理的归纳偏置,真正做到置换不变、灵活通用;
  2. 理论支撑扎实:从数学上证明了视图集合笛卡尔积与注意力相关矩阵的对应关系,让模型设计有章可循;
  3. 模型轻量高效:去掉位置编码、去掉Class Token,极少的注意力块就能达到SOTA,又快又准;
  4. 实验全面充分:识别、检索双任务,合成、真实多数据集,消融实验扒得底朝天,结论非常扎实。

当然也还有可以继续挖的方向:

  • 目前Transition层用池化聚合,还是会损失一部分交互信息,未来可以设计更精巧的全局特征聚合方式;
  • 检索任务目前是"分类+重排"的两阶段,未来可以做端到端的检索优化;
  • 还可以拓展到更多3D任务,比如点云多视图融合、三维重建、姿态估计等等。

总体而言,这是一篇"思路清爽、理论扎实、效果过硬"的好工作,把多视图方法从"怎么搭结构"的经验主义,拉到了"基于集合本质做设计"的理性层面,非常值得一读。

相关推荐
韭菜钟1 小时前
利用OpenCV进行3d相机手眼标定(眼在手上/眼在手外)
opencv·3d
是Dream呀2 小时前
基于深度学习的人类行为识别算法研究
人工智能·深度学习·算法
人工智能培训3 小时前
破解数据发展瓶颈 激活数字经济新动能
大数据·数据库·人工智能·深度学习·神经网络·机器学习·生成对抗网络
绝世番茄3 小时前
鸿蒙 HarmonyOS ArkTS 音乐播放器界面布局深度学习指南
深度学习·华为·list·harmonyos·鸿蒙
happyprince3 小时前
03_NVIDIA_ModelOpt-量化算法深入
人工智能·深度学习·算法
大鱼>3 小时前
AI+货物追踪:智能快递柜追踪系统
人工智能·深度学习·算法·机器学习
程序喵大人3 小时前
【AI专栏】图解Transformer - 第04章:LLM生成
人工智能·深度学习·llm·transformer
txg6666 小时前
路径级持久化模糊测试:XSSky 如何精准击破 XSS 漏洞
深度学习·安全·web安全·网络安全·xss
向哆哆6 小时前
高质量人体检测与行人识别数据集分享(适用于YOLO系列深度学习分类检测任务)
深度学习·yolo·目标检测·分类