1. 为什么单张图片也能学出3D结构:从"对称性"这个先验说起
如果你接触过三维重建,大概率会有个固有印象:想从图像恢复三维结构,要么需要多视角图像做立体匹配,要么需要激光雷达、深度相机这类主动传感器,要么得靠大量标注好的3D真值做监督。但有一类工作偏偏反着来——只给你一堆从网上随手抓来的单张图片,没有相机参数,没有3D标注,甚至连物体类别都不告诉它,它却能把物体的三维形状、反照率、光照方向全部拆解出来。这篇论文就是这条路线里相当有代表性的一篇,标题里的"Probably Symmetric"和"in the Wild"两个词组,基本把它的核心思路和适用场景都点明了。
先把这篇论文要解决的问题说清楚。给定一个物体类别的图像集合(比如所有人脸照片、所有汽车照片),目标是学到一个模型,输入任意一张该类别的新图片,就能输出这个物体的三维形状、表面反照率(也就是去掉光照后的本来颜色)以及当前的光照条件。注意这里的关键约束:训练时没有任何三维真值、没有多视角对应、没有相机内参和外参标注。这在学术上叫"无监督单视图三维重建",难度在于这是一个极度欠定的问题——一张二维图片理论上对应无穷多种三维解释,你必须引入额外的先验才能把解空间约束到合理范围。
这篇论文引入的核心先验就是对称性。现实世界里大量物体——人脸、人体、多数动物、很多人工制品——都近似左右对称。这个先验的价值在于:它天然提供了"多视角"信息。一个对称物体的左半边和右半边,其实是从两个不同视角看到的同一个结构。如果你能建立左右两半之间的对应关系,就相当于免费获得了第二个视角,从而把单视图的欠定问题变成某种意义上的双视图问题。这就是标题里"Probably Symmetric"的含义——它不是假设物体绝对对称,而是用一个概率化的对称性建模,允许一定程度的非对称(比如人脸的表情、发型、光照造成的明暗差异)。
我第一次读这篇论文时,最感兴趣的不是它的网络结构,而是它怎么把"对称"这个看似简单的直觉,转化成可以端到端训练的数学约束。因为如果你只是简单地把图片水平翻转然后要求重建结果一致,会遇到一堆麻烦:光照在左右两边是不一样的,反照率在左右两边也可能不同,直接强制一致会把模型带偏。论文的处理方式是把形状、反照率、光照三个因素显式解耦,让对称性只作用在它该作用的地方。这个解耦思路是整篇论文的骨架,理解了它,后面所有的损失函数设计、网络分支设计都顺理成章。
适合读这篇笔记的人大概分三类:一是做三维视觉、想了解无监督重建路线的研究者或工程师;二是想把这套思路迁移到自己领域(比如医学影像、工业检测、电商商品建模)的实践者;三是对"如何用先验知识约束深度学习"这个方法论感兴趣的读者。我会尽量把论文里的数学符号翻译成能直接理解的语言,同时补上论文正文里没写、但实际复现时一定会踩到的坑。
2. 把一张图拆成形状、反照率、光照:解耦建模的完整逻辑
2.1 为什么必须解耦,而不是让网络直接回归三维
很多人第一反应是:既然要重建三维,那就训一个编码器-解码器,输入图片,直接输出三维体素或者网格不就行了?在有3D监督的时候这确实可行,但在无监督设定下,这条路几乎必然失败。原因在于,如果网络直接输出三维形状,而监督信号只有"重建出来的图片要和原图像",那么网络会找到无数种作弊方式:它可以把所有几何信息都编码进纹理里,输出一个球体但贴上复杂的纹理,重建出来的图片照样很像原图,但三维形状完全错误。这在文献里叫"形状-纹理歧义"(shape-appearance ambiguity),是无监督重建最核心的难点。
论文的解法是把图像的形成过程显式建模成一个可微的渲染方程。一张图片的某个像素,其颜色由三部分决定:这个像素对应的三维表面点的反照率、这个点的表面法线方向、以及光照方向。用公式表达大致是:像素颜色 = 反照率 × 光照与法线的某种交互项。这个交互项通常用球谐函数(Spherical Harmonics)来参数化光照,因为球谐函数能用很少的系数(一般9个)很好地近似漫反射光照。
一旦你把渲染过程写清楚,形状、反照率、光照就变成了三个独立的、有明确物理含义的变量。形状决定法线,反照率决定本征颜色,光照决定明暗分布。这时候再施加对称性约束,就可以精确地只约束形状(因为对称物体的形状左右对称),而不去错误地约束反照率和光照(因为它们本来就不对称)。这就是解耦的价值——它让先验知识能作用在正确的变量上。
2.2 对称性到底约束了什么:一个容易搞混的细节
这里有个特别容易踩的坑,我必须单独拎出来讲。对称性约束的对象是三维空间中的形状,而不是二维图像。很多初学者会想当然地认为"对称物体的图片应该左右对称",这是错的。一个对称的物体,在非正面视角下拍出来的照片,左右两半在图像上完全不对称——因为透视投影会让靠近相机的一侧显得更大。所以你不能在图像层面做对称性约束,必须在三维形状层面做。
论文的做法是:把重建出的三维形状(通常表示成深度图或者体素)沿某个平面做镜像,然后要求镜像后的形状和原形状一致。但这里又有个细节:镜像平面在哪?对于人脸这类物体,对称平面大致是过鼻梁的竖直平面,但具体位置需要模型自己学出来。论文里通常把对称平面固定为相机坐标系下的某个平面(比如x=0平面),然后让网络在重建时把物体对齐到这个坐标系。这个对齐过程本身也是个难点,因为无监督设定下没有姿态标注,网络必须自己学会把物体摆正。
我实测下来的经验是:对称性约束的强度需要仔细调。如果约束太强,模型会强行把所有物体都建成完全对称的,遇到真正不对称的物体(比如侧脸、有表情的脸)就会失真;如果约束太弱,又起不到约束解空间的作用。论文用概率化的方式处理这个问题——它不要求严格对称,而是学习一个对称性的置信度或者方差,让模型自己决定哪些区域该对称、哪些区域可以偏离。这个设计比硬约束优雅得多,也是标题里"Probably"的由来。
2.3 光照模型的选择:为什么是球谐函数
光照建模这块,论文用的是二阶球谐函数,共9个系数。为什么是二阶而不是更高阶?这里有个权衡。球谐函数的阶数越高,能表达的光照越复杂(高频细节越多),但需要估计的参数也越多,在无监督设定下越容易过拟合。二阶球谐函数能表达的是低频、平滑的光照,这恰好符合大多数自然场景下的漫反射光照——太阳光、室内灯光经过多次反射后,到达物体表面的光照分布通常是平滑的。
从实操角度看,9个光照系数相对于整张图片的像素数量来说是非常少的参数,这本身就是一种强正则化。模型被迫用很少的参数解释整张图片的明暗变化,这反过来会促使它把明暗变化归因于正确的因素(形状和光照),而不是胡乱编码。我在复现时试过用一阶球谐(4个系数),结果光照表达能力太弱,重建质量明显下降;用三阶(16个系数)则训练不稳定,容易把噪声也拟合进去。二阶是个比较稳的甜点区。
还有一个细节:球谐函数估计的是光照方向,但论文里通常假设光照是"远场"的,也就是光源在无穷远处,物体表面各点受到的光照方向一致。这个假设在自然图片里不一定成立(比如室内近光源),但作为近似是可以接受的。如果你要迁移到自己的数据上,且你的场景有明显近光源,可能需要考虑更复杂的光照模型,或者接受一定的重建误差。
3. 网络结构与损失函数:每个设计选择背后的取舍
3.1 编码器共享还是分离:一个影响训练稳定性的关键决策
论文的网络结构大致是:一个编码器提取图像特征,然后分出几个头(head),分别预测形状(深度图)、反照率图、光照系数、以及相机视角(如果没固定的话)。这里有个设计选择:这几个预测头是共享编码器还是各自独立编码器?
共享编码器的好处是参数少、训练快,而且迫使网络学到一个对所有任务都有用的通用特征表示。坏处是不同任务需要的特征可能冲突——形状需要的是几何边缘、轮廓信息,反照率需要的是颜色、纹理信息,光照需要的是全局的明暗分布信息。如果强行共享,可能会互相干扰。
论文采用的是共享主干加独立头的折中方案。主干网络(通常是类似U-Net的结构,带跳跃连接)负责提取多尺度特征,然后每个任务用几个卷积层做专门的映射。跳跃连接在这里很关键,因为形状重建需要高分辨率的细节(比如人脸的鼻子、眼睛轮廓),如果只靠深层特征,分辨率会丢失。我在复现时发现,去掉跳跃连接后,重建的深度图会变得非常平滑,细节全无,这印证了跳跃连接对几何细节的重要性。
另一个实操细节是:反照率图的预测通常需要一个sigmoid或者类似的激活函数把值限制在合理范围(比如0到1),而深度图的预测则不能有界,因为深度可以是任意正数。光照系数也没有固定范围。这些激活函数的选择看似琐碎,但直接影响训练初期的稳定性。我踩过的坑是:一开始给深度图也加了tanh激活,结果深度被限制在-1到1之间,模型只能通过缩放来补偿,训练非常慢。后来改成线性输出加softplus(保证正值),收敛快了很多。
3.2 重建损失:为什么不能只用L2
最直接的重建损失就是让渲染出来的图片和原图的像素差异最小,通常用L2损失。但只用L2会有一个问题:L2对大误差敏感,对小的结构性误差反而不够敏感,导致重建结果在边缘、细节处模糊。论文里通常会组合多种损失。
常见的是L1损失加感知损失(perceptual loss)。L1比L2对异常值更鲁棒,而且倾向于产生更清晰的边缘。感知损失则是把渲染图和原图都送进一个预训练的网络(比如VGG),比较中间层的特征差异。这个损失能捕捉到人眼敏感的结构相似性,而不是逐像素的数值差异。我在实践中发现,加不加感知损失,重建结果的"观感"差别很大——加了之后,即使像素误差没有显著下降,人眼看起来也舒服很多。
还有一个容易被忽略的损失是轮廓损失或者叫掩码损失。如果渲染出来的物体轮廓和原图的物体轮廓对不上,说明形状肯定错了。这个损失可以通过比较渲染图的alpha通道(或者前景掩码)和原图的分割掩码来计算。在没有分割掩码的情况下,可以用一些无监督的前景提取方法,或者干脆用重建损失间接约束。但如果你的数据能拿到掩码(比如很多数据集自带前景分割),加上这个损失会显著提升形状精度。
3.3 对称性损失的具体实现:从直觉到代码
对称性损失的核心思想是:把重建出的三维形状沿对称平面镜像,然后要求镜像后的形状和原形状尽可能一致。但直接比较两个三维形状需要建立对应关系,这在实现上有点麻烦。论文通常采用一种更巧妙的等价形式:把原图和水平翻转后的图片分别送进网络,得到两组形状、反照率、光照,然后要求翻转图的形状等于原图形状的镜像,翻转图的反照率等于原图反照率的镜像,但光照要相应变换。
这里的关键是光照的变换。当你把图片水平翻转时,光照方向也翻转了,所以你不能直接要求两组光照系数相等,而要对光照系数做相应的镜像变换。这个变换在球谐函数基下是有解析形式的,实现时需要查一下球谐函数的旋转公式。我一开始偷懒,直接要求两组光照系数相等,结果模型学出来的光照总是偏向某一侧,重建质量上不去。后来老老实实实现了光照的镜像变换,问题才解决。
另一个实操细节是:对称性损失应该只在形状和反照率上施加,不要在渲染出的图片上施加。因为图片的对称性还受到光照和视角的影响,直接约束图片对称会引入错误的梯度。这个区分在论文里可能一笔带过,但复现时如果搞混,训练会非常不稳定。
4. 训练中的实际困难与排查思路
4.1 形状-光照歧义:最顽固的失败模式
即使有对称性约束,训练过程中仍然会出现一种典型的失败模式:模型把光照的明暗变化错误地归因于形状。比如一张人脸照片,左脸亮右脸暗,模型可能学成"左脸凸出、右脸凹陷"的形状,而不是"光照从左上方来"。这种错误在早期训练中特别常见,因为随机初始化的网络对光照和形状都没有先验。
排查这个问题的信号是:看重建的深度图,如果深度图的明暗分布和原图的明暗分布高度相关,那大概率是出现了形状-光照歧义。正常的深度图应该反映物体的几何结构(比如鼻子的凸起、眼窝的凹陷),而不是简单地复制原图的明暗。
解决这个问题的思路有几个。一是加强对称性约束,因为对称物体的形状左右应该一致,如果模型把光照编码进了形状,形状就会左右不一致,对称性损失会惩罚它。二是用多张图片共享光照或者共享形状的假设——如果同一个物体的多张图片光照不同但形状相同,模型会被迫把光照和形状分开。论文里通常是在一个batch内随机采样多张图片,让它们共享形状或者共享光照,具体共享哪个取决于你的假设。三是用一些正则化项,比如对深度图的平滑性做约束,防止它出现和光照相关的高频噪声。
我自己的经验是,对称性约束加上batch内共享光照,基本能解决大部分形状-光照歧义。但如果你的数据里物体本身就不太对称(比如侧脸很多),对称性约束会失效,这时候就得靠其他先验了。
4.2 视角估计的坑:固定还是学习
论文里对相机视角的处理有两种方案:一种是假设所有图片都是正面视角(或者固定视角),另一种是让网络自己估计视角。固定视角实现简单,但适用范围窄——如果你的数据里物体姿态变化很大,固定视角会导致重建质量急剧下降。学习视角则更灵活,但增加了训练难度,因为视角和形状是耦合的,错误的视角会导致错误的形状。
我建议的做法是分阶段训练。先固定视角(比如假设都是正面),让模型学到一个粗略的形状和光照,然后再解冻视角估计,做联合微调。这样比一上来就联合训练稳定得多。另外,视角估计的输出通常用四元数或者旋转矩阵表示,用四元数的话要注意归一化,否则会出现数值问题。
还有一个细节:如果数据里物体的尺度变化很大(有的图片物体大、有的小),你还需要估计尺度或者焦距。论文里通常假设焦距固定或者通过某种方式归一化。如果你的数据尺度变化大,不处理这个问题会导致重建的形状尺度不一致,对称性约束也会受影响。
4.3 训练不稳定时的排查清单
无监督三维重建的训练出了名的不稳定,我整理了一个排查清单,按优先级排序:
| 症状 | 可能原因 | 排查方法 |
|---|---|---|
| 损失突然爆炸 | 学习率太大或光照系数发散 | 降低学习率,对光照系数加L2正则 |
| 重建图全黑或全白 | 反照率或光照的激活函数饱和 | 检查激活函数范围,加梯度裁剪 |
| 深度图全是噪声 | 对称性损失权重太低 | 提高对称性损失权重,检查镜像变换是否正确 |
| 形状-光照歧义 | 缺少解耦约束 | 加batch内共享光照,加强对称性 |
| 训练后期loss震荡 | batch太小或数据噪声大 | 增大batch,加数据增强 |
这个清单不是万能的,但能覆盖大部分常见问题。我特别想强调的是梯度裁剪——在无监督重建里,早期梯度经常很大,不裁剪的话很容易一步把参数带飞。裁剪阈值一般设在1.0到5.0之间,具体看你的损失量级。
5. 从论文到落地:迁移到自有数据时的注意事项
5.1 数据准备:什么样的图片能用,什么样的不能用
这篇论文的方法对数据有一定要求,不是什么图片都能直接扔进去。首先,物体类别要相对一致——你不能把猫和汽车混在一起训,因为它们的形状分布差异太大,网络学不到统一的先验。其次,物体要大致居中且占据图片的主要部分,如果物体太小或者偏在角落,前景提取和对称性约束都会出问题。第三,光照要有多样性,如果所有图片都是同一个光照条件,模型无法学会解耦光照和形状。
还有一个容易被忽略的点:背景。论文里通常假设背景是简单的或者可以被分割掉的。如果背景很复杂,重建损失会被背景主导,模型会把精力花在拟合背景上而不是物体上。实操中,要么用带掩码的数据,要么先用一个现成的分割模型把前景抠出来。我试过不做分割直接训,结果模型把背景的纹理也编码进了反照率,重建出来的物体带着一堆背景噪声。
5.2 对称性假设不成立时怎么办
如果你的物体类别本身对称性很弱(比如鞋子、某些工具),这篇论文的核心先验就失效了。这时候有几个替代思路。一是用其他先验,比如"平面性"(很多人工物体有平面结构)、"光滑性"(大多数自然物体表面是光滑的)。二是用多视角数据,哪怕只有少量多视角标注,也能大大缓解欠定问题。三是用类别级的形状模板,先学一个平均形状,再让每张图片在这个模板基础上做变形。
我在一个工业零件的项目里试过这套方法,零件本身不太对称,直接套用效果很差。后来改成用少量CAD模型做弱监督,结合对称性作为辅助约束,效果才上来。所以我的建议是:先评估你的物体对称性有多强,如果左右两半的形状差异超过20%,就别指望纯对称性约束能work了。
5.3 评估指标:没有3D真值怎么判断好坏
无监督设定下最尴尬的是评估。你没有3D真值,怎么知道重建的形状对不对?论文里通常用几种间接指标。一是重建误差,就是渲染图和原图的差异,但这个指标会被形状-光照歧义欺骗,不可全信。二是用少量有3D标注的测试集做定量评估,比如计算预测深度图和真值深度图的相关性。三是人眼主观评估,看重建的深度图是否符合直觉。
我自己的做法是组合使用:先用重建误差做粗筛,再用少量标注数据做精评,最后人眼看一批样本。特别要警惕的是,重建误差低不代表形状对——前面说过,模型可以把几何信息编码进纹理来降低重建误差。所以一定要看深度图本身,而不是只看重建图。
还有一个技巧:把重建的深度图渲染成法线图(normal map),法线图对几何错误非常敏感,比深度图更容易看出问题。如果法线图看起来平滑合理,形状大概率是对的;如果法线图有奇怪的凹凸,形状就有问题。
6. 这套思路还能怎么用:几个值得尝试的扩展方向
对称性先验这套框架其实不局限于论文里的物体类别。我在实践中发现,只要你的数据满足"近似对称"这个条件,就可以套用。比如医学影像里的器官(很多器官近似对称)、电商里的商品(很多商品左右对称)、甚至一些抽象的数据可视化场景。
一个有意思的扩展是把对称性从"左右对称"推广到"旋转对称"或者"周期对称"。比如某些花朵有旋转对称性,某些纹理有周期性。这些对称性同样能提供多视角信息,约束解空间。实现上需要把镜像操作换成相应的群操作,数学上稍微复杂一点,但思路是一致的。
另一个方向是把这套方法和神经辐射场(NeRF)结合。NeRF在有多视角输入时效果很好,但单视图NeRF同样面临欠定问题。把对称性先验引入单视图NeRF,理论上能提升重建质量。我试过一个简化版本,用对称性约束NeRF的密度场,确实比不加约束稳定,但计算量比论文里的深度图方案大很多,适合对质量要求高、不在乎训练时间的场景。
最后说个我踩过的坑:不要试图用这套方法处理透明或者高反光的物体。对称性约束假设的是漫反射表面,透明和镜面反射会破坏渲染方程的基本假设,重建结果会非常离谱。如果你的数据里有这类物体,要么先做材质分类把它们筛掉,要么换一套专门处理这类材质的方法。
这套方法给我的最大启发其实不是技术细节,而是"如何把领域知识转化成可微的约束"这个方法论。对称性只是众多先验中的一种,你完全可以根据自己的领域知识设计其他先验——比如"物体应该由少量基元组成"、"表面应该是分段光滑的"——只要能把它们写成可微的损失函数,就能塞进这个框架里。这比单纯调网络结构有意思得多,也更可能做出真正有差异化的工作。