amisima commited on
Commit
cd9ebf4
·
verified ·
1 Parent(s): abce23b

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -4
app.py CHANGED
@@ -8,6 +8,7 @@ gradio API for every request; `reference_encoder` stays here, next to the autoen
8
  from __future__ import annotations
9
 
10
  import json
 
11
  import os
12
  import random
13
  import re
@@ -17,6 +18,11 @@ import time
17
  import traceback
18
  from functools import cache
19
 
 
 
 
 
 
20
  # Before anything that could initialize CUDA: `import spaces` patches `torch.cuda` so the 72 GiB load can happen at
21
  # startup rather than on GPU time.
22
  import spaces
@@ -44,7 +50,7 @@ MAX_SEQUENCE = int(os.environ.get("H3_MAX_SEQUENCE", "90000"))
44
  # An attached adapter adds its own layers and their activations to the same card, so the ceiling above is not the
45
  # ceiling any more. Refusing a request that is over the reduced one is a sentence on screen; letting it through is a
46
  # dead worker and a bare "runtime error".
47
- LORA_SEQUENCE_FRACTION = float(os.environ.get("H3_LORA_SEQUENCE_FRACTION", "0.8"))
48
 
49
 
50
  def sequence_ceiling(loras=()) -> int:
@@ -995,6 +1001,13 @@ def apply_loras(transformer, loras) -> list[str]:
995
 
996
  for name in list(getattr(transformer, "peft_config", None) or {}):
997
  transformer.delete_adapters(name)
 
 
 
 
 
 
 
998
 
999
  names, scales = [], []
1000
  for index, (path, scale) in enumerate(loras):
@@ -1011,6 +1024,14 @@ def apply_loras(transformer, loras) -> list[str]:
1011
  else:
1012
  state_dict = _convert_kohya_lora(state_dict) or state_dict
1013
  state_dict = _fit_to_transformer(transformer, state_dict)
 
 
 
 
 
 
 
 
1014
  name = f"lora{index}"
1015
  try:
1016
  transformer.load_lora_adapter(state_dict, adapter_name=name, prefix=_lora_prefix(state_dict))
@@ -1021,6 +1042,8 @@ def apply_loras(transformer, loras) -> list[str]:
1021
  "It is almost always an adapter for another partition or another base model - a `transformer/` "
1022
  "adapter does not fit the `transformer_ref/` half this Space runs."
1023
  ) from error
 
 
1024
  names.append(name)
1025
  scales.append(float(scale))
1026
 
@@ -1036,6 +1059,11 @@ def apply_loras(transformer, loras) -> list[str]:
1036
  param.data = param.data.to(device=base.device, dtype=base.dtype)
1037
 
1038
  transformer.set_adapters(names, scales)
 
 
 
 
 
1039
  return names
1040
 
1041
 
@@ -1222,11 +1250,47 @@ def _generate(prompt_embeds, text_token_tags, references, height, width, num_fra
1222
  state = _run()
1223
  except RuntimeError as error:
1224
  message = str(error).lower()
1225
- if "no available kernel" not in message and "cudnn" not in message:
 
 
 
 
 
 
 
 
 
 
1226
  raise
1227
- print(f"[ref2va] attention backend refused this shape ({error}); retrying on `native`", flush=True)
 
 
 
 
 
 
1228
  _backend("native")
1229
- state = _run()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1230
 
1231
  return state.get("videos")[0], state.get("audio")[0].cpu(), state.get("sampling_rate")
1232
 
 
8
  from __future__ import annotations
9
 
10
  import json
11
+ import gc
12
  import os
13
  import random
14
  import re
 
18
  import traceback
19
  from functools import cache
20
 
21
+ # Before torch exists: 72 GiB of resident weights leave the rest of the card in pieces, and a request that needs one
22
+ # more large contiguous block then fails on free memory it cannot use in one piece. Expandable segments let the
23
+ # allocator grow a block instead of hunting for one, which is the single biggest difference on this Space.
24
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
25
+
26
  # Before anything that could initialize CUDA: `import spaces` patches `torch.cuda` so the 72 GiB load can happen at
27
  # startup rather than on GPU time.
28
  import spaces
 
50
  # An attached adapter adds its own layers and their activations to the same card, so the ceiling above is not the
51
  # ceiling any more. Refusing a request that is over the reduced one is a sentence on screen; letting it through is a
52
  # dead worker and a bare "runtime error".
53
+ LORA_SEQUENCE_FRACTION = float(os.environ.get("H3_LORA_SEQUENCE_FRACTION", "0.75"))
54
 
55
 
56
  def sequence_ceiling(loras=()) -> int:
 
1001
 
1002
  for name in list(getattr(transformer, "peft_config", None) or {}):
1003
  transformer.delete_adapters(name)
1004
+ gc.collect()
1005
+ if torch.cuda.is_available():
1006
+ torch.cuda.empty_cache()
1007
+
1008
+ base_dtype = next(
1009
+ (param.dtype for key, param in transformer.named_parameters() if ".lora_" not in key), torch.bfloat16
1010
+ )
1011
 
1012
  names, scales = [], []
1013
  for index, (path, scale) in enumerate(loras):
 
1024
  else:
1025
  state_dict = _convert_kohya_lora(state_dict) or state_dict
1026
  state_dict = _fit_to_transformer(transformer, state_dict)
1027
+ # Cast on the host, before anything crosses onto the card. A float32 adapter injected as-is is put on the
1028
+ # card at float32 and cast afterwards, so for a moment both copies are resident - on top of 72 GiB of
1029
+ # weights that is exactly the block the allocator cannot find.
1030
+ state_dict = {
1031
+ key: (value.to(base_dtype) if hasattr(value, "is_floating_point") and value.is_floating_point()
1032
+ else value)
1033
+ for key, value in state_dict.items()
1034
+ }
1035
  name = f"lora{index}"
1036
  try:
1037
  transformer.load_lora_adapter(state_dict, adapter_name=name, prefix=_lora_prefix(state_dict))
 
1042
  "It is almost always an adapter for another partition or another base model - a `transformer/` "
1043
  "adapter does not fit the `transformer_ref/` half this Space runs."
1044
  ) from error
1045
+ del state_dict
1046
+ gc.collect()
1047
  names.append(name)
1048
  scales.append(float(scale))
1049
 
 
1059
  param.data = param.data.to(device=base.device, dtype=base.dtype)
1060
 
1061
  transformer.set_adapters(names, scales)
1062
+ # The load and the cast both leave freed blocks behind; handing them back before the denoise starts is what
1063
+ # keeps the first large allocation of the run from failing on a card that has the memory but not in one piece.
1064
+ gc.collect()
1065
+ if torch.cuda.is_available():
1066
+ torch.cuda.empty_cache()
1067
  return names
1068
 
1069
 
 
1250
  state = _run()
1251
  except RuntimeError as error:
1252
  message = str(error).lower()
1253
+ kernel = "no available kernel" in message or "cudnn" in message
1254
+ # `NVML_SUCCESS == r INTERNAL ASSERT FAILED` out of `CUDACachingAllocator.cpp` is not a bug to report to
1255
+ # PyTorch: it is the allocator failing to grow, i.e. out of memory, with its own error path falling over on
1256
+ # the way to saying so. Same treatment as a plain OOM.
1257
+ starved = (
1258
+ "out of memory" in message
1259
+ or "nvml" in message
1260
+ or "cudacachingallocator" in message
1261
+ or "cuda error" in message
1262
+ )
1263
+ if not (kernel or starved):
1264
  raise
1265
+
1266
+ gc.collect()
1267
+ try:
1268
+ torch.cuda.empty_cache()
1269
+ torch.cuda.synchronize()
1270
+ except Exception: # noqa: BLE001
1271
+ pass
1272
  _backend("native")
1273
+ print(f"[ref2va] first attempt failed ({type(error).__name__}); retrying once on a cleared card", flush=True)
1274
+
1275
+ try:
1276
+ state = _run()
1277
+ except RuntimeError as second:
1278
+ second_message = str(second).lower()
1279
+ if not (
1280
+ "out of memory" in second_message
1281
+ or "nvml" in second_message
1282
+ or "cudacachingallocator" in second_message
1283
+ ):
1284
+ raise
1285
+ traceback.print_exc()
1286
+ raise gr.Error(
1287
+ f"The card ran out of memory at {width}x{height}, {num_frames / FPS:.1f} s, "
1288
+ f"{len(references)} reference(s)"
1289
+ f"{f' and {len(loras or ())} lora' if loras else ''}. "
1290
+ "A lora takes its share of the same card, so a setting that worked without one can be too much "
1291
+ "with it. Pick a smaller canvas — a 1:1 one is the smallest — shorten the duration, or drop a "
1292
+ "reference, and it goes through."
1293
+ ) from second
1294
 
1295
  return state.get("videos")[0], state.get("audio")[0].cpu(), state.get("sampling_rate")
1296