HPCGames 题解 D E 题

在上一篇文章中,我们介绍了 HPCGames 题解 A 、 B 、 C 题的解决方案。本文将继续探讨 D 、 E 题的解决方案,深入分析每道题目的挑战和我们的应对策略。

D. Hyperlane Hopper

HPCGames 题解 A B C 题

A 题

B 题

这里又来到了经典的小北问答环节,结合一些理论知识和具体论文的查阅,我们可以对题目进行详细的分析和解答。

1. Amdahl & Gustafson

某程序的代码中 10% 必须串行执行, 90% 可完美并行。

  • 根据 Amdahl’s Law ,无论核心数如何增加,该程序的理论最大加速比极限是 ____ 倍;

  • 若在 10 核系统中通过扩大问题规模来保持每核计算负载不变,根据 Gustafson’s Law ,该系统的加速比将达到 ____ 倍。

    首先,根据 Amdahl 定律,加速比 S 可以通过以下公式计算:

    其中 P 是可并行部分的比例, N 是处理器的数量。对于该问题, P = 0.9 ( 90% 可并行),串行部分为 0.1 ( 10% 必须串行)。当 N 趋近于无穷大时,公式简化为:

    因此,该程序的理论最大加速比极限是 10 倍。

    接下来,根据 Gustafson 定律,加速比 S 可以通过以下公式计算:

    在 10 核系统中, N = 10 , P = 0.9 ,因此:

    因此,在 10 核系统中通过扩大问题规模来保持每核计算负载不变,该系统的加速比将达到 9.1 倍。

2. OpenMP

以下代码使用 OpenMP 并行执行循环:

1
2
3
4
5
6
int sum = 0; 
#pragma omp parallel for
for (int i = 0; i < 100; i++) {
sum += i;
}
printf(" sum = %d\n" , sum);

关于该代码,请问以下说法中正确的是 ____ 。

选项 描述
A 代码一定能正确计算出 0 到 99 的和( 4950 )
B 代码存在数据竞争, 结果不确定。
C sum变量默认为private,每个线程有自己的副本。
D OpenMP 会自动为sum变量添加原子操作,保证结果正确。

正确答案是 B 。

解释如下:

  • 选项 A 是错误的,因为代码中存在数据竞争,多个线程同时修改共享变量 sum,导致结果不确定。
  • 选项 B 是正确的,因为在并行执行时,多个线程可能同时读取和写入 sum,导致数据竞争,从而使得最终结果不确定。
  • 选项 C 是错误的,因为 sum 变量在默认情况下是共享的( shared ),而不是私有的( private )。因此,所有线程都访问同一个 sum 变量。
  • 选项 D 是错误的,因为 OpenMP 不会自动为共享变量添加原子操作。要确保结果正确,需要显式地使用 #pragma omp atomic 或其他同步机制来保护对 sum 的访问。

3. 低精度

已知 IEEE 754 标准的 FP32 拥有 8 位指数位。请问:

  • BF16 拥有 ____ 位指数位,____ 位尾数位
  • NVFP4 拥有 ____ 位指数位,____ 位尾数位

提示:可以查阅资料,了解 NVFP4 如何在低精度下保持较高的数值范围和动态范围。

经查阅资料可知: - BF16 拥有 8 位指数位, 7 位尾数位 - NVFP4 拥有 2 位指数位, 1 位尾数位
NVFP4 比较特殊,他只有 4 个 bit ,显然如果直接使用的话,其范围会很小,精度也不理想,但经过查阅资料可知:
NVFP4 首先将一组数视为了一个块,一个块中会共享一个高精度的 scale factor ,确定大致的数量级,然后 NVFP4 只存储每个数相对于这个 scale factor 的偏移量,这样就能在保持较大数值范围的同时,使用更少的位数来表示每个数,从而提高了存储效率和计算速度。

4. MPI 通信

4.1 基本原语

4 个进程执行以下代码,每个进程有局部值 local_val ,操作后每个进程都有所有进程的值。

1
2
3
4
5
int rank; 
MPI_Comm_rank(MPI_COMM_WORLD, & rank);
int local_val = rank; // rank 为进程编号, 0~3
int recv_buf[4];
/* 填这一行代码 */

4.2 通信器

创建一个 2 维笛卡尔拓扑,尺寸为 2×2 ,行优先排列,允许环绕连接。

1
2
3
4
MPI_Comm comm_cart; 
int dims[2] = { 2, 2} ;
int periods[2] = { 1, 1} ; // 环绕连接
/* 填这一行代码 */

4.1 基本原语

可以使用 MPI_Allgather 来实现该功能。代码如下:

1
MPI_Allgather(& local_val, 1, MPI_INT, recv_buf, 1, MPI_INT, MPI_COMM_WORLD); 

这行代码会将每个进程的 local_val 收集到所有进程的 recv_buf 中。

4.2 通信器

可以使用 MPI_Cart_create 来创建一个二维笛卡尔拓扑。代码如下:

1
MPI_Cart_create(MPI_COMM_WORLD, 2, dims, periods, 1, & comm_cart); 

这行代码会创建一个 2x2 的笛卡尔拓扑通信器 comm_cart,并允许环绕连接。

5.NCCL 延迟

在深度学习的并行推理与训练中,进程之间会频繁进行集合通信操作。 NVIDIA 的开源集合通信库 NCCL ,提供了在 GPU 之间进行集合通信的高性能解决方案。

当在异构的硬件上进行大规模集合通信时,如何选择通信的算法将很影响集合通信操作的效率。为了解决这个问题, NCCL 的解决方案是:基于一套硬编码的调优常数,估算不同集合通信算法下的集合通信完成时间,由此选择最优的算法。

问题:在 NCCL 2.28 的默认调优常量中,用 NVLink 连接的两 GPU 、采用 Tree 算法和 LL 协议时,在估算时每跳(单步)的硬件延迟取值为 ______ µs 。

根据 NCCL 2.28 的默认调优常量,当使用 NVLink 连接的两 GPU ,采用 Tree 算法和 LL 协议时,每跳(单步)的硬件延迟取值为 0.6 µs 。

具体参考资料可见 NCCL Github src/graph/tunning.cc中的 151 行。

6. 高性能网络

Rail-optimized networking 与 Clos 都是高性能网络设计方案。以下说法正确的有:

选项 描述
A 在 Rail-optimized 网络中,来自不同 HB 域( High-Bandwidth Domain )但具有相同 local rank 的 GPU 会被连接到同一个 rail switch 上,以减少跨域通信的延迟
B 常见部署模式下, Rail-optimized 网络相比传统 Clos 网络的主要优势是完全不需要 Spine 层交换机,因此可以大大节省网络设备成本
C Clos 网络因其使用 Spanning Tree Protocol (STP) 而在大规模部署时存在扩展性问题,这是 Rail-optimized 网络要解决的核心问题之一
D Rail-optimized 网络保证了任何情况下集群内任意两个 GPU 之间都能以网络线速(如 400 Gbps InfiniBand )进行通信,无论它们是否在同一个 rail 中
E NCCL 2.12 引入的 PXN 特性可以结合 NVLink 和 PCI 通信来优化网络流量,这个优化对于 Rail-optimized 网络尤为重要
F 对于 LLM 训练工作负载,最优的通信策略会将大部分网络流量集中在相同 local rank 的 NIC 之间,并且会多用 NVLink 等高速互联进行跨 rail 交换,这使得 Rail-optimized 架构特别适合此类场景

正确答案是 A 、 E 、 F 。

解释如下:

  • 选项 A 是正确的,这是 Rail-optimized 网络的核心定义。 在这种架构中,网络拓扑是根据 GPU 的 rank 进行物理隔离的。例如,所有服务器上的 0 号 GPU 都连接到同一组交换机( Rail 0 ), 1 号 GPU 连接到另一组( Rail 1 )。这使得在进行数据并行( Data Parallelism )训练时, AllReduce 等操作只需在同一个 Rail 内进行,无需跨越复杂的交换层级,大大降低了拥塞和延迟。

  • 选项 B 是错误的, Rail-optimized 仍然基于 Clos 架构,通常需要 Spine 交换机。 Rail-optimized 描述的是 Leaf 层交换机与 GPU 的连接方式以及流量的导向方式,而不是一种去除了 Spine 的新型拓扑。对于大规模集群(超过一个 Leaf 交换机的容量), Rail 0 的 Leaf 交换机之间仍然需要通过 Spine 交换机互联,以构成一个完整的 Rail 0 网络平面。

  • 选项 C 是错误的, Clos 网络并不使用 STP ,且 STP 是传统以太网的痛点。 传统二层以太网使用 STP (Spanning Tree Protocol) 防止环路,这会导致大量链路被阻塞,带宽利用率低。而现代 Data Center Clos 网络(无论是基于 IP 路由的 ECMP 还是 InfiniBand )的设计初衷就是利用所有链路进行负载均衡,完全摒弃了 STP 。因此, C 选项描述的前提本身就是错误的。

  • 选项 D 是错误的, Rail-optimized 并不保证“跨 Rail”通信的效率等同于“同 Rail”。 Rail-optimized 的设计哲学是“专路专用”。虽然物理上可以通过 Spine 进行跨 Rail 通信(例如 Node A 的 GPU 0 发给 Node B 的 GPU 1 ),但这通常不是最优路径,且可能面临 oversubscription (收敛比)的问题。实际上,这种架构倾向于利用 E 选项和 F 选项提到的技术来避免在网络层面上进行跨 Rail 数据传输。

  • 选项 E 是正确的, PXN (PCIe/NVLink Cross-NIC) 是解决 Rail 架构灵活性的关键。 在 Rail-optimized 网络中,如果 GPU 0 需要向网络中的 Rail 1 发送数据,传统的路径非常低效(走 PCIe -> CPU -> NIC -> Switch ->…)。 NCCL 的 PXN 特性允许 GPU 0 通过 NVLink 直接把数据传给同机的 GPU 1 ,然后由 GPU 1 的 NIC (连接着 Rail 1 )发送出去。这相当于在节点内部利用 NVLink 完成了“变轨”,从而充分利用 Rail 网络的优势。

  • 选项 F 是正确的,这准确描述了 LLM 训练中的混合通信模式。 在 LLM 训练中,通常结合了数据并行( DP )和模型并行( TP/PP )。

    + TP (Tensor Parallelism) 流量极大,通常限制在单机内部,完全走 NVLink 。
    
    + DP (Data Parallelism) 需要跨机同步梯度,流量发生在相同 rank 的 GPU 之间,这完美契合 Rail-optimized 的网络路径。
    
    + 如果需要跨 rank 的操作(如 Pipeline Parallelism 的某些阶段或特定的 All-to-All ),结合 NVLink (节点内)+ Rail (节点间)是目前最优的策略。
    

7. GPU

NVIDIA 的 Hopper 架构引入了 TMA ( Tensor Memory Accelerator ) 以提升 GPU 内存访问效率。以下说法正确的有:

选项 描述
A 相比 cp.async , TMA 可以直接将数据从全局内存加载到共享内存,无需经过寄存器中转,从而能节省寄存器
B 在 cutlass 的异步流水线抽象中, Producer 调用 producer_acquire 获取空闲的 buffer stage ,完成数据加载后调用 producer_commit 通知 Consumer ; Consumer 则通过 consumer_wait 等待数据就绪,使用完毕后调用 consumer_release 释放 buffer
C 在使用 TMA 进行数据传输时,所有参与的线程都需要执行相同的 TMA 指令, TMA 硬件会自动处理线程间的协调
D Cutlass Pipeline 使用多级缓冲( multi-stage buffering ),通过 PipelineState 追踪当前读写的 stage index 和 phase ,实现 Producer 和 Consumer 之间的流水线重叠
E TMA 的 multicast 功能允许一次 TMA 操作将同一块数据广播到 Cluster 内的多个 Thread Block 的共享内存中,减少了重复的全局内存访问
F TMA 描述符( TMA Descriptor )需要在 kernel 启动前在 host 端创建,描述符中包含了张量的形状、步长和 swizzle 模式等信息, kernel 执行时通过预取描述符( prefetch_tma_descriptor )来减少首次 TMA 操作的延迟

正确答案是 B D E F.

解释 :

- 选项 A 是错误的。 Ampere 架构引入的 cp.async 指令同样也是绕过寄存器( Register File ),直接将数据从全局内存( GMEM )搬运到共享内存( SMEM )。
- 选项 B 是正确的。这描述了 Cutlass 异步流水线中 Producer 和 Consumer 之间的交互方式,符合 Cutlass 的设计理念。
- 选项 C 是错误的。 TMA 操作允许线程组内的线程根据需要选择性地执行 TMA 指令,而不是所有线程都必须执行相同的指令。如果所有线程都执行,会导致重复发射多个拷贝操作(除非有特殊的掩码处理)。这一点与 Ampere 的 cp.async (通常每个线程负责一部分)不同。
- 选项 D 是正确的。 Cutlass Pipeline 确实使用多级缓冲,通过 PipelineState 来追踪当前读写的 stage index 和 phase ,从而实现 Producer 和 Consumer 之间的流水线重叠。
- 选项 E 是正确的。 TMA 的 multicast 功能允许一次 TMA 操作将同一块数据广播到 Cluster 内的多个 Thread Block 的共享内存中,减少了重复的全局内存访问。
- 选项 F 是正确的。 TMA 描述符需要在 kernel 启动前在 host 端创建,包含张量的形状、步长和 swizzle 模式等信息, kernel 执行时通过预取描述符来减少首次 TMA 操作的延迟。

8. LLM

对于参数如下的一个标准的 Transformer-Decoder 模型,所有的 all reduce 操作都使用 ring all reduce 。假设一共有 4 张卡。

模型参数

参数
层数 32 层
隐藏层维度 (h) 4096
FFN 结构 两层线性层,中间层维度为 4h
序列长度 2048
Batch Size 32
优化器 Adam + 混合精度训练
精度设置 参数和梯度使用 fp16 , Adam 优化器状态使用 fp32 (包括 momentum 、 variance 和 master weights )

问题

请计算在以下三种并行方式下,进行一个 batch 的前向传播和反向传播,每张卡需要的发送量(以 GB 为单位):

  • 数据并行:每张卡上存放完整的模型,把 batch 均匀拆分到每张卡上,分别计算完成后对梯度进行 All-Reduce 操作

  • 流水并行:按层拆分模型放到不同卡上,只需要前向传播的时候发送 activation ,反向传播的时候发送 gradient 。(计算通信量时只考虑中间的卡)

  • 张量并行:对于 MHA 操作,按照 head 拆分到不同卡上。对于 FFN ,第一个线性层按照输出维度进行拆分,第二个线性层按照输入维度进行拆分

    计算过程如下:

    • 数据并行

      • 模型参数量:

      • 梯度大小:

      • 通信量:

    • 流水并行

      • 每层激活大小:

      • 通信量:

    • 张量并行

      • 单次 All-Reduce 大小:

      • 单次 Ring All-Reduce 通信量:

      • 总通信量:

9. UB 互联

在高性能计算系统中,集合通信( Collective Communication )的性能主要受带宽( Bandwidth ) 与延迟( Latency ) 两个因素制约。

NVIDIA 通过 NVLink 与 NVSwitch 构建 GPU 间的高速 Scale-up 互联网络,而华为则提出了 Unified Bus ( UB )协议,作为面向 NPU 的统一互联与内存访问机制。 UB 协议基于华为自研的 UB Switch 交换芯片,并通过高带宽物理链路 HCCS ( High-Capacity Coherent System ) 进行连接。

传统 AI 集群通常以 8 卡服务器为基本单元进行 Scale-out 扩展,而华为在 CloudMatrix 384 ( CM384 ) 架构中,通过两级 UB Switch 组网,将 384 颗昇腾 910C NPU 构建为一个统一的超节点( SuperPod )。在该超节点范围内,所有 NPU 均处于同一个低延迟的轨道优化网络中,实现全对等 Scale-up 互联。

CM384 进一步将 UB 网络划分为 7 个相互独立的物理平面。每颗 NPU 的 7 个 HCCS 接口分别接入不同的交换平面,从而保证大规模并行通信过程中,数据流在物理路径上完全隔离、无链路冲突。

问题

在 CloudMatrix 384 的标准满配部署方案中,为了支撑 384 颗昇腾 910C NPU 实现无收敛、全对等的 Scale-up 互联,系统采用两级交换架构。在该超节点的物理拓扑中,分别使用了:

  • ____ 个 Level 1 UB Switch
  • ____ 个 Level 2 UB Switch
  • 最终实现了理论上 ____ GB/s 的系统级聚合带宽

假设 switch chip 提供的单个 Port 可以提供 28GB/s 的通信带宽

该问题直接查阅华为 CloudMatrix 384 的白皮书即可得到答案。

10. Cache 行为分析

假设我们需要进行一个矩阵乘法

测试环境

为了简化分析,假设:

参数类型 配置
数据类型 double (8 Bytes)
L1 Cache 大小 4KB (4096 Bytes)
相联度 直接映射 (Direct Mapped, E=1)
块大小 64 Bytes ( 1 个 Cache Line 可存 8 个 double )
矩阵规模 A, B, C 均为 64×64 的方阵 (N=64)
存储方式 数组按行优先存储
内存对齐 A, B, C 的起始地址均对齐到 Cache 的起始 Set

代码实现

1
2
3
4
5
6
7
8
9
10
11
// 假设变量 sum 已优化到寄存器中,忽略 C 的访存影响
// 仅考虑内层循环中 A 和 B 的读取
for (int j = 0; j < 64; ++j) { // Loop 1
for (int i = 0; i < 64; ++i) { // Loop 2
double sum = 0.0;
for (int k = 0; k < 64; ++k) { // Loop 3
sum += A[i][k] * B[k][j];
}
C[i][j] = sum;
}
}

问题 10.1

我们试图分析上述代码中最内层循环 Loop 3 对矩阵 的访存行为。

已知 Cache 总共有 个 Set 。

在计算
的过程中(即一次完整的 Loop 3 ),关于
的 Cache Miss Rate (不命中率),下列说法正确的是:

选项 描述
A 12.5% - 这里有良好的空间局部性,每 8 个 double 只有 1 次 Miss
B 25% - 虽然是列优先访问,但 Cache 够大,只有冷不命中
C 约 50% - A 和 B 互相打架(冲突),导致一半的数据被驱逐
D 100% - 发生了严重的 Cache Thrashing (抖动),每次读取都是 Miss

💡 提示:计算一下访问 时的内存地址差值( Stride ),以及它们映射到的 Set Index 的跨度。

正确答案是 D 。

解释如下:

  • 矩阵 B 是按行优先存储的,因此访问 B[k][j] 时, k 的变化会导致访问的内存地址以列为单位跳跃。

  • 计算地址差值( Stride ):

  • 每次访问 B[k][j] 时,地址增加 512 Bytes ,而每个 Cache Line 大小为 64 Bytes ,因此每次访问都会跨越多个 Cache Line 。

  • 计算 Set Index 的跨度:

  • 因为 Cache 有 64 个 Set ,跨度为 8 意味着每次访问都会映射到不同的 Set ,但由于 k 从 0 到 63 ,共有 64 次访问,这些访问会循环映射到同一组 Set 上,导致频繁的冲突和驱逐。

  • 最终结果是每次读取 B[k][j] 都会导致 Cache Miss ,即 Cache Thrashing 。

问题 10.2
为了进一步提升矩阵乘法的效率,我们决定使用分块技术。你将矩阵分成了 的小块( Block Size = 8 )。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 8x8 分块优化演示
for (int jj = 0; jj < 64; jj += 8) {
for (int ii = 0; ii < 64; ii += 8) {
for (int kk = 0; kk < 64; kk += 8) {
// 在这里处理 8x8 的子块乘法
for (int j = jj; j < jj + 8; ++j) {
for (int i = ii; i < ii + 8; ++i) {
double sum = C[i][j]; // 简化写法
for (int k = kk; k < kk + 8; ++k) {
sum += A[i][k] * B[k][j];
}
C[i][j] = sum;
}
}
}
}
}

针对一个 矩阵子块(假设该子块已预加载),在处理该子块内部的计算时,关于其在 L1 Cache 中的状态,下列分析正确的是:

选项 描述
A 一个 8×8 的子块大小为 512 Bytes ,远小于 Cache 大小,因此完全没有冲突,所有数据都能驻留在 Cache 中
B 尽管子块很小,但由于 B 的原始列宽( Stride )很大,导致子块内的 8 行数据全部映射到了同一个 Set 中,依然存在严重的冲突
C 子块内的 8 行数据分别映射到了 8 个不同的 Set 中( Set 索引间隔为 8 ),且在子块计算期间不会发生自我冲突( Self-Conflict )
D 分块主要是为了利用 L2/L3 Cache ,对这么小的 L1 Cache (4KB) 来说, 8×8 的分块没有任何意义

正确答案是 C 。

解释如下:

  • 一个 8×8 的子块大小为:

  • 该子块的大小( 512 Bytes )确实远小于 L1 Cache 大小( 4KB ),但关键在于访问模式。

  • 在处理该子块时,访问 B[k][j] 时, k 的变化会导致访问的内存地址以列为单位跳跃。

  • 计算 Set Index 的跨度:

  • 因此,子块内的 8 行数据分别映射到了 8 个不同的 Set 中,且在子块计算期间不会发生自我冲突( Self-Conflict )。

  • 分块技术有效地利用了 Cache 的空间局部性,减少了冲突,提高了数据的命中率。

C 题

SF3D 论文阅读记录

引言

mesh construction 是我刚刚开始了解的一个方向, 今天读了SF3D: Scene Fusion for 3D Reconstruction with Transformers这篇论文, 本文笔记记录用于后续翻阅学习。

读完这篇论文之后, 感觉 mesh reconstruction 与 point cloud reconstruction 还是有很大区别的, 尤其是这篇文章中引入的几个新的 mesh 专有的 module, 感觉要比 point cloud reconstruction 更加复杂一些.OK,
废话不多说, 直接进入正题.

Introduction

作者一上来就提出了几个 issue:
SF3D提出的问题

  1. Light bake-in: 现有的模型将光照信息直接 bake 到 texture 里, 使得生成的 mesh 难以利用, 而在 SF3D 中, 作者提出了使用 explicit illumination 和一个不同的使用 Spherical Gaussian 的 shading model 来解决这个问题(如上图第一行所示).
  2. Vertex Coloring: 现有的工作中, 生成的 vertex 的数量过多, 使得性能开销很大. 作者认为一个关键问题就是 UV unwrapping 的额外处理时间, 于是作者提出了一种 highly parallelizable fast box projection-based UV
    unwrapping method 来解决这个问题(如上图第二行所示), 这使得时间从 10-30s 减少到了 0.5s, 而且从图上来看, 细节比 baseline 的 TripoSR 的效果更好.
  3. Marching Cube Artifacts: feed-forward network 通常生成类似与 Triplane NeRFs 的体素网格, 然后使用 marching cube 来提取 mesh, 但是这种方法会引入一些 artifacts,
    作者提出了使用一个对高分辨率 Triplane 更有效的 architecture, 并且使用 DMTet 来对生成的 vetex diplacement 和 normal map 生成最终的 mesh, 这样可以有效减少 marching cube 引入的 artifacts(如上图第三行所示).
  4. Lack of Material Properties: 现有的工作生成的 mesh 在不同光照下都会看起来 dull, 这是因为缺乏 explicit 的 material properties.为解决这个问题, 作者预测了 non-spartially varying material properties
    (如上图第 4, 5 行所示).

通过以上的改进, SF3D 可以从单张图像生成高质量的 mesh, 且生成的 3D 资产体积小(1 MB)并且可以在 0.5s 内生成.

Method

为了解决上面提到的问题, 作者提出了 SF3D.

首先, SF3D 是在 TripoSR 的基础上进行改进的. TripoSR 训练了一个能够生成 Triplane 3D representation 的 transformer. 它使用 DINO encode image, 然后把 token 送入 transformer 中, transformer 输出一个分辨率的
triplane, 然后 triplane feature 之后被 decode 为 color 和渲染成标准 NeRF. TripoSR 只学到了 colors 并且不能处理反射等材质属性.

Overview

SF3D 的整体架构如下图所示:
SF3D架构图
可以看到, SF3D 由 5 个主要模块组成:

  1. Enhanced Transformer: 用于预测高分辨率的 triplane feature.
  2. Merterial Estimation: 用于预测材质属性.
  3. Illumination Modeling: 处理光照问题.
  4. Mesh extraction and refinement: 用于从 triplane 中提取 mesh 并进行细化.
  5. UV Unwrapping and Export: 产生 low-poly mesh 和 高分辨率 texture map.

Enhanced Transformer

为了生成高分辨率的 triplane feature, 作者对 TripoSR 的 transformer 进行了改进, 主要有以下几点:

  • 首先, 作者将 DINO 替换成了 DINOv2, 这样可以获得更好的 image feature.
  • 其次, 作者对 triplane 导致的 aliasing 问题进行了讨论
    aliasing问题
    如上图所示, 低分辨率的 triplane 会导致 aliasing 问题, 但是简单地提高 triplane 的分辨率会导致模型更复杂, 作者说, 他从 PointInfinity 中获得启发,
    (PointInfinity 提供了一个不需要计算 triplane 的 self-attention 的架构), 因此, 作者将分辨率提高到, 从而降低了走样.

Material Estimation

SF3D 输出了 metallic 和 roughness 两个材质属性. 论文中提到, 理想状况下, 人们希望材质属性是 spatially varying 的, 但是这样并不现实. 于是作者简化了这个问题, 为整个物体
预测这两个属性, 作者提到虽然这种非空间变化的材质属性通常适用于同质物体, 但是实际上能显著改善渲染效果.

为了实现这个预测, 作者引入了一个 Material net, 首先将图像通过 CLIP encoder 编码, 然后通过 2 个 MLP 预测 metallic 和 roughness.

Illumination Modeling

作者提出要显式 estimating 光照, 如果不这样做的话, 输出的 RGB 颜色会将光照信息 bake 进去, 使得生成的 mesh 难以利用. 为此, 作者提出了一个 Light net, estimate SG 光照. 因为 triplane encode 了场景的几何信息, 所以可以能够推断光照变化.

具体实现上, 作者使用 Transformer 输出的 分辨率的 triplane 作为输入, 使其通过 2 个 CNN 层, 接着进行 max pool,
最后通过一个 MLP 。 Light Net 输出 24 个 SG 的 grayscale amplitude values, 并使用 Softplus 以确保值为正数。这些 SG 的轴和锐度值保持固定, 其设置旨在覆盖整个球体。
利用这些振幅值, 作者实施了一种类似于 NeRD [4] 中使用的 deferred physically based rendering 方法.

此外, 作者的方法在训练阶段还引入了一个 lighting demodulation loss , 该损失函数旨在确保:一个具有 entirely white albedo 的物体上的光照,
能与输入图像的亮度紧密匹配。 lighting demodulation loss 强制学习到的光照与训练数据中观察到的光照条件保持一致.
这可以被视为一种 bias, 用于解决 appearance 和 shading 之间的 ambiguity.

Mesh Extraction and Refinement

为了从 triplane 中提取 mesh, 作者使用了 DMTet. 作者提出了两个 MLP head 来预测 vertex offsets 和 vertex normals. 这里受 MeshLRM 启发, 作者也单独使用了分离的 decoder MLP 来辅助这两个 head 的训练.
作者发现, vertex offset 能够反走样, 而 vertex normal 则能提升细节表现. 鉴于一开始 normal map 的预测不会太准确, 于是作者使用了 slerp 来稳定训练, 这是在一开始的 5K step 里发生.

然后引入了各种 loss 来训练这个 mesh extraction and refinement 模块:

  • $$\mathcal{L}_{\text{Nrmconsistency}}$$: 法线一致性损失
  • $$\mathcal{L}_{\text{Laplacian}}$$: Laplacian 平滑损失
  • $$\mathcal{L}_{\text{Offset}} = v_o^2$$: 顶点偏移正则化
  • $$\mathcal{L}_{\text{Nrmrepl}} = 1 - n \cdot \hat{n}$$: 法线复制损失
  • $$\mathcal{L}_{\text{Nrmsmooth}} = (\hat{n}(x) - \hat{n}(x + \epsilon))^2$$: 法线平滑损失

UV Unwrapping and Export

SF3D 模型的最终阶段是一个高效的导出流水线, 关键挑战在于传统 UV 展开的计算密集性, 这不符合快速生成的要求. 为此, 作者提出了一个基于立方体投影的展开方法. 该方法利用网格面法线独立决定投影方向, 实现了可并行化的展开过程.
具体实现上, 该方法执行 2D 三角形-三角形相交测试来处理 UV 图集中的遮挡, 并根据深度和接近度对相交面进行重新分配. 同时, 通过遵循径向 切线方向旋转 UV 岛以最小化阴影接缝. 接着, 通过 UV 展开将世界坐标和占用率烘焙到 UV 图集上
, 用于从 triplane 中查询反照率和表面法线. 为防止接缝伪影, 作者采用了一个迭代过程, 使用 部分卷积和最大池化来扩展 UV 边界, 确保纹理平滑向外混合.

之后, 作者将所有文件作为 glb 格式导出.

Overall Training and Loss Functions

由于直接在网格渲染任务上训练方法会产生不满意的结果, 作者首先在 NeRF 任务上进行了预训练. 完成预训练后, 模型过渡到网格训练,
将 NeRF 渲染替换为 differentiable mesh rendering 和基于 SG 的着色.

分步的损失函数如下所示:\begin{split}\mathcal{L}<em>{\rm render}&=\underbrace{ \lambda</em>{\rm MSE}}<em>{ 1 0}\mathcal{L}</em>{\rm MSE}+\underbrace{ \lambda_{\rm LPIPS}}<em>{ 2}\mathcal{L}</em>{\rm LPIPS}+\underbrace{\lambda_{ \rm Mask}}<em>{ 1 0}\mathcal{L}</em>{\rm Mask}\ \mathcal{L}<em>{\rm mesh}&=\underbrace{\lambda</em>{\rm Laplacian }}<em>{ 0.01}\mathcal{L}</em>{\rm Laplacian}+\underbrace{\lambda_{\rm Nrm Consistency}}<em>{ 0.001}\mathcal{L}</em>{\rm Nrm consistency}+\underbrace{\lambda_{\rm Offset}}<em>{ 0.1}\mathcal{L}</em>{\rm Offset}\ \mathcal{L}<em>{\rm shading}&=\underbrace{\lambda</em>{\rm Nrm repl}}<em>{ 0.2}\mathcal{L}</em>{\rm Nrm repl}\underbrace{\lambda_{\rm Nrm smooth}}<em>{ 0.02}\mathcal{L}</em>{\rm Nrm smooth}+\underbrace{\lambda_{\rm Demod}}<em>{ 0.01}\mathcal{L}</em>{\rm Demod}\end{split}
总损失为:

Results

作者在 GSO 和 OminiObject3D 数据集上对 SF3D 进行了评估. 结果如下图所示:
结果图
可以看到, SF3D 在视觉效果上明显优于其他方法, 并且在数值指标上也有显著提升.

在速度方面, 确实如作者所说, SF3D 的 UV 展开非常快, 只需 0.5s, 远快于其他方法的 10-30s.
速度对比

Conclusion

因此, 我似乎大致总结完了 SF3D 的主要结构, 从一张图像生成高质量的 mesh, 能不能对视频进行这样的操作呢? 我们看到这个任务里实际上用了大量生成的先验知识, 我在想一个完全
基于 image 的 3D reconstruction 方法, 能不能做到不依赖于这些先验知识?

ViT Transformer 的阅读?(应该算是阅读吧)

引言

在快要到 2026 年的今天, ViT 相比于当下的复杂的结构而言, 已经显得比较简单了, 我读论文的时候的最大感觉是, 它充满了 Transformer 在各领域蓬勃发展的野蛮生长的气息.
但是作为 Transformer 在 CV 领域的里程碑式的工作, 并且我作为这方面的初学者, 我觉着还是需要读一下这一篇论文An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale, 做一个简单的记录.

ViT 的整体结构

ViT 的整体结构如下图所示:
1
可以看到, 他的特殊处理是在于输入部分, 传统的 CNN 是通过 kernel 来滑动提取局部信息, 这样的一个 CNN 的输出很难直接送入 Transformer 中进行处理, 因为
Transformer 需要的是一个序列化的输入, 而 CNN 的输出是一个三维的 feature map.

因此, 相较于同期的其他处理, ViT 直接将输入图像划分为若干个小的 patch, 然后将每个 patch 展平并映射到一个固定维度的向量空间中, 形成一个序列化的输入, 这样就可以直接送入 Transformer 中进行处理.

  • 具体来说, 假设输入图像的尺寸为 (高度, 宽度, 通道数), 我们将其划分为大小为的若干个不重叠的 patch, 则总共会得到个 patch.
  • 每个 patch 被展平为一个向量, 并通过一个线性投影映射到一个维的向量空间中, 形成一个序列化的输入矩阵.
  • 此外, 为了让模型能够捕捉到位置信息, ViT 还引入了可学习的位置编码, 将其与输入序列相加, 形成最终的输入表示.
  • 接下来, 这个序列化的输入就可以直接送入标准的 Transformer Encoder 中进行处理, 经过多层的 Transformer Encoder Layer 的处理后, 得到最终的输出表示.

其具体的一个维数变换大概是这样:
为什么新加上的 class token work?

  • 因为在 transformer 中, 两两 token 之间是可以相互 attention 的, 因此 class token 可以和所有的 patch token 进行 attention, 从而聚合全局的信息, 这样我们就可以在最终的输出中使用 class token 来进行分类任务.

一些其他的细节

相对于 CNN 而言, ViT 的先验信息很少, 因此在中小数据集上的表现并不理想, 论文中提到需要在大规模数据集上进行预训练, 然后再进行微调, 才能取得较好的效果.

此外, ViT 的 attention 机制也与 Transformer 类似, 主要包括 Multi-Head Self-Attention 和 Feed-Forward Neural Network (FFN) 两个部分, 具体的计算过程与 Transformer 中的 Self-Attention 类似, 这里就不再赘述.

总的来说, ViT 通过将图像划分为 patch 并使用 Transformer 进行处理, 提供了一种新的思路来解决计算机视觉中的图像分类问题, 并且在大规模数据集上取得了优异的表现, 成为计算机视觉领域的重要里程碑.

回顾一下Transformer

引言

Transformer 在Attention is All You Need一文中被提出, 本来想读一下原文的, 但是时间并不太够, 因此我们这里就简单捋一下就行.

整体结构

Transformer 的整体结构如下图所示:
Transformer架构图
可以看到, 其主要由 Encoder 和 Decoder 两部分组成.

  • Transformer 的工作流程:
    • 首先获取输入每一个词的表示向量, 由单词的 embedding 和位置的 embedding 相加得到.
    • 然后将输入到 Encoder 中, 经过多层的 Encoder Layer 的处理, 得到编码后的表示.
      • 表示, 其中是序列长度, 是词向量的维度.
    • 接着将目标序列的输入输入到 Decoder 中, 经过多层的 Decoder Layer 的处理, 并结合 Encoder 的输出, 最终得到预测结果.如下图:
      Transformer Decoder架构图
      • 使用的过程中, 翻译到单词时, 需要通过Mask操作掩盖住未来的信息, 以防止模型在预测时看到未来的词.

OK, 下面我们来具体看看 Encoder Layer 和 Decoder Layer 的结构.

Self-Attention 机制

Transformer 的核心是 Self-Attention 机制, 其结构如下图所示:

Self-Attention架构图

  • 左侧为Encoder block
  • 右侧为Decoder block
  • 红圈中的部分为Multi-Head Attention机制, 是由多个 Self-Attention 组成的.
  1. 可以看到Encoder block包含一个Multi-Head Attention层.
  2. Decoder block包含两个Multi-Head Attention层, 第一个用于处理目标序列的输入, 第二个用于结合 Encoder 的输出.
  3. 每个 Attention 层后面都跟着一个**Feed-Forward Neural Network (FFN)**层.

因为Self-Attention机制是 Transformer 的核心, 因此我们重点来看一下它的计算过程.

dsa

上图是Self-Attention的计算流程图, 计算时需要用到三个矩阵: Query (), Key (), Value (), 实际过程中, 这三个矩阵都是通过输入的表示经过线性变换得到的.

Q, K, V 的计算

Self-Attention机制中, 对于输入的表示, 可以使用线性变换矩阵来计算:

2

实现

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
from math import sqrt


class SelfAttention(nn. Module):
def __init__(self, d_model, d_k, d_v):
" " "
input: X: (batch_size, n, d_model)
q: (batch_size, n, d_k)
k: (batch_size, n, d_k)
v: (batch_size, n, d_v)
" " "
super(SelfAttention, self).__init__()
self.d_k = d_k
self.W_Q = nn. Linear(d_model, d_k)
self.W_K = nn. Linear(d_model, d_k)
self.W_V = nn. Linear(d_model, d_v)
self._norm_factor = sqrt(d_k)

def forward(self, X):
Q = self.W_Q(X) # Q: (batch_size, n, d_k)
K = self.W_K(X) # K: (batch_size, n, d_k)
V = self.W_V(X) # V: (batch_size, n, d_v)

scores = torch.matmul(Q, K.transpose(-2, -1)) / sqrt(self.d_k) # (batch_size, n, n)
attn_weights = torch.softmax(scores, dim=-1) # (batch_size, n, n)
output = torch.matmul(attn_weights, V) # (n_batch_size, n, d_v)

return output

因此, 当我们得到了后, 就可以计算 Attention 的输出了:

3

得到之后, 使用 Softmax 函数对每一行进行归一化, 即每一行的和都变为 1.

4

最后将归一化后的权重矩阵与相乘, 得到最终的 Attention 输出.

5

上图中softmax矩阵的第一行可以理解为单词 1 对其他单词的关注程度, 最终单词 1 的输出等于所有单词的值加权求和.

Multi-Head Attention

上一步中, 我们已经知道怎么使用 Self-Attention 机制来计算 Attention 的输出了, 但是 Transformer 中使用的是Multi-Head Attention机制, 其结构如下图所示:

Multi-Head Attention架构图

从上图中可以看到Multi-Head Attention机制包含多个并行的 Self-Attention 头, 每个头都有自己的一组线性变换矩阵.

首先将输入分别传递到 h 个 Self-Attention 头中, 得到 h 个不同的 Attention 输出, 下面是 h = 8 的例子:

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
from math import sqrt

class MultiHeadAttention(nn. Module):
def __init__(self, d_model, d_k, d_v, h):
" " "
input: X: (batch_size, n, d_model)
q: (batch_size, d_model, d_k)
k: (batch_size, d_model, d_k)
v: (batch_size, d_model, d_v)
" " "
super(MultiHeadAttention, self).__init__()
self.h = h
self.d_k = d_k
self.d_v = d_v

self.W_Q = nn. ModuleList([nn. Linear(d_model, d_k) for _ in range(h)])
self.W_K = nn. ModuleList([nn. Linear(d_model, d_k) for _ in range(h)])
self.W_V = nn. ModuleList([nn. Linear(d_model, d_v) for _ in range(h)])
self.linear = nn. Linear(h * d_v, d_model)

def forward(self, X):
heads = []
for i in range(self.h):
Q = self.W_Q[i](X)
K = self.W_K[i](X)
V = self.W_V[i](X)

scores = torch.matmul(Q, K.transpose(-2, -1)) / sqrt(self.d_k)
attn_weights = torch.softmax(scores, dim=-1)
head = torch.matmul(attn_weights, V) # (batch_size, n, d_v)
heads.append(head)

concat_heads = torch.cat(heads, dim=-1) # (batch_size, n, h * d_v)
output = self.linear(concat_heads) # (batch_size, n, d_model)

return output

6

得到 8 个输出后, 将它们在最后一个维度上进行拼接, 得到一个新的表示, 然后通过一个线性变换矩阵将拼接后的表示映射回原始的维度.

7

可见Multi-Head Attention输出的矩阵维度与输入矩阵的维度相同, 这样就可以方便地将其与后续的层进行连接.

Other components

剩余的层比较简单, 因此不再赘述.

Decoder Layer

Decoder Layer 的结构如下图红框内所示:

8

其与 Encoder Layer 的主要区别在于多了一个Masked Multi-Head Attention层, 该层用于处理目标序列的输入, 并且在计算 Attention 时会掩盖住未来的信息, 以防止模型在预测时看到未来的词.

第一个Multi-Head Attention

我们重点解释一下 Mask 操作.

  1. 第一步是 Decoder 的输入矩阵和 Mask 矩阵, Mask 矩阵是一个上三角矩阵, 用于掩盖未来的信息.
  2. 接下来的操作和之前的 Self-Attention 机制类似, 通过输入矩阵计算., 之后计算.
  3. 然后将 Mask 矩阵应用到上, 将被掩盖的位置设置为负无穷大, 这样在 Softmax 计算时, 这些位置的权重会变为 0.

9

  1. 最后进行 Softmax 归一化, 并与相乘, 得到最终的 Attention 输出.

第二个Multi-Head Attention

第二个 Multi-Head Attention 层与 Encoder Layer 中的 Multi-Head Attention 层类似, 只是这里的来自于 Encoder 的输出, 而来自于第一个 Attention 层的输出.

根据 Encoder 的输出计算得到, 根据上一个 Attention 的输出计算得到, 然后计算 Attention 的输出.

时间复杂度分析

Transformer 的时间复杂度主要来自于 Self-Attention 机制. 对于一个长度为的序列, Self-Attention 的时间复杂度为, 其中是词向量的维度. 这是因为在计算时, 需要进行的矩阵乘法, 每个元素的计算涉及到维的向量点积.
因此, 对于一个包含层 Encoder 和 Decoder 的 Transformer 模型, 总的时间复杂度为.

总结

Transformer 应该是这样的.

SLAM Former 阅读

引言

最近几天读了SLAM-Former: Putting SLAM into One Transformer这篇很近很近的工作,本文笔记记录用于后续翻阅学习

首先, SLAM-Former 与之前读到的所有论文相似,都是致力于从 RGB 图像序列中恢复三维场景结构和相机位姿等属性的工作。但是与之前的工作(包含一个冗长复杂的 pipeline )不同,
SLAM-Former 对已有的 transformer 架构进行了大胆的改进,使之更适合进行重建任务,并在实验中得到了 competitive 的结果。

模型结构

SLAM-Former架构图

据作者所述, SLAM-Former 的主要 pipeline 由 frontend 和 backend 两部分组成,至于模型的 backbone , SLAM-Former 建立在一个 Transformer 架构之上,
而这个 Transformer aggregate 了 intraframe 和 interframe 的信息,并使用 task specific heads 预测不同的三维属性。
值得注意的是, 这个 Transformer 的输入与类似,对所有的输入的 image token 共享一个相同的 register tokens
从而使模型不依赖于一个不稳定的 reference frame 。

模型的 backbone 包含了层组合了 intra-frame attention 和 inter-frame attention
来联合捕捉图像内容和图像之间的关系。

此外, Front end 部分负责增量式的逐帧重建, back end 负责全局的点云对齐和相机优化,他们共享一个
Transformer backbone 。

Front end

图中大部分内容都是 front end 的处理细节,当一个新的 frame 输入时, frontend 首先会
决定其是否为 keyframe ,如果是的话,则会进行进一步处理。

当给定一个 frame sequence 时, frontend 将每一个 frame 映射到一个 map token 集合中:
这里, ${C_k}{K\in S}$表示之前 keyframe 的KV cache
代表着 keyframe 的索引集合,是当前 frame 的 map token, 作为该 frame 的
一个隐式神经表示。 同时新的 KV cache 也通过$C_t = Cache(f(\mathbb{F}t))$产生,
也会视情况被扩充到${C_k}
{K\in S}$中。

Keyframe detection

在上一步中我们已经对当前帧 generated 了 map token ,接下来我们需要决定是否为 keyframe.

作者采用了 pose head 来预测当前帧的 pose :

当当前 frame 的 relative pose 与最近的 keyframe 的 pose 之间的差异大于一个阈值时,
则将当前 frame 标记为 keyframe 。

但是作者在论文里又表明,在检测 frame 是否为 keyframe 时,他们并没有依赖 KV cache
, 而是直接应用了来检测,就相当于之前的 KV cache 是将该图片
与所有的 keyframe 进行 attention 计算,而这里则是只与最近的 keyframe 进行 attention 计算。
这样增加了效率并且避免了选取一个特定的 reference frame 。(这里似乎我没怎么懂跟特定的 reference frame 有什么关系)

Front end tracking and mapping

接着上一步,如果一个新的 frame 已经被认为是一个 keyframe ,我们就可以重新利用全部的 KV cache 来重新
计算他的 map token, 并更新 M, S.

好了, front end 到这里差不多结束了,作者说 frontend 只依赖于过去的 keyframe ,
使得其适合于 online 的 tracking ,然而, 这种处理顺序会导致误差累积和局部不一致,
为了解决这一问题,作者引入了一个 back end 模块来进行 global refinement.

Backend

Backend 的主要任务是 refine 所有的 frame 来达到全局的一致性。传统的
SLAM 系统通常会使用 loop closure 和 bundle adjustment 来实现这一点,
但是这些方法都非常的 costly, 作为对比,作者使用了一个 transformer-based 的
back end 来进行全局的优化。

作者认为这个设计的有效性在于 backend transformer 内部的 full attention 机制,
他的全局感受野使得模型能够完成误差纠正和结构一致性。

此外, 为了继承 backend refinement 的优势, frontend 和 backend 共享了 KV cache ,
使得 frontend 能够受益于 backend 的全局优化。

Training Strategy

与以往的一些论文不同, SLAM-Former 的创新点不止在于模型架构,也在于一些训练策略。

作者的目标是使一个 transformer 同时胜任 frontend 和 backend 的任务,为了达到这个目标,
作者用三种模式联合训练,每一个模式都对应着不同的输入输出对。

训练模式图

Training Frontend

Frontend 用了一个 causal mask 来确保每一个 frame 只能访问之前的 keyframe 。

然而,纯净的使用 causal mask 会自动的将第一帧作为 reference frame ,
作者又注意到党对两帧或更多帧进行联合操作时,没有单一的 refernce frame,
这避免了后续帧需要与 reference frame pose 相似的要求。

因此, 作者对前两帧使用了 full attention ,并同时对所有后续 frame 使用 causal mask,
在这种情况下, inference 时, keyframe detection 将最后一帧关键帧和当前的输入帧进行处理,
tracking and mapping 时, 前两个 keyframe 则会联合处理决定全局坐标。

作者的原文是:

For tracking and mapping, the
first two keyframes are jointly processed to determine the
global coordinate.

取前两帧的做法与之前的 tracking and mapping 部分提到的 use full KV cache 不符,
我感觉不怎么理解。

Training Frontend with Backend Cooperation

为了在 frontend 和 backend 之间建立联系,作者使用 maxed attention 来模拟 backend 和
cache sharing 的过程。

具体来说,采用混合注意力在一个统一的正向传播中同时完成地图精炼(后端/全注意力)和新数据处理,
并且前端的 casual attention 并非独立工作,而是以 KV cache 为条件,实现了高效且信息流一致的前端-后端协作,确保前端的实时处理结果能够立即对齐到后端修正后的全局结构。

woc 这什么花式操作啊

Training Backend

作者最后使用 full attention 来训练 backend transformer ,

Joint Training

在所有的三种模式中,三维属性均是由 task specific heads 预测的:

但值得注意的是, 并不像其他的工作一样, SLAM-Former 只预测每一帧的 local
pointmap 来避免设定一个特定的世界坐标系的需求,这倒是与非常相似。

剩下的 loss 函数都比较常规。
这三种模式都会在一个 batch 中共享权重依次训练。

Pipeline

在图片和叙述过程中, pipeline 已经是显而易见的,于是我便不再赘述。

Experimental Setup

本模型有 36 层 framewise 和 global attention 相结合的 transformer layer, 训了 10 个
epoch, 在 32 个 A100 上训练了 11 小时。可以可以。

Results

模型在 pose , tracking 和 reconstruction 等任务上都达到了很好的指标。数据冗长不再多说。
值得一提的是作者对 Front end 和 back end 的联系的理解。

back end assist front end 无疑是显而易见的,但是作者还发现 back end 同样也
benefit from front end, 作者解释了是因为 back end 使用了来自于 frontend 的
implicit 的顺序信息,从而使得 back end 能够更好地理解 frame 之间的关系。(迷)

总结

总之, SLAM-Former 通过对 transformer 架构的改进和训练策略的设计,
成功地实现了一个统一的模型来处理 SLAM 任务。

但 SLAM-Former 仍然存在一些局限性,比如说作者用 full attention 来替代传统的 loop
closure 和 bundle adjustment ,受限于 full attention 的计算复杂度,模型难以处理非常长的序列,
其次, frontend 不支持一个 local 的 inference , 因为在 inference 之前需要将所有的 KV cache 输入到 frontend 中。

此外, 文章中没有提到的是,我去看他们的 demo , 发现重建结果有很明显的分块化现象,目前不知是否与 transformer 的架构有关。
重建结果

此文撰写的时候, SLAM-Former 的代码尚未开源,期待后续的代码发布。

重返vggt

引言

这是本人在学了一些基础知识并做了一些实验之后, 察觉到之前对于一些经典论文的阅读并不充分, 于是决定重新阅读VGGT一文, 并写下这篇文章, 以供后续查阅.

首先, VGGT 是一个完全的前馈式神经网络用于多目重建任务, 通过 look into 他的代码, 可以看到基本上是没有什么 pipeline 的, 直接将图片输入网络, 然后输出各种三维属性, 并在作者的宣称下, 他们所预测的多个指标在存在 BA 的前提下
均达到
子领域的 SOTA 水平, 这一点非常厉害.

模型结构

VGGT 的 backbone 是一个标准的 transformer 结构, 首先接受大量图片作为输入, 首先通过一个 DINO 提取了分块的 feature, 然后将这些 feature 通过一个主体网络结构(包含了 Alternating frame-wise layer 和 global attention layer)
进行处理, 最后通过多个 task-specific heads 输出不同的三维属性.
VGGT架构图
接下来, 我们详细叙述各个细节部分:

Alternating attention frame-wise layer

据文章作者所述, 该 AA 机制与标准的 transformer attention 机制有所不同, 能够使 Transformer 以交替的方式聚焦每一帧和全局.

  • frame wise attention layer: 该层的 attention 仅在同一帧内进行, 也就是说, 每个 patch 只能与同一帧内的其他 patch 进行 attention 计算. 这样做的好处是能够更好地捕捉每一帧内部的局部特征.
  • global attention layer: 该层的 attention 在所有帧之间进行, 也就是说, 每个 patch 可以与所有帧内的其他 patch 进行 attention 计算. 这样做的好处是能够捕捉不同帧之间的全局特征.

另外值得一提的是, 作者采用了层的 AA 机制, 并通过消融实验证明了 AA 机制的有效性, 此外, 作者声称他们的架构并没有采用 cross attention, 只采用 self attention.

任务特定的heads

将输入的图片通过 backbone 网络处理后, 会得到一个全局的 feature 表示, 然后通过多个 task-specific heads 输出不同的三维属性. 值得注意的是, DINO 编码的 feature 并非直接输入到 AA 中, 而是被添加了一个额外的相机 token
和四个 register tokens进行增强, 然后将作为最终的输入.

此处值得注意的是, 第一帧的输入 token 是, 之后的帧的输入 token 是, 也就是说, 第一帧和之后的帧的 camera token 和 register token 是不同的.
但是作者说他们都是 learnable 的. 这使得模型能够将第一帧和其他帧区分开来, 并在第一个相机的坐标系下表示全局点云以及各种数据.但是, 经过 AA 层之后, 本来被赋予同一初值的 camera token 和 register
token 均会变为帧特定的, 这是因为 AA 层的 frame-wise attention layer 会使得每一帧的 token 在不同的计算中产生不同的表示.

最后遵循常规做法, register token 会被丢弃, camera token 和 image token 会被保留用于预测.

Camera parameter head

这个 head 从上图中的模型的 backbone 就可以看到, 他是将 camera token 通过 4 个 self-attention layers 进行处理, 然后通过一个 MLP 预测出每一帧的相机参数(包含内参和外参).

Dense Prediction

输出的 image token 在这里被使用, 用于预测 depth map , point map 和 tracking features . 更具体地来讲, 首先会通过一个 DPT head 转化为一个 dense feature map
, 之后每一个会通过一个的卷积层解析出 corresponding depth 和 point map. 另外, DPT 头同样也会输出 dense feature map 用于后续的 tracking,
在此同时, vggt 同样也会输出 confidence map 用于表示 depth 和 point 的置信度. 这个置信度用于后续的模型的 loss 计算和
真实预测时的 conf 输出.

Tracking

这一方面我并不打算去深入了解, 因此先跳过.

Training

Loss function

VGGT 的 loss function 包含多个部分, 主要包含以下几种:

  • Camera loss: 这个 loss 监管了相机参数$L_{camera} = \sum_{i=1}^{N} ||\hat{g}i - g_i||{\epsilon}$, 使用了 Huber loss.
  • Depth loss: 这个 loss 沿用了 dust3r 的 loss 设计$\mathcal{L}{\mathrm{depth}}=\sum{i=1}^N|\Sigma_i^D\odot(\hat{D}_i-D_i)|+|\Sigma_i^D\odot(\nabla\hat{D}_i-\nabla D_i)|-\alpha\log\Sigma_i^D$
  • Point loss: 这个 loss 同样沿用了 dust3r 的 loss 设计$\mathcal{L}{\mathrm{point}}=\sum{i=1}^N|\Sigma_i^P\odot(\hat{P}_i-P_i)|+|\Sigma_i^P\odot(\nabla\hat{P}_i-\nabla P_i)|-\beta\log\Sigma_i^P$
  • Tracking loss: 这个 loss 监管了 tracking feature 的质量, 具体细节我并不打算深入了解, 因此先跳过.

因此, 最终的 loss function 为:

坐标Normalization

如果缩放的话, 重建结果应该同样也是正确的, 为了消除这种不确定性, 作者采用了归一化进行处理. 首先将所有量表示在第一个相机的坐标系中, 然后计算所有点的平均欧氏距离, 然后利用该尺度归一化相机平移, 点云坐标和深度值.

值得注意的是, 作者没有对预测结果施加任何归一化, 相反强制模型去学习预测归一化后的值, 这样做的好处是能够使得模型更好地适应不同尺度的场景.

Details

我难以想象训练的规模, 按照作者所述, 这一个 transformer 模型包含了的参数, 在 64 块 A100 上训练了 9 天, 属实是第一次见了.

另外, 训练的数据集之多也是难以想象:
dsfa
有点离谱了.

结论

vggt 的指标基本上达到 SOTA 水平, 但是值得注意的是, 直接的输出并没有达到, 作者加入了 BA 优化之后才达到了 SOTA, 因为 BA 是一个 costly 的优化过程, 因此我觉着这一方面或许还可以改进? 作者在论文中提到了
应用 diffentiable BA 的可行性, 但是也因为 BA 的计算量过大, 因此并没有进行进一步的尝试.

此外, VGGT 向我们展示了不需要一个复杂的 pipeline 也可以进行高质量的多目重建说你呢, SLAM3R, 我 TM 的快改吐了, 再结合最近发布的 SLAM Former, 我觉着这是一个很有意义的方向.

非常重要的是, vggt 证明了联合预测多个任务是有益的, 虽然并没有在 loss 阶段进行互相的监督, 但是通过多个任务的单独监督, 使得模型学到了更好的表示,

此外, vggt 另一个重要的发现是, 通过 depth 和 pose 反解出来的点云比直接预测的点云要好.

ok, 让我们把仓库链接抬出来:

另外, 这是真的可以的嘛?

iasdf

论文阅读记录:reloc3r

引言

最近,我们在尝试将 SLAM3R 进行使之输出不限于点云,还有位姿估计、深度图、局部定位等结果的改造,大体上来讲,我对这个改造的感觉就是端了一个类似于 VGGT 的重建结构出来。于是,为了了解一下现在利用 transformer 做位姿估计的工作,我选择了组里的学长的论文:Reloc3r: Large-Scale Training of Relative Camera Pose Regression for
Generalizable, Fast, and Accurate Visual Localization
来阅读,本文用来记录对这个模型的理解以及个人的感受。

首先,论文上来又是经典的针砭时弊环节🤣,论文指出了之前的工作分为APRRPR两种方式,但是各有各的缺点:

  • APR: 绝对位姿回归,它主要是从图片中直接回归位姿,优点是有更高的推理速度和准确度,但是它的缺点也很明显:大多数这种方法都是针对场景有效,并且在训练时需要密集点图,这限制了他们在真实世界中投入应用。
  • RPR: 相对位姿回归:它是估计一对图片的相对位姿,相比于绝对位姿回归的好处在于它不需要密集点图的训练,但是,它的准确度表现非常差,远远不及 APR 。

为了解决这些问题,论文提出了一种新型的对称有效的网络,并在一个特大的数据集上进行训练,最终得到了state of the art的水平。

模型结构

d
模型主要由两个模块组成:相对位姿回归网络和运动平均模块

相对位姿回归网络

这个网络如图片左边所示,是由两个完全相同的 vit transformer 分支构成,并且两个分支共享权重,这有效的消除了输入顺序带来的不利影响,代表着训练得到了大幅简化,并且提高了计算速度和存储效率。

细节在于通过 ViT encoder 图片被编码成特征序列之后,他们之后通过的 decoder 是 Cross attention 的,这能够使模型同时理解两张图片之中的信息,最后, decoder 输出的信息会经过 Pose regression Head
这个 head 会将 decoder 的输出转化为相对旋转和相对位移,其中相对旋转一开始会以一个 9 维向量来表示,随后通过 SVD 分解完成得到旋转矩阵。

因此,我们这个网络最后的输出就是图 A 相对于图 B 的位姿变换和图 B 相对于图 A 的位姿变换。

运动平均模块

理论上来说,第一步网络的输出的精度应当已经达标,并且网络同时输出的两个相对位姿变换矩阵应该互你,从经验上来看,这两个位姿变换矩阵的精度相似,因此我们直接选择了一个非学习的模块用于转换两个输出的相对位姿。

其中有一些细节:

  • 旋转平均的处理:模型将多个对于一张图片的相对旋转转换为绝对旋转处理,并使用四元数表示,最终选取中位数来作为绝对旋转,增强了模型的鲁棒性。
  • 相机中心三角化的处理:因为几何点的平均/中位数化并不可解,因此我们转而通过最小二乘法寻找到所有平移方向距离之和最小的点,将这个点作为相机预测的光心。

损失函数

模型的损失包括两方面:旋转损失和位移损失。文章将他们都表示成了角度:
然后将两者相加得到最后的总损失。显然这是一种无尺度的方法,解决了不同数据集之间度量尺度不统一的问题。

分析流程

该模型的处理流程大致如下:

  • 输入: 一个查询图像和一个带位姿数据的数据库.
  • 检索: 使用 NetVLAD 在数据库中为检索出 Top-K 个最相似的图像.
  • 相对位姿预测:将个图像对逐一送入相对位姿回归网络,得到个相对位姿估计(旋转矩阵和无尺度的平移方向)
  • 绝对位姿聚合:
    • 利用数据库图像已知的绝对位姿旋转和预测的相对旋转计算出个图像的绝对旋转统计,然后通过取中值得到最终的旋转
    • 利用所有有效的图像对和估计的进行相机中心的三角化,然后通过最小二乘法解出相机中心,从而得到所有的位姿估计。
  • 输出

数据分析

第一次写数据分析模块🧐,有所不完善请原谅🥺。

性能评价指标

相对位姿

rra

  • RRA@15, RTA@15, mAA@30,分别是相对旋转、相对位移在 15°阈值内的准确度、以及 30°阈值下的平均准确率。

auc

  • AUC@5°/10°/20°: 位姿误差(旋转和平移角度误差的最小值)在 5°/10°/20°阈值下的精度曲线下面积 。

绝对位姿

平移和旋转中位数误差( m and degree ):
abso

有效性验证

查看上面的图表便可看出,模型在个主流的公开数据集 (ScanNet1500, RealEstate 10K, ACID, CO3Dv2, 7 Scenes, Cambridge Landmarks) 上与当前最先进的方法(包括非回归和回归两大类)进行全面对比:

  • 相对位姿估计: 在 ScanNet1500, RealEstate 10K 和 ACID 数据集上, Reloc3r 显著优于所有其他相对位姿回归(PR)方法,并且性能达到甚至超过了顶尖的非 PR 方法,同时速度快了几个数量级(例如,在 ScanNet 上比 NoPoSplat 快 50 倍以上) 。在 CO3Dv2 数据集上, Reloc3r 在所有多视图评估指标上均达到 SOTA 。
  • 视觉定位
    • 在 7Scenes (室内) 数据集上, Reloc3r 的平均误差为 0.04m / 1.02°,超越了所有之前在新场景上评估的 RPR 方法,并达到了与需要场景专门训练的 APR 方法相媲美的精度。
    • 在 Cambridge Landmarks (室外) 数据集上, Reloc3r 同样超越了所有 RPR 方法,与之前的 SOTA RPR 方法相比,平均位姿误差降低了约一半,其平均旋转误差甚至优于所有 APR 方法 。

消融实验

lab

  • 对称性
    论文另外训练了一个使用了独立的两个 ViT 分支的相对位姿回归网络,显而易见性能是弱于 default 版本的

  • 不含尺度信息
    同样训练了一个同时输出尺度信息的模型,显而易见其准确性比不对称还差。

有趣的发现

论文在查看 decoder 的交叉熵注意力图时发现:模型在没有直接监督的情况下,自发地学会了在图像对之间建立有意义的块级别匹配。(如下图)
finding

局限性

作者发现当检索到的数据库图像与目标图像共线的时候,运动平均模块并不能恢复尺度。

总结

Reloc3r 使用了一个相当简洁的模型结构完成了 SOTA 水平,但其付出的代价是非常庞大的训练数据。这似乎在向我们说明只要数据够多够大,我们便可以训练出足够高性能的模型,这似乎在
告诉我们多造一下 SLAM3R V2 的数据🤣。

OK ,这篇论文的代码仓库如下:

论文阅读记录:Fast3R

引言

OK, 本人昨天又读了一篇 3D reconstruction 方向的论文:Fast3R: Towards 3D Reconstruction of 1000+ Images in One Forward Pass,因此写下此篇 Blog 分享自己的理解与发现。

Fast3R 从本质上来说感觉和 SLAM3R 解决的是一类问题,都是对原本 DUst3R 存在的局限性:一次只能对两张图片进行处理,如果对多张图片进行处理的话, DUst3R 则是选择进行两两配对进行重建,最后进行全局坐标下的对齐,显然这将会是一个
的过程。而 Fast3R 提出了对于打乱序列的多张图片( 1000+)的处理方法, SLAM3R 则是解决了由视频进行重建的方法。感觉两者的本质上的区别就是 input 的图像集是否有序,后续两者的网络结构区别也正是在此。

从论文的 introduction 上来看,他们主要做了以下三方面的贡献:

  • 创建了 Fast3R ,一个基于 Transformer 的对多目图片重建点图的端到端的模型,据论文所述,它在速度上取得显著提升,并且可以规模化计算。
  • 展示了随着训练时视角增多,模型表现也会加强。另外,当推理时视角增多时,每张视角重建结果的精确度也会提升。并且模型可以处理比训练时多得多的模型。
  • 在相机的位姿定位上达到了SOTA水平,另外也展现出了极快的速度。

好的,现在到了我们喜闻乐见的介绍模型环节啦!

模型

Fast3R 给出了一个看起来在推理环境就很庞大的结构图:
Fast3R

问题定义

从图中右边就可以看到, Fast3R 采用了两个头: Global Head 和 Local Head 来处理输出的 token ,因此可见, Fast3R 为每张图片预测了两个点图:本地坐标系下的点图和全局坐标系下的点图,可以用公式表示:
指代的是点图的置信度。

值得注意的是,全局坐标系值得是第一张图片的坐标系,本地坐标系是每个对应图片的坐标系。(虽然 Fast3R 并没有次序的概念,但其也需要一个切入点,所以随机选取了一张图片作为第一张图片)

训练对象

类似于 Dust3R , Fast3R 的损失函数分别采用了同样的处理方法处理本地点图和全局点图两部分:
阅读其论文,发现其与 Dust3R 的损失函数基本一致,因此不多赘述。

模型架构

Image Encoder

由上图所示,我们可以看到每一个输入的图片都会经过一个共享权重的 Vit Encoder 生成对应的 token 序列 ,即:
论文中提到,他们使用了和 Dust3R 相同的 Encoder : CroCo ViT ,但是他们提到了 DINOv2 的表现与之相似。

另外,在把 token 传入 fusion transformer 之前,作者为每一个 token 添加了一个一维的位置编码,目的是让模型知道哪些图像块来自于同一张图片,并且帮助模型认出上文标定的第一张图片。这同样也能让模型隐式地去理解这些图片里反映的相机位姿。

Fusion Transformer

模型中大多数计算都发生在 Fusion Transformer 里面,作者使用了一个类似于ViT-L的 24 层的 transformer 作为这一模块的主体。它将来自所有的视角的 token 作为输入,并且通过全连接的自注意力机制进行处理,使的模型能够理解所有视角的信息,远超 Dust3R 能理解的两个视角的信息。

Pointmap Decoding Heads

最后, Fast3R 使用了两个独立的 DPT 解码头将 Fusion Transformer 的输出解码为点图,即图片中右边部分。

位置编码

论文最后的目标是进行多图片处理,并且实现推理时的可以处理的图片数量远远多于训练时的图片数量,因此我们就要考虑推理时为 token 嵌入位置编码的手段。

  • 一开始,文章尝试使用相同的球谐函数嵌入编码,文章中又提到:在 LLM 中,这种方法导致性能不佳。果不其然,在文章的初步实现中,他们同样发现当输入图像数量超过训练时使用图像的数量时,模型的效果并不好。
  • 因此,文章借鉴了大预言模型中的位置插值方法:在训练时从一个集合均匀随机抽取个索引,这样模型便被迫去学习处理更大范围的索引。

对于 transformer 来说,这种策略感觉和 masking 没什么区别,文章中也说:

This strategy enables Fast3R to handle N = 1000 images during inference, even if only trained with N = 20 images.

有效利用显存

从模型架构的图片来看,这看起来就是一个占用很大显存的模型。但是文章提出,由于模型的特点( meta-architecture ),这个模型可以广泛使用各种并行化以及分片技术。
文章提出他们在训练和推理的时候利用了两种不同形式的并行化和 FlashAttention 技术,并认为随着未来的技术成熟他们的模型会持续受益(废话)。

具体采用的策略来实现高效训练。

首先,使用 FlashAttention 来提高时间和内存效率。即便如此,当 N>16 时,一个朴素的实现即使在批量大小为 1 的情况下也会耗尽内存( 128 x A100-80GB 啊,离大谱)。
因此,后来使用了 DeepSpeed ZeRO stage 2 训练,将优化器状态、动量估计和梯度在不同的机器上进行分区。这样就能够以每个数据样本最多 N=28 个视角进行训练,同时每个 GPU 的批量大小为 1 。

模型效果:

miaomiao
就模型所给出的表格而言,确实是达到了 Sota 水平。

在推理速度上,由于所做的各种优化,它也得到了显著的提升。

但是,其实我更好奇的是它跟同期的 SLAM3R 的性能比较,阅读论文,发现两者并没有过同一个精度指标的比较,通过本人的本地测试,发现对于一个很小的数据集( 82 张有序图片),两者速度上并没有太多差距,但是重建质量上来说
, SLAM3R 的质量远超 Fast3R 。这很好的符合了 SLAM3R 对有序图像序列进行针对性重建的特性,而 fast3R 是对一个随机图像重建的方法。

所以,当我看到 Fast3R 的 demo 里有对视频重建的选项时,我感觉并不适合。因为从直觉上来说,人们从一个没有次序的图像集中理解环境的过程也大致遵循一个先排序再重建的过程,也就是说人们对无次序的图片集中还原 3D 场景的难度远大于从视频中还原场景的难度。

论文中也提到了局限性的存在:

  • 缺少包含大型场景的数据因而缺少在此类场景下的泛化能力。
  • 没有更好的位置嵌入,不过论文提出可以参考那些能处理极长上下文序列的大语言模型。

ok ,关于 Fast3R 我就处理到这里,欸,我觉着或许我以后应该认真去看看训练细节和实验部分,总去看模型结构有种高屋建瓴的感觉,还是应该多看看代码( x

论文阅读记录:MAst3R

引言

经过一周的对SLAM3R进行 online 以及可视化 demo 改造的低效率劳作且工作完成,我终于有时间来补档我这篇早在近两个周之前就读完的论文Grounding Image Matching in 3D with MASt3R

读完这篇论文之后,我的第一感觉就是:这是一个 DUst3R 的修补模型,他并没有太多的像 DUst3R 那样的开创性地将 transformer 运用于双目三维重建那样的举动,而是在 DUst3R 模型上进行了
少许修补,并提出了少许修补中的一些独创性方法,感觉是一篇介绍 small trick 的论文。同时,我们似乎也可以这么说: MAst3R 发现本聚焦于三维重建任务的 DUst3R 在像素匹配问题上同样达到了 SOTA
于是, MAst3R 将 DUst3R 稍加改造,得到了一个在像素匹配上表现更强的模型 MAst3R.

模型介绍

MASt3R 的模型结构与 Dust3R 大致相同:
mast3r

Encoder

与 DUst3R 相同, MAst3R 的 encoder 部分同样是由 ViT 组成的,且与 DUst3R 相同的是, MAst3R 的 encoder 部分也是共享权重的。
就像这样:

Decoder

MASt3R 的 Decoder 同样采用了 cross-attention 的机制,这能使得 MAst3R 能够理解同一像素在不同视角下的信息,有助于后续进行像素匹配。

Heads

对于 Dust3R 来说,他只有一个 head ,直接将 decoder 的输出转化为点图信息和置信度(上图灰色部分)

3D Heads

MASt3R 对这个 head 基本上与 DUst3R 的 head 相同,都是将 decoder 的输出转化为点图信息和置信度。

Matching Heads

MASt3R 在此基础上又增加了一个 head ,专门用于像素匹配任务(上图蓝色部分),这个头部由一个简单的两层的 MLP 组成,使用了 GELU 作为激活函数,另外在处理完后进行归一化处理,负责输出两张密集的特征图:

Loss

Mast3R 的损失函数由两部分组成:

3D Loss

MAst3R 的 3D Loss 与 DUst3R 的 3D Loss 基本相同,都是由点图的 L1 损失和置信度的交叉熵损失组成。
但是, MAst3R 在计算回归损失的时候,原本的 DUst3R 计算公式是这样的:
MAst3R 认为在它的应用场景中,并不鼓励尺度不变性,而更多的是需要绝对的尺度一致性,因此 MAst3R 将上式改为了:
因此, MAst3R 的 3D Loss 计算公式为:

Matching Loss

这个损失函数是对 Matching Head 输出的特征图进行监督的,基本思想是:我们鼓励一个图像中的一个特征匹配符,最多与另一张图像中代表同一个 3D 点的特征匹配符进行匹配,
需要注意的是,这个匹配本质上是一个交叉熵分类损失,当网络猜到正确的像素(而非邻近的像素)时,才会得到奖励。

具体实现上,我们利用了 InfoNCE loss 来实现这个想法,其作用于一组对应关系,具体公式如下:
其中,是一个温度参数,分别是图像 1 和图像 2 中所有像素的集合。

这极大地鼓励了网络进行高精度匹配。

最后,两个损失函数被结合起来,形成了 MAst3R 的总损失函数:
有了上述模型与 Loss 就可以训练了,但是网络的输出还需要经过一些处理,才能得到需要的匹配关系。注意,网络只输出了 PointMap 和每个像素的 LocalFeature ,而期望得到的是两个图像之间的像素点级别的匹配,匹配相关的部分就是图中新增的 NN 模块。

快速互惠匹配

当给定两张特定的预测图时,我们的目标是提取一组可靠的像素对应关系,即互惠最近邻。

数学定义:

  • 互惠最近邻集合由公式定义:̲</li> <li>这里的$…" style="color:#cc0000">\mathcal{M}={(i,j)|j=\mathrm{NN}_2(D_i^1)\mathrm{<del>and</del>}i=\mathrm{NN}_1(D_j^2)} $$</li> <li>这里的表示在特征图中与特征距离最近的特征的索引。其数学定义为:
    \mathrm{NN}_A(D_j^B)=\arg\min_i|D_i^A-D_j^B|</li> </ul> <h3 id="传统方法"><a href="#传统方法" class="headerlink" title="传统方法"></a>传统方法</h3><p>传统上,计算互惠最近邻的方法是通过暴力搜索来实现的,这种方法的时间复杂度为,这在高分辨率图像中是不可行的。</p> <p>虽然优化最近邻搜索是可能的,例如使用 <strong>K-d</strong> 树,但这种优化在高维特征空间中通常会变得非常低效,在某些情况下,其速度甚至比 MASt3R 输出的推理时间慢几个数量级。</p> <h3 id="MASt3R的方法"><a href="#MASt3R的方法" class="headerlink" title="MASt3R的方法"></a>MASt3R的方法</h3><p>MASt3R 提出了一种基于<strong>子采样</strong>*的快速方法。</p> <p>这个方法是从一个稀疏的第一张图片的像素集合出发的,通过找到这个集合中每个像素在第二张图片上的最近邻得到最近邻集合,然后再从这个最近邻集合中找到每个像素在第一张图片上的最近邻,最后通过检查互惠性来得到最终的互惠最近邻集合。</p> <p>整个过程可以表示为:
    U^t\mapsto[\mathrm{NN}2(D_u^1)]{u\in U^t}=V^t\mapsto[\mathrm{NN}1(D_v^2)]{v\in V^t}=U^{t+1}
    $$

    • 时,这些像素形成了一个闭环,并被收集为一组互惠匹配
    • 对于下一次迭代,那些已经收敛的像素(即 )会被过滤掉,新的 更新为
    • 这个过程会迭代固定的次数,直到所有的对应关系都收敛到稳定的(互惠)对为止。
    • 最终的输出对应关系集合 由所有互惠匹配集合的拼接而成:

    这种快速匹配算法的总体复杂度大概是,相比朴素方法的,有了显著的提升。
    chart

    具体证明过程可以参考论文的附录部分。

    个人总结

    MAst3R 这篇论文的阅读,本人自己对 mast3r 的理解,以及对 transformer 在三维重建任务中应用的理解,基本上就到这里了,当然, mast3r 的实验部分我并没有过多地去阅读,因为我觉得 mast3r 的实验部分并没有太多的创新性,基本上都是在验证 mast3r 在各个任务上都达到了 SOTA 的水平。
    我个人觉得 mast3r 的创新点主要有以下几点:

    1. 在 DUst3R 的基础上,增加了一个匹配头,用于像素匹配任务,这个头部的设计比较简单,但是效果却非常好。
    2. 在 3D 损失函数中,改变了点图回归损失的计算方式,使其更加适合绝对尺度一致性的任务。
    3. 提出了一个快速的互惠匹配算法,大大提升了匹配的效率。
      总的来说, MAst3R 是一篇比较实用的论文,通过一些小的改动和创新,使得模型在多个任务上都达到了 SOTA 的水平,值得学习和借鉴。

    另外, MAst3R 的代码也已经开源:

    喵喵补坑完毕,虽然感觉说了和没说一样😭