plice13 commited on
Commit
2c3b02e
·
verified ·
1 Parent(s): 3df4fd2

Upload backend.py

Browse files
Files changed (1) hide show
  1. backend.py +93 -2
backend.py CHANGED
@@ -61,6 +61,57 @@ DEVICE, DTYPE = select_device()
61
  # YOLO runs outside the @spaces.GPU call, and on ZeroGPU CUDA is only usable inside it
62
  POSE_DEVICE = "cpu" if os.environ.get("SPACES_ZERO_GPU") else DEVICE.type
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  # Global variables to cache models and config so they are not reloaded on every video translation
65
  model = None
66
  pose_models = None
@@ -92,19 +143,28 @@ def initialize_model():
92
  return
93
 
94
  print("Initializing Uni_Sign model...")
 
95
  args = InferenceConfig()
96
  model = Uni_Sign(args=args)
97
 
98
  # Load model weights
99
  if args.finetune and os.path.exists(args.finetune):
100
  print(f"Loading checkpoint weights from: {args.finetune}")
 
101
  state_dict = torch.load(args.finetune, map_location='cpu')['model']
102
- model.load_state_dict(state_dict, strict=False)
 
 
103
  else:
104
  print(f"WARNING: Checkpoint weights not found at '{args.finetune}'!")
105
 
 
106
  model.to(device=DEVICE, dtype=DTYPE).eval()
107
  print(f"Uni_Sign model on {DEVICE} ({DTYPE}); pose detection on {POSE_DEVICE}")
 
 
 
 
108
 
109
  print("Initializing Mediapipe and YOLO models...")
110
  pose_checkpoint_folder = 'checkpoints/pose/'
@@ -156,6 +216,10 @@ def process_pose_data_in_memory(pose_results, args):
156
  formatted_frame[part] = np.array(kps)[:, :2].tolist()
157
  pose.append(formatted_frame)
158
 
 
 
 
 
159
  # Subsample frames to match target length (max_length)
160
  duration = len(pose)
161
  tmp = select_frame_indices(duration, args.max_length, phase='test')
@@ -194,21 +258,48 @@ def process_pose_data_in_memory(pose_results, args):
194
  src_input['src_length_batch'] = torch.LongTensor([seq_len])
195
  src_input['name_batch'] = ["gradio_video"]
196
 
 
 
 
197
  return src_input
198
 
199
 
200
  @spaces.GPU(duration=60) # on ZeroGPU, only this call holds a GPU (time limit in seconds)
201
  def run_translation(src_input):
202
  """Runs Uni_Sign on prepared pose tensors and returns the decoded translation."""
 
 
 
 
 
 
 
203
  for key, value in src_input.items():
204
  if isinstance(value, torch.Tensor):
205
  src_input[key] = value.to(device=DEVICE, dtype=DTYPE if value.is_floating_point() else value.dtype)
206
  tgt_input = {'gt_sentence': [""], 'gt_gloss': [""]}
 
207
 
208
  with torch.inference_mode():
209
  stack_out = model(src_input, tgt_input)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  output = model.generate(stack_out, max_new_tokens=100, num_beams=4)
211
- return model.mt5_tokenizer.batch_decode(output, skip_special_tokens=True)[0].strip()
 
 
212
 
213
 
214
  def process_input(input_video_path, progress=None, log_callback=None):
 
61
  # YOLO runs outside the @spaces.GPU call, and on ZeroGPU CUDA is only usable inside it
62
  POSE_DEVICE = "cpu" if os.environ.get("SPACES_ZERO_GPU") else DEVICE.type
63
 
64
+
65
+ # ---------- Diagnostics (set SLT_DEBUG=0 to silence) ----------
66
+ DEBUG = os.environ.get("SLT_DEBUG", "1") != "0"
67
+
68
+
69
+ def diag(message):
70
+ if DEBUG:
71
+ print(f"[diag] {message}", flush=True)
72
+
73
+
74
+ def tensor_summary(t):
75
+ """Shape, dtype, device and value range of a tensor (NaN/Inf flagged)."""
76
+ if not isinstance(t, torch.Tensor):
77
+ return repr(t)[:60]
78
+ f = t.detach().float()
79
+ finite = f[torch.isfinite(f)]
80
+ rng = f"min={finite.min().item():.4g} max={finite.max().item():.4g} mean={finite.mean().item():.4g}" if finite.numel() else "no finite values"
81
+ bad = f" NaN={torch.isnan(f).sum().item()} Inf={torch.isinf(f).sum().item()}" if finite.numel() != f.numel() else ""
82
+ return f"{tuple(t.shape)} {t.dtype} {t.device} {rng}{bad}"
83
+
84
+
85
+ def weights_fingerprint(m):
86
+ """A few numbers that change if weights are lost, zeroed or garbled when moved between devices."""
87
+ params = dict(m.named_parameters())
88
+ names = [next(iter(params)), "mt5_model.shared.weight", "mt5_model.lm_head.weight", list(params)[-1]]
89
+ return {n.split(".")[-2] + "." + n.split(".")[-1]: round(params[n].detach().float().abs().sum().item(), 3) for n in names if n in params}
90
+
91
+
92
+ def weights_location(m):
93
+ """How many parameters sit on each device/dtype."""
94
+ counts = {}
95
+ for p in m.parameters():
96
+ key = f"{p.device}/{p.dtype}"
97
+ counts[key] = counts.get(key, 0) + p.numel()
98
+ return {k: f"{v / 1e6:.1f}M" for k, v in counts.items()}
99
+
100
+
101
+ def environment_report():
102
+ import transformers
103
+ try:
104
+ import spaces as _spaces_module # noqa: F401
105
+ has_spaces = not isinstance(spaces, type)
106
+ except ImportError:
107
+ has_spaces = False
108
+ cuda = torch.cuda.is_available()
109
+ diag(f"python {sys.version.split()[0]} | torch {torch.__version__} (cuda build {torch.version.cuda}) | transformers {transformers.__version__}")
110
+ diag(f"spaces package: {has_spaces} | SPACES_ZERO_GPU={os.environ.get('SPACES_ZERO_GPU')!r} | "
111
+ f"SLT_DEVICE={os.environ.get('SLT_DEVICE', 'auto')!r} SLT_DTYPE={os.environ.get('SLT_DTYPE', 'auto')!r}")
112
+ diag(f"torch.cuda.is_available()={cuda}" + (f" | {torch.cuda.device_count()} GPU(s): {torch.cuda.get_device_name(0)} | bf16 supported: {torch.cuda.is_bf16_supported()}" if cuda else ""))
113
+ diag(f"selected DEVICE={DEVICE} DTYPE={DTYPE} POSE_DEVICE={POSE_DEVICE}")
114
+
115
  # Global variables to cache models and config so they are not reloaded on every video translation
116
  model = None
117
  pose_models = None
 
143
  return
144
 
145
  print("Initializing Uni_Sign model...")
146
+ environment_report()
147
  args = InferenceConfig()
148
  model = Uni_Sign(args=args)
149
 
150
  # Load model weights
151
  if args.finetune and os.path.exists(args.finetune):
152
  print(f"Loading checkpoint weights from: {args.finetune}")
153
+ diag(f"checkpoint size: {os.path.getsize(args.finetune) / 1e6:.1f} MB")
154
  state_dict = torch.load(args.finetune, map_location='cpu')['model']
155
+ result = model.load_state_dict(state_dict, strict=False)
156
+ diag(f"checkpoint tensors: {len(state_dict)} | model tensors: {len(model.state_dict())} | "
157
+ f"missing: {len(result.missing_keys)} {result.missing_keys[:5]} | unexpected: {len(result.unexpected_keys)} {result.unexpected_keys[:5]}")
158
  else:
159
  print(f"WARNING: Checkpoint weights not found at '{args.finetune}'!")
160
 
161
+ diag(f"weights fingerprint after loading (cpu, float32): {weights_fingerprint(model)}")
162
  model.to(device=DEVICE, dtype=DTYPE).eval()
163
  print(f"Uni_Sign model on {DEVICE} ({DTYPE}); pose detection on {POSE_DEVICE}")
164
+ diag(f"weights location after .to(): {weights_location(model)}")
165
+ diag(f"weights fingerprint after .to(): {weights_fingerprint(model)}")
166
+ diag(f"mT5 generation config: decoder_start={model.mt5_model.config.decoder_start_token_id} "
167
+ f"eos={model.mt5_model.config.eos_token_id} pad={model.mt5_model.config.pad_token_id}")
168
 
169
  print("Initializing Mediapipe and YOLO models...")
170
  pose_checkpoint_folder = 'checkpoints/pose/'
 
216
  formatted_frame[part] = np.array(kps)[:, :2].tolist()
217
  pose.append(formatted_frame)
218
 
219
+ detected = {part: sum(1 for f in pose if len(f[part]) > 0) for part in YTASL_GROUP_SIZES}
220
+ diag(f"keypoints: {len(pose)} frames; frames with detections per part: "
221
+ + ", ".join(f"{part}={n}/{len(pose)}" for part, n in detected.items()))
222
+
223
  # Subsample frames to match target length (max_length)
224
  duration = len(pose)
225
  tmp = select_frame_indices(duration, args.max_length, phase='test')
 
258
  src_input['src_length_batch'] = torch.LongTensor([seq_len])
259
  src_input['name_batch'] = ["gradio_video"]
260
 
261
+ for key, value in src_input.items():
262
+ if isinstance(value, torch.Tensor):
263
+ diag(f"model input {key}: {tensor_summary(value)}")
264
  return src_input
265
 
266
 
267
  @spaces.GPU(duration=60) # on ZeroGPU, only this call holds a GPU (time limit in seconds)
268
  def run_translation(src_input):
269
  """Runs Uni_Sign on prepared pose tensors and returns the decoded translation."""
270
+ # Inside the GPU call: is CUDA really there, and did the weights arrive intact?
271
+ cuda = torch.cuda.is_available()
272
+ diag(f"in run_translation: torch.cuda.is_available()={cuda}"
273
+ + (f", current device {torch.cuda.current_device()} ({torch.cuda.get_device_name(0)})" if cuda else ""))
274
+ diag(f"weights location in run_translation: {weights_location(model)}")
275
+ diag(f"weights fingerprint in run_translation: {weights_fingerprint(model)}")
276
+
277
  for key, value in src_input.items():
278
  if isinstance(value, torch.Tensor):
279
  src_input[key] = value.to(device=DEVICE, dtype=DTYPE if value.is_floating_point() else value.dtype)
280
  tgt_input = {'gt_sentence': [""], 'gt_gloss': [""]}
281
+ tokenizer = model.mt5_tokenizer
282
 
283
  with torch.inference_mode():
284
  stack_out = model(src_input, tgt_input)
285
+ diag(f"encoder input embeds: {tensor_summary(stack_out['inputs_embeds'])}")
286
+ diag(f"encoder attention mask: {tensor_summary(stack_out['attention_mask'])}")
287
+
288
+ # What the model wants to say first: top-5 candidates for the first output token
289
+ start = torch.full((1, 1), model.mt5_model.config.decoder_start_token_id, device=DEVICE, dtype=torch.long)
290
+ logits = model.mt5_model(inputs_embeds=stack_out['inputs_embeds'], attention_mask=stack_out['attention_mask'],
291
+ decoder_input_ids=start).logits[0, -1].float()
292
+ diag(f"first-token logits: {tensor_summary(logits)}")
293
+ probs, ids = logits.softmax(-1).topk(5)
294
+ diag("first-token top-5: " + ", ".join(f"{tok!r}={p:.3f}" for tok, p in
295
+ zip(tokenizer.convert_ids_to_tokens(ids.tolist()), probs.tolist())))
296
+
297
+ greedy = model.generate(stack_out, max_new_tokens=100, num_beams=1)
298
+ diag(f"greedy: ids {greedy[0].tolist()[:20]} -> {tokenizer.batch_decode(greedy, skip_special_tokens=True)[0]!r}")
299
  output = model.generate(stack_out, max_new_tokens=100, num_beams=4)
300
+ diag(f"beam search (4): ids {output[0].tolist()[:20]} -> {tokenizer.batch_decode(output, skip_special_tokens=True)[0]!r}")
301
+
302
+ return tokenizer.batch_decode(output, skip_special_tokens=True)[0].strip()
303
 
304
 
305
  def process_input(input_video_path, progress=None, log_callback=None):