txus's picture
download
raw
5.36 kB
"""Claim 6 (mechanism-level) + Claim 3 (param count) efficiency microbenchmark.
The paper's headline efficiency numbers (45% faster, 18% less memory than LGD on
3DPW, Table 2) come from the *full* Human-Mesh-Recovery pipeline, which we cannot
run here (needs AMASS + SMPL + 3DPW + the LGD framework + 400K meta-iters). What
we CAN test is the mechanism the claim rests on: L-SR1's update rule is three
small *element-wise* MLPs (d_hidden=128) plus a low-rank buffer, i.e. its per-step
cost is independent of the number of optimized parameters, whereas an LGD-style
learned updater regresses the update from the whole state and grows with it.
We therefore compare, per inner optimization step, the runtime and peak memory of:
* the L-SR1 update module (encoder + vector-gen + lr-gen, element-wise), and
* a plausible LGD-style updater (a fully-connected regressor over the full
state), sized to the same order of parameters.
This is a *proxy* for the direction of Claim 6, NOT a reproduction of the exact
3DPW numbers. Everything is labelled accordingly in the logbook.
"""
from __future__ import annotations
import argparse
import json
import time
import torch
import torch.nn as nn
from lsr1 import LSR1Config, LSR1Optimizer
class LGDStyleUpdater(nn.Module):
"""LGD (Song et al. 2020) style learned update: a fully-connected regressor
that maps the concatenated state (params, gradient) of the WHOLE problem to a
parameter update. Width does not shrink with dimension -> cost grows with the
number of optimized parameters, unlike L-SR1's element-wise modules."""
def __init__(self, dim, hidden=1024, layers=3):
super().__init__()
blocks = [nn.Linear(2 * dim, hidden), nn.ReLU()]
for _ in range(layers - 1):
blocks += [nn.Linear(hidden, hidden), nn.ReLU()]
blocks += [nn.Linear(hidden, dim)]
self.net = nn.Sequential(*blocks)
def forward(self, x, g):
return self.net(torch.cat([x, g], dim=-1))
def lsr1_step(opt, x, buffer):
"""One L-SR1 update-module forward on state x [B, N] (element-wise):
3 element-wise MLP forwards (encoder, vector-gen, lr-gen) + low-rank buffer
sum over the L stored vectors."""
g = torch.randn_like(x)
p = torch.zeros_like(x); d_prev = torch.zeros_like(x); q = torch.zeros_like(x)
v, alpha, d, buffer = opt.step_direction(x, p, d_prev, g, q, buffer)
return x - alpha * d, buffer
def bench(fn, warmup=5, iters=30, device="cpu"):
cuda = device.startswith("cuda")
for _ in range(warmup):
fn()
if cuda:
torch.cuda.synchronize(); torch.cuda.reset_peak_memory_stats()
t0 = time.time()
for _ in range(iters):
fn()
if cuda:
torch.cuda.synchronize()
dt = (time.time() - t0) / iters * 1000.0 # ms/iter
mem = (torch.cuda.max_memory_allocated() / 2**30) if cuda else float("nan")
return dt, mem
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--device", default="cpu")
ap.add_argument("--batch", type=int, default=256) # paper Table 2 batch
ap.add_argument("--dim", type=int, default=256,
help="number of optimized parameters per sample")
ap.add_argument("--buffer", type=int, default=4) # HMR buffer L=4
ap.add_argument("--out", default="outputs/efficiency.json")
args = ap.parse_args()
dev = args.device
torch.manual_seed(0)
cfg = LSR1Config(buffer_L=args.buffer, d_hidden=128)
lsr1 = LSR1Optimizer(cfg).to(dev).eval()
lgd = LGDStyleUpdater(args.dim, hidden=1024, layers=3).to(dev).eval()
x = torch.randn(args.batch, args.dim, device=dev)
# Pre-fill the L-vector buffer ONCE so we time a single steady-state inner
# step (not the warm-up), matching the paper's per-inner-iteration Table 2.
steady_buf = [torch.randn(args.batch, args.dim, device=dev)
for _ in range(args.buffer)]
def run_lsr1():
_, _ = lsr1_step(lsr1, x, list(steady_buf))
return _
def run_lgd():
g = torch.randn_like(x)
return lgd(x, g)
with torch.no_grad():
t_lsr1, m_lsr1 = bench(run_lsr1, device=dev)
t_lgd, m_lgd = bench(run_lgd, device=dev)
p_lsr1 = sum(p.numel() for p in lsr1.parameters())
p_lgd = sum(p.numel() for p in lgd.parameters())
res = dict(
device=dev, batch=args.batch, dim=args.dim, buffer=args.buffer,
lsr1_ms=t_lsr1, lgd_ms=t_lgd,
lsr1_mem_gib=m_lsr1, lgd_mem_gib=m_lgd,
speedup_pct=100 * (1 - t_lsr1 / t_lgd),
mem_reduction_pct=(100 * (1 - m_lsr1 / m_lgd)
if m_lgd == m_lgd else None),
lsr1_update_params=p_lsr1, lgd_update_params=p_lgd,
paper_reference=dict(lgd_ms=166, lsr1_ms=91, lgd_mem_gib=17.81,
lsr1_mem_gib=14.60, speedup_pct=45,
mem_reduction_pct=18,
lgd_full_params=17.4e6, lsr1_full_params=10.4e6),
)
with open(args.out, "w") as f:
json.dump(res, f, indent=2)
print(json.dumps({k: res[k] for k in
("lsr1_ms", "lgd_ms", "speedup_pct", "lsr1_mem_gib",
"lgd_mem_gib", "mem_reduction_pct",
"lsr1_update_params", "lgd_update_params")}, indent=2))
if __name__ == "__main__":
main()

Xet Storage Details

Size:
5.36 kB
·
Xet hash:
3a90a2b1a5899e493eb04f0dce7fc6edc744d88f605d1134ad3cc9653a20d5f4

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.