RealCat

· 99 min read

论文解读:LightGlue,让稀疏特征匹配随难度自适应地快

TL;DR LightGlue 是 ETH Zürich 与 Microsoft Mixed Reality & AI Lab 的 Lindenberger、Sarlin、Pollefeys 在 ICCV 2023 提出的稀疏局部特征匹配器[1]。它保留 SuperGlue 的「自注意力 + 交叉注意力」骨架,但把三个最贵的部件换掉:自注意力用旋转相对位置编码,交叉注意力只算一次相似度、双向共用,匹配头用「可匹配性 sigmoid × 双 softmax」替代 Sinkhorn 与 dustbin;同时每一层都输出匹配和置信度,于是简单图像对可以在第 3 层退出,低匹配概率的点在中间层被剪掉。MegaDepth-1500 上 SuperPoint+LightGlue 的 LO-RANSAC AUC@5° 为 66.7、匹配耗时 44.2 ms,SuperGlue 为 65.8、70.0 ms;开启自适应后为 66.3、31.4 ms。Aachen Day-Night 夜间召回 87.8 / 93.9 / 100,吞吐 17.2 对/秒(优化版 26.1),SuperGlue 为 86.7 / 92.9 / 100、6.5 对/秒。代码、权重与训练库以 Apache-2.0 开源(SuperPoint 权重例外)。

一、动机:SuperGlue 的精度,是用固定算力换来的

SuperGlue 在 2020 年把稀疏特征匹配变成了一个端到端可学习的图神经网络:两张图的特征互相注意,再用最优传输(optimal transport)解一个部分分配问题[2]。它学到的先验很强,跨视角、跨光照都稳,之后几年一直是稀疏匹配的默认答案。但它的代价也很清楚:注意力对关键点数二次增长,Sinkhorn 要迭代很多次,而且无论一对图是相邻视频帧还是昼夜跨视角,网络都跑满固定的 9 层。

这在部署时会变成两个具体问题。第一,平均算力被最难的图像对拖住。SLAM、视频重建、大规模 SfM 里大量图像对其实很容易匹配,视觉重叠大、外观变化小,前几层已经能给出稳定答案,后面几层是在重复计算。第二,很多关键点根本不参与匹配。遮挡、非重复纹理、视场外,这些点在另一张图里没有对应点,让它们继续参与注意力只会增加开销。SuperGlue 对这两种情况一视同仁。

在 LightGlue 之前,已经有两类效率改进。SGMNet 用种子匹配限制注意力范围,ClusterGNN 把关键点聚类后分簇计算[3, 4];它们能降低大输入下的计算量,但标准输入尺寸收益有限,而且在最难的条件上往往追不回 SuperGlue 的精度。另一条路是 LoFTR 代表的稠密匹配器,在稠密网格上做 Transformer,鲁棒性很好,但元素数量多、分辨率受限、推理慢,MatchFormer、ASpanFormer 也在这个权衡里[5, 6, 7]。LightGlue 没有选「把网络整体做小」或「换成稠密网格」,而是选了一个更直接的目标:让网络根据每一对图的难度,自己决定算多深、算多宽。

这个目标建立在两个观察上。其一,匹配难度差异很大:简单对在前几层就已经收敛,难对才需要更深的上下文。其二,逐层预测可以很便宜:如果把匹配头做得足够轻,每一层都能给出一个分配矩阵,那么「现在是否已经够好」就可以由网络自己判断。Figure 2 是论文的开场例子:简单对停在第 3 层,16.9 ms;困难对停在第 8 层,32.3 ms。同一个网络,计算量随难度变化。

LightGlue 的速度-精度权衡
图 1MegaDepth-1500 上的相对位姿精度与吞吐。星号是优化后的自适应版,L=3/5/7/9 是固定层数。LightGlue 在同等精度下比 SuperGlue、LoFTR、MatchFormer 快一个量级;优化后的自适应版在 46 对/秒附近,精度已接近 LoFTR,速度约 8 倍。

把论文的贡献拆开,是四件事,后面几节按这个顺序讲:

  1. 更快的注意力:自注意力只编码相对位置,交叉注意力只算一次相似度、双向共用。
  2. 更轻的匹配头:把「两个点像不像」和「这个点是否可匹配」拆成两个输出,用双 softmax 代替 Sinkhorn。
  3. 自适应推理:逐层置信度决定何时停(depth),逐层 matchability 决定哪些点不再参与(width)。
  4. 更容易训练:每一层都直接监督,浅层不再是「只为最后一层服务」的中间状态。

二、核心方法

2.1 总体架构:每一层都回答一次「谁和谁匹配」

输入是两张图各自的一组稀疏局部特征。每个特征 由归一化到 的坐标 和描述子 组成;网络为每个点维护一个状态 ,初始化为 。一层由一次自注意力(self-attention)和一次交叉注意力(cross-attention)组成,每次注意力的输出通过一个 MLP 更新状态:

其中 是从源图像 聚合来的消息, 表示拼接。自注意力里 ,交叉注意力里 是另一张图。MLP 是一个隐藏层、LayerNorm、GELU、线性投影的残差结构;默认 层、4 个头、。

LightGlue 架构
图 3输入特征先做一次线性投影,然后进入 个结构相同的层。每层先自注意力、再交叉注意力;层末的置信度分类器 决定是否提前退出。若不退出,低 matchability 的点被剪掉,只把剩下的点送入下一层。最终层用 pairwise 相似度和 unary matchability 算分配矩阵。

关键设计是:匹配头不只在最后一层出现。每一层结束后,网络都可以用当前状态算一个分配矩阵,并额外算每个点的置信度。轻量匹配头是后面所有自适应机制的前提:没有逐层预测,就无从判断「现在是否已经够好」;没有逐层监督,浅层预测也不可信。论文用深监督把这两件事绑在一起,2.5 节会展开。

2.2 自注意力:只编码相对位置,而且只旋转 q、k

SuperGlue 在输入端给每个描述子加一个学到的绝对位置编码,之后所有层共享。LightGlue 改成在每一次自注意力里注入相对位置,用的是 RoFormer 提出的旋转位置编码(rotary position embedding, RoPE)[8]。对点 和 ,注意力分数写成:

其中 是状态经线性变换得到的 query/key, 是相对位置 的旋转矩阵。具体做法是把 维空间分成 个二维子空间,每个子空间用一个学到的二维基 把相对位置投影成一个角度,再旋转:

图 3 中的 就是这一步:位置编码不进入 value,只旋转 query 和 key;编码对所有层相同,因此只算一次并缓存。

这个设计有三个直接后果:

  • 只依赖相对位置。 在图像整体平移时不变,这正是投影几何里希望的性质;绝对坐标在不同相机、不同裁剪之间不可比。
  • 不引入二次的 positional bias 矩阵。RoPE 通过旋转 实现相对性,不需要为每对点存一个偏置,复杂度仍是 的注意力本身。
  • 不在交叉注意力里加位置。两张图的相对位置没有几何意义;跨图的空间关系应该由特征内容和自注意力聚合出的上下文来学,而不是靠坐标差。

消融很干净:把旋转相对编码换成绝对位置编码,合成单应测试集上的 precision/recall 从 86.8/96.3 降到 84.2/94.7,耗时只从 19.4 ms 降到 18.7 ms(表 4)。也就是说,相对编码几乎不付时间代价,却换来 +2.6 precision、+1.6 recall。

代码:自注意力块(官方 lightglue/lightglue.py,原文照录)
class SelfBlock(nn.Module):
    def __init__(
        self, embed_dim: int, num_heads: int, flash: bool = False, bias: bool = True
    ) -> None:
        super().__init__()
        self.embed_dim = embed_dim
        self.num_heads = num_heads
        assert self.embed_dim % num_heads == 0
        self.head_dim = self.embed_dim // num_heads
        self.Wqkv = nn.Linear(embed_dim, 3 * embed_dim, bias=bias)
        self.inner_attn = Attention(flash)
        self.out_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
        self.ffn = nn.Sequential(
            nn.Linear(2 * embed_dim, 2 * embed_dim),
            nn.LayerNorm(2 * embed_dim, elementwise_affine=True),
            nn.GELU(),
            nn.Linear(2 * embed_dim, embed_dim),
        )

    def forward(
        self,
        x: torch.Tensor,
        encoding: torch.Tensor,
        mask: Optional[torch.Tensor] = None,
    ) -> torch.Tensor:
        qkv = self.Wqkv(x)
        qkv = qkv.unflatten(-1, (self.num_heads, -1, 3)).transpose(1, 2)
        q, k, v = qkv[..., 0], qkv[..., 1], qkv[..., 2]
        q = apply_cached_rotary_emb(encoding, q)
        k = apply_cached_rotary_emb(encoding, k)
        context = self.inner_attn(q, k, v, mask=mask)
        message = self.out_proj(context.transpose(1, 2).flatten(start_dim=-2))
        return x + self.ffn(torch.cat([x, message], -1))

2.3 交叉注意力:相似度只算一次,双向共用

交叉注意力是另一个大开销。标准做法是分别算 和 两个方向的注意力,每个方向都要一套 query/key/value 和一份相似度矩阵。LightGlue 注意到,交叉注意力里其实不需要 query:每个点只需要一个 key,方向 的分数和反方向是同一个内积:

于是只算一次 的相似度矩阵,行 softmax 给 ,列 softmax 给 ,消息分别聚合。论文把这种结构叫双向交叉注意力(bidirectional cross-attention)。复杂度仍是 ,但常数减半;在表 4 的消融里,完整交叉注意力是 86.6/96.1、22.8 ms,双向版是 86.8/96.3、19.4 ms,精度不降、耗时少约 15%。论文正文说节省 20%,差异来自测量口径。

更重要的是它和后面的匹配头配合:交叉注意力只需要一组 key,说明「哪些点值得和另一张图比较」可以由同一组表示同时服务两个方向。剪枝一个点,两个方向都省。

代码:双向交叉注意力(官方 lightglue/lightglue.py,原文节选:forward)
    def forward(
        self, x0: torch.Tensor, x1: torch.Tensor, mask: Optional[torch.Tensor] = None
    ) -> List[torch.Tensor]:
        qk0, qk1 = self.map_(self.to_qk, x0, x1)
        v0, v1 = self.map_(self.to_v, x0, x1)
        qk0, qk1, v0, v1 = map(
            lambda t: t.unflatten(-1, (self.heads, -1)).transpose(1, 2),
            (qk0, qk1, v0, v1),
        )
        if self.flash is not None and qk0.device.type == "cuda":
            m0 = self.flash(qk0, qk1, v1, mask)
            m1 = self.flash(
                qk1, qk0, v0, mask.transpose(-1, -2) if mask is not None else None
            )
        else:
            qk0, qk1 = qk0 * self.scale**0.5, qk1 * self.scale**0.5
            sim = torch.einsum("bhid, bhjd -> bhij", qk0, qk1)
            if mask is not None:
                sim = sim.masked_fill(~mask, -float("inf"))
            attn01 = F.softmax(sim, dim=-1)
            attn10 = F.softmax(sim.transpose(-2, -1).contiguous(), dim=-1)
            m0 = torch.einsum("bhij, bhjd -> bhid", attn01, v1)
            m1 = torch.einsum("bhji, bhjd -> bhid", attn10.transpose(-2, -1), v0)
            if mask is not None:
                m0, m1 = m0.nan_to_num(), m1.nan_to_num()
        m0, m1 = self.map_(lambda t: t.transpose(1, 2).flatten(start_dim=-2), m0, m1)
        m0, m1 = self.map_(self.to_out, m0, m1)
        x0 = x0 + self.ffn(torch.cat([x0, m0], -1))
        x1 = x1 + self.ffn(torch.cat([x1, m1], -1))
        return x0, x1

2.4 匹配头:把「相似」和「可匹配」拆开

SuperGlue 的匹配头把相似度和 dustbin 一起送进 Sinkhorn,迭代出一张双随机矩阵。Sinkhorn 在训练时需要约 100 次迭代才能稳定,既费计算也费显存;dustbin 还会把所有点的相似度耦合在一起,梯度不够干净。LightGlue 换成一个没有迭代的闭式头。

先算 pairwise 相似度。每个点的状态经一个线性投影,然后做内积:

再给每个点算一个 可匹配性(matchability),表示「这个点在另一张图里到底有没有对应点」:

最后把两者合成分配矩阵:

这就是论文说的双 softmax:先沿列做一次 softmax,再沿行做一次 softmax。它等价于一个可微的互最近邻(mutual nearest neighbor)松弛:只有当 在 中相对 的所有点足够突出,且 在 中相对 的所有点也足够突出时, 才会大。推理时再取双向 argmax,并要求分数大于阈值 ,得到最终匹配。

这个头的关键不是「双 softmax 比 Sinkhorn 更准」,而是它把两种信息解耦了:

  • 相似度矩阵负责「谁看起来像谁」,它会产生很多候选,包括重复纹理造成的错误候选;
  • matchability 是一个 unary 项,只由每个点自己的状态决定,负责把「在另一张图里根本不可见」的点整体压暗。

SuperGlue 的 dustbin 把这两件事混在一个归一化里,LightGlue 则让它们各司其职。代码里仍然保留 dustbin 行/列,但那些分数只来自 的 log sigmoid,不再由 pairwise 相似度共同决定;因此梯度不会被所有点纠缠。

消融的结果是全文最大的单项差距:去掉 matchability,precision 从 86.8 掉到 67.4,recall 反而从 96.3 升到 97.0,耗时 18.9 ms(表 4)。换句话说,没有 matchability,网络会给出更多匹配,但其中大量是错的;matchability 把「多」换成了「准」。Figure 6 是定性的例子:关闭 matchability 时,勃兰登堡门上出现大量红色错误连线;打开后,大部分重复结构和背景误匹配被压掉。

matchability 的作用
图 6上:没有 matchability,网络给出大量红色外点(视觉上相似但几何错误);下:加上 matchability 后,错误匹配被过滤,保留绿色 inlier。
代码:双 softmax 与 matchability 匹配头(官方 lightglue/lightglue.py,原文照录)
def sigmoid_log_double_softmax(
    sim: torch.Tensor, z0: torch.Tensor, z1: torch.Tensor
) -> torch.Tensor:
    """create the log assignment matrix from logits and similarity"""
    b, m, n = sim.shape
    certainties = F.logsigmoid(z0) + F.logsigmoid(z1).transpose(1, 2)
    scores0 = F.log_softmax(sim, 2)
    scores1 = F.log_softmax(sim.transpose(-1, -2).contiguous(), 2).transpose(-1, -2)
    scores = sim.new_full((b, m + 1, n + 1), 0)
    scores[:, :m, :n] = scores0 + scores1 + certainties
    scores[:, :-1, -1] = F.logsigmoid(-z0.squeeze(-1))
    scores[:, -1, :-1] = F.logsigmoid(-z1.squeeze(-1))
    return scores


class MatchAssignment(nn.Module):
    def __init__(self, dim: int) -> None:
        super().__init__()
        self.dim = dim
        self.matchability = nn.Linear(dim, 1, bias=True)
        self.final_proj = nn.Linear(dim, dim, bias=True)

    def forward(self, desc0: torch.Tensor, desc1: torch.Tensor):
        """build assignment matrix from descriptors"""
        mdesc0, mdesc1 = self.final_proj(desc0), self.final_proj(desc1)
        _, _, d = mdesc0.shape
        mdesc0, mdesc1 = mdesc0 / d**0.25, mdesc1 / d**0.25
        sim = torch.einsum("bmd,bnd->bmn", mdesc0, mdesc1)
        z0 = self.matchability(desc0)
        z1 = self.matchability(desc1)
        scores = sigmoid_log_double_softmax(sim, z0, z1)
        return scores, sim

    def get_matchability(self, desc: torch.Tensor):
        return torch.sigmoid(self.matchability(desc)).squeeze(-1)

2.5 逐层监督:让浅层预测变成可用的预测

光有轻量匹配头还不够。如果只在最后一层监督,前面的层会退化成「为最后一层提供特征」的中间状态,早停拿到的预测不可信。LightGlue 的做法是每一层都算 loss:对每一层 ,用该层自己的匹配头预测分配矩阵,然后对所有 ground-truth 匹配 最大化其对数似然,同时对标注为不可匹配的点 最大化「不被匹配」的概率:

ground truth 来自单应或「深度 + 相对位姿」的重投影:重投影误差小于阈值、且互为最近邻的点是正样本;找不到对应点、或最近邻误差过大的点是负样本。每一项都在 log 空间里算,数值上等价于 balanced NLL;代码里正负样本的权重由 nll_balancing: 0.5 控制。

置信度分类器是第二阶段单独训练的。它是一个线性层加 sigmoid,输入是当前层的状态(detach 后,梯度不回传到匹配特征),监督信号不是 ground truth,而是当前层的 argmax 是否与最后一层的 argmax 一致。也就是说,它学的是「我的答案会不会被后面的层改掉」,而不是「我的答案对不对」。这个目标更接近早停真正需要的判断:如果当前预测已经和最终预测一致,再算下去大概率不会改变结果。论文附录特别说明,分类器的梯度不回传到状态,避免它干扰匹配精度。

这一步是 LightGlue 和 SuperGlue 训练上最本质的差别。SuperGlue 的 Sinkhorn 太贵,无法在每层都预测并监督,只能监督最后一层;LightGlue 的闭式头让每层都能输出,于是深监督成为可能。收益不只是「早停可用」:论文报告在合成单应预训练上,LightGlue 用 5M 对、约 2 个 GPU 天达到的损失比 SuperGlue 低 33%,召回高 4%;SuperGlue 需要 7 天以上才能接近同样的精度(Figure 5)。这也是论文标题里「更容易训练」的来源。

预训练收敛曲线
图 5合成单应预训练上的损失与召回。LightGlue(红)在 5M 对、约 2 个 GPU 天内收敛到更低损失和更高召回;SuperGlue(蓝)需要 7 天以上才能接近。

浅层到底有多可用?表 4 的消融给了一个直接的答案:只用第 5/9 层的预测,precision/recall 是 78.1/92.7,耗时 11.9 ms;完整 9 层是 86.8/96.3,19.4 ms。第 5 层已经是一个「能用的粗匹配器」,但还不是「和最终答案一样好」;自适应机制要做的,是在简单对上用第 5 层,在难对上继续算,而不是永远固定在第 5 层。

代码:逐层监督与置信度损失(官方 glue-factory,原文照录:LightGlue.loss)
    def loss(self, pred, data):
        def loss_params(pred, i):
            la, _ = self.log_assignment[i](
                pred["ref_descriptors0"][:, i], pred["ref_descriptors1"][:, i]
            )
            return {
                "log_assignment": la,
            }

        sum_weights = 1.0
        nll, gt_weights, loss_metrics = self.loss_fn(loss_params(pred, -1), data)
        N = pred["ref_descriptors0"].shape[1]
        losses = {"total": nll, "last": nll.clone().detach(), **loss_metrics}

        if self.training:
            losses["confidence"] = 0.0

        # B = pred['log_assignment'].shape[0]
        losses["row_norm"] = pred["log_assignment"].exp()[:, :-1].sum(2).mean(1)
        for i in range(N - 1):
            params_i = loss_params(pred, i)
            nll, _, _ = self.loss_fn(params_i, data, weights=gt_weights)

            if self.conf.loss.gamma > 0.0:
                weight = self.conf.loss.gamma ** (N - i - 1)
            else:
                weight = i + 1
            sum_weights += weight
            losses["total"] = losses["total"] + nll * weight

            losses["confidence"] += self.token_confidence[i].loss(
                pred["ref_descriptors0"][:, i],
                pred["ref_descriptors1"][:, i],
                params_i["log_assignment"],
                pred["log_assignment"],
            ) / (N - 1)

            del params_i
        losses["total"] /= sum_weights

        # confidences
        if self.training:
            losses["total"] = losses["total"] + losses["confidence"]

        if not self.training:
            # add metrics
            metrics = matcher_metrics(pred, data)
        else:
            metrics = {}
        return losses, metrics

逐层预测到底长什么样?下面的部件用一个 36×36 的示意矩阵把它画出来:15 个真匹配在对角线上,7 个重复结构诱饵在非对角线上,其余点不可匹配。拖动层数可以看到对角线逐渐变亮、诱饵逐渐变暗;取消 matchability 则诱饵重新出现。矩阵是手工构造的示意,不是真实网络输出,但趋势对应表 4 和 Figure 6。

匹配数 18precision 83.3%recall 100.0%TP / FP 15 / 3

2.6 自适应深度:置信度够了就走

每一层结束后(最后一层除外),网络为每个点算一个置信度:

如果所有点中「置信度超过阈值 」的比例超过 ,就停止推理,直接把当前层的预测作为输出:

这里 是用户可调的早停置信度,默认 0.95; 是逐层阈值,按下面的指数形式衰减:

阈值在浅层更高( 时约 0.864, 时约 0.802),意思是「早期层要更自信才允许退出」。原因是分类器本身在浅层更不准,如果用一个统一的低阈值,网络会在还没看清时就跑掉。这个衰减曲线是在验证集上按每层分类器的准确率拟合的。

直接控制速度-精度权衡。表 11 在 MegaDepth 上给出了一组很清楚的数:

表 11:MegaDepth 上早停设置的消融。匹配数、precision 和 AUC 是相对位姿估计的结果,“相对耗时”以固定 9 层为 100%。

设置匹配数precisionAUC@5°AUC@10°AUC@20°耗时
完整 9 层61396.266.779.387.9100%
固定 7 层70596.066.279.188.082.4%
固定 5 层70294.565.077.887.060.0%
固定 3 层68790.064.076.785.841.9%
置信度 98%61096.266.679.388.080.5%
置信度 95%60895.466.379.087.970.6%
置信度 90%60794.565.978.587.261.5%
置信度 80%60592.665.277.886.748.4%

注意两件事。第一,自适应早停比「固定砍到同样层数」更好:95% 置信度用 70.6% 的时间拿到 66.3,而固定第 5 层用 60.0% 的时间只有 65.0;98% 置信度用 80.5% 的时间拿到 66.6,接近完整模型的 66.7。第二,早停主要牺牲 precision,recall 和 AUC 掉得慢;这也是为什么在 SLAM 这类对匹配数量不敏感、对精度敏感的任务里,95% 是一个好默认值。

Figure 2 是同一机制的直观版本:简单对在第 3 层退出,16.9 ms;困难对在第 8 层退出,32.3 ms。分类器本身只增加约 2% 的推理时间,但大多数时候省下的远不止 2%。

自适应深度
图 2上:视觉重叠大的简单对,第 3 层就足够,16.9 ms;下:视角和光照变化大的困难对,需要第 8 层,32.3 ms。同一个网络,深度由图像对决定。

早停不是 LightGlue 首创。BranchyNet 在 CNN 上做过类似的 early exiting,Anytime Dense Prediction 把置信度自适应用在稠密预测上[9, 10];LightGlue 的新意是把早停和「每层都能输出匹配」的轻量头、以及后面的点剪枝组合在同一个匹配器里。没有逐层监督,置信度分类器没有可靠的训练信号;没有轻量头,逐层预测本身就会把省下的时间吃回去。

代码:早停阈值与退出判定(官方 lightglue/lightglue.py,原文照录)
    def confidence_threshold(self, layer_index: int) -> float:
        """scaled confidence threshold"""
        threshold = 0.8 + 0.1 * np.exp(-4.0 * layer_index / self.conf.n_layers)
        return np.clip(threshold, 0, 1)

    def get_pruning_mask(
        self, confidences: torch.Tensor, scores: torch.Tensor, layer_index: int
    ) -> torch.Tensor:
        """mask points which should be removed"""
        keep = scores > (1 - self.conf.width_confidence)
        if confidences is not None:  # Low-confidence points are never pruned.
            keep |= confidences <= self.confidence_thresholds[layer_index]
        return keep

    def check_if_stop(
        self,
        confidences0: torch.Tensor,
        confidences1: torch.Tensor,
        layer_index: int,
        num_points: int,
    ) -> torch.Tensor:
        """evaluate stopping condition"""
        confidences = torch.cat([confidences0, confidences1], -1)
        threshold = self.confidence_thresholds[layer_index]
        ratio_confident = 1.0 - (confidences < threshold).float().sum() / num_points
        return ratio_confident > self.conf.depth_confidence

这段代码把 get_pruning_mask 也放在了一起,因为早停和剪枝共用同一个 confidence_thresholds 表;2.7 节会单独讲剪枝的判定。

2.7 自适应宽度:低匹配概率的点不再参与后续层

早停决定「算到第几层」,点剪枝决定「每层算多少个点」。后者的依据是 matchability:如果一个点被网络自信地判定为不可匹配,它在后续层里大概率不会变成可匹配,继续让它参与注意力只是浪费。判定写成:

其中 是 matchability 阈值。两个条件缺一不可:只看 低就剪,会误删那些「当前层还没看清、但后面可能匹配上」的点;加上「置信度高」这个条件,只剪「已经看清了,确实不可匹配」的点。代码里的 width_confidence=0.99 对应 ,keep = scores > 1 - width_confidence;keep |= confidences <= threshold 又把低置信度的点强制保留,和公式完全一致。

剪枝在每个中间层结束后做,最后一层不做。原因是最后一层没有再「后续」可言,剪了也不会省计算。每次剪枝用 index_select 把保留下来的描述子和位置编码取出来,同时用 prune0/prune1 记录每个原始点是在第几层被剪掉的,最后再把匹配索引映射回原始关键点。这个 gather/scatter 本身有开销,所以代码只在点数足够多时才启用:FlashAttention 下阈值是 1536,普通 CUDA 是 1024,CPU 和 MPS 直接关掉(pruning_keypoint_thresholds)。这也是一个很实际的工程取舍:小输入上剪枝省下的注意力还不够补偿索引操作。

点剪枝
图 4点剪枝。颜色表示每个点在第几层被剪掉,红/橙色的点较早被剪掉,蓝色较晚;天空、穹顶和前景行人等另一张图看不到的区域最先被剪掉,共视区域的点保留到后层。后续层只在缩小后的集合上算注意力。
逐层剪枝比例
图 11每层累计判定为不可匹配的点比例。前几层上升最快,之后趋缓,到第 8 层累计约 47%。

表 5 把深度和宽度的收益放在一起:

表 5:自适应深度与宽度在 MegaDepth 不同重叠度图像对上的效果。停止层数相对 9 层,“不可匹配点比例”是剪掉的点的平均比例,加速相对非自适应版。

难度平均停止层数不可匹配点比例相对非自适应加速
简单4.7 / 919.8%1.86×
中等5.5 / 923.4%1.33×
困难6.9 / 927.9%1.16×
平均5.7 / 923.7%1.45×

这张表有一个反直觉的地方:难对的剪枝率更高,但加速反而更小。原因是难对需要更多层,深度的收益被抵消了;简单对虽然只剪掉约 20% 的点,但它在第 5 层左右就退出,深度和宽度同时省,所以加速最大。论文报告整体运行时间降低 33%,并特别指出自适应机制对简单对最有效,这和表 5 的 1.86× 一致。

代码:点剪枝的推理循环(官方 lightglue/lightglue.py,原文节选)
        for i in range(self.conf.n_layers):
            if desc0.shape[1] == 0 or desc1.shape[1] == 0:  # no keypoints
                break
            desc0, desc1 = self.transformers[i](
                desc0, desc1, encoding0, encoding1, mask0=mask0, mask1=mask1
            )
            if i == self.conf.n_layers - 1:
                continue  # no early stopping or adaptive width at last layer

            if do_early_stop:
                token0, token1 = self.token_confidence[i](desc0, desc1)
                if self.check_if_stop(token0[..., :m], token1[..., :n], i, m + n):
                    break
            if do_point_pruning and desc0.shape[-2] > pruning_th:
                scores0 = self.log_assignment[i].get_matchability(desc0)
                prunemask0 = self.get_pruning_mask(token0, scores0, i)
                keep0 = torch.where(prunemask0)[1]
                ind0 = ind0.index_select(1, keep0)
                desc0 = desc0.index_select(1, keep0)
                encoding0 = encoding0.index_select(-2, keep0)
                prune0[:, ind0] += 1
            if do_point_pruning and desc1.shape[-2] > pruning_th:
                scores1 = self.log_assignment[i].get_matchability(desc1)
                prunemask1 = self.get_pruning_mask(token1, scores1, i)
                keep1 = torch.where(prunemask1)[1]
                ind1 = ind1.index_select(1, keep1)
                desc1 = desc1.index_select(1, keep1)
                encoding1 = encoding1.index_select(-2, keep1)
                prune1[:, ind1] += 1

2.8 两个旋钮合起来:一笔算力预算

把 和 width_confidence 放在一起,就能看到 LightGlue 的实际工作方式:每层结束后先看置信度,够自信就整对退出;不够自信就继续,但把「自信地不可匹配」的点从下一层的输入里删掉。前者省层数,后者省每层的宽度;两者都依赖同一个 matchability/confidence 头。

下面的部件把这两个旋钮做成可拖动的模拟器。默认是简单对、、宽度置信度 0.99,对应论文表 5 的 easy 行:4.7 层、剪掉 19.8%、1.86× 加速、AUC@5° 66.3。把 拉低,层数和耗时下降,AUC 也跟着掉;把宽度置信度拉低,剪枝更激进,难对的加速更明显。数值是按表 5 的三个难度基准和表 11 的 -AUC 曲线插值出来的示意模型,真实网络每对图的层数由置信度逐层决定,不会正好落在这些小数上。

图像对难度
停止层数 4.7 / 9保留点 1642 / 2048相对耗时 54%加速 1.86×AUC@5° 66.3

拖一拖会发现两个不对称。第一, 主要影响简单对:简单对在 时约 5.0 层,降到 0.80 后只剩 3.5 层;困难对从 7.2 层只降到 5.7 层,仍有大半计算要付。第二,宽度旋钮对难对更有效:简单对本来就只剪 20% 左右,再激进也省不了多少;难对的不可匹配点更多,把宽度置信度从 0.99 拉到 0.95,剪枝率能从 27.9% 升到 39.8%,加速从 1.16× 升到 1.66×。这也解释了为什么论文的整体 33% 是深度和宽度共同贡献的,单独看任何一个都会低估。

2.9 与 SuperGlue 的逐项差别

把前面的改动汇总成一张表,能更清楚地看到 LightGlue 不是「SuperGlue 加一个早停开关」,而是从位置编码到匹配头都重做了一遍:

组件SuperGlueLightGlue
位置编码输入端加学到的绝对位置编码,所有层共享每次自注意力用旋转相对位置编码;交叉注意力不加位置
交叉注意力两个方向分别算 query/key/value 和相似度只算 key,一次相似度双向复用
匹配头Sinkhorn + dustbin,多次迭代双 softmax + unary matchability,无迭代
逐层预测只有最后一层每层都有 assignment 和 confidence
推理固定 层置信度早停 + matchability 点剪枝
训练只监督最后一层每层深监督;置信度分类器单独训练
MegaDepth-150065.8 AUC@5°、70.0 ms66.7 AUC@5°、44.2 ms(自适应 66.3、31.4 ms)
HPatches precision87.4%88.9%

最后两行不是「架构差别」,但把它们放在同一张表里,能直观看到改动累积后的结果。接下来进入实验细节。

三、实验结果

3.1 训练与评测设置

先澄清两个容易混淆的基准:这篇论文没有在 ScanNet-1500 上评测,也没有 IMC 2022 的结果。它的完整评测集是 HPatches[11]、MegaDepth-1500/1800[12]、Aachen Day-Night(含 v1.1)[13]、InLoc[14]、IMC 2020/2021[15],以及 IMC 2023 的排行榜[16]。ScanNet 的室内匹配数字通常来自 LoFTR、DKM 这类稠密匹配论文;IMC 2022 是另一届比赛。把那些数字安到 LightGlue 头上会误导,所以本文只报告论文真正跑过的基准。

训练分两阶段。第一阶段在 Oxford-Paris 的 17 万张图上生成合成单应做预训练:图像缩放到 640×480,SuperPoint/SIFT/DISK 分别取 512/1024/1024 个关键点,batch 64,初始学习率 ,20 个 epoch 后每 epoch 乘 0.8,总共 40 个 epoch(约 6M 图像对),2 张 RTX 3090 训练 2 天。论文报告验证/测试集上 recall > 99%、precision > 90%。第二阶段在 MegaDepth 上微调:368/5/24 个场景做训练/验证/测试,图像长边 1024、零填充到 1024×1024,每图取 top-2048 个关键点,batch 32,学习率 ,10 个 epoch 后每 epoch 乘 0.95,训练 50 个 epoch(2 天,2 张 3090)。缓存检测子和描述子约占 200 GB 磁盘;训练时用梯度检查点(gradient checkpointing)和混合精度,把 32 对图像塞进单张 24 GB 显卡。

MegaDepth 的 ground truth 由「深度 + 相对位姿」重投影得到:重投影误差小于 3 px 且互为最近邻的点是正样本;最近邻误差大于 5 px、没有深度、或 Sampson 误差大于 3 px 的点是负样本。这个标注策略和 SuperGlue 类似,但 LightGlue 用了 2k 个关键点而不是 1k,论文说这对精度有直接帮助。

评测口径也要注意:HPatches 用最长边 480 px、每图 1024 个 SuperPoint 特征;MegaDepth-1500 用 2048 个特征、长边 1600 px;Aachen 用最多 4096 个特征,接 hloc 的 NetVLAD 检索 + PnP RANSAC;IMC 用各自的 SfM 流程。时间数字只算匹配网络,不含特征提取和鲁棒估计;Aachen 的「对/秒」是匹配阶段的吞吐。这些口径决定了表里的数字不能和端到端 SLAM 的帧率直接等同。

3.2 HPatches:匹配质量与 DLT

HPatches 是平面场景的单应估计基准,包含光照和视角变化。表 1 同时看两件事:原始匹配的召回/精度(3 px 重投影误差),以及用 RANSAC 或加权 DLT 估计单应后的 AUC。

方法R (%)P (%)RANSAC AUC@1pxRANSAC AUC@5pxDLT AUC@1pxDLT AUC@5px
SuperPoint NN+mutual72.767.235.075.30.02.0
SuperPoint + SuperGlue94.987.438.379.333.876.7
SuperPoint + SGMNet95.583.038.679.031.776.0
SuperPoint + LightGlue94.388.938.379.635.978.6
LoFTR(1024 匹配)-92.741.578.838.570.6

表 1:HPatches 单应估计。R/P 是原始匹配在 3 px 下的召回/精度;后四列是单应估计的 AUC。LightGlue 在稀疏匹配器中 precision 最高,DLT 的 AUC@5px 也高于 SuperGlue。

LightGlue 的原始召回略低于 SuperGlue(94.3 vs 94.9),但精度更高(88.9 vs 87.4)。这个「少一点、准一点」的取舍在 DLT 上体现得最明显:LightGlue 的 DLT AUC@5px 是 78.6,SuperGlue 是 76.7;DLT 这种不带鲁棒估计的最小二乘解法,最怕外点,匹配精度提高后它就能接近 RANSAC 的效果。论文因此说 LightGlue 让 DLT 这种简单解法足以和更贵、更慢的 MAGSAC 竞争[17]。和稠密匹配器比,LoFTR 的 RANSAC AUC@1px 更高(41.5),但 DLT 明显更差(70.6),说明稠密匹配的对应点数量多、粗精度好,但外点比例不一定更低。

3.3 MegaDepth-1500:精度、速度与「自适应」的代价

MegaDepth-1500 是论文的主战场:1500 对来自 St. Peter’s Square 和 Reichstag 的图像,按视觉重叠平衡难度,评相对位姿的 AUC@5°/10°/20°。表 2 是主结果。

方法RANSAC AUC@5/10/20LO-RANSAC AUC@5/10/20时间 (ms)
SuperPoint + SuperGlue49.7 / 67.1 / 80.665.8 / 78.7 / 87.570.0
SuperPoint + SGMNet43.2 / 61.6 / 75.659.8 / 74.1 / 83.973.8
SuperPoint + LightGlue49.9 / 67.0 / 80.166.7 / 79.3 / 87.944.2
LightGlue-adaptive49.4 / 67.2 / 80.166.3 / 79.0 / 87.931.4
LoFTR52.8 / 69.2 / 81.266.4 / 78.6 / 86.5181
MatchFormer53.3 / 69.7 / 81.866.5 / 78.9 / 87.5388
ASpanFormer55.3 / 71.5 / 83.169.4 / 81.1 / 88.9369
DISK + LightGlue43.5 / 61.0 / 75.361.3 / 74.3 / 83.844.5

表 2:MegaDepth-1500 相对位姿估计。2048 个特征,图像长边 1600 px;时间为匹配阶段,不含特征提取和 RANSAC。

几个要点。第一,在稀疏匹配器内部,LightGlue 比 SuperGlue 高 0.9 AUC@5°(66.7 vs 65.8),耗时少 37%(44.2 vs 70.0 ms);SGMNet 在这个基准上既慢又低。第二,自适应版只掉 0.4 AUC@5°(66.3),却把时间压到 31.4 ms,比 SuperGlue 快 2.2 倍;这就是「用一点点精度换大量延迟」的典型。第三,稠密匹配器在精度上仍然强:ASpanFormer 69.4 最高,LoFTR 和 MatchFormer 在 LO-RANSAC 下是 66.4/66.5,和 LightGlue 的 66.7 基本同档;但它们的耗时是 181–388 ms,LightGlue 是 44.2 ms。论文的结论是 Pareto 最优:在「精度-速度」平面上,LightGlue 没有被任何一个稠密匹配器同时支配。

DISK + LightGlue 值得单独看。DISK 的描述子更强、检测更可重复,但如果没有好的匹配器,DISK + 最近邻 + ratio 只有 38.1/55.4/69.6(RANSAC@5/10/20),57.2/69.5/78.6(LO-RANSAC),7.4 ms;接上 LightGlue 后变成 43.5/61.0/75.3、61.3/74.3/83.8,耗时 44.5 ms。精度提升很大,代价是匹配时间变长;这说明 LightGlue 的收益不只对 SuperPoint 成立,换一套局部特征仍然有效。

速度-精度对比
图 7运行时间随关键点数的变化。1024 个点时 LightGlue 约 18 ms,SuperGlue 约 30 ms;2048 个点时是 44 ms 对 70 ms;4096 个点时自适应版约 80 ms,SuperGlue 超过 200 ms。SGMNet 在 512/1024 时明显更慢,到 4k 才与 LightGlue 接近,但仍远慢于自适应版。

关于 DKM:它不在论文的对比表里。DKM 是 CVPR 2023 的稠密匹配器,不做稀疏关键点的图匹配,而是在稠密特征图上用高斯过程回归出 warp,再用深度一致性监督置信度[18]。把 DKM 在 ScanNet/MegaDepth 上的数字和 LightGlue 的 MegaDepth-1500 数字并排,会因为输入分辨率、特征、鲁棒估计和计时口径不同而不可比;这里只把它作为「稠密 warp vs 稀疏图匹配」的路线对照,不强行排名。

显存方面,论文没有给出 VRAM 表,只给了训练配置:单张 24 GB 卡装 32 对 1024×1024,靠梯度检查点和混合精度;推理侧能确定的是注意力复杂度 、双向交叉注意力把相似度计算减半、点剪枝减少 ,以及 FlashAttention 对 IO 和显存的优化[19]。除此之外的具体显存数字,本文没有报告,也不应该替它编。

3.4 Aachen Day-Night 与 InLoc:视觉定位

Aachen Day-Night 是长期视觉定位基准:白天参考图、昼夜查询图,用 hloc 检索 50 张参考图,匹配后 PnP + RANSAC,报告不同阈值下的位姿召回。表 3 是主结果。

方法白天 0.25/0.5/1.0m夜晚 0.25/0.5/1.0m对/秒
SuperPoint + SuperGlue88.2 / 95.5 / 98.786.7 / 92.9 / 1006.5
SuperPoint + SGMNet86.8 / 94.2 / 97.783.7 / 91.8 / 99.010.2
SuperPoint + ClusterGNN89.4 / 95.5 / 98.581.6 / 93.9 / 10013†
SuperPoint + LightGlue89.2 / 95.4 / 98.587.8 / 93.9 / 10017.2 / 26.1

表 3:Aachen Day-Night 视觉定位。指标是满足 (0.25 m,2°)、(0.5 m,5°)、(1.0 m,10°) 的查询比例;吞吐是匹配阶段,LightGlue 的两个数分别是默认版和 FlashAttention 版。ClusterGNN 的 13† 是原作者估计的时间,代码未公开。

LightGlue 的白天精度和 SuperGlue 基本持平(89.2 vs 88.2 在 0.25 m),夜晚 0.25 m 高 1.1 个点(87.8 vs 86.7),0.5 m 高 1.0 个点,1.0 m 都是 100。真正大的差别在吞吐:17.2 对/秒 vs 6.5 对/秒,2.6 倍;用 FlashAttention 优化后到 26.1 对/秒,正好 4 倍。ClusterGNN 的白天 0.25 m 略高(89.4),但夜晚掉到 81.6,而且它的时间只是估计值;在昼夜定位这种场景里,LightGlue 的稳定性更重要。

Aachen v1.1 的补充结果(表 9)是同一趋势:SuperGlue 白天 89.8/96.1/99.4、夜晚 77.0/90.6/100、6.4 对/秒;LightGlue 白天 90.2/96.0/99.4、夜晚 77.0/91.1/100、17.3 对/秒。两个版本的差异在 1% 以内,但吞吐差 2.7 倍。

InLoc 是室内定位,用 DUC1/DUC2 两个子集,阈值是 (0.25 m,10°)、(0.5 m,10°)、(1.0 m,10°)。SuperPoint+SuperGlue 在 DUC1 是 47.0/69.2/79.8,DUC2 是 53.4/77.1/80.9;LightGlue 是 49.0/68.2/79.3 和 55.0/74.8/79.4。差异都在 2% 以内,论文说在数据集本身的波动范围内:DUC1/DUC2 分别只有 205/151 个查询,1.5% 的差异约等于 3 个查询。LightGlue 在 0.25 m 的严格阈值上略好,在 0.5 m/1.0 m 上略差;这不是一个能下强结论的基准。

InLoc 失败案例
图 10InLoc 上的失败案例。LightGlue 有时会把场景中重复纹理的物体(例如图里的沙发和售货机)匹配上,而不是几何结构;当检索本身较弱时,更多匹配反而可能把位姿带偏。

3.5 IMC 2020/2021/2023 与 SfM

IMC(Image Matching Challenge)评的是端到端 SfM,而不只是匹配精度。IMC 2020 的 PhotoTourism 数据分 stereo 和 multiview 两条赛道,表 6 报告 AUC 和匹配吞吐:

方法Stereo AUC@5/10Multiview AUC@5/10/25对/秒
SuperPoint + SuperGlue58.64 / 71.0761.88 / 78.97 / 86.7516.2
SuperPoint + LightGlue59.03 / 71.1362.87 / 79.36 / 86.9843.4
DISK + NN + ratio57.76 / 68.7359.91 / 78.95 / 87.54196.7
DISK + LightGlue67.02 / 77.8267.91 / 80.58 / 88.3544.5

表 6:IMC 2020 的 SfM 结果。Stereo 赛道用 AUC@5°/10°,Multiview 用 AUC@5°/10°/25°;吞吐是匹配阶段。

SuperPoint+LightGlue 相对 SuperGlue 的提升不大(stereo +0.4,multiview +1.0 左右),但速度快 2.7 倍。真正的跃迁来自 DISK+LightGlue:stereo AUC@5° 67.02,比 DISK+NN+ratio 高 9.3 个点,比 SuperPoint+LightGlue 高 8.0 个点;multiview AUC@5° 67.91,也全面领先。论文的解释是 DISK 的检测更可重复、描述子更强,LightGlue 把这个优势兑现成了匹配质量。这个结果也说明「匹配器 + 局部特征」是一个整体,不能只看匹配器本身。

IMC 2021 的 PhotoTourism 子集更强调跨场景泛化。表 7 的关键数字:SuperPoint+LightGlue stereo 36.7/50.7、multiview 63.6/74.4、平均 50.2/62.6;SuperPoint+SuperGlue 是 36.5/50.5、63.3/73.8、49.9/62.2;DISK+LightGlue 是 43.1/56.6、66.2/76.2、54.7/66.4;DISK(8K)+LightGlue 是 48.7/61.8、68.9/78.2、58.8/70.0。最后一行的 8K 关键点版本甚至超过了带 test-time augmentation 的 DISK(8K)+NN+ratio(44.6/56.2、65.0/74.4、54.8/65.3)和 SP+SuperGlue*(44.6/58.6、66.8/77.1、55.7/67.9)。论文强调 LightGlue 没有用 TTA,只是把关键点加多;这说明在 SfM 场景里,「更多关键点 + 更好的匹配器」仍然是最直接的 scaling 轴。

IMC 2023 是端到端 SfM 排行榜:SuperPoint+SuperGlue 在 public/private 上是 36.1/43.8,SuperPoint+LightGlue 是 38.4/46.1,提升 2.3 个点。这个提升幅度没有 MegaDepth 上那么夸张,因为 IMC 2023 的场景更多样、检索和重建流程的方差更大;论文取 3 次运行的平均来降低随机性。

3.6 运行时间、消融与「为什么快」

Figure 7 给出运行时间随关键点数的变化:在 1024 个点时,LightGlue 约 18 ms、SuperGlue 约 30 ms;2048 个点时 44 ms 对 70 ms;4096 个点时自适应版约 80 ms,SuperGlue 超过 200 ms。SGMNet 在 512/1024 时反而比 LightGlue 慢,因为它需要先构造种子图;到 4k 才与 LightGlue 接近,但仍远慢于自适应版。

运行时间分解
图 131024 个关键点时各计算块的耗时(每张图 1024 个点)。自注意力两边都在 22 ms 左右,基本持平;交叉注意力因为双向共享相似度,从约 22 ms 降到 16.8 ms;匹配头从约 22 ms 降到 5 ms。三块相加约 66 ms 对 44 ms,与表 2 的 70/44.2 ms 同一量级。

表 4 的消融把每个设计的贡献拆开(合成单应测试集,SuperPoint,512 个关键点,5M 训练样本):

变体precisionrecall时间 (ms)
SuperGlue74.690.529.1
LightGlue 完整86.896.319.4
去掉 matchability67.497.018.9
换成绝对位置编码84.294.718.7
完整交叉注意力86.696.122.8
只用第 5/9 层78.192.711.9

表 4:合成单应消融。matchability 是精度贡献最大的单项;相对位置编码几乎不花时间;双向交叉注意力省时间不降精度;浅层预测已经有用,但不如完整模型。

从这张表能读出三件事。第一,matchability 是精度的主要来源:去掉它 precision 掉 19.4 个点,recall 反而升 0.7,典型的「匹配变多但变脏」。第二,相对位置编码的收益/成本比极高:+2.6 precision、+1.6 recall,只多 0.7 ms。第三,双向交叉注意力是纯赚:-3.4 ms,精度在噪声范围内。浅层预测的 78.1/92.7 则说明深监督确实让第 5 层「可用」,但完整模型仍然明显更好;自适应机制的价值在于按需使用,而不是永远停在浅层。

Figure 11 补充了剪枝的动态:前几层不可匹配点的比例上升最快,到第 8 层累计约 47%。表 5 则按难度拆分:简单对平均剪掉 19.8%,困难对 27.9%,难对的剪枝率更高、但加速更小;难对的深度收益被更难收敛抵消了。

3.7 定性结果与跨特征通用性

Figure 9 是同一套 LightGlue 结构接三种局部特征的匹配结果,从上到下依次是 SIFT+LightGlue、SuperPoint+LightGlue、DISK+LightGlue。三组都能给出密集且几何一致的匹配,但细节不同:SIFT 的匹配更集中在角点和斑点,SuperPoint 更均匀,DISK 在弱纹理和大视角下更稳。论文用这张图想说明的是:LightGlue 不是绑死在 SuperPoint 上的后处理,它是一个可以接不同局部特征的匹配器。

不同局部特征接 LightGlue
图 9从上到下:SIFT+LightGlue、SuperPoint+LightGlue、DISK+LightGlue 的匹配结果。同一套匹配器设计可以跨特征工作;SIFT 需要额外的尺度/方向输入,DISK/ALIKED 的 128 维描述子通过输入投影对齐到 256 维。

论文正式训练和评测了 SuperPoint、SIFT、DISK 三种特征[20, 21]。表 8 的 MegaDepth-1800 结果里,SIFT+LightGlue 有 383 个匹配、precision 84.1、AUC@5/10/20 为 57.0/71.3/81.8、44.3 ms;SIFT+NN+ratio 是 160 个匹配、82.3、48.3/62.2/73.2、5.7 ms;SIFT+SGMNet 是 405 个匹配、82.5、50.7/66.6/76.5、71.7 ms。LightGlue 让 SIFT 的 AUC@5° 提高 8.7 个点,代价是匹配时间从 5.7 ms 涨到 44.3 ms。这说明「更好的匹配器」对传统特征同样有效,不只是对学习到的描述子。

ALIKED 的情况要单独说明。论文正文和表格只覆盖 SuperPoint/SIFT/DISK,没有报告 ALIKED 的 LightGlue 数字;但官方仓库在论文之后加入了 aliked 权重和 ALIKED 提取器,输入维度是 128,通过 input_proj 对齐到 256[22]。所以「LightGlue 能接 ALIKED」有官方代码支持,但「ALIKED+LightGlue 在某个基准上是多少」不能从这篇论文里找。XFeat 也是同一类可替换的局部特征[23],但更轻量,适合和 LightGlue 组合做端侧实验;这属于后续工程选择,不是论文的实验结论。

四、局限性

论文没有单独的 Limitations 一节,结论甚至用了「drop-in replacement with only benefits」这样的说法。这句话是作者对自家方法的总结,不是严格的实验结论;把正文和附录里散落的证据拼起来,能看到几条明确的边界。

论文承认或数据直接显示的:

  • 稠密匹配器在最高精度上仍略胜一筹。MegaDepth-1800 上,ASpanFormer 68.0、LoFTR 66.4,LightGlue 65.5(LO-RANSAC AUC@5°);论文原话说 detector-free 方法领先 “by a mere 2% AUC@5°“。LightGlue 的优势是速度,不是绝对精度上限。
  • 早停牺牲 precision。表 11 里,置信度从 98% 降到 80%,precision 从 96.2 掉到 92.6,AUC@5° 从 66.6 掉到 65.2,时间从 80.5% 降到 48.4%。这是一条明确的权衡曲线,不是一个「无损加速」。
  • 难对的加速有限。表 5 里困难对只有 1.16×,因为需要更多层;自适应机制对简单对最有效,对低重叠、大视角变化的图像对收益小。
  • 重复纹理会导致定位失败。InLoc 上 LightGlue 有时匹配垃圾桶等重复物体,而不是几何结构;在检索较弱时,更多匹配反而可能把位姿带偏。论文报告 6/356 张图在 1 m 阈值上比 SuperGlue 差,差异约 1.5%(3 个查询)。
  • 置信度分类器有固定开销。最坏情况下增加约 2% 的推理时间;它靠省下的层数回本,如果输入总是很难、几乎不早停,这 2% 就是净成本。
  • 阈值是全局超参数。、宽度 0.99、 都是在验证集上选的,不是按图像对或特征类型自适应;换数据集、换特征、换分辨率时可能需要重调。

我补充的、论文没有展开的:

  • 它仍然依赖稀疏检测器。LightGlue 只匹配给定的关键点,不解决「关键点本身不可重复」的问题;在弱纹理、极端视角、严重遮挡下,检测器没检测到的点,匹配器再强也变不出来。这也是 LoFTR/ASpanFormer 这类 detector-free 方法仍然有市场的原因。
  • 训练域偏户外。预训练是 Oxford-Paris 的合成单应,微调是 MegaDepth 的户外地标;Aachen 和 InLoc 也是户外/室内定位基准,但 InLoc 的提升并不稳定。论文没有给出室内 SfM 或跨域的大规模验证,不能把 MegaDepth 的结论直接外推到室内。
  • 剪枝是贪心的。当前层「自信地不可匹配」不等于后面一定不可匹配;置信度条件降低了误剪概率,但没有理论保证。被剪掉的点不会复活,错误剪枝会直接变成 recall 损失。
  • 宽度自适应在 CPU/MPS 上默认关闭。代码的 pruning_keypoint_thresholds 对 CPU/MPS 设成 -1,原因是 gather/scatter 开销划不来;所以在这些设备上只有深度自适应,没有宽度自适应。这是一个工程限制,不是模型限制。
  • 端到端收益会被其他环节稀释。论文的时间只算匹配网络,不含特征提取、检索、RANSAC/PnP。在完整 SLAM/SfM 系统里,LightGlue 省下的几十毫秒可能只是总时间的一部分;它的价值在匹配成为瓶颈时最大。
  • 没有 ScanNet-1500 / IMC 2022 的验证。如 3.1 节所说,这两个基准不在论文里;如果关心室内 SfM,应该看 LoFTR/DKM 等论文的原始数字,或自己在新数据上重跑,而不是引用这篇论文。

这些局限不影响 LightGlue 的核心命题——「计算量应该随难度变化」——但决定了它适合放在什么位置:延迟敏感的稀疏匹配、SLAM/定位的 front-end、以及需要和不同局部特征组合的系统;不适合期待它单独解决所有室内弱纹理匹配的场景。

五、结论与展望

LightGlue 的核心贡献不是某个单点技巧,而是把匹配器从「固定深度的回归器」变成了「可以中途交卷的迭代推理器」。三个架构改动——旋转相对位置、双向交叉注意力、双 softmax + matchability——共同把「每层预测」的成本降到可接受;深监督让每层预测变得可信;置信度早停和点剪枝则把这种可信度变成实际的算力节省。四件事缺一不可:没有轻量头,逐层预测太贵;没有深监督,浅层预测不可信;没有置信度,网络不知道何时停;没有 matchability,剪枝会误删。

从工程角度看,LightGlue 的默认配置已经很稳:SuperPoint + LightGlue、2048 个关键点、depth_confidence=0.95、width_confidence=0.99、filter_threshold=0.1。如果延迟是关键,可以把 depth_confidence 降到 0.90 左右,或减少关键点;如果精度是关键,把两个 confidence 设成 -1、关闭自适应,并把关键点加到 4096。论文的 Figure 1 已经给出了这条曲线:固定层数的 L=3/5/7/9 是一条条离散的点,自适应版则把这条曲线连了起来。

往后看,至少有三条自然的方向。第一,更细粒度的自适应:现在早停是整对图像共用一个退出决策,剪枝是逐点但只基于当前层的 matchability;如果能让每个点自己决定「我还要不要继续被算」,或者让网络预测「再算一层能带来多少收益」,预算分配会更精确。第二,和稀疏图方法结合:LightGlue 的注意力仍是 ,在 8K/16K 关键点下,SGMNet/ClusterGNN 式的图稀疏化仍然有价值;把「按难度自适应」和「按图结构稀疏化」结合,可能比单独任何一条路线都好。第三,和稠密/混合匹配器融合:FlexGlue 已经把稀疏查询送到稠密网格里,LoMa、DKM 走的是稠密 warp 路线[18];LightGlue 的逐层置信度和 matchability 可以作为这些方法的「何时停、哪里停」的控制器,而不是只能用于稀疏匹配。

对读者来说,最实用的结论可能很简单:如果你已经在用 SuperGlue,LightGlue 是一个可以直接替换的升级——同样的输入输出、同样的特征,精度略好、速度快 1.6–2.5 倍,而且代码和权重都开源;如果你在做延迟敏感的 SLAM 或大规模 SfM,自适应版值得作为默认起点。但如果你追求的是稠密、弱纹理、室内场景的极限精度,LoFTR/ASpanFormer/DKM 仍然是更合适的路线,LightGlue 的贡献是让稀疏匹配在「够用」的场景里不再成为瓶颈。

六、上手

官方仓库提供推理、预训练权重和 Colab demo;训练与评测在 glue-factory。下面的安装和推理命令取自 LightGlue README,训练与评测命令取自 glue-factory README,都折叠起来,避免长代码淹没正文。

安装 LightGlue(可编辑安装)
git clone https://github.com/cvg/LightGlue.git && cd LightGlue
python -m pip install -e .
最小匹配示例(SuperPoint + LightGlue)
from lightglue import LightGlue, SuperPoint, DISK, SIFT, ALIKED, DoGHardNet
from lightglue.utils import load_image, rbd

# SuperPoint+LightGlue
extractor = SuperPoint(max_num_keypoints=2048).eval().cuda()  # load the extractor
matcher = LightGlue(features='superpoint').eval().cuda()  # load the matcher

# or DISK+LightGlue, ALIKED+LightGlue or SIFT+LightGlue
extractor = DISK(max_num_keypoints=2048).eval().cuda()  # load the extractor
matcher = LightGlue(features='disk').eval().cuda()  # load the matcher

# load each image as a torch.Tensor on GPU with shape (3,H,W), normalized in [0,1]
image0 = load_image('path/to/image_0.jpg').cuda()
image1 = load_image('path/to/image_1.jpg').cuda()

# extract local features
feats0 = extractor.extract(image0)  # auto-resize the image, disable with resize=None
feats1 = extractor.extract(image1)

# match the features
matches01 = matcher({'image0': feats0, 'image1': feats1})
feats0, feats1, matches01 = [rbd(x) for x in [feats0, feats1, matches01]]  # remove batch dimension
matches = matches01['matches']  # indices with shape (K,2)
points0 = feats0['keypoints'][matches[..., 0]]  # coordinates in image #0, shape (K,2)
points1 = feats1['keypoints'][matches[..., 1]]  # coordinates in image #1, shape (K,2)

LightGlue(features=...) 会自动加载对应权重,可选的 features 包括 superpoint、disk、aliked、sift、doghardnet 和 raco-aliked。输入描述子的维度不是 256 时(例如 DISK/ALIKED 的 128 维),网络会自动加一个 input_proj;SIFT 需要额外的尺度和方向,仓库会自动处理。默认参数是 n_layers=9、flash=True、mp=False、depth_confidence=0.95、width_confidence=0.99、filter_threshold=0.1。

常用参数与关闭自适应的写法(官方 README 示例)
# 最大化精度:保留全部关键点,关闭早停和剪枝
extractor = SuperPoint(max_num_keypoints=None)
matcher = LightGlue(features='superpoint', depth_confidence=-1, width_confidence=-1)

训练和评测在 glue-factory。训练会下载 Oxford-Paris 和 MegaDepth,磁盘需求约 450 GB + 420 GB,默认 batch size 128 面向 2 张 24 GB 的 RTX 3090。

训练 LightGlue(合成单应预训练 + MegaDepth 微调)
git clone https://github.com/cvg/glue-factory
cd glue-factory
python3 -m pip install -e .

# 1) 合成单应预训练
python -m gluefactory.train sp+lg_homography \
    --conf gluefactory/configs/superpoint+lightglue_homography.yaml

# 2) MegaDepth 微调
python -m gluefactory.train sp+lg_megadepth \
    --conf gluefactory/configs/superpoint+lightglue_megadepth.yaml \
    train.load_experiment=sp+lg_homography

# 可选:先缓存 MegaDepth 的局部特征,加速训练(约 150 GB)
python -m gluefactory.scripts.export_megadepth --method sp --num_workers 8
python -m gluefactory.train sp+lg_megadepth \
    --conf gluefactory/configs/superpoint+lightglue_megadepth.yaml \
    train.load_experiment=sp+lg_homography \
    data.load_features.do=True
在 MegaDepth-1500 上评测(含自适应版)
python -m gluefactory.eval.megadepth1500 --conf superpoint+lightglue-official

# 自适应版:depth/width confidence 都设成 0.95
python -m gluefactory.eval.megadepth1500 --conf superpoint+lightglue-official \
    model.matcher.{depth_confidence=0.95,width_confidence=0.95}

两个实操提醒。第一,论文的数字使用 1600 px、2048 个特征、逐方法调过的 RANSAC 阈值;直接跑官方 eval 可能因版本、阈值和硬件不同而略有差异,比较时先对齐口径。第二,许可证不是一刀切:LightGlue 的代码和权重是 Apache-2.0,ALIKED 是 BSD-3-Clause,DISK 随仓库使用 Apache-2.0,但 SuperPoint 的原版权重和推理文件是另一套限制性许可证(lightglue/superpoint.py 也受其约束)。商用前要分别确认。

参考

  1. Lindenberger, Sarlin, Pollefeys. LightGlue: Local Feature Matching at Light Speed. ICCV 2023. arXiv · 代码
  2. Sarlin, DeTone, Malisiewicz, Rabinovich. SuperGlue: Learning Feature Matching with Graph Neural Networks. CVPR 2020. arXiv · 代码
  3. Chen, Luo, Zhang, Zhou, Bai, Hu, Tai, Quan. Learning to Match Features with Seeded Graph Matching Network. ICCV 2021. arXiv
  4. Shi, Cai, Shavit, Mu, Feng, Zhang. ClusterGNN: Cluster-based Coarse-to-fine Graph Neural Network for Efficient Feature Matching. CVPR 2022. arXiv
  5. Sun, Shen, Wang, Bao, Zhou. LoFTR: Detector-Free Local Feature Matching with Transformers. CVPR 2021. arXiv · 代码
  6. Wang, Zhang, Yang, Peng, Stiefelhagen. MatchFormer: Interleaving Attention in Transformers for Feature Matching. ACCV 2022. arXiv
  7. Chen, Luo, Zhou, Tian, Zhen, Fang, McKinnon, Tsin, Quan. ASpanFormer: Detector-Free Image Matching with Adaptive Span Transformer. ECCV 2022. arXiv
  8. Su, Lu, Pan, Wen, Liu. RoFormer: Enhanced Transformer with Rotary Position Embedding. arXiv 2021. arXiv
  9. Teerapittayanon, McDanel, Kung. BranchyNet: Fast Inference via Early Exiting from Deep Neural Networks. ICPR 2016. arXiv
  10. Liu, Xu, Wang, Darrell, Shelhamer. Anytime Dense Prediction with Confidence Adaptivity. ICLR 2022. arXiv · 代码
  11. Balntas, Lenc, Vedaldi, Mikolajczyk. HPatches: A Benchmark and Evaluation of Handcrafted and Learned Local Descriptors. CVPR 2017. arXiv
  12. Li, Snavely. MegaDepth: Learning Single-View Depth Prediction from Internet Photos. CVPR 2018. arXiv
  13. Sattler, Maddern, Toft, Torii, Hammarstrand, Stenborg, Safari, Okutomi, Pollefeys, Sivic, Kahl, Pajdla. Benchmarking 6DOF Outdoor Visual Localization in Changing Conditions. CVPR 2018. arXiv · 基准
  14. Taira, Okutomi, Sattler, Cimpoi, Pollefeys, Sivic, Pajdla, Torii. InLoc: Indoor Visual Localization with Dense Matching and View Synthesis. CVPR 2018. arXiv
  15. Jin, Mishkin, Mishchuk, Matas, Fua, Yi, Trulls. Image Matching across Wide Baselines: From Paper to Practice. IJCV 2021. arXiv · 代码
  16. CVPR 2023 Image Matching Challenge. Image Matching Challenge 2023. Kaggle competition, 2023. 比赛页面
  17. Barath, Matas, Noskova. MAGSAC: Marginalizing Sample Consensus. CVPR 2019. arXiv · 代码
  18. Edstedt, Athanasiadis, Wadenbäck, Felsberg. DKM: Dense Kernelized Feature Matching for Geometry Estimation. CVPR 2023. arXiv · 代码
  19. Dao, Fu, Ermon, Rudra, Ré. FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness. NeurIPS 2022. arXiv · 代码
  20. DeTone, Malisiewicz, Rabinovich. SuperPoint: Self-supervised Interest Point Detection and Description. CVPR Workshops 2018. arXiv · 代码
  21. Tyszkiewicz, Fua, Trulls. DISK: Learning Local Features with Policy Gradient. NeurIPS 2020. arXiv · 代码
  22. Zhao, Wu, Chen, Chen, Xu, Li. ALIKED: A Lighter Keypoint and Descriptor Extraction Network via Deformable Transformation. IEEE Transactions on Instrumentation and Measurement, 2023. arXiv · 代码
  23. Potje, Cadar, Araujo, Martins, Nascimento. XFeat: Accelerated Features for Lightweight Image Matching. CVPR 2024. arXiv · 代码

Comments