Flora-Rewarder / batch_utils /terminal_reward.py
Omar L
Add Flora inference examples (#1)
79dd27b
Raw History Blame Contribute Delete
4.62 kB
"""Terminal reward: one 1-5 success score for each whole episode.
python -m batch_utils.terminal_reward --jobs jobs.jsonl --output terminal.jsonl
python -m batch_utils.terminal_reward --video top.mp4 wrist.mp4 --task "Put the cup on the tray." --output terminal.jsonl
Each job is ``{"id": ..., "task": ..., "videos": [camera videos, in collage order]}``.
"""
from __future__ import annotations
from . import core
from .engine import (
COLLAGE_VIDEO_SIZE,
base_parser,
load_llm,
load_processor,
log,
open_output,
read_jobs,
render,
resolve_model,
write_record,
)
def main(argv=None) -> int:
p = base_parser("FLORA rewarder: terminal (episode success) score, 1-5.")
p.add_argument("--with-rationale", action="store_true", help="Ask for a one-line rationale before the score.")
p.add_argument("--max-model-len", type=int, default=16384)
args = p.parse_args(argv)
jobs = read_jobs(args)
model_path = resolve_model(args.model, args.revision, weights=not args.dry_run)
processor = load_processor(model_path)
prepared = []
for job in jobs:
collage = core.Collage(job["videos"])
indices = core.terminal_frame_indices(collage.num_frames, collage.fps)
messages = core.terminal_messages(job["task"], args.with_rationale)
prepared.append((job, collage, indices, render(processor, messages, thinking=False)))
if args.dry_run:
job, collage, indices, prompt = prepared[0]
log(f"{job['id']}: {collage.num_frames} frames at {collage.fps:g} FPS, "
f"collage {collage.width}x{collage.height}, {len(indices)} sampled frames\n{prompt}")
return 0
from vllm import SamplingParams
from vllm.sampling_params import StructuredOutputsParams
llm = load_llm(model_path, args, max_model_len=args.max_model_len,
mm_processor_kwargs={"do_sample_frames": False, "size": COLLAGE_VIDEO_SIZE},
max_logprobs=core.TERMINAL_TOP_LOGPROBS)
tokenizer = llm.get_tokenizer()
params = SamplingParams(
temperature=0.0,
max_tokens=core.terminal_max_tokens(args.with_rationale),
logprobs=core.TERMINAL_TOP_LOGPROBS,
structured_outputs=StructuredOutputsParams(regex=core.terminal_regex(args.with_rationale)),
)
with open_output(args) as stream:
for start in range(0, len(prepared), args.max_num_seqs):
batch = prepared[start : start + args.max_num_seqs]
requests = [
{
"prompt": prompt,
"multi_modal_data": {
"video": (collage.frames(indices), core.video_metadata(indices, collage.fps, collage.num_frames))
},
"mm_processor_kwargs": {"do_sample_frames": False},
}
for _, collage, indices, prompt in batch
]
outputs = llm.generate(requests, params, use_tqdm=False)
for _, collage, _, _ in batch:
collage.release()
for (job, collage, indices, _), output in zip(batch, outputs):
completion = output.outputs[0]
score, rationale = core.parse_terminal(completion.text, args.with_rationale)
positions = [
(
tokenizer.decode([token]),
float(candidates[token].logprob),
[(tokenizer.decode([i]), float(c.logprob)) for i, c in candidates.items()],
)
for token, candidates in zip(completion.token_ids, completion.logprobs)
]
probabilities, expected = core.score_probabilities(positions, score)
write_record(stream, {
"id": job["id"],
"task": job["task"],
"score": score,
"expected_score": round(expected, 4),
# Expected score rescaled to [0, 1]: a progress value, not a success probability.
"progress": round((expected - 1.0) / 4.0, 4),
"probabilities": {k: round(v, 4) for k, v in probabilities.items()},
"rationale": rationale,
"duration_s": round(collage.duration_s, 3),
"sampled_frames": len(indices),
"output": completion.text,
})
log(f"terminal: {min(start + len(batch), len(prepared))}/{len(prepared)} episodes")
return 0
if __name__ == "__main__":
raise SystemExit(main())