# Longvila Scaling Rl Long Videos

> Train vision-language models on hour-long videos using a two-stage pipeline combining supervised fine-tuning with GRPO, reaching 71% accuracy on VideoMME while supporting 8,192 frames through efficient multi-modal sequence parallelism.

- Skill: `adu2021/longvila-scaling-rl-long-videos` (Agent Skill)
- Install (CLI): `npx skillmds@latest add adu2021/longvila-scaling-rl-long-videos`
- Raw SKILL.md: https://api.skillmd.com/api/skills/adu2021/longvila-scaling-rl-long-videos/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: MIT
- Author: adu2021 (https://skillmd.com/u/adu2021)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/adu2021/longvila-scaling-rl-long-videos

---


# LongVILA-R1: Extending Video LLMs to Hour-Long Reasoning Tasks

Standard video understanding models process clips of seconds, not entire movies or events. LongVILA-R1 addresses this through three components: a 104K question-answer dataset with chain-of-thought reasoning annotations, a training pipeline that starts with supervised fine-tuning then applies reinforcement learning, and a novel infrastructure innovation (MR-SP) that parallelizes both video encoding and prefilling to handle thousands of frames efficiently.

The key design choice is two-stage training: first establish reasoning capabilities via supervised examples, then push performance higher through RL rewards while remaining sample-efficient. Infrastructure innovations avoid the memory nightmare of processing hour-long videos on standard hardware.

## Core Concept

Long video reasoning is fundamentally different from short-clip understanding. Models must maintain temporal context over hours, track relationships across distant frames, and answer questions requiring holistic narrative understanding. LongVILA-R1 tackles this in three stages: (1) data engineers create a diverse reasoning dataset with temporal, spatial, goal-oriented, and plot-focused questions; (2) the model learns from these examples via supervised fine-tuning, establishing baseline capabilities; (3) GRPO RL refines answers using outcome-based rewards, pushing toward higher accuracy without requiring more annotations.

Infrastructure parallelism (MR-SP) is the practical enabler: by splitting the video encoding and LLM prefilling stages across GPUs, the approach achieves 2.1× speedup, making hour-long video training tractable.

## Architecture Overview

- **Base Model**: 7B and 1.5B parameter LLaVA-style vision-language models
- **Video Encoding**: Vision transformer processes frame tokens, supporting up to 8,192 frames
- **Multi-Modal Sequence Parallelism (MR-SP)**: Distributes video encoding across GPUs, prefills LLM layer-by-layer in parallel
- **Stage 1 Training**: Supervised fine-tuning on 36K filtered chain-of-thought examples
- **Stage 2 Training**: GRPO on 68K challenging + 102K open-source examples
- **LongVideo-Reason Dataset**: 104K QA pairs across sports, games, vlogs with reasoning annotations

## Implementation

### Step 1: Prepare Long Video Dataset with Reasoning Annotations

Construct a dataset of long videos paired with reasoning questions. Use video captioning and LLM generation to create diverse question types:

```python
import json
from datasets import Dataset
from typing import List, Dict

def create_long_video_dataset(video_paths: List[str],
                             video_captions: List[str]) -> Dataset:
    """
    Create dataset of long videos with reasoning questions.
    Input: list of video paths and their captions from NVILA-8B.
    Output: dataset with questions of 4 types: temporal, spatial, goal, narrative.
    """
    examples = []

    for video_path, caption in zip(video_paths, video_captions):
        # Use LLM (DeepSeek-R1) to generate diverse questions
        # Example prompt structure
        prompt = f"""Video caption: {caption}

Generate 4 reasoning questions about this video:
1. Temporal: What sequence of events occurs?
2. Goal/Purpose: What is the character/object trying to achieve?
3. Spatial: Where are the key objects/people located?
4. Plot/Narrative: How does the scene develop?

Provide questions and their answers based on typical video content."""

        # Call LLM to generate questions
        generated = call_llm(prompt, model="deepseek-r1-671b")

        # Parse generated questions and answers
        questions = parse_qa_from_llm_output(generated)

        for question_type, question, answer in questions:
            examples.append({
                "video_path": video_path,
                "question": question,
                "question_type": question_type,
                "answer": answer,
                "caption": caption
            })

    return Dataset.from_dict({
        "video_path": [ex["video_path"] for ex in examples],
        "question": [ex["question"] for ex in examples],
        "answer": [ex["answer"] for ex in examples],
        "question_type": [ex["question_type"] for ex in examples]
    })

def call_llm(prompt: str, model: str) -> str:
    """Call DeepSeek-R1 or equivalent to generate reasoning questions."""
    # Placeholder; in practice use API or local deployment
    import anthropic
    client = anthropic.Anthropic()
    response = client.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1000,
        messages=[{"role": "user", "content": prompt}]
    )
    return response.content[0].text

def parse_qa_from_llm_output(output: str) -> List[tuple]:
    """Extract Q&A from LLM structured output."""
    # Parse LLM output into (question_type, question, answer) tuples
    lines = output.strip().split('\n')
    results = []
    for line in lines:
        if ':' in line:
            parts = line.split(':', 1)
            results.append(("generic", parts[0], parts[1]))
    return results
```

### Step 2: Supervised Fine-Tuning Stage

Train on 36K filtered examples with chain-of-thought reasoning to establish baseline capabilities:

```python
import torch
from transformers import AutoModel, Trainer, TrainingArguments
from peft import get_peft_model, LoraConfig

class VideoQAModel(torch.nn.Module):
    def __init__(self, model_name="qwen/qwen-vl-7b"):
        super().__init__()
        self.model = AutoModel.from_pretrained(model_name, trust_remote_code=True)

        # Optional: use LoRA for efficiency
        lora_config = LoraConfig(
            r=64,
            lora_alpha=128,
            target_modules=["q_proj", "v_proj"],
            lora_dropout=0.05,
            bias="none"
        )
        self.model = get_peft_model(self.model, lora_config)

    def forward(self, video_frames, question, answer=None):
        """
        Process long video and generate reasoning chain + answer.
        video_frames: [num_frames, 3, 224, 224]
        question: string
        answer: string (for training)
        """
        # Encode video frames
        vision_outputs = self.model.visual_encoder(video_frames)

        # Create prompt with chain-of-thought instruction
        prompt = f"Video understanding task.\nQuestion: {question}\nReasoning:\n"

        # Generate chain-of-thought reasoning
        reasoning_output = self.model.generate(
            vision_outputs,
            prompt,
            max_length=512,
            do_sample=False
        )

        if answer is not None:
            # Training: compute loss
            full_text = reasoning_output + f" Answer: {answer}"
            loss = compute_language_modeling_loss(self.model, full_text)
            return loss
        else:
            # Inference: return reasoning and answer
            return reasoning_output

def sft_training_loop(model, train_dataset, num_epochs=2):
    """
    Supervised fine-tuning on 36K examples.
    """
    training_args = TrainingArguments(
        output_dir="./models/longvila-sft",
        num_train_epochs=num_epochs,
        per_device_train_batch_size=4,
        gradient_accumulation_steps=8,
        learning_rate=2e-4,
        warmup_steps=500,
        logging_steps=100,
        save_strategy="epoch",
        bf16=True  # Use bfloat16 on A100
    )

    trainer = Trainer(
        model=model,
        args=training_args,
        train_dataset=train_dataset,
        data_collator=lambda batch: default_data_collator(batch)
    )

    trainer.train()
    return model
```

### Step 3: GRPO Reinforcement Learning Stage

Refine using GRPO with outcome-based rewards on 68K challenging examples:

```python
import torch.nn.functional as F

def compute_grpo_loss(model, video_frames, question, ground_truth_answer):
    """
    Group Relative Policy Optimization: compare outputs within a batch group.
    Reward based on answer correctness and reasoning quality.
    """
    batch_size = 4
    num_samples_per_prompt = 4

    all_outputs = []
    all_rewards = []

    # Generate multiple samples per input
    for sample_id in range(num_samples_per_prompt):
        output = model.generate(
            video_frames,
            question,
            max_length=512,
            temperature=0.7 + 0.1 * sample_id  # Vary temperature
        )
        all_outputs.append(output)

        # Extract final answer from reasoning chain
        final_answer = extract_final_answer(output)

        # Reward 1: Accuracy - is answer correct?
        accuracy = 1.0 if final_answer == ground_truth_answer else 0.0

        # Reward 2: Reasoning quality - does output show work?
        reasoning_quality = 1.0 if "because" in output.lower() else 0.5

        # Combine rewards
        reward = 0.8 * accuracy + 0.2 * reasoning_quality
        all_rewards.append(reward)

    # Group relative loss: compare within the group
    all_rewards = torch.tensor(all_rewards, device=model.device)
    mean_reward = all_rewards.mean()
    advantages = all_rewards - mean_reward

    # Policy gradient with advantage weighting
    log_probs = compute_log_probs(model, all_outputs, question)
    loss = -(log_probs * advantages).mean()

    # KL divergence penalty (stay close to SFT model)
    kl_loss = compute_kl_divergence(model, sft_model)
    total_loss = loss + 0.04 * kl_loss

    return total_loss

def grpo_training_loop(model, sft_model, train_dataset, num_epochs=1):
    """
    GRPO refinement on 68K challenging + 102K open-source examples.
    """
    optimizer = torch.optim.AdamW(model.parameters(), lr=2e-5)
    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(
        optimizer, T_max=len(train_dataset)
    )

    for epoch in range(num_epochs):
        for batch in train_dataset:
            video_frames = batch["video"]
            question = batch["question"]
            answer = batch["answer"]

            loss = compute_grpo_loss(
                model, video_frames, question, answer
            )

            loss.backward()
            torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
            optimizer.step()
            scheduler.step()
            optimizer.zero_grad()

    return model

def extract_final_answer(reasoning_text: str) -> str:
    """Extract final answer from chain-of-thought reasoning."""
    lines = reasoning_text.split('\n')
    for line in reversed(lines):
        if 'answer' in line.lower():
            return line.split(':')[-1].strip()
    return reasoning_text.split()[-1]
```

### Step 4: Inference with Multi-Modal Sequence Parallelism

During inference, distribute video encoding across GPUs and parallelize LLM prefilling:

```python
def inference_with_mrsp(model, video_path, question,
                       num_gpus=8, device_ids=None):
    """
    Inference using Multi-modal Reinforcement Sequence Parallelism.
    Distributes video encoding and LLM prefilling across GPUs.

    Achieves ~2.1x speedup for hour-long videos (8,192 frames).
    """
    if device_ids is None:
        device_ids = list(range(num_gpus))

    # Stage 1: Distribute video encoding across GPUs
    video_frames = load_video_frames(video_path)  # [8192, 3, 224, 224]
    chunk_size = len(video_frames) // num_gpus

    vision_outputs_per_gpu = []
    for gpu_id in device_ids:
        start_idx = gpu_id * chunk_size
        end_idx = (gpu_id + 1) * chunk_size
        video_chunk = video_frames[start_idx:end_idx]

        with torch.device(f"cuda:{gpu_id}"):
            chunk_output = model.visual_encoder(video_chunk)
            vision_outputs_per_gpu.append(chunk_output)

    # Gather outputs on primary GPU
    all_vision_outputs = torch.cat(vision_outputs_per_gpu, dim=0)

    # Stage 2: Parallelize LLM prefilling
    # Prefix tree structure: different prefixes processed simultaneously
    prompt = f"Question: {question}\nAnswer:"
    prompt_tokens = tokenizer(prompt)["input_ids"]

    # Run LLM with distributed prefilling
    output_tokens = model.generate(
        input_ids=prompt_tokens,
        vision_outputs=all_vision_outputs,
        max_length=256,
        do_sample=False,
        num_return_sequences=1
    )

    answer = tokenizer.decode(output_tokens[0], skip_special_tokens=True)
    return answer

def load_video_frames(video_path, num_frames=8192):
    """Load and sample frames from hour-long video."""
    import cv2
    cap = cv2.VideoCapture(video_path)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

    # Uniformly sample num_frames
    frame_indices = torch.linspace(
        0, total_frames - 1, num_frames
    ).long()

    frames = []
    for idx in frame_indices:
        cap.set(cv2.CAP_PROP_POS_FRAMES, idx.item())
        ret, frame = cap.read()
        if ret:
            # Resize to 224x224
            frame = cv2.resize(frame, (224, 224))
            frames.append(torch.from_numpy(frame).float() / 255.0)

    cap.release()
    return torch.stack(frames)
```

## Practical Guidance

| Hyperparameter | Recommended Value | Notes |
|---|---|---|
| SFT Dataset Size | 36K examples | High-quality, filtered examples |
| GRPO Dataset Size | 68K challenging + 102K open-source | Diverse for robustness |
| Max Frames Supported | 8,192 frames | ~40-50 minutes of video |
| SFT Learning Rate | 2e-4 | Standard for instruction tuning |
| GRPO Learning Rate | 2e-5 | Conservative for RL stability |
| KL Beta | 0.04 | Prevents divergence from SFT |
| Accuracy Reward Weight | 0.8 | Prioritize correctness |
| Reasoning Quality Weight | 0.2 | Secondary emphasis |
| Gradient Accumulation | 8 steps | Effective batch size ~32 |
| GPU Setup | 8 A100s or equivalent | Multi-modal sequence parallelism needs multiple GPUs |
| Temperature Variation | 0.7-1.0 for GRPO | Encourage diverse outputs |

**When to use LongVILA-R1:**
- Long-form video understanding (movies, sports games, tutorials)
- Reasoning tasks requiring temporal understanding across hours
- Scenarios where RL can improve upon supervised baselines
- Applications with sufficient GPU resources (multi-GPU setups)

**When NOT to use LongVILA-R1:**
- Short video clips (< 1 minute) where standard models suffice
- Single-GPU deployments (sequence parallelism requires multiple GPUs)
- Real-time inference (preprocessing and multi-GPU coordination adds latency)
- Data-scarce domains without ability to create reasoning annotations

**Common pitfalls:**
- Not filtering SFT examples, leading to learned poor reasoning patterns
- KL beta too high, preventing RL from improving over SFT baseline
- Temperature too low in GRPO, reducing output diversity
- Not balancing accuracy and reasoning rewards, overfitting to answering without explanation
- Video sampling not matching temporal patterns (sports vs narrative vs tutorial)
- GPU communication overhead negating parallelism gains with small batch sizes

## Reference

Long, Y., Yu, M., Shen, S., & Chen, W. (2025). Scaling RL to Long Videos. arXiv:2507.07966. https://arxiv.org/abs/2507.07966

