Download src/data/dataset.py from TAI-Research/Lumina_Dev_Legacy: direct link, hf CLI and curl.
- Browser
- Download file 10.7 kB
-
https://huggingface.co/TAI-Research/Lumina_Dev_Legacy/resolve/main/src/data/dataset.py
- Command line
-
hf download hf://TAI-Research/Lumina_Dev_Legacy/src/data/dataset.py
-
curl -L -o dataset.py https://huggingface.co/TAI-Research/Lumina_Dev_Legacy/resolve/main/src/data/dataset.py
10.7 kB
| import torch | |
| from torch.utils.data import Dataset, DataLoader | |
| from PIL import Image | |
| import pandas as pd | |
| import os | |
| from typing import Dict, List, Optional, Tuple | |
| import numpy as np | |
| import json | |
| from pathlib import Path | |
| class LAIONDataset(Dataset): | |
| """LAION数据集""" | |
| def __init__(self, config: dict, transform=None, split: str = 'train'): | |
| self.config = config | |
| self.transform = transform | |
| self.split = split | |
| # 加载元数据 | |
| metadata_path = config['dataset'].get('metadata_file', './data/laion/metadata.parquet') | |
| self.metadata = pd.read_parquet(metadata_path) | |
| # 应用过滤条件 | |
| self._apply_filters(config.get('filters', {})) | |
| # 数据拆分 | |
| self._split_data(config.get('split', {})) | |
| # 缓存 | |
| self.use_cache = config.get('use_cache', False) | |
| self.cache_dir = config.get('cache_dir', './data/cache') | |
| if self.use_cache: | |
| os.makedirs(self.cache_dir, exist_ok=True) | |
| # 限制样本数 | |
| max_samples = config.get('max_samples', None) | |
| if max_samples is not None and len(self.metadata) > max_samples: | |
| self.metadata = self.metadata.sample(max_samples, random_state=42) | |
| # 文本缓存 | |
| self.text_cache = {} | |
| print(f"数据集加载完成: {len(self.metadata)} 个样本 ({split}集)") | |
| def _apply_filters(self, filters: dict): | |
| """应用过滤条件""" | |
| if 'aesthetic_score' in filters: | |
| threshold = filters['aesthetic_score'] | |
| if 'aesthetic_score' in self.metadata.columns: | |
| self.metadata = self.metadata[self.metadata['aesthetic_score'] >= threshold] | |
| if 'watermark_prob' in filters: | |
| threshold = filters['watermark_prob'] | |
| if 'watermark_prob' in self.metadata.columns: | |
| self.metadata = self.metadata[self.metadata['watermark_prob'] <= threshold] | |
| if 'nsfw' in filters and not filters['nsfw']: | |
| if 'NSFW' in self.metadata.columns: | |
| self.metadata = self.metadata[self.metadata['NSFW'] != 'NSFW'] | |
| def _split_data(self, split_config: dict): | |
| """拆分数据集""" | |
| if self.split not in ['train', 'val']: | |
| return | |
| train_ratio = split_config.get('train', 0.95) | |
| val_ratio = split_config.get('val', 0.05) | |
| # 确保拆分比例之和为1 | |
| total = train_ratio + val_ratio | |
| train_ratio /= total | |
| val_ratio /= total | |
| # 随机拆分 | |
| seed = split_config.get('seed', 42) | |
| shuffled = self.metadata.sample(frac=1, random_state=seed).reset_index(drop=True) | |
| if self.split == 'train': | |
| split_point = int(len(shuffled) * train_ratio) | |
| self.metadata = shuffled[:split_point] | |
| else: | |
| split_point = int(len(shuffled) * train_ratio) | |
| self.metadata = shuffled[split_point:] | |
| def __len__(self) -> int: | |
| return len(self.metadata) | |
| def _get_image_path(self, row) -> str: | |
| """获取图像路径""" | |
| # 尝试不同的列名 | |
| for col in ['image_file', 'filepath', 'path', 'url_local']: | |
| if col in row: | |
| path = row[col] | |
| # 如果是相对路径,添加基础路径 | |
| if not os.path.isabs(path): | |
| base_path = self.config['dataset'].get('path', './data/laion') | |
| path = os.path.join(base_path, path) | |
| return path | |
| # 如果没有找到路径,使用URL哈希 | |
| if 'url' in row: | |
| import hashlib | |
| url_hash = hashlib.md5(row['url'].encode()).hexdigest() | |
| base_path = self.config['dataset'].get('path', './data/laion') | |
| path = os.path.join(base_path, f"{url_hash}.jpg") | |
| return path | |
| raise ValueError(f"无法找到图像路径: {row}") | |
| def __getitem__(self, idx: int) -> Dict: | |
| row = self.metadata.iloc[idx] | |
| # 缓存键 | |
| cache_key = f"{self.split}_{idx}" | |
| # 检查缓存 | |
| if self.use_cache and cache_key in self.text_cache: | |
| text_embedding = self.text_cache[cache_key] | |
| else: | |
| # 获取文本描述 | |
| text = row.get('caption', row.get('text', row.get('description', ''))) | |
| # 这里应该调用文本编码器,但为了简化,我们返回原始文本 | |
| # 在实际使用中,应该使用预训练的CLIP编码器 | |
| text_embedding = text | |
| # 缓存 | |
| if self.use_cache: | |
| self.text_cache[cache_key] = text_embedding | |
| # 获取图像 | |
| try: | |
| image_path = self._get_image_path(row) | |
| image = Image.open(image_path).convert('RGB') | |
| # 应用变换 | |
| if self.transform: | |
| image = self.transform(image) | |
| except Exception as e: | |
| # 如果图像加载失败,返回一个空白图像 | |
| print(f"加载图像失败 {image_path}: {e}") | |
| image = torch.zeros(3, 512, 512) | |
| text = "invalid image" | |
| text_embedding = text | |
| return { | |
| 'image': image, | |
| 'text': text_embedding if isinstance(text_embedding, str) else '', | |
| 'text_embedding': text_embedding if not isinstance(text_embedding, str) else None, | |
| 'image_path': image_path if 'image_path' in locals() else '', | |
| 'index': idx | |
| } | |
| class TextImageDataset(Dataset): | |
| """文本-图像对数据集""" | |
| def __init__(self, image_dir: str, caption_file: str, transform=None): | |
| self.image_dir = image_dir | |
| self.transform = transform | |
| # 加载标注文件 | |
| if caption_file.endswith('.json'): | |
| with open(caption_file, 'r') as f: | |
| self.captions = json.load(f) | |
| elif caption_file.endswith('.csv'): | |
| self.captions = pd.read_csv(caption_file) | |
| else: | |
| raise ValueError(f"不支持的标注文件格式: {caption_file}") | |
| # 验证图像文件是否存在 | |
| self.valid_samples = [] | |
| for item in self.captions: | |
| if isinstance(item, dict): | |
| image_name = item.get('image_name', item.get('file_name', '')) | |
| caption = item.get('caption', '') | |
| else: | |
| image_name = item[0] | |
| caption = item[1] | |
| image_path = os.path.join(self.image_dir, image_name) | |
| if os.path.exists(image_path): | |
| self.valid_samples.append((image_path, caption)) | |
| print(f"找到 {len(self.valid_samples)} 个有效样本") | |
| def __len__(self) -> int: | |
| return len(self.valid_samples) | |
| def __getitem__(self, idx: int) -> Dict: | |
| image_path, caption = self.valid_samples[idx] | |
| # 加载图像 | |
| image = Image.open(image_path).convert('RGB') | |
| # 应用变换 | |
| if self.transform: | |
| image = self.transform(image) | |
| return { | |
| 'image': image, | |
| 'text': caption, | |
| 'image_path': image_path | |
| } | |
| class CachedDataset(Dataset): | |
| """缓存数据集,加速训练""" | |
| def __init__(self, dataset: Dataset, cache_dir: str = './cache'): | |
| self.dataset = dataset | |
| self.cache_dir = cache_dir | |
| os.makedirs(cache_dir, exist_ok=True) | |
| self.cache_files = [] | |
| for i in range(len(dataset)): | |
| cache_file = os.path.join(cache_dir, f'sample_{i}.pt') | |
| self.cache_files.append(cache_file) | |
| def __len__(self) -> int: | |
| return len(self.dataset) | |
| def __getitem__(self, idx: int) -> Dict: | |
| cache_file = self.cache_files[idx] | |
| # 如果缓存存在,直接加载 | |
| if os.path.exists(cache_file): | |
| try: | |
| return torch.load(cache_file) | |
| except: | |
| pass | |
| # 否则,从原始数据集加载并缓存 | |
| sample = self.dataset[idx] | |
| torch.save(sample, cache_file) | |
| return sample | |
| def create_data_loaders(config: dict) -> Tuple[DataLoader, Optional[DataLoader]]: | |
| """创建数据加载器""" | |
| from .preprocessing import get_transform | |
| # 获取数据变换 | |
| train_transform = get_transform(config, mode='train') | |
| val_transform = get_transform(config, mode='val') | |
| # 创建数据集 | |
| train_dataset = LAIONDataset(config, transform=train_transform, split='train') | |
| val_dataset = LAIONDataset(config, transform=val_transform, split='val') | |
| # 可选:启用缓存 | |
| if config.get('cache_dataset', True): | |
| train_dataset = CachedDataset(train_dataset, cache_dir='./data/cache/train') | |
| val_dataset = CachedDataset(val_dataset, cache_dir='./data/cache/val') | |
| # 创建数据加载器 | |
| train_loader = DataLoader( | |
| train_dataset, | |
| batch_size=config.get('batch_size', 1), | |
| shuffle=config.get('shuffle', True), | |
| num_workers=config.get('num_workers', 2), | |
| pin_memory=config.get('pin_memory', True), | |
| prefetch_factor=config.get('prefetch_factor', 2), | |
| persistent_workers=config.get('persistent_workers', True) | |
| ) | |
| val_loader = DataLoader( | |
| val_dataset, | |
| batch_size=1, # 验证时批次大小为1 | |
| shuffle=False, | |
| num_workers=config.get('num_workers', 2), | |
| pin_memory=True | |
| ) | |
| return train_loader, val_loader | |
| def test_dataset(): | |
| """测试数据集""" | |
| import yaml | |
| # 加载配置 | |
| with open('configs/data/laion_filtered.yaml', 'r') as f: | |
| config = yaml.safe_load(f) | |
| # 创建数据集 | |
| dataset = LAIONDataset(config, split='train') | |
| # 测试样本 | |
| sample = dataset[0] | |
| print(f"样本键: {list(sample.keys())}") | |
| print(f"图像形状: {sample['image'].shape if hasattr(sample['image'], 'shape') else type(sample['image'])}") | |
| print(f"文本: {sample['text'][:100]}...") | |
| return dataset | |
| if __name__ == '__main__': | |
| test_dataset() |