560 lines
22 KiB
Markdown
560 lines
22 KiB
Markdown
---
|
||
title: "Chapter 07 — 管线 D:记忆巩固 (Memory Consolidation)"
|
||
date: 2026-05-20
|
||
draft: false
|
||
tags: ["PRISM", "世界模型", "空间记忆", "Gaussian Splatting", "机器人"]
|
||
categories: ["PRISM"]
|
||
---
|
||
|
||
# Chapter 07 — 管线 D:记忆巩固 (Memory Consolidation)
|
||
|
||
> 本章目标:机器人**充电 / 空闲**时跑一次"睡眠",把 `delta/pending.jsonl` 中**反复确认**的变化真正写入 LTM,并淘汰过时锚点、重训 3DGS、版本化备份。
|
||
|
||
---
|
||
|
||
## 7.1 为什么需要"睡眠"
|
||
|
||
如果 ZED 看到一次"沙发挪了"就立刻改 LTM,机器人就会变得**易骗**:
|
||
- 客人挪一下沙发拍照?被记成永久挪动
|
||
- ZED 单帧检测错把椅子识成桌子?长期记忆被污染
|
||
- 镜面区噪声偶尔产生"虚影"?写进去再也清不掉
|
||
|
||
仿照人脑:**白天积累短期记忆 → 睡眠时筛选 → 仅把多次确认的信号转入长期记忆**。
|
||
|
||
PRISM 也一样:
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph DAY["白天(Online)"]
|
||
D1[("delta/pending.jsonl<br/><i>各种 DeltaEvent 累积,可能很乱</i>")]
|
||
end
|
||
subgraph NIGHT["夜里 / 充电时(Consolidation)"]
|
||
direction LR
|
||
N1["pending"] --> N2["筛选"] --> N3["confirmed"] --> N4["应用到 LTM"] --> N5["版本化"]
|
||
end
|
||
DAY -. 触发 .-> NIGHT
|
||
style DAY fill:#fff7d6,stroke:#c97a00
|
||
style NIGHT fill:#d8e4ff,stroke:#1565c0
|
||
```
|
||
|
||
---
|
||
|
||
## 7.2 触发条件
|
||
|
||
| 触发 | 频率 | 模式 |
|
||
|------|------|------|
|
||
| 充电桩 + 静止 > 10 min | 通常每天 1 次 | **完整巩固** |
|
||
| 手动命令 `prism consolidate` | 按需 | 完整巩固 |
|
||
| `pending.jsonl` > 5000 条 | 紧急 | **轻量巩固**(只筛选不重训) |
|
||
| LTM 版本年龄 > 30 天 | 月级 | **深度巩固**(含重算 anchors + CLIP) |
|
||
|
||
---
|
||
|
||
## 7.3 巩固总流程
|
||
|
||
> ⚠️ **v1.5 升级**:本节描述的"筛选 → 仲裁 → 应用 → 重训"线性流程及其
|
||
> 内部 deduplicate / merge 决策器(Step 1–3)的**训练范式**已被
|
||
> [§ 7.13.1](#7131-v15-升级self-augmentation-巩固训练) 增强为
|
||
> self-augmentation 监督学习;本节流程本身仍可作为 baseline 使用,
|
||
> 决策器若由简单规则(`PROMOTION_RULES` + `arbitrate()`)实现则完全不受影响。
|
||
> 仅当决策器升级为可训练策略网络(v1.5 → v2.0 路线图)时,§ 7.13.1
|
||
> 的 self-aug 范式才生效。
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
P[("pending.jsonl")]
|
||
S1["<b>Step 1: 事件分类与筛选</b><br/>observation_count > N<br/>时间跨度 > T<br/>多 keyframe 多视角"]
|
||
S2["<b>Step 2: 冲突仲裁</b><br/>iPhone 标 vs ZED 改"]
|
||
S3["<b>Step 3: 应用到 LTM</b><br/>add / update / remove node"]
|
||
S4["<b>Step 4: 锚点 / 索引 / CLIP</b><br/>重新计算受影响项"]
|
||
S5["<b>Step 5: 增量 3DGS 重训</b><br/>只重训受影响房间"]
|
||
S6["<b>Step 6: 版本化 + 健康检查</b><br/>snapshots/, validate()"]
|
||
P --> S1 --> S2 --> S3 --> S4 --> S5 --> S6
|
||
style P fill:#f5e1ff,stroke:#7b1fa2
|
||
style S6 fill:#d4f0d4,stroke:#2e7d32
|
||
```
|
||
|
||
---
|
||
|
||
## 7.4 Step 1:事件筛选
|
||
|
||
```python
|
||
# consolidation/filter.py
|
||
from datetime import timedelta
|
||
|
||
# 不同事件类型有不同"晋升门槛"
|
||
PROMOTION_RULES = {
|
||
"object_moved": dict(min_obs=5, min_span_s=300, min_views=2),
|
||
"object_removed": dict(min_obs=10, min_span_s=600, min_views=3),
|
||
"object_added": dict(min_obs=8, min_span_s=300, min_views=2),
|
||
"geometry_changed":dict(min_obs=15, min_span_s=900, min_views=4),
|
||
}
|
||
|
||
def classify(ev: DeltaEvent, mem: SpatialMemory) -> str:
|
||
rule = PROMOTION_RULES[ev.event_type]
|
||
span = ev.last_observed - ev.first_observed
|
||
unique_views = count_unique_viewpoints(ev.evidence, mem)
|
||
|
||
# 若是 iPhone 的高 confidence 节点,门槛 1.5x
|
||
target = mem.nodes.get(ev.target_uid) if ev.target_uid else None
|
||
if target and target.source == "iphone" and target.confidence > 0.85:
|
||
rule = {k: v*1.5 for k, v in rule.items()}
|
||
|
||
if ev.observation_count >= rule["min_obs"] and \
|
||
span >= rule["min_span_s"] and \
|
||
unique_views >= rule["min_views"]:
|
||
return "confirm"
|
||
if span > 7*86400 and ev.observation_count < rule["min_obs"]//2:
|
||
return "reject" # 7 天还没攒够观测 → 拒绝
|
||
return "keep" # 继续等待
|
||
```
|
||
|
||
**关键直觉**:
|
||
- `min_obs`:要"看过很多次"
|
||
- `min_span_s`:必须**跨越足够长时间**(防止瞬时假象)
|
||
- `min_views`:必须**来自多个视点**(防止单点死磕)
|
||
|
||
---
|
||
|
||
## 7.5 Step 2:冲突仲裁
|
||
|
||
不同 delta 之间,或 delta 与 LTM 之间可能冲突:
|
||
|
||
```python
|
||
# consolidation/arbiter.py
|
||
def arbitrate(events: List[DeltaEvent], mem: SpatialMemory) -> List[DeltaEvent]:
|
||
"""对同一 target 的冲突事件做仲裁"""
|
||
by_target = group_by_target(events)
|
||
final = []
|
||
for uid, evs in by_target.items():
|
||
if len(evs) == 1:
|
||
final.append(evs[0]); continue
|
||
# 多个事件涉及同一节点
|
||
sorted_evs = sorted(evs, key=lambda e: e.observation_count, reverse=True)
|
||
primary = sorted_evs[0]
|
||
# "搬走"+"挪到新位置" → 合并为一个 moved 事件
|
||
if {e.event_type for e in evs} == {"object_removed","object_added"}:
|
||
primary = merge_remove_add(evs)
|
||
final.append(primary)
|
||
return final
|
||
|
||
|
||
def merge_remove_add(evs):
|
||
rem = next(e for e in evs if e.event_type == "object_removed")
|
||
add = next(e for e in evs if e.event_type == "object_added")
|
||
return DeltaEvent(
|
||
event_id=uuid(),
|
||
event_type="object_moved",
|
||
target_uid=rem.target_uid,
|
||
new_pose=add.new_pose,
|
||
new_bbox=add.new_bbox,
|
||
evidence=rem.evidence + add.evidence,
|
||
observation_count=rem.observation_count + add.observation_count,
|
||
first_observed=min(rem.first_observed, add.first_observed),
|
||
last_observed=max(rem.last_observed, add.last_observed))
|
||
```
|
||
|
||
---
|
||
|
||
## 7.6 Step 3:应用到 LTM
|
||
|
||
```python
|
||
# consolidation/apply.py
|
||
import shutil
|
||
from copy import deepcopy
|
||
|
||
def apply_events(events: List[DeltaEvent], mem: SpatialMemory) -> SpatialMemory:
|
||
"""返回应用后的新 SpatialMemory;不修改原对象"""
|
||
new = deepcopy(mem)
|
||
for ev in events:
|
||
if ev.event_type == "object_moved":
|
||
n = new.nodes[ev.target_uid]
|
||
n.pose = ev.new_pose
|
||
n.bbox_3d = ev.new_bbox
|
||
n.last_seen = ev.last_observed
|
||
n.confidence = min(1.0, n.confidence * 0.95) # 轻微降低(被动过)
|
||
# 重新计算 parent_room
|
||
n.parent_room = find_room_for_point(n.pose.position[:2], new)
|
||
elif ev.event_type == "object_removed":
|
||
uid = ev.target_uid
|
||
n = new.nodes[uid]
|
||
# 不立刻硬删除,标记 deprecated 一段时间
|
||
n.attributes["state"] = "removed"
|
||
n.confidence *= 0.3
|
||
# 切断关系
|
||
new.edges = [e for e in new.edges
|
||
if e.src_uid != uid and e.dst_uid != uid]
|
||
elif ev.event_type == "object_added":
|
||
node = SpatialNode(**ev.payload)
|
||
node.source = Source.FUSED # 经过 consolidation
|
||
node.confidence = 0.7 # 提升
|
||
new.add_node(node)
|
||
new.add_edge(SpatialEdge(node.parent_room, node.uid,
|
||
"contains", source=Source.FUSED))
|
||
elif ev.event_type == "geometry_changed":
|
||
# 在 L2 TSDF / OctoMap 上打孔重建,不改场景图
|
||
update_dense_layer_at_bbox(new, ev.new_bbox)
|
||
return new
|
||
```
|
||
|
||
→ 注意 `deepcopy + 整体替换` 保证**原子性**:要么全部成功,要么回滚。
|
||
|
||
---
|
||
|
||
## 7.7 Step 4:锚点 / 索引 / CLIP 更新
|
||
|
||
```python
|
||
# consolidation/refresh.py
|
||
def refresh_anchors_and_index(new_mem: SpatialMemory):
|
||
# 1) 锚点重算
|
||
new_mem.anchors = build_anchors(new_mem) # 同 Chapter 04
|
||
|
||
# 2) 受影响节点的 CLIP 重算
|
||
for ev in events_just_applied:
|
||
if ev.event_type in ("object_moved","object_added"):
|
||
uid = ev.target_uid or ev.payload["uid"]
|
||
node = new_mem.nodes[uid]
|
||
# 渲染或从近期 keyframe 裁剪
|
||
crops = collect_recent_crops(uid)
|
||
node.clip_embedding = mean_clip_embedding(crops)
|
||
|
||
# 3) Faiss 索引重建(L4 全量)
|
||
build_faiss_index(new_mem)
|
||
|
||
# 4) L3 房间 CLIP 受影响时也重算
|
||
affected_rooms = {ev.target_uid_room for ev in events_just_applied}
|
||
for r_uid in affected_rooms:
|
||
new_mem.nodes[r_uid].clip_embedding = recompute_room_clip(r_uid)
|
||
```
|
||
|
||
---
|
||
|
||
## 7.8 Step 5:增量 3DGS / TSDF 重训
|
||
|
||
完全重训 3DGS 太贵(一房间 15 k iter × 5 min),所以**只重训受影响房间** + **复用未变区域**:
|
||
|
||
```python
|
||
def incremental_retrain_3dgs(new_mem: SpatialMemory, events):
|
||
affected_rooms = set(find_room_for_point(ev.new_pose.position[:2], new_mem)
|
||
for ev in events if ev.new_pose)
|
||
for r_uid in affected_rooms:
|
||
# 收集该房间最近 24h 的关键帧
|
||
kfs = list_keyframes_in_room(r_uid, since=time.time()-86400)
|
||
# 从老 ckpt warmstart
|
||
cmd = f"""ns-train splatfacto-bigtraining \
|
||
--pipeline.model.warmstart-ckpt outputs/{r_uid}/last.ckpt \
|
||
--max-num-iterations 3000 \
|
||
--data {kfs_dir}"""
|
||
subprocess.run(cmd, shell=True)
|
||
# 替换 LTM 中的 .ply
|
||
shutil.move(f"outputs/{r_uid}/final.ply",
|
||
f"robot_memory/ltm/dense/3dgs/{r_uid}.ply")
|
||
|
||
def incremental_update_tsdf(new_mem, events):
|
||
"""TSDF 更便宜,直接在受影响 bbox 内重新融合最近关键帧"""
|
||
for ev in events:
|
||
bbox = ev.new_bbox if ev.new_bbox is not None else \
|
||
new_mem.nodes[ev.target_uid].bbox_3d
|
||
# 把 LTM TSDF 在该 bbox 内"清零"
|
||
new_mem.dense.tsdf.reset_in_bbox(bbox)
|
||
# 用最近 keyframes 重融合
|
||
for kf in keyframes_observing_bbox(bbox, since_h=24):
|
||
new_mem.dense.tsdf.integrate(kf)
|
||
```
|
||
|
||
---
|
||
|
||
## 7.9 Step 6:版本化 + 健康检查 + 切换
|
||
|
||
巩固结果不直接覆盖在线版本,先写到一个 staging,校验通过才切换:
|
||
|
||
```python
|
||
# consolidation/commit.py
|
||
def commit(new_mem, old_dir, new_dir):
|
||
# 1) 写 staging
|
||
save(new_mem, f"{new_dir}/spatial_memory.json")
|
||
copy_dense_dir(f"{old_dir}/dense", f"{new_dir}/dense")
|
||
apply_dense_updates(new_dir, new_mem)
|
||
|
||
# 2) 校验
|
||
errs = validate(new_mem)
|
||
sanity = run_sanity_relocalize(new_dir) # 用 10 张历史关键帧重定位
|
||
if errs or sanity.success_rate < 0.9:
|
||
log.error(f"consolidation rejected: {errs}, success={sanity}")
|
||
return False
|
||
|
||
# 3) 原子切换
|
||
timestamp = datetime.now().strftime("%Y%m%d_%H%M")
|
||
snapshot = f"robot_memory/snapshots/{timestamp}.tar.zst"
|
||
archive(old_dir, snapshot)
|
||
os.rename(old_dir, f"{old_dir}.prev")
|
||
os.rename(new_dir, old_dir)
|
||
return True
|
||
```
|
||
|
||
---
|
||
|
||
## 7.10 锚点淘汰策略
|
||
|
||
```python
|
||
def prune_stale_anchors(mem, max_age_days=60, min_recent_validations=2):
|
||
keep = []
|
||
for anc in mem.anchors:
|
||
node = mem.nodes[anc.anchor_uid]
|
||
age_days = (time.time() - anc.last_validated) / 86400
|
||
# 仍是不可移动 + 最近 60 天被巩固确认过 → 保留
|
||
if not anc.is_mobile and \
|
||
node.attributes.get("state","") != "removed" and \
|
||
age_days < max_age_days:
|
||
keep.append(anc)
|
||
elif node.observation_count >= min_recent_validations:
|
||
keep.append(anc)
|
||
mem.anchors = keep
|
||
```
|
||
|
||
→ 每次巩固跑一次。
|
||
|
||
---
|
||
|
||
## 7.11 完整 CLI:`prism consolidate`
|
||
|
||
```python
|
||
# tools/prism_consolidate.py
|
||
import click
|
||
|
||
@click.command()
|
||
@click.option("--mode", default="full",
|
||
type=click.Choice(["full","light","deep"]))
|
||
@click.option("--ltm", default="robot_memory/ltm")
|
||
@click.option("--delta", default="robot_memory/delta/pending.jsonl")
|
||
def main(mode, ltm, delta):
|
||
mem = load(f"{ltm}/spatial_memory.json")
|
||
raw_events = load_pending(delta)
|
||
print(f"[consolidate-{mode}] start, ltm_nodes={len(mem.nodes)}, "
|
||
f"pending={len(raw_events)}")
|
||
|
||
# Step 1: filter
|
||
decisions = [(ev, classify(ev, mem)) for ev in raw_events]
|
||
confirmed = [ev for ev, d in decisions if d == "confirm"]
|
||
rejected = [ev for ev, d in decisions if d == "reject"]
|
||
kept = [ev for ev, d in decisions if d == "keep"]
|
||
|
||
# Step 2: arbitrate
|
||
final = arbitrate(confirmed, mem)
|
||
|
||
# Step 3-4: apply + refresh
|
||
staging = f"{ltm}.staging"
|
||
new_mem = apply_events(final, mem)
|
||
refresh_anchors_and_index(new_mem)
|
||
|
||
# Step 5: dense(mode=full/deep 时才跑)
|
||
if mode in ("full","deep"):
|
||
incremental_update_tsdf(new_mem, final)
|
||
if mode == "deep":
|
||
incremental_retrain_3dgs(new_mem, final)
|
||
|
||
# Step 6: commit
|
||
ok = commit(new_mem, ltm, staging)
|
||
if not ok:
|
||
print("[consolidate] rolled back; pending.jsonl unchanged")
|
||
return
|
||
|
||
# 重写 pending:把 confirmed 移走,rejected 归档,kept 留下
|
||
write_jsonl(f"robot_memory/delta/confirmed.jsonl", final, append=True)
|
||
write_jsonl(f"robot_memory/delta/rejected.jsonl", rejected, append=True)
|
||
write_jsonl(delta, kept) # 覆盖
|
||
print(f"[consolidate-{mode}] applied={len(final)} "
|
||
f"rejected={len(rejected)} kept={len(kept)}")
|
||
```
|
||
|
||
运行:
|
||
|
||
```bash
|
||
# 充电时自动触发
|
||
systemd-timer --on=20:00 --weekly --command "prism consolidate --mode full"
|
||
# 月度深度巩固
|
||
systemd-timer --on=monthly --command "prism consolidate --mode deep"
|
||
```
|
||
|
||
---
|
||
|
||
## 7.12 安全机制
|
||
|
||
| 风险 | 缓解 |
|
||
|------|------|
|
||
| 巩固后机器人 reloc 失败 | Step 6 的 sanity check 不通过 → 自动回滚 |
|
||
| 巩固过程中断电 | staging 写完之前不替换 `ltm/`;半成品 staging 启动时清理 |
|
||
| 误删 iPhone 高 conf 节点 | 应用 remove 前再次检查 `source==iphone && conf>0.85`,若是则需 ≥ 10 倍证据 |
|
||
| 数据竞争(在线感知与巩固同时写) | 巩固开始前发 `set_readonly(true)`,在线感知此期间只允许写 `delta/`,不能切换 LTM 句柄 |
|
||
|
||
---
|
||
|
||
## 7.13 巩固后的指标记录
|
||
|
||
```python
|
||
def log_metrics(events, mem_before, mem_after):
|
||
with open("robot_memory/logs/consolidation.log", "a") as f:
|
||
f.write(json.dumps({
|
||
"ts": datetime.now().isoformat(),
|
||
"applied": len(events),
|
||
"nodes_before": len(mem_before.nodes),
|
||
"nodes_after": len(mem_after.nodes),
|
||
"anchors_before": len(mem_before.anchors),
|
||
"anchors_after": len(mem_after.anchors),
|
||
"ltm_version_new": mem_after.schema_version,
|
||
}) + "\n")
|
||
```
|
||
|
||
便于后续画"机器人记忆演化"曲线。
|
||
|
||
---
|
||
|
||
## 7.13.1 v1.5 升级:Self-Augmentation 巩固训练
|
||
|
||
### 背景:train-test discrepancy
|
||
|
||
§ 7.4–7.7 的 dedup / merge / arbitrate 决策当前由**简单规则**
|
||
(`PROMOTION_RULES` 阈值 + `arbitrate()` 启发式)做。v1.5 → v2.0 路线
|
||
图中,我们计划把这些规则升级为可训练的**策略网络**
|
||
(`policy_net: SceneRepr → {confirm, reject, merge, ...}`)。一旦走到
|
||
策略网络,就立刻撞上 Lyra 2.0 § 3.3 描述的 train-test discrepancy:
|
||
|
||
- **训练时**:策略网络看到的是"干净的当前 L3 snapshot"——离线流水线
|
||
bundle adjustment 完毕、节点位姿无累计漂移、CLIP 嵌入由完整观测重算。
|
||
- **推理时**:策略网络面对的是"有累积误差的 snapshot"——白天 Pipeline C
|
||
在线写入、L2 已漂、CLIP 由匆忙的 ZED crop 算出、还混着第 50 次巩固
|
||
之后才逐渐显形的系统性偏差。
|
||
|
||
这种分布偏移导致策略网络在第 1–10 次巩固时表现良好、到第 50 次开始
|
||
误删高 confidence 节点或把两个真实独立的家具误合为一个。Lyra 2.0
|
||
§ 3.3(b) 给出的解法是 **self-augmentation**:训练时**主动把模型自己
|
||
之前的不完美输出当作输入**,但**监督信号仍用干净 ground-truth**——模型
|
||
反复见到"自己会犯的错",因此学会自我纠错。完整原则陈述与 $p_{\text{aug}}$
|
||
取值讨论见 [`18_lyra_inspirations.md` § 18.4](18_lyra_inspirations.md)。
|
||
|
||
### 算法:`consolidate_with_self_aug()`
|
||
|
||
```python
|
||
# consolidation/self_aug.py (v1.5 新增,配合 policy_net 升级使用)
|
||
import numpy as np
|
||
|
||
def consolidate_with_self_aug(
|
||
l3_clean: SceneRepr, # 当前离线巩固出的"干净"L3 快照
|
||
history_l3s: List[SceneRepr], # 过去 N 次巩固留下的不完美快照
|
||
policy_net, # 待训练的 dedup/merge 决策器
|
||
p_aug: float = 0.7,
|
||
t_max: float = 0.5,
|
||
) -> torch.Tensor:
|
||
"""
|
||
单步训练:以 p_aug 概率把"历史不完美 L3"当输入,
|
||
监督信号始终是从 l3_clean 推得的 dedup/merge 目标。
|
||
"""
|
||
# ---- 1. 决定本步是否使用 self-aug ----
|
||
if np.random.rand() < p_aug and len(history_l3s) > 0:
|
||
# 从历史中采样一份"曾经的不完美 snapshot"做基底
|
||
t = np.random.uniform(0.0, t_max) # 噪声强度,见下 t 的物理意义
|
||
base = sample_history(history_l3s)
|
||
corrupt_input = inject_noise_from_history(
|
||
l3_clean, base, t=t) # 见下"噪声注入策略"
|
||
else:
|
||
# 1 - p_aug = 0.3 概率用真实的"干净"输入做兜底
|
||
corrupt_input = l3_clean
|
||
|
||
# ---- 2. 前向 + 统一向"干净目标"对齐 ----
|
||
pred = policy_net(corrupt_input) # logits over actions
|
||
targets = dedup_targets_from(l3_clean) # 监督一律取 clean
|
||
loss = cross_entropy(pred, targets)
|
||
return loss # 调用方负责 backward + step;典型 1000 iter / 场景(见参数表)
|
||
```
|
||
|
||
### 噪声注入策略:把历史 L3 当 "corruption source"
|
||
|
||
`inject_noise_from_history(l3_clean, base, t)` 不是凭空加 Gaussian,而是
|
||
**让 `l3_clean` 退化成"看起来像 `base` 那个时点的中间状态"**。具体扰动
|
||
按 `t ∈ [0, 0.5]` 线性加权应用以下三类:
|
||
|
||
| # | 扰动名 | 实现 | 模拟的真实失败模式 |
|
||
|---|--------|------|-------------------|
|
||
| 1 | **节点位置抖动** | 对每个节点 `pose.position` 加 `N(0, σ_xyz)`,`σ_xyz = t · 0.10 m` | L2 长走廊漂移导致 L3 节点中心偏移 ±10 cm |
|
||
| 2 | **CLIP 嵌入扰动** | 对 `clip_embedding` 加 `N(0, σ_clip)` 后重新 L2 归一化,`σ_clip = t · 0.05` | ZED 暗光 / 模糊 crop 导致嵌入轻微偏离 |
|
||
| 3 | **节点随机丢弃** | 以概率 `p_drop = t · 0.10`(即 t=0.5 时丢 5%)随机移除非锚点节点 | 在线管线漏检小物品 / 被遮挡未上报 |
|
||
|
||
实现时三种扰动**独立采样、叠加施加**,并对应 `history_l3s` 中真实出现过
|
||
的偏差量级做了线性归一(最大扰动幅度对齐"第 50 次巩固"时的统计观测)。
|
||
|
||
### 参数表
|
||
|
||
| 参数 | 默认值 | 物理意义 / 来源 |
|
||
|------|--------|----------------|
|
||
| `p_aug` | **0.7** | Lyra 2.0 § 3.3(b) 报告的最佳点;> 0.5 让模型主要见自己的错,< 1.0 保留 30% 真实兜底,避免沉迷自生失败模式 |
|
||
| `t_max` | **0.5** | $t \in [0, 0.5]$ 表示"最多让历史看起来像**巩固到一半时**的中间状态";t=1.0 会让样本退化到几乎全噪声,监督信号失效 |
|
||
| `σ_xyz` | t · 0.10 m | 与典型 L2 长走廊漂移上限对齐 |
|
||
| `σ_clip` | t · 0.05 | 与 ZED 暗光 crop 实测 CLIP 偏移分位数对齐 |
|
||
| `p_drop` | t · 0.10 | 与 Pipeline C 漏检率(v1.4 实测 ~5%)对齐 |
|
||
| 收敛迭代数 | **~1000 iter / 场景** | Lyra 用 7000 iter(视频扩散数据规模大);PRISM 单场景规模约小 5–10×,1000 iter 经验上够 |
|
||
| 训练批大小 | 1 场景 / step | 场景图本身就是一个 graph batch,无需 mini-batch |
|
||
|
||
### 评测建议(回链 [`13_evaluation.md`](13_evaluation.md))
|
||
|
||
在 [`13_evaluation.md`](13_evaluation.md) 的"**长时一致性**"指标族
|
||
(典型项:第 N 次巩固后的节点误删率、误合率、ghost-node 残留率)下,
|
||
**工程预估**:使用 self-aug 训练的策略网络在第 50 次巩固之后,
|
||
|
||
- **误删高 confidence 节点率**:下降 **30–50%**(baseline 无 self-aug 训练时通常 ~8% → 预期 4–5.5%)
|
||
- **误合相邻独立家具率**:下降 **30–40%**
|
||
- 第 1–10 次巩固指标基本持平(self-aug 主要解决长尾分布偏移,不解决初期能力问题)
|
||
|
||
ablation 计划:固定其他条件,对 `p_aug ∈ {0.3, 0.5, 0.7, 0.9}` 与
|
||
`t_max ∈ {0.25, 0.5, 0.75}` 跑 2D 扫描,验证 (0.7, 0.5) 是否对 PRISM
|
||
真实数据也最优——如果最优点漂到 (0.5, 0.5),说明 Pipeline C 已足够稳定,
|
||
self-aug 信号可以降权。
|
||
|
||
### 与原巩固管线的关系
|
||
|
||
本小节**不替换** § 7.4–7.7 的任何步骤,只升级其中 dedup/merge 决策器的
|
||
**训练范式**——决策器接口、`apply_events()` 的下游调用、commit / 回滚
|
||
机制全部不变。旧版规则式决策器(`PROMOTION_RULES` + `arbitrate()`)仍
|
||
可作为 baseline 与 self-aug 训练出的策略网络做 A/B。回链
|
||
[`18_lyra_inspirations.md` § 18.4](18_lyra_inspirations.md)
|
||
"原则三:巩固期 Self-Augmentation"。
|
||
|
||
---
|
||
|
||
## 7.14 与 Chapter 06 的接口
|
||
|
||
| 项 | Chapter 06 | Chapter 07 |
|
||
|----|-----------|------------|
|
||
| 写 LTM | ❌ 严禁 | ✅ 唯一允许的写者 |
|
||
| 写 delta | ✅ append | 读取 + 重写 confirmed/rejected/kept |
|
||
| 读 LTM | 只读 | 只读 + 写 staging |
|
||
| 频率 | 实时 | 离线(充电时) |
|
||
|
||
两者通过 **文件系统** 解耦:在线感知不必知道巩固何时跑,巩固也不必停下感知。
|
||
|
||
---
|
||
|
||
## 7.15 本章小结
|
||
|
||
| 关键点 | 一句话 |
|
||
|--------|--------|
|
||
| **何时跑** | 充电 / 空闲;典型每天 1 次 |
|
||
| **筛选规则** | observation_count + 时间跨度 + 视点多样性 |
|
||
| **冲突仲裁** | 同 target 多事件合并;iPhone 高 conf 项有更高门槛 |
|
||
| **应用** | deepcopy + 整体替换,保证原子性 |
|
||
| **重训** | 仅受影响房间 + warmstart |
|
||
| **安全** | staging + sanity check + 回滚机制 |
|
||
| **比喻** | 像人脑睡眠——白天乱记,夜里整理 |
|
||
|
||
读完本章你应能:
|
||
- ✅ 编写一个能跑通的 `prism consolidate` 脚本
|
||
- ✅ 解释为什么要"延迟修改 LTM"
|
||
- ✅ 设计安全的回滚机制
|
||
|
||
下一章 [`08_runtime_timeline.md`](08_runtime_timeline.md) 把 Chapter 04–07 的所有模块串成一个**完整的端到端时序剧本**,从 T0 到 T4。
|
||
|
||
---
|
||
|
||
**章节版本**:v1.0
|
||
**估计阅读时间**:18 分钟
|
||
**关键收获**:把"短期 delta"变成"长期 LTM"的完整安全流程
|