Video-Text-to-Text
Transformers
Safetensors
agent
Harry-1234 commited on
Commit
2deee36
·
verified ·
1 Parent(s): 3072a7c

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +1 -288
README.md CHANGED
@@ -77,200 +77,11 @@ from peft import LoraConfig, get_peft_model, PeftModel
77
 
78
  from qwen_omni_utils import process_mm_info
79
 
80
- # ============================================================
81
- # Main Agent Stages
82
- # ============================================================
83
- def stage1_collector(model, processor, video_path, query, device):
84
- """Collector uses base_model.thinker with LoRA disabled"""
85
- has_audio = check_if_video_has_audio(video_path)
86
- content = [{"type": "video", "video": video_path,
87
- "min_pixels": 36 * 28 * 28,
88
- "max_pixels": 96 * 28 * 28,
89
- "max_frames": 150,
90
- "fps": 1.0}]
91
- if has_audio:
92
- content.append({"type": "audio", "audio": video_path})
93
- text_prompt = COLLECTOR_USER_TEMPLATE.format(problem=query)
94
- content.append({"type": "text", "text": text_prompt})
95
- messages = [
96
- {"role": "system", "content": [{"type": "text", "text": COLLECTOR_SYSTEM_PROMPT}]},
97
- {"role": "user", "content": content}
98
- ]
99
- print(f"collector input: {messages}")
100
-
101
- audios, images, videos = process_mm_info(messages, use_audio_in_video=False)
102
- text = processor.apply_chat_template([messages], tokenize=False, add_generation_prompt=True)
103
- inputs = processor(text=text, audio=audios, images=images, videos=videos, return_tensors="pt", padding=True).to(device)
104
-
105
- for k in inputs:
106
- if isinstance(inputs[k], torch.Tensor) and inputs[k].is_floating_point():
107
- inputs[k] = inputs[k].to(torch.bfloat16)
108
-
109
- with torch.inference_mode():
110
- output_ids = model.generate(**inputs, max_new_tokens=512, do_sample=False)
111
- resp = processor.decode(output_ids[0][inputs.input_ids.size(1):], skip_special_tokens=True)
112
- return resp
113
-
114
- def stage2_planner(model, processor, video_path, query, collector_text, device):
115
- """Planner uses base_model.thinker with LoRA enabled"""
116
- has_audio = check_if_video_has_audio(video_path)
117
- content = [{"type": "video",
118
- "video": video_path,
119
- "min_pixels": 36 * 28 * 28,
120
- "max_pixels": 64 * 28 * 28,
121
- "max_frames": 150,
122
- "fps": 1.0}]
123
- if has_audio:
124
- content.append({"type": "audio", "audio": video_path})
125
- text_prompt = PLANNER_USER_TEMPLATE.format(collector_text=collector_text, problem=query)
126
- content.append({"type": "text", "text": text_prompt})
127
-
128
- messages = [
129
- {"role": "system", "content": [{"type": "text", "text": PLANNER_SYSTEM_PROMPT}]},
130
- {"role": "user", "content": content}
131
- ]
132
- print(f"planner input: {messages}")
133
-
134
- audios, images, videos = process_mm_info(messages, use_audio_in_video=False)
135
- text = processor.apply_chat_template([messages], tokenize=False, add_generation_prompt=True)
136
- inputs = processor(text=text, audio=audios, images=images, videos=videos, return_tensors="pt", padding=True).to(device)
137
-
138
- for k in inputs:
139
- if isinstance(inputs[k], torch.Tensor) and inputs[k].is_floating_point():
140
- inputs[k] = inputs[k].to(torch.bfloat16)
141
-
142
- with torch.inference_mode():
143
- output_ids = model.generate(**inputs, max_new_tokens=256, do_sample=False)
144
- resp = processor.decode(output_ids[0][inputs.input_ids.size(1):], skip_special_tokens=True)
145
- return parse_planner_output(resp), resp
146
-
147
- def stage3_grounder(grounder_proc, video_path, query, duration):
148
- req = json.dumps({"video_path": video_path, "query": query, "duration": duration})
149
- try:
150
- grounder_proc.stdin.write(req + "\n")
151
- grounder_proc.stdin.flush()
152
- except (BrokenPipeError, OSError) as exc:
153
- raise RuntimeError(f"Grounder worker unavailable while sending request: {exc}") from exc
154
-
155
- resp_line = grounder_proc.stdout.readline().strip()
156
- if not resp_line:
157
- raise RuntimeError("Grounder worker returned empty response.")
158
-
159
- try:
160
- resp = json.loads(resp_line)
161
- except json.JSONDecodeError as exc:
162
- raise RuntimeError(f"Grounder worker returned invalid JSON: {resp_line}") from exc
163
-
164
- if "error" in resp and resp["error"]:
165
- raise RuntimeError(f"Grounder worker error: {resp['error']}")
166
-
167
- return resp.get("pred_spans", [[0, duration]]), resp.get("success", False)
168
-
169
- def build_humanomni_query(sample):
170
- """Build the full question text with options and TYPE_TEMPLATE, aligned with eval_humanomniv2.py."""
171
- problem_type = sample.get("problem_type", "free-form")
172
- if problem_type in ("multiple choice", "emer_ov_mc"):
173
- question = sample["problem"] + " Options:\n"
174
- for op in sample.get("options", []):
175
- question += op + "\n"
176
- else:
177
- question = sample["problem"]
178
- question += TYPE_TEMPLATE.get(problem_type, TYPE_TEMPLATE["free-form"])
179
- print(f"[HumanOmni question] {question}")
180
- return question
181
-
182
- def get_humanomni_inputs(processor, video_path, query_text, sample, device):
183
- """Build model inputs for HumanOmniV2, aligned with eval_humanomniv2.py message format."""
184
- has_audio = check_if_video_has_audio(video_path)
185
- data_type = sample.get("data_type", "video")
186
- content = [{"type": data_type,
187
- data_type: video_path,
188
- "min_pixels": 128 * 28 * 28,
189
- "max_pixels": 256 * 28 * 28,
190
- "max_frames": 64,
191
- "fps": 2.0}]
192
- if has_audio:
193
- content.append({"type": "audio", "audio": video_path})
194
- text_prompt = f"Here is a {data_type}, with the audio from the video.\n" + query_text
195
- else:
196
- text_prompt = query_text
197
- content.append({"type": "text", "text": text_prompt})
198
- messages = [
199
- {"role": "system", "content": [{"type": "text", "text": HUMANOMNI_SYSTEM_PROMPT}]},
200
- {"role": "user", "content": content}
201
- ]
202
- print(f"humanomni input: {messages}")
203
- audios, images, videos = process_mm_info(messages, use_audio_in_video=False)
204
- text = processor.apply_chat_template([messages], tokenize=False, add_generation_prompt=True)
205
- inputs = processor(text=text, audio=audios, images=images, videos=videos, return_tensors="pt", padding=True).to(device)
206
-
207
- for k in inputs:
208
- if isinstance(inputs[k], torch.Tensor) and inputs[k].is_floating_point():
209
- inputs[k] = inputs[k].to(torch.bfloat16)
210
- return inputs
211
-
212
- def revise_answer(model, processor, video_path, query, answer, device):
213
- """Reviser uses base_model.thinker with LoRA disabled"""
214
- has_audio = check_if_video_has_audio(video_path)
215
- content = [{"type": "video",
216
- "video": video_path,
217
- "min_pixels": 64 * 28 * 28,
218
- "max_pixels": 128 * 28 * 28,
219
- "max_frames": 100,
220
- "fps": 1.0}]
221
- if has_audio:
222
- content.append({"type": "audio", "audio": video_path})
223
- user_prompt = f"Query: {query}\nCandidate Answer: {answer}"
224
- content.append({"type": "text", "text": user_prompt})
225
-
226
- messages = [
227
- {"role": "system", "content": [{"type": "text", "text": REVISER_SYSTEM_PROMPT}]},
228
- {"role": "user", "content": content}
229
- ]
230
-
231
- audios, images, videos = process_mm_info(messages, use_audio_in_video=False)
232
- text = processor.apply_chat_template([messages], tokenize=False, add_generation_prompt=True)
233
- inputs = processor(text=text, audio=audios, images=images, videos=videos, return_tensors="pt", padding=True).to(device)
234
-
235
- for k in inputs:
236
- if isinstance(inputs[k], torch.Tensor) and inputs[k].is_floating_point():
237
- inputs[k] = inputs[k].to(torch.bfloat16)
238
-
239
- with torch.inference_mode():
240
- output_ids = model.generate(**inputs, max_new_tokens=1024, do_sample=False)
241
- resp = processor.decode(output_ids[0][inputs.input_ids.size(1):], skip_special_tokens=True)
242
- return extract_score(resp), resp
243
-
244
  # ============================================================
245
  # Main Process
246
  # ============================================================
247
  def main():
248
- parser = argparse.ArgumentParser()
249
- parser.add_argument("--base_model_path", required=True)
250
- parser.add_argument("--planner_lora_path", required=True)
251
- parser.add_argument("--humanomni_path", required=True)
252
- parser.add_argument("--grounder_path", required=True)
253
- parser.add_argument("--grpo_adapter_path", required=True)
254
- parser.add_argument("--grounder_python", required=True)
255
- parser.add_argument("--grounder_cwd", required=True)
256
- parser.add_argument("--input_file", required=True)
257
- parser.add_argument("--output_file", required=True)
258
- parser.add_argument("--lora_save_dir", required=True)
259
- parser.add_argument("--video_root", default="", help="Root directory for video files")
260
- parser.add_argument("--id_key", default="qid,video", help="Comma separated keys for identifying samples")
261
- parser.add_argument("--main_gpu", default="cuda:0")
262
- parser.add_argument("--grounder_gpu", default="cuda:1")
263
- parser.add_argument("--humanomni_gpu", default="cuda:0")
264
- parser.add_argument("--t_max", type=int, default=3)
265
- parser.add_argument("--tau", type=int, default=7)
266
- parser.add_argument("--alpha", type=float, default=0.7)
267
- parser.add_argument("--b0", type=float, default=5.0)
268
- parser.add_argument("--lr", type=float, default=1e-5)
269
- parser.add_argument("--num_chunks", type=int, default=1, help="Total number of parallel processes")
270
- parser.add_argument("--chunk_idx", type=int, default=0, help="Current process index (0-based)")
271
- args = parser.parse_args()
272
 
273
-
274
  # ---- Initialize Models ----
275
  print(f"\n[INIT] Loading Base Model ({args.base_model_path}) on {args.main_gpu}")
276
  base_model = Qwen2_5OmniForConditionalGeneration.from_pretrained(
@@ -322,16 +133,6 @@ def main():
322
 
323
  # ---- 3. Loop through dataset ----
324
  for sample in samples_to_process:
325
- dataset_id = get_sample_id(sample, args.id_key)
326
- optimizer = None
327
- query = sample.get("problem")
328
- video_path = sample.get("video") or sample.get("path")
329
- if args.video_root and video_path:
330
- video_path = os.path.join(args.video_root, video_path)
331
- trim_path = None
332
- adapter_name = None
333
- adapter_deleted = False
334
-
335
  try:
336
  print(f"\n======== Processing [{dataset_id}] ========")
337
  duration = get_video_duration(video_path)
@@ -394,7 +195,6 @@ def main():
394
  for t in range(1, args.t_max + 1):
395
  gc.collect(); torch.cuda.empty_cache()
396
 
397
- # --- Generate y_t ---
398
  humanomni_model.eval()
399
  inputs = get_humanomni_inputs(humanomni_processor, generation_video, humanomni_query, sample, args.humanomni_gpu)
400
 
@@ -405,26 +205,11 @@ def main():
405
  y_t_text = humanomni_processor.decode(generated_sequence, skip_special_tokens=True)
406
  print(f" [Iter {t}/{args.t_max}] Answer = {y_t_text}")
407
 
408
- # --- Reviser scores r_t (LoRA disabled) ---
409
  base_model.thinker.disable_adapters()
410
  score_t, reviser_raw = revise_answer(base_model.thinker, base_processor, video_path, query, y_t_text, args.main_gpu)
411
- print(f" [Iter {t}/{args.t_max}] Score = {score_t}/10")
412
 
413
  all_history.append({"iter": t, "answer": y_t_text, "score": score_t, "reviser_raw": reviser_raw})
414
 
415
- if score_t > best_score:
416
- best_score = score_t
417
- best_answer = y_t_text
418
- best_raw_resp = reviser_raw
419
-
420
- if score_t >= args.tau:
421
- print(f" -> Score {score_t} >= Tau({args.tau}), breaking loop early.")
422
- early_stop = True
423
- break
424
-
425
- if t == args.t_max:
426
- print(f" -> Reached T_max, stopping.")
427
- break
428
 
429
  # --- RL Update (REINFORCE) ---
430
  humanomni_model.train()
@@ -433,18 +218,6 @@ def main():
433
  advantage = float(score_t - b)
434
  advantage_tensor = torch.tensor([advantage], device=args.humanomni_gpu, dtype=torch.bfloat16)
435
 
436
- concat_ids = torch.cat([inputs.input_ids, generated_sequence.unsqueeze(0)], dim=1)
437
- attention_mask = torch.ones_like(concat_ids)
438
- labels = torch.cat([
439
- torch.full_like(inputs.input_ids, -100),
440
- generated_sequence.unsqueeze(0)
441
- ], dim=1)
442
-
443
- forward_kwargs = {k: v for k, v in inputs.items() if k not in ["input_ids", "attention_mask", "labels"]}
444
- forward_kwargs["input_ids"] = concat_ids
445
- forward_kwargs["attention_mask"] = attention_mask
446
- forward_kwargs["labels"] = labels
447
-
448
  outputs = humanomni_model(**forward_kwargs)
449
 
450
  nll_loss = outputs.loss
@@ -454,67 +227,7 @@ def main():
454
  optimizer.step()
455
 
456
  b = args.alpha * b + (1.0 - args.alpha) * score_t
457
- print(f" -> Updated Baseline b = {b:.2f}, Loss = {final_loss.item():.4f} (NLL: {nll_loss.item():.4f}, Adv: {advantage:.2f})")
458
-
459
- # ====== CLEANUP & PERSIST ======
460
- sample_lora_dir = os.path.join(args.lora_save_dir, dataset_id)
461
- humanomni_model.save_pretrained(sample_lora_dir, selected_adapters=[adapter_name])
462
-
463
- humanomni_model.delete_adapter(adapter_name)
464
- adapter_deleted = True
465
-
466
- save_entry = {
467
- "id": dataset_id,
468
- "query": query,
469
- "video": video_path,
470
- "use_grounder": use_grounder,
471
- "grounded_span": grounded_span,
472
- "collector_output": collector_text,
473
- "planner_output": planner_raw,
474
- "best_score": best_score,
475
- "best_answer": best_answer,
476
- "history": all_history,
477
- "early_stop": early_stop,
478
- }
479
- with open(args.output_file, 'a', encoding='utf-8') as f:
480
- f.write(json.dumps(save_entry, ensure_ascii=False) + "\n")
481
- except Exception as exc:
482
- error_type, error_message, traceback_text = format_exception_info(exc)
483
- print(f"[Skip] Sample [{dataset_id}] failed with {error_type}: {error_message}")
484
- append_skipped_sample_log(
485
- dataset_id=dataset_id,
486
- query=query,
487
- video_path=video_path,
488
- error_type=error_type,
489
- error_message=error_message,
490
- traceback_text=traceback_text,
491
- chunk_idx=args.chunk_idx,
492
- )
493
- print("[Skip] Logged to eval_results/skipped_samples_intentbench.log; sample will be retried in future runs.")
494
- continue
495
- finally:
496
- if optimizer is not None:
497
- del optimizer
498
- if adapter_name and not adapter_deleted:
499
- try:
500
- humanomni_model.delete_adapter(adapter_name)
501
- except Exception as cleanup_exc:
502
- print(f"[Cleanup Warning] Failed to delete adapter {adapter_name}: {cleanup_exc}")
503
- if trim_path and os.path.exists(trim_path):
504
- try:
505
- os.remove(trim_path)
506
- except OSError as cleanup_exc:
507
- print(f"[Cleanup Warning] Failed to remove temp file {trim_path}: {cleanup_exc}")
508
- gc.collect()
509
- torch.cuda.empty_cache()
510
-
511
- grounder_proc.stdin.write(json.dumps({"command": "shutdown"}) + "\n")
512
- grounder_proc.stdin.flush()
513
- grounder_proc.wait()
514
-
515
- if __name__ == "__main__":
516
- main()
517
 
518
- ```
519
 
520
 
 
77
 
78
  from qwen_omni_utils import process_mm_info
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  # ============================================================
81
  # Main Process
82
  # ============================================================
83
  def main():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
 
85
  # ---- Initialize Models ----
86
  print(f"\n[INIT] Loading Base Model ({args.base_model_path}) on {args.main_gpu}")
87
  base_model = Qwen2_5OmniForConditionalGeneration.from_pretrained(
 
133
 
134
  # ---- 3. Loop through dataset ----
135
  for sample in samples_to_process:
 
 
 
 
 
 
 
 
 
 
136
  try:
137
  print(f"\n======== Processing [{dataset_id}] ========")
138
  duration = get_video_duration(video_path)
 
195
  for t in range(1, args.t_max + 1):
196
  gc.collect(); torch.cuda.empty_cache()
197
 
 
198
  humanomni_model.eval()
199
  inputs = get_humanomni_inputs(humanomni_processor, generation_video, humanomni_query, sample, args.humanomni_gpu)
200
 
 
205
  y_t_text = humanomni_processor.decode(generated_sequence, skip_special_tokens=True)
206
  print(f" [Iter {t}/{args.t_max}] Answer = {y_t_text}")
207
 
 
208
  base_model.thinker.disable_adapters()
209
  score_t, reviser_raw = revise_answer(base_model.thinker, base_processor, video_path, query, y_t_text, args.main_gpu)
 
210
 
211
  all_history.append({"iter": t, "answer": y_t_text, "score": score_t, "reviser_raw": reviser_raw})
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
  # --- RL Update (REINFORCE) ---
215
  humanomni_model.train()
 
218
  advantage = float(score_t - b)
219
  advantage_tensor = torch.tensor([advantage], device=args.humanomni_gpu, dtype=torch.bfloat16)
220
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  outputs = humanomni_model(**forward_kwargs)
222
 
223
  nll_loss = outputs.loss
 
227
  optimizer.step()
228
 
229
  b = args.alpha * b + (1.0 - args.alpha) * score_t
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
 
231
+
232
 
233