Instructions to use Harry-1234/MODF-SIR with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Harry-1234/MODF-SIR with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Harry-1234/MODF-SIR", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Update README.md
Browse files
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 |
|