- 移除 JEPA/lejepa-identifiability 子模块 gitlink - 移除 research/multiply/MultiPLY 子模块 gitlink - 删除 .gitmodules(不再有外部 URL 依赖) - 两个目录内容作为普通文件纳入主仓库追踪 - 删除各自内部 .git 目录,消除嵌套 git 仓库
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
"""
|
||||
Generate LaTeX tables for the paper, matching scaling table style.
|
||||
|
||||
Usage:
|
||||
python analysis/make_table_reacher.py --results_dir results/reacher
|
||||
"""
|
||||
|
||||
import json
|
||||
import argparse
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def load_all_results(results_dir):
|
||||
ou, traj = [], []
|
||||
for p in Path(results_dir).rglob("result.json"):
|
||||
r = json.load(open(p))
|
||||
if "delta" in r and r.get("rho") is None:
|
||||
traj.append(r)
|
||||
elif "rho" in r:
|
||||
ou.append(r)
|
||||
return ou, traj
|
||||
|
||||
|
||||
def best_lambda_per_x(results, x_key):
|
||||
grouped = defaultdict(list)
|
||||
for r in results:
|
||||
grouped[(r[x_key], r["lamb"])].append(r)
|
||||
|
||||
best = {}
|
||||
for x in sorted(set(k[0] for k in grouped)):
|
||||
best_mean, best_lamb = -np.inf, None
|
||||
for lamb in set(k[1] for k in grouped if k[0] == x):
|
||||
m = np.mean([r["r2_hz"] for r in grouped[(x, lamb)]])
|
||||
if m > best_mean:
|
||||
best_mean, best_lamb = m, lamb
|
||||
best[x] = {
|
||||
"lamb": best_lamb,
|
||||
"runs": grouped[(x, best_lamb)],
|
||||
}
|
||||
return best
|
||||
|
||||
|
||||
def pm(vals, fmt=".2f"):
|
||||
"""Format as value\\tiny{±std} matching paper style."""
|
||||
m, s = np.mean(vals), np.std(vals)
|
||||
return f"{m:{fmt}}\\tiny{{$\\pm${s:.0e}}}"
|
||||
|
||||
|
||||
def make_combined_table(ou_results, traj_results):
|
||||
ou_best = best_lambda_per_x(ou_results, "rho")
|
||||
traj_best = best_lambda_per_x(traj_results, "delta")
|
||||
|
||||
lines = []
|
||||
lines.append(r"\begin{table}[t]")
|
||||
lines.append(r"\centering")
|
||||
lines.append(r"\caption{")
|
||||
lines.append(r" \textbf{Pixel-observation identifiability on DMC Reacher} "
|
||||
r"(mean $\pm$ std over 3 seeds, best $\lambda$ per condition).")
|
||||
lines.append(r" \textbf{Left:} OU process with Gaussian marginals. "
|
||||
r"$R^2$ increases monotonically with $\rho$, reaching $0.95$ "
|
||||
r"at $\rho = 0.99$, confirming linear identifiability from pixels.")
|
||||
lines.append(r" \textbf{Right:} Real SAC trajectories with non-Gaussian marginals. "
|
||||
r"The two joints have different autocorrelation timescales ($\rho_0 \neq \rho_1$) "
|
||||
r"and the wrist has a near-uniform marginal distribution, "
|
||||
r"leading to anisotropic and reduced identifiability.")
|
||||
lines.append(r"}")
|
||||
lines.append(r"\label{tab:reacher}")
|
||||
lines.append(r"\resizebox{\textwidth}{!}{%")
|
||||
lines.append(r"\begin{tabular}{r cc | r cc ccc}")
|
||||
lines.append(r" \multicolumn{3}{c}{\textbf{OU (Gaussian)}} & "
|
||||
r"\multicolumn{6}{c}{\textbf{Trajectory (non-Gaussian)}} \\")
|
||||
lines.append(r"\cmidrule(lr){1-3} \cmidrule(lr){4-9}")
|
||||
lines.append(r"$\rho$ & $R^2(z \to h)$ & $R^2(h \to z)$ & "
|
||||
r"$\delta$ & $\rho_0$ & $\rho_1$ & "
|
||||
r"$R^2(z \to h)$ & $R^2(h \to z_0)$ & $R^2(h \to z_1)$ \\")
|
||||
lines.append(r"\midrule")
|
||||
|
||||
ou_rhos = sorted(ou_best.keys())
|
||||
traj_deltas = sorted(traj_best.keys())
|
||||
n_rows = max(len(ou_rhos), len(traj_deltas))
|
||||
|
||||
for i in range(n_rows):
|
||||
# OU columns
|
||||
if i < len(ou_rhos):
|
||||
rho = ou_rhos[i]
|
||||
runs = ou_best[rho]["runs"]
|
||||
r2_zh = pm([r["r2_zh"] for r in runs])
|
||||
r2_hz = pm([r["r2_hz"] for r in runs])
|
||||
ou_str = f" {rho:.2f} & {r2_zh} & {r2_hz}"
|
||||
else:
|
||||
ou_str = r" & &"
|
||||
|
||||
# Traj columns
|
||||
if i < len(traj_deltas):
|
||||
delta = traj_deltas[i]
|
||||
runs = traj_best[delta]["runs"]
|
||||
rho0 = runs[0].get("rho_shoulder", None)
|
||||
rho1 = runs[0].get("rho_wrist", None)
|
||||
rho0_s = f"{rho0:.3f}" if rho0 is not None else "---"
|
||||
rho1_s = f"{rho1:.3f}" if rho1 is not None else "---"
|
||||
r2_zh = pm([r["r2_zh"] for r in runs])
|
||||
r2_d0 = pm([r["r2_hz_per_dim"][0] for r in runs])
|
||||
r2_d1 = pm([r["r2_hz_per_dim"][1] for r in runs])
|
||||
traj_str = f"{delta} & {rho0_s} & {rho1_s} & {r2_zh} & {r2_d0} & {r2_d1}"
|
||||
else:
|
||||
traj_str = r"& & & & &"
|
||||
|
||||
lines.append(f"{ou_str} & {traj_str} \\\\")
|
||||
|
||||
lines.append(r"\bottomrule")
|
||||
lines.append(r"\end{tabular}}")
|
||||
lines.append(r"\vspace{5pt}")
|
||||
lines.append(r"\vspace{-20pt}")
|
||||
lines.append(r"\end{table}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def make_ou_table_standalone(ou_results):
|
||||
"""Standalone OU table for appendix if needed."""
|
||||
ou_best = best_lambda_per_x(ou_results, "rho")
|
||||
|
||||
lines = []
|
||||
lines.append(r"\begin{table}[t]")
|
||||
lines.append(r"\centering")
|
||||
lines.append(r"\begin{tabular}{r c cc}")
|
||||
lines.append(r"\toprule")
|
||||
lines.append(r" \multicolumn{1}{c}{\textbf{Correlation}} & "
|
||||
r"\multicolumn{1}{c}{\textbf{Regularizer}} & "
|
||||
r"\multicolumn{2}{c}{\textbf{Linear identifiability}} \\")
|
||||
lines.append(r"\cmidrule(lr){1-1} \cmidrule(lr){2-2} \cmidrule(lr){3-4}")
|
||||
lines.append(r"$\rho$ & $\lambda$ & $R^2(z \to h)$ & $R^2(h \to z)$ \\")
|
||||
lines.append(r"\midrule")
|
||||
|
||||
for rho in sorted(ou_best.keys()):
|
||||
runs = ou_best[rho]["runs"]
|
||||
lamb = ou_best[rho]["lamb"]
|
||||
r2_zh = pm([r["r2_zh"] for r in runs])
|
||||
r2_hz = pm([r["r2_hz"] for r in runs])
|
||||
lines.append(f" {rho:.2f} & {lamb:.0e} & {r2_zh} & {r2_hz} \\\\")
|
||||
|
||||
lines.append(r"\bottomrule")
|
||||
lines.append(r"\end{tabular}")
|
||||
lines.append(r"\vspace{5pt}")
|
||||
lines.append(r"\caption{")
|
||||
lines.append(r" \textbf{OU (Gaussian) identifiability from pixels} "
|
||||
r"(mean $\pm$ std over 3 seeds).")
|
||||
lines.append(r" $R^2$ increases monotonically with temporal correlation $\rho$, "
|
||||
r"reaching $0.95$ at $\rho = 0.99$.")
|
||||
lines.append(r"}")
|
||||
lines.append(r"\label{tab:reacher_ou}")
|
||||
lines.append(r"\end{table}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def make_traj_table_standalone(traj_results):
|
||||
"""Standalone traj table for appendix if needed."""
|
||||
traj_best = best_lambda_per_x(traj_results, "delta")
|
||||
|
||||
lines = []
|
||||
lines.append(r"\begin{table}[t]")
|
||||
lines.append(r"\centering")
|
||||
lines.append(r"\resizebox{\textwidth}{!}{%")
|
||||
lines.append(r"\begin{tabular}{r cc c c cc}")
|
||||
lines.append(r"\toprule")
|
||||
lines.append(r" \multicolumn{1}{c}{\textbf{Stride}} & "
|
||||
r"\multicolumn{2}{c}{\textbf{Autocorrelation}} & "
|
||||
r"\multicolumn{1}{c}{\textbf{Regularizer}} & "
|
||||
r"\multicolumn{1}{c}{\textbf{Identifiability}} & "
|
||||
r"\multicolumn{2}{c}{\textbf{Per-dimension}} \\")
|
||||
lines.append(r"\cmidrule(lr){1-1} \cmidrule(lr){2-3} \cmidrule(lr){4-4} "
|
||||
r"\cmidrule(lr){5-5} \cmidrule(lr){6-7}")
|
||||
lines.append(r"$\delta$ & $\rho_0$ & $\rho_1$ & $\lambda$ & "
|
||||
r"$R^2(z \to h)$ & $R^2(h \to z_0)$ & $R^2(h \to z_1)$ \\")
|
||||
lines.append(r"\midrule")
|
||||
|
||||
for delta in sorted(traj_best.keys()):
|
||||
runs = traj_best[delta]["runs"]
|
||||
lamb = traj_best[delta]["lamb"]
|
||||
rho0 = runs[0].get("rho_shoulder", None)
|
||||
rho1 = runs[0].get("rho_wrist", None)
|
||||
rho0_s = f"{rho0:.3f}" if rho0 is not None else "---"
|
||||
rho1_s = f"{rho1:.3f}" if rho1 is not None else "---"
|
||||
r2_zh = pm([r["r2_zh"] for r in runs])
|
||||
r2_d0 = pm([r["r2_hz_per_dim"][0] for r in runs])
|
||||
r2_d1 = pm([r["r2_hz_per_dim"][1] for r in runs])
|
||||
lines.append(f" {delta} & {rho0_s} & {rho1_s} & {lamb:.0e} "
|
||||
f"& {r2_zh} & {r2_d0} & {r2_d1} \\\\")
|
||||
|
||||
lines.append(r"\bottomrule")
|
||||
lines.append(r"\end{tabular}}")
|
||||
lines.append(r"\vspace{5pt}")
|
||||
lines.append(r"\caption{")
|
||||
lines.append(r" \textbf{Trajectory (non-Gaussian) identifiability from pixels} "
|
||||
r"(mean $\pm$ std over 3 seeds).")
|
||||
lines.append(r" The shoulder ($z_0$) and wrist ($z_1$) have different "
|
||||
r"autocorrelation timescales and marginal distributions, "
|
||||
r"leading to anisotropic identifiability.")
|
||||
lines.append(r"}")
|
||||
lines.append(r"\label{tab:reacher_traj}")
|
||||
lines.append(r"\end{table}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--results_dir", type=str, default="results/reacher")
|
||||
args = parser.parse_args()
|
||||
|
||||
ou, traj = load_all_results(args.results_dir)
|
||||
print(f"Loaded {len(ou)} OU runs, {len(traj)} traj runs\n")
|
||||
|
||||
if ou and traj:
|
||||
print("=" * 70)
|
||||
print("COMBINED TABLE (for main text)")
|
||||
print("=" * 70)
|
||||
print(make_combined_table(ou, traj))
|
||||
print()
|
||||
|
||||
if ou:
|
||||
print("=" * 70)
|
||||
print("OU TABLE (standalone, for appendix)")
|
||||
print("=" * 70)
|
||||
print(make_ou_table_standalone(ou))
|
||||
print()
|
||||
|
||||
if traj:
|
||||
print("=" * 70)
|
||||
print("TRAJ TABLE (standalone, for appendix)")
|
||||
print("=" * 70)
|
||||
print(make_traj_table_standalone(traj))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user