manu commited on
Commit
4fecdd7
·
verified ·
1 Parent(s): 54b3c04

Upload processor

Browse files
preprocessor_config.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "auto_map": {
3
- "AutoProcessor": "manu/colqwen2-v0.1-hf--processing_colqwen2.ColQwen2Processor"
4
  },
5
  "do_convert_rgb": true,
6
  "do_normalize": true,
 
1
  {
2
  "auto_map": {
3
+ "AutoProcessor": "processing_colqwen2.ColQwen2Processor"
4
  },
5
  "do_convert_rgb": true,
6
  "do_normalize": true,
processing_colqwen2.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from typing import List, Optional, Union
3
+
4
+ import torch
5
+ from PIL import Image
6
+ from transformers import BatchFeature
7
+ from transformers.models.qwen2_vl import Qwen2VLProcessor
8
+
9
+ from colpali_engine.utils.processing_utils import BaseVisualRetrieverProcessor
10
+
11
+
12
+ class ColQwen2Processor(BaseVisualRetrieverProcessor, Qwen2VLProcessor):
13
+ """
14
+ Processor for ColQwen2.
15
+ """
16
+
17
+ def __init__(self, *args, **kwargs):
18
+ super().__init__(*args, **kwargs)
19
+ self.tokenizer.padding_side = "left"
20
+ self.min_pixels = 4 * 28 * 28
21
+ self.max_pixels = 768 * 28 * 28
22
+ self.factor = 28
23
+ self.max_ratio = 200
24
+
25
+ @staticmethod
26
+ def round_by_factor(number: float, factor: int) -> int:
27
+ """Returns the closest integer to 'number' that is divisible by 'factor'."""
28
+ return round(number / factor) * factor
29
+
30
+ @staticmethod
31
+ def ceil_by_factor(number: float, factor: int) -> int:
32
+ """Returns the smallest integer greater than or equal to 'number' that is divisible by 'factor'."""
33
+ return math.ceil(number / factor) * factor
34
+
35
+ @staticmethod
36
+ def floor_by_factor(number: float, factor: int) -> int:
37
+ """Returns the largest integer less than or equal to 'number' that is divisible by 'factor'."""
38
+ return math.floor(number / factor) * factor
39
+
40
+
41
+ def smart_resize(self, height: int, width: int, factor: int, min_pixels: int, max_pixels: int) -> tuple[int, int]:
42
+ """
43
+ Rescales the image so that the following conditions are met:
44
+
45
+ 1. Both dimensions (height and width) are divisible by 'factor'.
46
+
47
+ 2. The total number of pixels is within the range ['min_pixels', 'max_pixels'].
48
+
49
+ 3. The aspect ratio of the image is maintained as closely as possible.
50
+ """
51
+ if max(height, width) / min(height, width) > self.max_ratio:
52
+ raise ValueError(
53
+ f"absolute aspect ratio must be smaller than {self.max_ratio}, "
54
+ f"got {max(height, width) / min(height, width)}"
55
+ )
56
+ h_bar = max(factor, self.round_by_factor(height, factor))
57
+ w_bar = max(factor, self.round_by_factor(width, factor))
58
+ if h_bar * w_bar > max_pixels:
59
+ beta = math.sqrt((height * width) / max_pixels)
60
+ h_bar = self.floor_by_factor(height / beta, factor)
61
+ w_bar = self.floor_by_factor(width / beta, factor)
62
+ elif h_bar * w_bar < min_pixels:
63
+ beta = math.sqrt(min_pixels / (height * width))
64
+ h_bar = self.ceil_by_factor(height * beta, factor)
65
+ w_bar = self.ceil_by_factor(width * beta, factor)
66
+ return h_bar, w_bar
67
+
68
+ def process_images(
69
+ self,
70
+ images: List[Image.Image],
71
+ ) -> BatchFeature:
72
+ """
73
+ Process images for ColPali.
74
+ """
75
+ texts_doc = (["<|im_start|>user\n<|vision_start|><|image_pad|><|vision_end|>Describe the image.<|im_end|>\n"]
76
+ * len(images))
77
+
78
+ def resize_and_convert(image: Image.Image) -> Image.Image:
79
+ image_size = image.size
80
+ resized_height, resized_width = self.smart_resize(image_size[1],
81
+ image_size[0],
82
+ factor=self.factor,
83
+ min_pixels=self.min_pixels,
84
+ max_pixels=self.max_pixels)
85
+ # print(f"Resizing image from {image_size} to {(resized_height, resized_width)}")
86
+ return image.convert("RGB").resize((resized_width, resized_height))
87
+
88
+ images = [resize_and_convert(image) for image in images]
89
+
90
+
91
+ batch_doc = self(
92
+ text=texts_doc,
93
+ images=images,
94
+ padding="longest",
95
+ return_tensors="pt"
96
+ )
97
+
98
+
99
+ # The following code is a hack to make sure the scatter in DDP is done correctly when training on multiple GPUs
100
+ offsets = batch_doc["image_grid_thw"][:, 1] * batch_doc["image_grid_thw"][:, 2]
101
+ # separate pixel_values for each image
102
+ pixel_values = torch.split(batch_doc["pixel_values"], offsets.tolist())
103
+ # pad pixel_values to the same length to be able to make it into a tensor
104
+ max_length = max([len(pv) for pv in pixel_values])
105
+ pixel_values = [torch.cat([pv,
106
+ torch.zeros((max_length - len(pv), pv.shape[1]),
107
+ dtype=pv.dtype, device=pv.device)]) for pv in pixel_values]
108
+ batch_doc["pixel_values"] = torch.stack(pixel_values)
109
+
110
+
111
+ return batch_doc
112
+
113
+ def process_queries(
114
+ self,
115
+ queries: List[str],
116
+ max_length: int = 50,
117
+ suffix: Optional[str] = None,
118
+ ) -> BatchFeature:
119
+ """
120
+ Process queries for ColPali.
121
+ """
122
+ if suffix is None:
123
+ suffix = "<pad>" * 10
124
+ texts_query: List[str] = []
125
+
126
+ for query in queries:
127
+ query = f"Query: {query}"
128
+ query += suffix # add suffix (pad tokens)
129
+ texts_query.append(query)
130
+
131
+ batch_query = self(
132
+ text=texts_query,
133
+ return_tensors="pt",
134
+ padding="longest",
135
+ # max_length=max_length + self.image_seq_length,
136
+ )
137
+
138
+ return batch_query
139
+
140
+ def score(
141
+ self,
142
+ qs: List[torch.Tensor],
143
+ ps: List[torch.Tensor],
144
+ device: Optional[Union[str, torch.device]] = None,
145
+ **kwargs,
146
+ ) -> torch.Tensor:
147
+ """
148
+ Compute the MaxSim score (ColBERT-like) for the given multi-vector query and passage embeddings.
149
+ """
150
+ return self.score_multi_vector(qs, ps, device=device, **kwargs)
processor_config.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "auto_map": {
3
+ "AutoProcessor": "processing_colqwen2.ColQwen2Processor"
4
+ },
5
+ "processor_class": "ColQwen2Processor"
6
+ }
tokenizer_config.json CHANGED
@@ -130,7 +130,7 @@
130
  "<|video_pad|>"
131
  ],
132
  "auto_map": {
133
- "AutoProcessor": "manu/colqwen2-v0.1-hf--processing_colqwen2.ColQwen2Processor"
134
  },
135
  "bos_token": null,
136
  "chat_template": "{% set image_count = namespace(value=0) %}{% set video_count = namespace(value=0) %}{% for message in messages %}{% if loop.first and message['role'] != 'system' %}<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n{% endif %}<|im_start|>{{ message['role'] }}\n{% if message['content'] is string %}{{ message['content'] }}<|im_end|>\n{% else %}{% for content in message['content'] %}{% if content['type'] == 'image' or 'image' in content or 'image_url' in content %}{% set image_count.value = image_count.value + 1 %}{% if add_vision_id %}Picture {{ image_count.value }}: {% endif %}<|vision_start|><|image_pad|><|vision_end|>{% elif content['type'] == 'video' or 'video' in content %}{% set video_count.value = video_count.value + 1 %}{% if add_vision_id %}Video {{ video_count.value }}: {% endif %}<|vision_start|><|video_pad|><|vision_end|>{% elif 'text' in content %}{{ content['text'] }}{% endif %}{% endfor %}<|im_end|>\n{% endif %}{% endfor %}{% if add_generation_prompt %}<|im_start|>assistant\n{% endif %}",
 
130
  "<|video_pad|>"
131
  ],
132
  "auto_map": {
133
+ "AutoProcessor": "processing_colqwen2.ColQwen2Processor"
134
  },
135
  "bos_token": null,
136
  "chat_template": "{% set image_count = namespace(value=0) %}{% set video_count = namespace(value=0) %}{% for message in messages %}{% if loop.first and message['role'] != 'system' %}<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n{% endif %}<|im_start|>{{ message['role'] }}\n{% if message['content'] is string %}{{ message['content'] }}<|im_end|>\n{% else %}{% for content in message['content'] %}{% if content['type'] == 'image' or 'image' in content or 'image_url' in content %}{% set image_count.value = image_count.value + 1 %}{% if add_vision_id %}Picture {{ image_count.value }}: {% endif %}<|vision_start|><|image_pad|><|vision_end|>{% elif content['type'] == 'video' or 'video' in content %}{% set video_count.value = video_count.value + 1 %}{% if add_vision_id %}Video {{ video_count.value }}: {% endif %}<|vision_start|><|video_pad|><|vision_end|>{% elif 'text' in content %}{{ content['text'] }}{% endif %}{% endfor %}<|im_end|>\n{% endif %}{% endfor %}{% if add_generation_prompt %}<|im_start|>assistant\n{% endif %}",