Files
wehub-resource-sync 542cfa195c
CI / Frontend build (push) Failing after 9m6s
CI / Plugin validate (push) Failing after 9m27s
CI / Python lint (push) Failing after 16m1s
CI / Tests (push) Successful in 18m0s
Deploy / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:27 +08:00

105 lines
3.2 KiB
Python

#!/usr/bin/env python3
"""Dataset and DataLoader for query-chunk pair training."""
import json
import logging
from PIL import Image
from torch.utils.data import Dataset
logger = logging.getLogger(__name__)
# Task-specific instructions (Qwen3-VL-Embedding style)
# Query instruction describes the retrieval task; document instruction is generic.
QUERY_INSTRUCTION = "Retrieve images or text relevant to the user's query."
DOC_INSTRUCTION = "Represent the user's input."
class QueryChunkDataset(Dataset):
"""Dataset of (query, chunk_path) pairs from JSONL."""
def __init__(self, jsonl_path: str):
self.pairs = []
with open(jsonl_path) as f:
for line in f:
item = json.loads(line)
self.pairs.append((item["query"], item["chunk_path"]))
logger.info(f"Loaded {len(self.pairs)} pairs from {jsonl_path}")
def __len__(self):
return len(self.pairs)
def __getitem__(self, idx):
return self.pairs[idx]
def make_collate_fn(processor, device="cuda"):
"""Create a collate function that preprocesses queries and images.
Returns None for batches where all images fail to load.
Uses the same chat template as the production embedding pipeline.
"""
def collate(batch):
valid = []
for query, path in batch:
try:
img = Image.open(path).convert("RGB")
valid.append((query, path, img))
except Exception as e:
logger.warning(f"Skipping bad image {path}: {e}")
if not valid:
return None
queries, paths, images = zip(*valid)
# Build query inputs using chat template (text-only)
q_messages_batch = [
[
{
"role": "system",
"content": [{"type": "text", "text": QUERY_INSTRUCTION}],
},
{"role": "user", "content": [{"type": "text", "text": q}]},
]
for q in queries
]
q_texts = [
processor.apply_chat_template(m, tokenize=False, add_generation_prompt=True)
for m in q_messages_batch
]
q_inputs = processor(text=q_texts, return_tensors="pt", padding=True)
q_inputs = {
k: v.to(device) if hasattr(v, "to") else v for k, v in q_inputs.items()
}
# Build image inputs using chat template (image)
i_messages_batch = [
[
{
"role": "system",
"content": [{"type": "text", "text": DOC_INSTRUCTION}],
},
{"role": "user", "content": [{"type": "image", "image": img}]},
]
for img in images
]
i_texts = [
processor.apply_chat_template(m, tokenize=False, add_generation_prompt=True)
for m in i_messages_batch
]
i_inputs = processor(
text=i_texts,
images=list(images),
return_tensors="pt",
padding=True,
device=device,
)
i_inputs = {
k: v.to(device) if hasattr(v, "to") else v for k, v in i_inputs.items()
}
return q_inputs, i_inputs
return collate