File size: 3,867 Bytes
f2fb01e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
"""LaCT (Large-Chunk TTT, LVSM ๊ณ„์—ด) ๋ฅผ abl kit ํ•™์Šต ๋ฃจํ”„์— ๋ผ์šฐ๋Š” wrapper.

๋ชจ๋ธ ์ฝ”๋“œ๋Š” git clone ํ•œ LaCT repo (lact_nvs/model.py, lact_ttt.py) ๋ฅผ ์ˆ˜์ • ์—†์ด ๊ทธ๋Œ€๋กœ ์“ด๋‹ค.
kit ์˜ ์ž…๋ ฅ (images, rays, camera_cond, num_cond_views) ์„ LaCT ์ž…๋ ฅ์œผ๋กœ ๋ฐ”๊ฟ€ ๋ฟ์ด๋‹ค:
  camera_cond["extrinsics"] = ์ •๊ทœํ™”๋œ c2w ์˜ ์—ญํ–‰๋ ฌ (w2c, normalize_for_da3=False)
  camera_cond["intrinsics"] = ํ•™์Šต ํ•ด์ƒ๋„ (256x256) ๊ธฐ์ค€ K
-> LaCT ์˜ c2w / fxfycxcy. kit ์˜ renderer ๊ฐ€ ์“ฐ๋Š” Plucker ray ์™€ ๊ฐ™์€ ์นด๋ฉ”๋ผ ์ •๊ทœํ™”
   (์ฒซ context ์นด๋ฉ”๋ผ ๊ธฐ์ค€, ๊ฐ€์žฅ ๋จผ context ์นด๋ฉ”๋ผ๊ฐ€ 1/1.35) ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ›๋Š”๋‹ค.
์ถœ๋ ฅ์€ kit ๋ชจ๋ธ๊ณผ ๊ฐ™์ด [context ์ด๋ฏธ์ง€, ๋ Œ๋”๋งํ•œ target] ์„ view ์ถ•์œผ๋กœ ๋ถ™์—ฌ ๋Œ๋ ค์ค€๋‹ค.
"""

import os
import sys

import torch
import torch.nn as nn
import torch.utils.checkpoint
from omegaconf import OmegaConf


def _import_lact(lact_nvs_dir):
    # LaCT config ์˜ block type ์ด "model.SelfAttention", "lact_ttt.FastWeightGluMLPMultihead"
    # ์ฒ˜๋Ÿผ top-level ๋ชจ๋“ˆ ์ด๋ฆ„์ด๋ผ lact_nvs ํด๋”๋ฅผ sys.path ์— ๋„ฃ์–ด์•ผ ํ•œ๋‹ค.
    lact_nvs_dir = os.path.abspath(lact_nvs_dir)
    if lact_nvs_dir not in sys.path:
        sys.path.insert(0, lact_nvs_dir)
    import model as lact_model  # noqa: E402  (LaCT/lact_nvs/model.py)
    assert os.path.dirname(os.path.abspath(lact_model.__file__)) == lact_nvs_dir, lact_model.__file__
    return lact_model


class LaCTWrapper(nn.Module):
    def __init__(self, lact_nvs_dir, lact_config, ckpt_blocks=0, compile_blocks=False):
        super().__init__()
        lact_model = _import_lact(lact_nvs_dir)
        cfg = OmegaConf.to_container(OmegaConf.load(lact_config), resolve=True)
        self.lact = lact_model.LaCTLVSM(**cfg)
        # ํ•™์Šต ์ค‘ ์•ž์—์„œ๋ถ€ํ„ฐ ckpt_blocks ๊ฐœ Block ์„ activation checkpointing ํ•œ๋‹ค (0 = ๋”).
        # Block.forward ๋งŒ ๋ฐ”๊พธ๋ฏ€๋กœ state_dict key ๋Š” LaCTLVSM ๊ทธ๋Œ€๋กœ๋‹ค.
        self.ckpt_blocks = int(ckpt_blocks)
        for i, blk in enumerate(self.lact.blocks):
            if i < self.ckpt_blocks:
                blk.forward = self._ckpt_forward(blk.forward)
        # LaCT README: TTT ๋ชจ๋ธ์€ compile ํ•ด์•ผ ์ œ ์†๋„๊ฐ€ ๋‚œ๋‹ค. Block ์„ ํ•˜๋‚˜์”ฉ in-place ๋กœ compile
        # ํ•˜๊ณ  checkpoint ๋Š” compile ์•ˆ์—์„œ ๋ˆ๋‹ค (2026-11-13 memtest, B300):
        #   LaCTLVSM ์ „์ฒด๋ฅผ compile -> inductor ์ปค๋„์ด "misaligned address" ๋กœ ์ฃฝ๋Š”๋‹ค.
        #   checkpoint ๋ฐ”๊นฅ + compile ์•ˆ์ชฝ -> backward ์žฌ๊ณ„์‚ฐ์—์„œ CheckpointError.
        self.compile_blocks = bool(compile_blocks)
        if self.compile_blocks:
            # block ๋งˆ๋‹ค guard (forward closure id) ๊ฐ€ ๋‹ฌ๋ผ block ์ˆ˜๋งŒํผ ์žฌ์ปดํŒŒ์ผ๋œ๋‹ค (๊ธฐ๋ณธ ํ•œ๋„ 8).
            torch._dynamo.config.recompile_limit = 128
            torch._dynamo.config.accumulated_recompile_limit = 1024
            for blk in self.lact.blocks:
                blk.compile()

    def _ckpt_forward(self, fwd):
        def run(x, info):
            if self.training and torch.is_grad_enabled():
                return torch.utils.checkpoint.checkpoint(fwd, x, info, use_reentrant=False)
            return fwd(x, info)
        return run

    def forward(self, images, rays, camera_cond, num_cond_views, timeit=False):
        n = int(num_cond_views)
        w2c = camera_cond["extrinsics"].to(images.device).float()
        K = camera_cond["intrinsics"].to(images.device).float()
        c2w = torch.linalg.inv(w2c)
        fxfycxcy = torch.stack([K[..., 0, 0], K[..., 1, 1], K[..., 0, 2], K[..., 1, 2]], dim=-1)

        input_data = {"image": images[:, :n].float(), "fxfycxcy": fxfycxcy[:, :n], "c2w": c2w[:, :n]}
        target_data = {"fxfycxcy": fxfycxcy[:, n:], "c2w": c2w[:, n:]}
        rendered = self.lact(input_data, target_data)
        return torch.cat([images[:, :n], rendered.to(images.dtype)], dim=1)