Views
No views yet
| Benchmark | Type | InternVL3-8B (Base) | LongVPO-InternVL3-8B (Stage 1) | LongVPO-InternVL3-8B (Stage 2) |
|---|---|---|---|---|
| MLVU | Long Video | 71.4 | 75.1 | 76.4 |
| LongVideoBench | Long Video | 62.3 | 66.8 | 66.0 |
| LVBench | Long Video | 48.8 | 52.4 | 53.6 |
| Video-MME (w/o sub) | Long Video | 66.5 | 68.1 | 68.9 |
| Video-MME (w/ sub) | Long Video | 72.5 | 74.0 | 74.0 |
| MVBench | Short Video | 75.4 | 75.1 | 75.0 |
[!IMPORTANT] Please usetransformers>=4.37.2to ensure the model works normally.bash1pip install "transformers>=4.37.2" 2# optional
1import math
2import numpy as np
3import torch
4import torchvision.transforms as T
5from decord import VideoReader, cpu
6from PIL import Image
7from torchvision.transforms.functional import InterpolationMode
8from transformers import AutoModel, AutoTokenizer
9
10# Constants for image normalization
11IMAGENET_MEAN = (0.485, 0.456, 0.406)
12IMAGENET_STD = (0.229, 0.224, 0.225)
13
14def build_transform(input_size):
15 MEAN, STD = IMAGENET_MEAN, IMAGENET_STD
16 transform = T.Compose([
17 T.Lambda(lambda img: img.convert('RGB') if img.mode != 'RGB' else img),
18 T.Resize((input_size, input_size), interpolation=InterpolationMode.BICUBIC),
19 T.ToTensor(),
20 T.Normalize(mean=MEAN, std=STD)
21 ])
22 return transform
23
24def find_closest_aspect_ratio(aspect_ratio, target_ratios, width, height, image_size):
25 best_ratio_diff = float('inf')
26 best_ratio = (1, 1)
27 area = width * height
28 for ratio in target_ratios:
29 target_aspect_ratio = ratio[0] / ratio[1]
30 ratio_diff = abs(aspect_ratio - target_aspect_ratio)
31 if ratio_diff < best_ratio_diff:
32 best_ratio_diff = ratio_diff
33 best_ratio = ratio
34 elif ratio_diff == best_ratio_diff:
35 if area > 0.5 * image_size * image_size * ratio[0] * ratio[1]:
36 best_ratio = ratio
37 return best_ratio
38
39def dynamic_preprocess(image, min_num=1, max_num=12, image_size=448, use_thumbnail=False):
40 orig_width, orig_height = image.size
41 aspect_ratio = orig_width / orig_height
42
43 # calculate the existing image aspect ratio
44 target_ratios = set(
45 (i, j) for n in range(min_num, max_num + 1) for i in range(1, n + 1) for j in range(1, n + 1) if
46 i * j <= max_num and i * j >= min_num)
47 target_ratios = sorted(target_ratios, key=lambda x: x[0] * x[1])
48
49 # find the closest aspect ratio to the target
50 target_aspect_ratio = find_closest_aspect_ratio(
51 aspect_ratio, target_ratios, orig_width, orig_height, image_size)
52
53 # calculate the target width and height
54 target_width = image_size * target_aspect_ratio[0]
55 target_height = image_size * target_aspect_ratio[1]
56 blocks = target_aspect_ratio[0] * target_aspect_ratio[1]
57
58 # resize the image
59 resized_img = image.resize((target_width, target_height))
60 processed_images = []
61 for i in range(blocks):
62 box = (
63 (i % (target_width // image_size)) * image_size,
64 (i // (target_width // image_size)) * image_size,
65 ((i % (target_width // image_size)) + 1) * image_size,
66 ((i // (target_width // image_size)) + 1) * image_size
67 )
68 # split the image
69 split_img = resized_img.crop(box)
70 processed_images.append(split_img)
71 assert len(processed_images) == blocks
72 if use_thumbnail and len(processed_images) != 1:
73 thumbnail_img = image.resize((image_size, image_size))
74 processed_images.append(thumbnail_img)
75 return processed_images
76
77def get_index(bound, fps, max_frame, first_idx=0, num_segments=32):
78 if bound:
79 start, end = bound[0], bound[1]
80 else:
81 start, end = -100000, 100000
82 start_idx = max(first_idx, round(start * fps))
83 end_idx = min(round(end * fps), max_frame)
84 seg_size = float(end_idx - start_idx) / num_segments
85 frame_indices = np.array([
86 int(start_idx + (seg_size / 2) + np.round(seg_size * idx))
87 for idx in range(num_segments)
88 ])
89 return frame_indices
90
91def load_video(video_path, bound=None, input_size=448, max_num=1, num_segments=32):
92 vr = VideoReader(video_path, ctx=cpu(0), num_threads=1)
93 max_frame = len(vr) - 1
94 fps = float(vr.get_avg_fps())
95
96 pixel_values_list, num_patches_list = [], []
97 transform = build_transform(input_size=input_size)
98 frame_indices = get_index(bound, fps, max_frame, first_idx=0, num_segments=num_segments)
99 for frame_index in frame_indices:
100 img = Image.fromarray(vr[frame_index].asnumpy()).convert('RGB')
101 img = dynamic_preprocess(img, image_size=input_size, use_thumbnail=True, max_num=max_num)
102 pixel_values = [transform(tile) for tile in img]
103 pixel_values = torch.stack(pixel_values)
104 num_patches_list.append(pixel_values.shape[0])
105 pixel_values_list.append(pixel_values)
106 pixel_values = torch.cat(pixel_values_list)
107 return pixel_values, num_patches_list
108
109# 1. Load Model
110model_path = "MCG-NJU/LongVPO-Stage2-InternVL3-8B"
111tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
112model = AutoModel.from_pretrained(model_path, torch_dtype=torch.bfloat16, trust_remote_code=True).cuda().eval()
113generation_config = dict(max_new_tokens=1024, do_sample=True)
114
115# 2. Prepare Video
116video_path = './examples/red-panda.mp4'
117pixel_values, num_patches_list = load_video(video_path, num_segments=8, max_num=1)
118pixel_values = pixel_values.to(torch.bfloat16).cuda()
119
120# 3. Multi-round Conversation
121
122# Round 1
123video_prefix = ''.join([f'Frame{i+1}: <image>\n' for i in range(len(num_patches_list))])
124question1 = video_prefix + 'What is the red panda doing?'
125# Input format: Frame1: <image>\n...Frame8: <image>\n{question}
126response, history = model.chat(tokenizer, pixel_values, question1, generation_config,
127 num_patches_list=num_patches_list, history=None, return_history=True)
128print(f'User: {question1}\nAssistant: {response}')
129
130# Round 2
131question2 = 'Describe this video in detail.'
132response, history = model.chat(tokenizer, pixel_values, question2, generation_config,
133 num_patches_list=num_patches_list, history=history, return_history=True)
134print(f'User: {question2}\nAssistant: {response}')1@inproceedings{huang2025longvpo,
2 title={Long{VPO}: From Anchored Cues to Self-Reasoning for Long-Form Video Preference Optimization},
3 author={Zhenpeng Huang and Jiaqi Li and Zihan Jia and Xinhao Li and Desen Meng and Lingxue Song and Xi Chen and Liang Li and Limin Wang},
4 booktitle={The Thirty-ninth Annual Conference on Neural Information Processing Systems},
5 year={2025},
6 url={[https://openreview.net/forum?id=LKAp7Dknxf](https://openreview.net/forum?id=LKAp7Dknxf)}
7}