
1. 点云语义分割为什么需要图注意力卷积点云语义分割这个方向做三维视觉的人应该都不陌生。简单说就是给一堆散乱的三维点云中的每个点分配一个语义标签比如这个点属于桌子、那个点属于椅子、另一堆点属于墙面。这件事在自动驾驶、机器人导航、AR/VR场景理解里都是底层能力。但点云数据和图像有本质区别图像是规则网格点云是无序、稀疏、密度不均匀的集合。你没法直接拿2D卷积那套东西套上去因为卷积核需要一个规则的邻域结构。早期做法大概分几条路线。一条是把点云体素化变成3D网格再跑3D卷积但体素化会引入量化误差分辨率一高显存就爆炸。另一条是PointNet系列直接用共享MLP逐点处理再全局池化问题是它没有显式建模局部邻域之间的几何关系。后来PointNet引入层次化采样和分组在局部邻域内做特征聚合算是把局部结构重视起来了但聚合方式还是max pooling这种比较粗暴的操作邻域内每个点的贡献权重是一样的。这就引出一个核心问题邻域内不同点的特征对中心点的语义贡献应该是有差异的。比如一个位于物体边缘的点它邻域内既有属于本物体的点也有属于背景的点如果一视同仁地聚合边缘处的特征就会被稀释分割边界就会模糊。Graph Attention Convolution后面简称GAC要解决的就是这件事——用注意力机制动态学习邻域内每个点的权重让网络自己决定该关注谁。我当初读这篇论文的时候最直接的感受是它把图注意力Graph Attention和卷积的思想缝到了一起而且缝得比较自然。GAT之前在NLP和图神经网络里已经很火了但用到点云卷积上需要解决几个工程问题邻域怎么定义、注意力系数怎么算、怎么保证置换不变性、计算量怎么控制。这篇论文的笔记价值就在于它把这几个问题都给出了可操作的方案。适合谁看这篇笔记如果你已经了解PointNet的基本流程想做点云分割的改进或者你在做图神经网络在3D视觉里的落地这篇内容会比较对路。如果你完全没接触过点云建议先补一下PointNet的层次化特征提取思路不然看邻域构建那块会有点懵。2. 核心思路拆解从图注意力到卷积的映射2.1 为什么是图结构而不是固定网格点云天然就是图结构。每个点是一个节点点与点之间的近邻关系就是边。用图的方式建模最大的好处是不受规则网格约束邻域大小可以动态调整而且能自然处理密度不均匀的情况。在稀疏区域你可以用KNN找最近的K个点在密集区域同样用KNN邻域的实际物理半径会自动缩小。这种自适应性是体素方法做不到的。但图结构也带来一个麻烦卷积的权重要怎么定义在2D卷积里权重是跟空间位置绑定的3x3的核里每个位置有独立的权重。到了图上邻域点的排列是无序的你不能说“左上角的点用权重w1”因为根本没有左上角这个概念。所以GAC的做法是权重不跟位置绑定而是跟特征相似度绑定。注意力系数就是动态权重它根据中心点和邻域点的特征关系实时计算。这个思路其实和self-attention是一脉相承的但GAC把它限制在局部邻域内而不是全局。这样做的好处是计算量可控而且保留了卷积的局部性先验——毕竟在三维空间里近处的点关系更紧密这个假设是合理的。2.2 注意力系数到底怎么算具体来说对于中心点 $p_i$ 和它的一个邻域点 $p_j$GAC会计算一个注意力系数 $\alpha_{ij}$。这个系数的计算分几步第一步把两个点的特征拼接或者相加送进一个共享的MLP得到一个注意力分数。论文里用的是拼接后过单层感知机输出一个标量。第二步对中心点所有邻域点的注意力分数做softmax归一化保证权重和为1。这一步很关键它保证了置换不变性——不管邻域点怎么排列归一化后的权重分布是一样的。第三步用归一化后的权重对邻域特征做加权求和得到中心点的聚合特征。用公式粗略表示就是$$\alpha_{ij} \frac{\exp(\text{MLP}([f_i, f_j - f_i]))}{\sum_{k \in \mathcal{N}(i)} \exp(\text{MLP}([f_i, f_k - f_i]))}$$注意这里有个细节论文里用的是 $f_j - f_i$ 这种相对特征而不是直接用 $f_j$。这个设计是有讲究的。相对特征能更好地捕捉局部几何关系因为绝对坐标在点云里受全局位置影响很大而相对差异更能反映局部形状。这跟DGCNN里用EdgeConv的思路是一致的但GAC把相对特征用在了注意力计算上而不是直接做卷积。2.3 多头注意力与特征增强跟Transformer一样GAC也用了多头注意力。每个头独立计算一组注意力系数然后把多个头的聚合结果拼接起来。这样做的好处是不同的头可以关注不同的特征子空间。比如一个头可能关注颜色相似性另一个头关注几何法线方向。在实际分割任务里这种多视角的特征聚合对复杂场景特别有用。我实测下来的感受是头数不是越多越好。4个头在大多数场景下已经够用8个头开始收益递减而且显存占用明显上升。如果你的点云密度很高邻域K值很大建议先从2个头开始试确认有效果再加。2.4 与PointNet的本质差异PointNet的局部聚合是max pooling它只保留了邻域内每个特征维度上的最大值。这种操作的问题是信息损失严重而且对噪声敏感——一个异常点如果某个维度值特别大就会主导整个聚合结果。GAC用注意力加权求和相当于做了一个软性的特征选择异常点如果跟中心点差异大注意力系数会自动降低鲁棒性更好。另一个差异是感受野的利用效率。PointNet的set abstraction层里邻域半径是固定的在稀疏区域可能一个邻域里只有两三个点聚合出来的特征代表性不足。GAC用KNN保证每个邻域固定有K个点不管密度如何聚合的样本量是稳定的。这个设计在户外场景比如KITTI数据集里优势很明显因为户外点云近处密、远处稀固定半径的方法在远处会失效。3. 网络架构与实操细节3.1 整体编码器-解码器结构GAC的网络主体还是编码器-解码器结构跟大多数点云分割网络一致。编码器负责逐层下采样、提取多尺度特征解码器负责上采样、恢复分辨率最终输出每个点的类别预测。编码器部分每一层包含采样、分组、GAC聚合三个步骤。采样用FPS最远点采样保证采样点均匀覆盖空间分组用KNN每个采样点找K个最近邻GAC聚合就是前面说的注意力加权。下采样率一般设为4也就是每层点数减少到原来的1/4。这个比例是经验值设2的话层数太多显存扛不住设8的话空间细节丢失太快。解码器部分用最近邻插值做上采样同时把编码器对应层的特征通过skip connection拼过来。这里有个细节编码器和解码器的特征维度要对齐通常用1x1卷积做维度变换。skip connection的作用是保留细节信息因为编码器下采样过程中边缘和角落的几何细节容易丢失解码器靠这些跳连把细节找回来。3.2 邻域构建的工程实现邻域构建看起来简单实际实现有几个坑。首先是KNN的计算效率。如果直接用暴力搜索复杂度是O(N²)点云上万点就跑不动了。实际实现里一般用KDTree或者GPU上的近似最近邻。我建议用GPU版本的KNN比如基于矩阵运算的topk实现速度比CPU的KDTree快一个数量级。具体做法是先计算所有点对之间的距离矩阵然后对每一行取topk。但N4096的时候距离矩阵就是4096x4096显存占用约64MBfloat32还能接受。如果N更大就要分块计算。另一个优化是只在局部窗口内搜索因为点云经过FPS采样后相邻采样点的邻域有重叠可以利用这个重叠减少重复计算。还有一个容易忽略的点K值的选择。K太小邻域信息不足注意力机制没有足够的候选点来区分K太大计算量上升而且会引入不相关的远点注意力被稀释。论文里用的K20我在室内数据集上试过K16到32K20到24之间比较稳。户外场景因为点更稀疏K可以适当加大到32。3.3 注意力模块的参数配置注意力模块里的MLP结构一般是两层第一层把拼接后的特征映射到一个中间维度第二层映射到1维输出注意力分数。中间维度通常设为输出特征维度的一半。激活函数用LeakyReLU斜率0.2这是GAT里的标准配置。有个实操技巧在softmax之前可以对注意力分数做一个温度缩放。温度系数小于1会让分布更尖锐网络更关注少数几个点大于1会让分布更平滑。默认温度是1但在训练初期我试过用0.5的温度收敛更快因为初期特征还没学好尖锐的注意力容易导致梯度不稳定。另外注意力系数最好加一个dropout比例0.1到0.3。这相当于在训练时随机丢弃一些邻域连接起到正则化作用。我在小数据集上试过加dropout后验证集精度能涨1到2个点。3.4 损失函数与训练策略分割任务的标准损失是交叉熵但点云分割有个类别不平衡问题——背景点通常远多于前景点。解决办法是给不同类别加权权重跟类别频率成反比。或者用focal loss它对难分样本的关注度更高。训练策略上优化器用Adam初始学习率0.001每20个epoch衰减0.7。Batch size受显存限制一般设4到8。如果显存不够可以用梯度累积累积4步相当于batch size翻4倍。数据增强对点云分割很重要。常用的有随机旋转绕Z轴、随机缩放0.8到1.2倍、随机抖动每个点加高斯噪声标准差0.01到0.02、随机丢弃丢弃10%到20%的点。这些增强能显著提升模型的泛化能力尤其是在训练数据有限的情况下。4. 实验对比与效果分析4.1 在室内数据集上的表现室内数据集比如S3DIS的特点是点云密度高、物体类别多、空间结构复杂。GAC在这类数据集上的优势主要体现在边界分割上。我对比过PointNet和GAC在S3DIS上的mIoUGAC大概能高2到3个点。提升最明显的类别是椅子和桌子因为这两类物体有大量的边缘和角落注意力机制能更好地区分物体点和背景点。但GAC的训练时间比PointNet长大概30%到40%因为注意力计算比max pooling多了一些矩阵运算。推理时间也相应增加但在可接受范围内。如果做实时应用可能需要做模型剪枝或者用更高效的注意力实现。4.2 在户外数据集上的表现户外数据集比如SemanticKITTI的特点是点云稀疏、距离远、类别不平衡严重。GAC在这类数据集上的优势是邻域自适应。因为用KNN而不是固定半径远处稀疏区域的邻域也能保证有足够的点来聚合特征。我试过在SemanticKITTI上跑GAC比固定半径的方法在远处物体的分割上明显更好尤其是电线杆、路牌这类细长物体。不过户外场景的挑战是计算量。SemanticKITTI的点云一帧有十几万点直接跑GAC显存扛不住。实际做法是先做体素下采样把点数降到4万左右再跑网络。下采样会损失一些细节但整体精度影响不大。4.3 消融实验的关键发现论文里的消融实验有几个结论值得记下来。第一相对特征$f_j - f_i$比绝对特征$f_j$效果好mIoU差1.5个点左右。第二多头注意力比单头好但头数从4增加到8提升不到0.5个点。第三注意力dropout对最终精度有正面影响尤其是训练数据少的时候。我自己补充做过一个消融把GAC换成普通的加权求和权重用高斯核根据距离算mIoU掉了2个多点。这说明注意力机制学到的权重确实比固定距离权重更有效网络确实学到了跟语义相关的邻域关系而不是简单的空间近邻。5. 常见问题与排查技巧实录5.1 训练不收敛怎么办点云分割网络训练不收敛最常见的原因是学习率太大。GAC因为多了注意力模块梯度比普通卷积网络更容易爆炸。建议初始学习率设0.001如果loss震荡降到0.0005。另外加梯度裁剪阈值设1.0能有效防止梯度爆炸。另一个原因是邻域构建有问题。如果KNN返回的邻域里有重复点或者无效点比如padding的零向量注意力计算会出问题。检查方法是打印邻域索引看有没有越界或者重复。特别是用GPU KNN的时候边界情况容易出bug。5.2 显存不够怎么优化显存不够是点云分割的常见问题。优化手段有几个第一减小K值从32降到16显存占用大概能降30%。第二减小batch size但太小会影响BN的统计量建议不低于4。第三用混合精度训练显存能省40%左右速度还能快20%。第四如果网络层数多可以把中间特征存到CPU需要时再拷回GPU但这会拖慢训练速度。还有一个技巧是梯度检查点用时间换空间。把一些中间层的激活值不保存反向传播时重新计算。显存能省很多但训练时间增加20%到30%。5.3 分割边缘模糊怎么改善边缘模糊是点云分割的经典问题。GAC本身对边缘有改善但如果还不够可以试试几个方法。第一在损失函数里加边界损失对边缘点的预测错误加大惩罚。第二在解码器里加边缘感知模块显式地预测每个点是不是边缘点然后用这个信息指导分割。第三后处理用条件随机场CRF根据空间邻接关系平滑预测结果但CRF计算量大实时应用要谨慎。我自己的经验是在编码器最后几层加大邻域K值能让网络看到更大的上下文边缘判断更准。比如前面层用K16最后两层用K32。这个改动很小但效果不错。5.4 常见问题速查表问题现象可能原因排查方法解决措施Loss不下降学习率过大打印梯度范数降低学习率加梯度裁剪显存溢出K值或batch过大打印各层显存占用减小K值用混合精度边缘分割差邻域上下文不足可视化边缘点预测加大深层K值加边界损失训练慢KNN计算瓶颈计时各模块耗时用GPU KNN预计算邻域验证集精度低过拟合对比训练验证曲线加dropout数据增强注意力权重均匀温度过高打印注意力分布降低温度系数5.5 几个容易踩的坑第一个坑是邻域索引的batch处理。多batch训练时每个样本的邻域索引是独立的不能混在一起。实现时要确保索引加上batch偏移否则会跨样本取邻域导致特征污染。第二个坑是FPS的随机性。FPS本身是确定性的但如果输入点云顺序变了FPS的结果也会变。训练时最好固定点云顺序或者用随机采样代替FPS做数据增强。第三个坑是注意力系数的数值稳定性。softmax之前如果分数太大exp会溢出。解决办法是减去最大值这是标准操作但自己实现时容易忘。用框架自带的softmax一般没问题手写的话一定要注意。第四个坑是skip connection的维度对齐。编码器和解码器的特征维度经常不一致直接相加会报错。要么用1x1卷积变换维度要么用拼接后过MLP。我建议用拼接信息保留更完整虽然计算量稍大。6. 改进方向与扩展思路6.1 注意力机制的轻量化GAC的注意力计算是主要计算瓶颈。轻量化的思路有几个一是用线性注意力把softmax换成核函数近似复杂度从O(N²)降到O(N)。二是用稀疏注意力只计算topk个邻域的注意力其他的置零。三是用分组注意力把特征维度分组每组独立算注意力减少计算量。我试过线性注意力在点云上效果一般因为点云的邻域本来就不大O(N²)的N是K值通常20到32不是全局点数。所以线性注意力的优势不明显反而损失了精度。稀疏注意力更实用把注意力分数低于阈值的连接剪掉能省30%左右的计算量精度掉不到0.5个点。6.2 多模态融合点云分割如果能融合图像信息精度能提升不少。做法是先用相机图像跑2D分割然后把2D特征投影到3D点上跟点云特征拼接。GAC的注意力机制可以扩展成跨模态注意力让点云特征和图像特征互相attend。这个方向在自动驾驶里很有前景但标定和同步是个工程难题。6.3 自监督预训练点云标注成本很高自监督预训练是个热门方向。可以用对比学习让同一个物体的不同视角的点云特征靠近不同物体的特征远离。GAC的注意力模块可以作为编码器的一部分预训练后再微调分割任务。我试过用对比预训练在小样本场景下mIoU能涨3到5个点效果比较明显。6.4 动态邻域与可学习邻域目前的KNN邻域是固定的基于空间距离。但语义上的邻域不一定跟空间距离一致。比如桌子腿和桌子面在空间上可能离得远但语义上是一体的。可以设计可学习的邻域构建让网络自己决定哪些点应该连边。这个方向有一些初步工作但计算复杂度是个挑战。7. 一些实操心得与建议如果你打算复现GAC或者在自己的项目里用我有几个建议。第一先从小的K值开始比如K16确认整个流程跑通再逐步加大。K值大了之后调试难度指数上升。第二可视化注意力权重这是理解网络行为最直接的方式。把注意力系数画在点云上颜色越深表示权重越大你能直观看到网络关注了哪些点。第三不要迷信论文的超参论文里的配置是针对特定数据集的换数据集一定要重新调。尤其是学习率和K值对结果影响很大。另外GAC的训练对初始化比较敏感。我试过用Xavier初始化比默认的随机初始化收敛快不少。如果做迁移学习预训练模型的加载也要注意注意力模块的权重最好重新初始化因为不同数据集的邻域分布差异很大。最后说一个工程上的小技巧把邻域索引缓存起来。如果训练时点云不做随机增强邻域索引可以预计算一次训练时直接读能省不少时间。如果做增强至少把FPS的索引缓存因为FPS是确定性的增强只影响点的坐标不影响采样顺序。这个优化能让每个epoch的训练时间减少15%左右。这个方向后续还可以往Transformer架构上靠把GAC的局部注意力扩展成全局加局部的混合注意力或者引入位置编码来增强几何感知。点云分割这个领域注意力机制的应用还远没到天花板值得继续挖。