Coco_caption_classification / src /data_loader.py
ThaiLearnCoding's picture
Initial clean commit with LFS weights
057041a
Raw History Blame Contribute Delete
4.39 kB
import os
import json
import torch
from torch.utils.data import Dataset, DataLoader
from PIL import Image
import random
from collections import defaultdict
from sklearn.model_selection import train_test_split
class CocoCaptionDataset(Dataset):
def __init__(self, data_list, image_dir, preprocess, n_captions=12):
self.data_list = data_list
self.image_dir = image_dir
self.preprocess = preprocess
self.n_captions = n_captions
def __len__(self):
return len(self.data_list)
def __getitem__(self, idx):
item = self.data_list[idx]
image_id = item['image_id']
image_path = os.path.join(self.image_dir, f"{image_id}.jpg")
image = self.preprocess(Image.open(image_path).convert("RGB"))
# Get all choices and figure out ground truth & distractors
choices = item['choices']
gt_idx_orig = item['ground_truth_idx']
gt_caption = choices[gt_idx_orig]
distractors = [c for i, c in enumerate(choices) if i != gt_idx_orig]
# Sample distractors to match n_captions - 1
num_distractors_needed = self.n_captions - 1
if len(distractors) >= num_distractors_needed:
selected_distractors = random.sample(distractors, num_distractors_needed)
else:
# Handle edge case if not enough distractors
selected_distractors = distractors + [distractors[0]] * (num_distractors_needed - len(distractors))
captions = [gt_caption] + selected_distractors
# Shuffle captions
shuffled_indices = list(range(self.n_captions))
random.shuffle(shuffled_indices)
shuffled_captions = [captions[i] for i in shuffled_indices]
gt_index = shuffled_indices.index(0) # Index of the ground truth in the shuffled list
return image, shuffled_captions, gt_index, image_id, item['true_label']
def create_few_shot_splits(data, n_max_shots=32, seed=42):
class_to_items = defaultdict(list)
for item in data:
class_to_items[item['true_label']].append(item)
train_32_list = []
test_list = []
for cls, items in class_to_items.items():
if len(items) > n_max_shots:
train_cls, test_cls = train_test_split(items, train_size=n_max_shots, random_state=seed)
train_32_list.extend(train_cls)
test_list.extend(test_cls)
else:
print(f"Warning: Class {cls} has only {len(items)} samples, which is less than {n_max_shots}.")
test_list.extend(items)
return train_32_list, test_list
def create_dataloaders(
train_data,
test_data,
image_dir,
preprocess,
batch_size=32,
k=32,
n_captions=12,
num_workers=2,
prev_k=None,
seed=42,
only_new=False,
):
# Sample k shots from the max shots train set
train_k_data = []
class_to_train_items = defaultdict(list)
for item in train_data:
class_to_train_items[item['true_label']].append(item)
rng = random.Random(seed)
for cls, items in class_to_train_items.items():
items_sorted = sorted(items, key=lambda x: x.get('image_id', 0))
if prev_k is None or prev_k <= 0:
if len(items_sorted) >= k:
train_k_data.extend(rng.sample(items_sorted, k))
else:
train_k_data.extend(items_sorted)
continue
prev_k_eff = min(prev_k, len(items_sorted))
prev_samples = rng.sample(items_sorted, prev_k_eff)
if k <= prev_k_eff:
new_samples = []
else:
remaining = [item for item in items_sorted if item not in prev_samples]
new_samples = rng.sample(remaining, min(k - prev_k_eff, len(remaining)))
if only_new:
train_k_data.extend(new_samples)
else:
train_k_data.extend(prev_samples + new_samples)
train_dataset = CocoCaptionDataset(train_k_data, image_dir, preprocess, n_captions)
test_dataset = CocoCaptionDataset(test_data, image_dir, preprocess, n_captions)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=num_workers)
return train_loader, test_loader