luh1124 commited on
Commit
b5fb94e
·
1 Parent(s): 31f61c1

fix(spaces): Hdri torch.load after ConvNeXt; default off CPU preload with spaces

Browse files

- hdri_encoder: torch.load(..., weights_only=False, map_location=cpu) so Stateless
GPU WeightsUnpickler does not run after torchvision may have touched CUDA
- app: default NEAR_MODEL_CPU_PRELOAD_AT_START=0 when spaces is installed
- test: assert Hdri load_weights uses full unpickle on CPU

Made-with: Cursor

app.py CHANGED
@@ -253,10 +253,18 @@ def _truthy_env(name: str, default: str) -> bool:
253
  return v in ("1", "true", "yes", "on")
254
 
255
 
256
- _CPU_PRELOAD_AT_START = _truthy_env("NEAR_MODEL_CPU_PRELOAD_AT_START", "1")
 
 
 
 
 
 
 
 
257
  print(
258
  f"[NeAR] NEAR_MODEL_CPU_PRELOAD_AT_START={'1' if _CPU_PRELOAD_AT_START else '0'} "
259
- "(Hunyuan + NeAR weights on CPU at process start; GPU callbacks only .to(cuda) + infer).",
260
  flush=True,
261
  )
262
 
 
253
  return v in ("1", "true", "yes", "on")
254
 
255
 
256
+ # Background CPU preload runs in the Gradio host process. HF Stateless / ZeroGPU forbids
257
+ # CUDA init there; torchvision + torch.load (weights_only) interactions can still fail
258
+ # during NeAR build. Default to off when `spaces` is present (typical Space deploy); use
259
+ # NEAR_MODEL_CPU_PRELOAD_AT_START=1 to force preload locally or on dedicated GPU VMs.
260
+ _CPU_PRELOAD_DEFAULT = "0" if spaces is not None else "1"
261
+ _CPU_PRELOAD_AT_START = _truthy_env(
262
+ "NEAR_MODEL_CPU_PRELOAD_AT_START",
263
+ _CPU_PRELOAD_DEFAULT,
264
+ )
265
  print(
266
  f"[NeAR] NEAR_MODEL_CPU_PRELOAD_AT_START={'1' if _CPU_PRELOAD_AT_START else '0'} "
267
+ f"(default {_CPU_PRELOAD_DEFAULT!r} when spaces={'set' if spaces is not None else 'absent'}).",
268
  flush=True,
269
  )
270
 
tests/test_hdri_encoder_torch_load.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import unittest
4
+ from pathlib import Path
5
+
6
+
7
+ HDRI_PATH = Path(__file__).resolve().parents[1] / "trellis" / "models" / "structured_latent_vae" / "hdri_encoder.py"
8
+
9
+
10
+ class HdriEncoderTorchLoadTests(unittest.TestCase):
11
+ def test_load_weights_uses_full_unpickle_on_cpu(self) -> None:
12
+ text = HDRI_PATH.read_text(encoding="utf-8")
13
+ self.assertIn("weights_only=False", text)
14
+ self.assertIn("torch.device(\"cpu\")", text)
15
+
16
+
17
+ if __name__ == "__main__":
18
+ unittest.main()
trellis/models/structured_latent_vae/hdri_encoder.py CHANGED
@@ -374,7 +374,15 @@ class Hdri_Encoder(nn.Module):
374
 
375
  def load_weights(self, pretrained_path):
376
  if pretrained_path is not None:
377
- checkpoint = torch.load(pretrained_path, map_location='cpu')
 
 
 
 
 
 
 
 
378
  self.load_state_dict(checkpoint)
379
 
380
  def forward(self, context):
 
374
 
375
  def load_weights(self, pretrained_path):
376
  if pretrained_path is not None:
377
+ # ConvNeXt ImageNet weights are loaded above; that can initialize CUDA in the
378
+ # process. On Hugging Face Stateless / ZeroGPU, the next torch.load with
379
+ # weights_only=True (PyTorch 2.6+ default) then fails inside WeightsUnpickler.
380
+ # This checkpoint is from the NeAR bundle (trusted); full unpickle on CPU only.
381
+ checkpoint = torch.load(
382
+ pretrained_path,
383
+ map_location=torch.device("cpu"),
384
+ weights_only=False,
385
+ )
386
  self.load_state_dict(checkpoint)
387
 
388
  def forward(self, context):