☰
场景生成新思路:可学习程序库如何解决真实感与可控性难题
2026/10/20 12:28:53 网站建设 项目流程

从Programs到Poses:我读懂了"可学习的程序库"为什么能解决真实场景生成的老大难问题

以前做过室内场景合成的人应该都有同感:过去我们想让模型生成"一个客厅",最高频的翻车点是生成的图看起来像画,但细看要么沙发叠在茶几上,要么椅子悬空十厘米,要么窗帘把门堵死。用扩散模型硬磨像素,能磨出质感特别好的材质,但它的"空间常识"其实相当有限。真正到了"我要一个沙发对面放电视柜、沙发左前方0.4米再放一个茶几、灯不许挡门"这种带约束的场景,光靠像素级别的生成器根本保证不了约束成立。

今年这篇NeurIPS论文《From Programs to Poses: Factored Real-World Scene Generation via Learned Program Libraries》给了我一个很有价值的思路:把场景生成拆成"学出来的程序库"和"程序到姿态的落地"两件事。程序库负责表达场景的抽象规则,Poses负责把这些规则翻译成具体三维摆放,中间的连接点是Factored(分解式)表示。换句话说,它不是用一个黑盒模型一把梭生成整张图,而是让模型先学会"关于场景的代码",再一个函数一个函数地渲染出来。

这篇内容我打算拆成几段来讲:先说说为什么像素级生成解决不了可控性问题,再拆解Factored表示到底在拆什么,然后是Programs到Poses这条链路的每一层是怎么工作的,最后聊聊训练程序库时最容易踩的坑,以及如果要复现这类工作,我会从哪里入手。

1. 场景生成的硬骨头:为什么像素级生成解决不了"可控"问题

1.1 真实场景和一张好看的图像,是两层问题

很多人会把"场景生成"理解成"生成一张像样的图"。但把它当图像生成任务做,只解决了外层问题。内层问题是场景的三维结构:物体类别是否合理、物体之间的支撑关系是否成立、相对位置是否符合人类使用习惯、光照和布局是否匹配起居室这个语义。

举个最常见的例子,一张AI生成的卧室图,床尾对着门,床头柜浮空,桌子上摆着两盏互相穿透的台灯,这种图在2D图像层面几乎看不出毛病,但在任何需要与场景交互的应用里——机器人搬东西、游戏角色穿行、室内装修预览——都是不能用的。因为真实场景的合理性不是像素层面的,而是结构层面的。

真实空间里,一把椅子之所以能出现在餐桌旁,是因为"椅子围绕餐桌摆放"这个规则。桌椅间距、椅子面朝餐桌的方向、椅子不穿过桌腿,这些约束同时成立,才构成人类认知里"正常的餐厅场景"。

1.2 扩散模型的重灾区:离散约束和连续表示之间有一道裂缝

扩散模型在生成图像的时候,本质是在连续潜空间里做去噪。它对连续域的分布拟合能力很强,但"约束"这种东西往往是离散的、逻辑性的:"物体A在物体B的左侧上方""物体C不能与物体D重叠""所有物体都要落在支撑平面上"。这类条件不是平滑函数,很难靠隐变量插值来处理。

我曾经试过用扩散模型加空间条件来生成客厅场景,结果发现,只要条件里同时出现两个以上的空间关系,模型就开始顾此失彼。左边满足了,右边的物体关系就开始乱。原因不复杂:像素级模型看到的永远是"排列好的一堆像素",它没有真正的物体概念。你给它一个模块化的布局条件,它也只能把它当成一种模糊的视觉提示,而不是可执行的规则。

1.3 手写规则的尽头:程序化生成为什么又累又不通用

那干脆用代码生成场景呢?程序化建模确实可控,我曾经见过美术同事用节点式程序生成器搭了大量房间模板,效果不错,但每一类场景的规则都要人肉调试很久。换个风格、换个户型、换种家具组合,规则就要重写。这暴露了纯程序化方法的核心问题:规则本身是人工的,而不是从数据里学出来的。

如果说"图画模型有常识但没逻辑","手写程序有逻辑但没常识",那理想方案显然是把两者结合。这就是标题里Learned Program Libraries要干的事情——把程序规则从真实场景数据中自动学出来,让程序库里积累的是数据驱动得来的空间常识,而不是人的一拍脑袋。

2. Factored的底层逻辑:场景不是一张图,而是一组可组合因子

2.1 分解式表示到底在拆什么

论文标题里的关键词是Factored。直白讲,就是把一个完整场景拆成若干个独立的因子来建模。一个真实场景至少包含这几类因子:物体类别(床头柜、台灯)、物体几何外观(床的长宽高比例、柜子的造型)、空间关系(床头柜在床的左侧、台灯在床头柜上)、支撑关系(一切物体都落在支撑面上)、光照与氛围(黄昏暖光或者阴天冷白)。

这些因子各有各的规律。床头柜的外观规律和"床左边摆床头柜"的空间规律不是一回事。前者由家具设计风格决定,后者由人类的起居行为决定。把它们放在同一个隐向量里建模,模型会把两类信息搅在一起。Factored的意思就是:分开建,分开学,最后再合起来。

2.2 为什么"拆开"比"合在一起"更适合学程序

从程序的角度看,因子天然对应函数。一个程序库里的"BedPlacement"函数只管放床:"给定房间边界和朝向偏好,返回床的位置、朝向、尺寸"。另一个"NightstandPlacement"函数只管在床的左右侧找位置放床头柜。各自职责清晰,组合起来就能生成一个完整的卧室。

合在一起做统一模型的问题在于牵一发动全身。你改了某个物体类别的外观统计,整个隐空间分布都要跟着变;你新增一个物体类别,原来的分布可能被污染。分解之后,每个程序模块相对独立,可以单独更新、单独扩展,过程中不会影响其他模块。这种可组合性,是程序库解决场景生成问题最大的结构优势。

2.3 因子组合的语法:场景不是梦话,是有文法结构的

拆成因子之后还需要一个东西把它们组织起来,否则因子再独立,组合的时候也会出现"沙发长在墙上"这种非法结构。研究者通常用场景文法(Scene Grammar)来做这件事:规定一个场景必须由地面、墙面、物体集合组成;物体必须有一个类别、一个形状、一个姿态和一组与其他物体的关系。

这类文法很像编程语言的语法树。语法树保证了任意生成结果在结构上是合法的,就像编译器不允许你写出"if if else"这种没有操作数的语法。程序库里的每个程序,就是语法树上的一个节点实现。这一层结构是"可控"二字的来源——不是靠运气生成,而是每一次生成都会先搭建一棵合法语义树,再去执行。

我记得第一次接触这个思路的时候,一句话点醒了我:场景生成的问题不在于精度,而在于结构合法性。Factored表示加场景文法,就是在源头掐断非法结构的出现。

对比维度统一表示(Unified)分解式表示(Factored)
类别和关系的关系强耦合,改一个影响全局解耦,独立建模独立调整
新增物体类别需要重新训练整个模型新增对应程序模块即可
可解释性黑盒,只能看输出可以通过程序树追踪生成依据
典型风险分布坍缩后难以定位原因因子之间组合时可能出现误差累积

3. Programs到Poses:从符号说明书到具体摆放的完整链路

3.1 程序写出来是要被执行,不是用来观赏的

程序树的产物看起来像一段伪代码:客厅场景,包含一个沙发,位置靠东墙,面朝电视;一个茶几,位于沙发正前方,距离0.4米;一盏落地灯,位于沙发右侧,贴墙。它本身只是抽象描述。一个完整的场景生成系统,必须能够把这些抽象描述执行成具体的三维姿态——每个物体中心点的坐标、朝向四元数、缩放比例——这就是标题里的Programs to Poses。

这一段是整条流水线里最难做的部分。因为"沙发正前方0.4米"落实到3D坐标时,要考虑房间的实际大小、沙发的实际尺寸、茶几不能压到沙发底、地面必须水平支撑。抽象产物和具体数值之间有天然的语义鸿沟。

3.2 语义关系不是查表,而是一个求解过程

一开始做类似项目时,我的直觉是所有语义关系都可以靠查表解决:先定义"前方"是物体朝向的正方向,"0.4米"是直接偏移。实际操作才发现完全不是这样。如果每件家具的位置都是孤立地按照语义翻译,那最后物体之间必然大面积穿模。真实的姿态生成一定是一个联合约束求解的过程。

正确的姿势是:先用程序树给出每个物体的初始候选姿态,把这些候选放进一个优化器,同时约束支撑、碰撞、相对距离和朝向关系,不断调整这些姿态直到所有约束都满足。也就是说,语义关系提供的是"硬性条件",而姿态求解器负责在满足硬性条件的前提下寻找最合理的一组数值。这个过程的计算量不小,但它不会出现"桌子穿过沙发"这样低级错误。

3.3 姿态之后还有一公里:渲染出图才算真实感闭环

把姿态算完只是完成了几何布局。下一步要解决外观、材质、光照、相机参数,最终渲染出图。我在复现类似方案时发现,几何布局做漂亮之后,最怕的是渲染环节掉链子:要么材质太假,要么灯光方向不对,渲染出来的图像一眼就看穿是合成的。

工业上的做法一般分两条路。一条是用离线渲染器做高保真渲染,质量好但速度慢;另一条是实时渲染或者可微渲染,速度快、能回传梯度,但容易出现纹理漂移。论文里更常见的做法是两者结合:先用快速渲染器得到一个粗糙图像,再用可微渲染微调姿态和材质参数,让图像级损失参与反向传播。这一步的意义是把整个Program-to-Pose的管线最终接到图像质量上。

用一句直白的话来打比方:程序是乐高说明书,姿态是拼装出来的积木位置,渲染是给拼好的模型拍照。链条很长,但好在每一层都是可观察、可干预的——你不需要在所有环节同时Debug。

4. 学习程序库:数据从哪来,Loss怎么设计,程序怎么生长

4.1 程序库不是人写的,是拿"重构能力"当学习信号

Learned Program Libraries最核心的主张是:程序库里的程序是可以被学习出来的。学习信号是什么?是重构能力。

具体来说,给定一个真实场景的结构化标注——物体类别、位置、朝向、支撑关系——系统尝试用当前程序库里的程序去重构这个场景。如果某个程序能生成和真实场景高度一致的布局,说明它准确捕捉了这个场景的规律;如果重构误差很大,说明现有程序库还解释不了这类场景,要么调整已有程序的参数,要么增长新程序。

这套思路可以用自编码器来理解:输入是真实场景的结构化标注,中间表示是程序树,输出是重构出的场景布局。只不过中间表示从连续向量换成了离散程序树。好处是中间表示可读、可编辑、可插入约束。

4.2 训练循环的骨架:候选程序生成、解释执行、误差回传

训练过程大致是三步。

第一步,从观测场景提取结构化标注。这步依赖上游的物体检测、位姿估计、关系推理模型,标注质量直接影响程序库学习上限。

第二步,在程序库里做候选程序搜索。由于程序树的搜索空间极大,通常会用束搜索(beam search)采样一批候选程序,而不是暴力枚举所有可能性。

第三步,用解释器把程序树解成场景预测,和观测场景对比计算损失。这里有个工程难点:程序树是离散结构,没法直接对离散树做梯度回传。实际方案大体分两派:一派是设计可微解释器,让程序参数都能参与梯度计算;另一派是用强化学习的策略梯度训练程序选择器。

我自己更看好可微解释器方案,因为它训练更稳定,也不需要额外设计奖励函数。但它对解释器库的完备性要求很高,如果你想让程序库支持"在墙上挂画"这种操作,解释器里就得有对应的可微实现。

4.3 程序库不只要"学会",还要"会长"

程序库在训练过程中不是一成不变的。最开始可能只有一个万能"放家具"程序,随着训练进行,系统会发现"放沙发"和"放茶几"的布局规律差异太大,一个程序解释不过来,于是会自动分裂成两个程序;反过来,如果两种物体在布局上高度一致,系统又会把它们合并成一个带类型参数的通用程序。

这种"库的结构也参与学习"的设计,是程序库方法区别于普通参数模型的地方。它有点像孩子积累词汇的过程:第一次见到新物体,先建立一个新程序条目;随着见识增多,发现有些条目可以泛化,就合并成更抽象的规则。为了让库不至于无限膨胀,Loss里通常要加入最小描述长度(MDL)正则项:程序越简洁,正则惩罚越小。这保证了程序库既能覆盖数据,又不至于复杂到无法管理。

4.4 训练数据与模型规模的坑:偏差是程序库的慢性病

我在实际做这类方向时踩过的最大坑是数据偏差。程序库是从场景数据里学的,如果训练数据里"沙发"程序只在豪华客厅出现,程序库生成的普通客厅就一定不对味。程序库的规则是全局共享的,一旦某个类别学歪了,它会影响所有包含该类别程序的生成结果,而且错误还会向下游传递。

另一个工程问题是训练效率。程序树的变长结构批处理起来非常麻烦,树节点数量不一致导致GPU利用率很难拉满。重复试验中,程序搜索最耗时的瓶颈往往不在模型计算本身,而在树结构的组织与调度。

5. 如果我要复现这个方向,我会从这几个地方入手

5.1 先从小程序库小数据集起步,别一上来就想全场景

复现这类工作最忌讳的是一上来就追求完整的室内全场景程序库。正确路径是先搭一个只有3到5种家具的客厅程序库,比如沙发、茶几、电视柜、落地灯、地毯,每个程序手写一个简化版本,先把"程序生成-姿态求解-渲染出图"的闭环跑通。

只要你能让一个程序稳定生成"茶几在沙发前方0.4米,并排不穿模",你就已经掌握了整套方法的核心。后续再往程序库里加物体类别,都只是量变。我的经验是,小库上跑通的闭环,比大库上跑通一半要值钱得多,因为调试成本完全不是一个量级。

5.2 评价指标别只看FID,给布局和关系留一把尺子

如果只用FID来评估场景生成质量,很多空间上的错误都会被图像质量的表面光鲜掩盖。实际做评估时我建议至少看三类指标:

  • 布局质量:物体类别与数量的分布是否匹配真实数据,物体间的距离分布是否和真实场景的分布一致,可以用KL散度衡量;
  • 关系正确率:物体之间的语义关系是否满足程序库里的规则,比如"支撑关系"是否正确,"茶几在沙发前方"是否成立;
  • 视觉质量:FID、人工评估等,这部分负责衡量渲染和材质的真实感。

实际项目中经常出现"视觉质量涨了但关系正确率跌了"的对冲局面。这时候我会分阶段看指标:先锁定布局质量和关系正确率合格,再谈视觉质量优化,否则你只是在给自己造一个"看起来很真的错误场景"。

5.3 复现过程中最容易翻车的五个细节

以下五个位置是我和身边朋友在类似工作中反复翻车的地方,列出来供参考:

  1. 碰撞检测阈值:设得太小会导致物体相互穿插,设得太大则家具间距明显失真。建议根据物体实际尺寸动态调整,而不是全场景用同一个固定值。
  2. 支撑面判断:必须区分地板、桌面、床面、墙面,不同支撑面有不同的姿态约束。一套逻辑试图通吃所有支撑面,发生器立刻失衡。
  3. 数据标注噪声:半自动标注的关系标签不能直接信任,尤其是"前方""左侧"这类相对关系的标注常常错位,训练前需要清洗或按置信度加权。
  4. 程序搜索步数:步数太少,程序库找不到深度组合的优质解;步数太多,训练速度大幅下降。需要根据程序树的平均深度做折中。
  5. 随机种子:程序采样的随机性很大,不要只跑一次试验就下结论,固定种子并多组重复更靠谱。

5.4 这个方向真正有价值的地方,不只是出图

延展一下为什么我认为这类方法值得关注。如果程序库和姿态求解都成熟了,它就不只是生成一张好看的室内图,而是变成了一套"可编辑场景生成器"的底层引擎:机器人可以在程序生成的各种仿真场景里做策略训练,自动驾驶可以用它构建可编辑的场景库,游戏和VR可以用它快速生成符合关卡设计的程序化房间。

从这个角度看,程序库方法的最大价值在于它把"生成"和"模拟"连通了。生成的场景自带结构语义,可以直接对接物理引擎和决策系统,这是纯图像生成模型很难做到的。


我在读完这篇论文后,最大的体会是:它没有试图用一个巨大模型一步到位生成场景,而是老老实实地把问题分层——程序库负责抽提规则,姿态求解负责落实几何,渲染负责视觉真实。每一层都不算复杂,但组合起来才能应对真实场景的多重约束。我最近一直在琢磨,如果把程序库的检索接口换成大模型,让自然语言直接驱动程序树的组装,场景生成的自由度应该会再上一个台阶。这大概是这个方向下面最值得持续跟进的一条路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询