Download src/data/preprocessing.py from TAI-Research/Lumina_Dev_Legacy: direct link, hf CLI and curl.
- Browser
- Download file 10.8 kB
-
https://huggingface.co/TAI-Research/Lumina_Dev_Legacy/resolve/main/src/data/preprocessing.py
- Command line
-
hf download hf://TAI-Research/Lumina_Dev_Legacy/src/data/preprocessing.py
-
curl -L -o preprocessing.py https://huggingface.co/TAI-Research/Lumina_Dev_Legacy/resolve/main/src/data/preprocessing.py
10.8 kB
| import torch | |
| import torchvision.transforms as T | |
| from PIL import Image | |
| import numpy as np | |
| from typing import Dict, List, Optional, Tuple | |
| import random | |
| def get_transform(config: dict, mode: str = 'train') -> T.Compose: | |
| """获取数据预处理变换""" | |
| preprocessing = config.get('preprocessing', {}) | |
| target_size = preprocessing.get('target_size', 512) | |
| resize_mode = preprocessing.get('resize_mode', 'center_crop') | |
| transforms_list = [] | |
| # 训练和验证的不同变换 | |
| if mode == 'train': | |
| # 随机裁剪 | |
| if preprocessing.get('random_crop', True): | |
| transforms_list.append(T.RandomResizedCrop( | |
| target_size, | |
| scale=(0.8, 1.0), | |
| ratio=(0.8, 1.2) | |
| )) | |
| else: | |
| transforms_list.append(T.Resize(target_size, interpolation=T.InterpolationMode.BILINEAR)) | |
| # 随机水平翻转 | |
| if preprocessing.get('random_flip', True): | |
| transforms_list.append(T.RandomHorizontalFlip(p=0.5)) | |
| # 颜色抖动 | |
| if preprocessing.get('color_jitter', 0.05) > 0: | |
| color_jitter = preprocessing['color_jitter'] | |
| transforms_list.append(T.ColorJitter( | |
| brightness=color_jitter, | |
| contrast=color_jitter, | |
| saturation=color_jitter, | |
| hue=min(0.1, color_jitter) | |
| )) | |
| # 随机旋转 | |
| if preprocessing.get('random_rotation', 0.0) > 0: | |
| max_angle = preprocessing['random_rotation'] | |
| transforms_list.append(T.RandomRotation(degrees=(-max_angle, max_angle))) | |
| else: # 验证/测试模式 | |
| # 中心裁剪 | |
| if resize_mode == 'center_crop': | |
| transforms_list.extend([ | |
| T.Resize(target_size, interpolation=T.InterpolationMode.BILINEAR), | |
| T.CenterCrop(target_size) | |
| ]) | |
| elif resize_mode == 'resize': | |
| transforms_list.append(T.Resize(target_size, interpolation=T.InterpolationMode.BILINEAR)) | |
| elif resize_mode == 'random_crop': | |
| transforms_list.append(T.RandomCrop(target_size)) | |
| else: | |
| raise ValueError(f"未知的resize_mode: {resize_mode}") | |
| # 转换为Tensor | |
| transforms_list.append(T.ToTensor()) | |
| # 归一化 | |
| normalize_config = preprocessing.get('normalize', {}) | |
| mean = normalize_config.get('mean', [0.5, 0.5, 0.5]) | |
| std = normalize_config.get('std', [0.5, 0.5, 0.5]) | |
| transforms_list.append(T.Normalize(mean=mean, std=std)) | |
| return T.Compose(transforms_list) | |
| class TextPreprocessor: | |
| """文本预处理器""" | |
| def __init__(self, config: dict): | |
| self.config = config.get('preprocessing', {}) | |
| # 文本处理参数 | |
| self.max_length = self.config.get('max_length', 77) | |
| self.truncation = self.config.get('truncation', True) | |
| self.padding = self.config.get('padding', 'max_length') | |
| # 尝试加载tokenizer | |
| self.tokenizer = None | |
| self._init_tokenizer() | |
| def _init_tokenizer(self): | |
| """初始化tokenizer""" | |
| try: | |
| from transformers import CLIPTokenizer | |
| tokenizer_name = self.config.get('tokenizer', 'openai/clip-vit-base-patch32') | |
| self.tokenizer = CLIPTokenizer.from_pretrained(tokenizer_name) | |
| except ImportError: | |
| print("警告: 未安装transformers,无法使用CLIP tokenizer") | |
| except Exception as e: | |
| print(f"加载tokenizer失败: {e}") | |
| def preprocess_text(self, text: str) -> Dict: | |
| """预处理文本""" | |
| if self.tokenizer is not None: | |
| # 使用CLIP tokenizer | |
| inputs = self.tokenizer( | |
| text, | |
| max_length=self.max_length, | |
| padding=self.padding, | |
| truncation=self.truncation, | |
| return_tensors="pt" | |
| ) | |
| return { | |
| 'input_ids': inputs['input_ids'].squeeze(0), | |
| 'attention_mask': inputs['attention_mask'].squeeze(0) | |
| } | |
| else: | |
| # 简单的文本处理 | |
| return { | |
| 'text': text, | |
| 'length': len(text) | |
| } | |
| def batch_preprocess(self, texts: List[str]) -> Dict: | |
| """批量预处理文本""" | |
| if self.tokenizer is not None: | |
| inputs = self.tokenizer( | |
| texts, | |
| max_length=self.max_length, | |
| padding=self.padding, | |
| truncation=self.truncation, | |
| return_tensors="pt" | |
| ) | |
| return inputs | |
| else: | |
| return {'texts': texts} | |
| class ImagePreprocessor: | |
| """图像预处理器""" | |
| def __init__(self, config: dict): | |
| self.config = config.get('preprocessing', {}) | |
| self.transform = get_transform(config, mode='train') | |
| def preprocess_image(self, image: Image.Image) -> torch.Tensor: | |
| """预处理单张图像""" | |
| return self.transform(image) | |
| def batch_preprocess(self, images: List[Image.Image]) -> torch.Tensor: | |
| """批量预处理图像""" | |
| return torch.stack([self.transform(img) for img in images]) | |
| def preprocess_for_vae(self, image: torch.Tensor) -> torch.Tensor: | |
| """为VAE编码预处理图像""" | |
| # VAE期望输入在[-1, 1]范围内 | |
| return image * 2.0 - 1.0 | |
| def postprocess_from_vae(self, latents: torch.Tensor) -> torch.Tensor: | |
| """从VAE解码后处理图像""" | |
| # 将[-1, 1]范围转换回[0, 1] | |
| return (latents + 1.0) / 2.0 | |
| class DataPreprocessor: | |
| """数据预处理器(整合文本和图像处理)""" | |
| def __init__(self, config: dict): | |
| self.config = config | |
| self.image_preprocessor = ImagePreprocessor(config) | |
| self.text_preprocessor = TextPreprocessor(config) | |
| # 文本编码器 | |
| self.text_encoder = None | |
| self._init_text_encoder() | |
| def _init_text_encoder(self): | |
| """初始化文本编码器""" | |
| try: | |
| from transformers import CLIPTextModel | |
| model_name = self.config.get('preprocessing', {}).get('text_encoder', 'openai/clip-vit-base-patch32') | |
| self.text_encoder = CLIPTextModel.from_pretrained(model_name) | |
| # 冻结参数 | |
| for param in self.text_encoder.parameters(): | |
| param.requires_grad = False | |
| # 设置为评估模式 | |
| self.text_encoder.eval() | |
| print(f"已加载文本编码器: {model_name}") | |
| except Exception as e: | |
| print(f"加载文本编码器失败: {e}") | |
| def encode_text(self, text: str) -> torch.Tensor: | |
| """编码文本为嵌入向量""" | |
| if self.text_encoder is None: | |
| raise ValueError("文本编码器未初始化") | |
| # 预处理文本 | |
| inputs = self.text_preprocessor.preprocess_text(text) | |
| # 编码 | |
| with torch.no_grad(): | |
| if 'input_ids' in inputs: | |
| outputs = self.text_encoder( | |
| input_ids=inputs['input_ids'].unsqueeze(0), | |
| attention_mask=inputs['attention_mask'].unsqueeze(0) if 'attention_mask' in inputs else None | |
| ) | |
| return outputs.last_hidden_state.squeeze(0) | |
| else: | |
| # 回退到简单的嵌入 | |
| return torch.randn(77, 768) # 默认维度 | |
| def batch_encode_text(self, texts: List[str]) -> torch.Tensor: | |
| """批量编码文本""" | |
| if self.text_encoder is None: | |
| raise ValueError("文本编码器未初始化") | |
| # 预处理文本 | |
| inputs = self.text_preprocessor.batch_preprocess(texts) | |
| # 编码 | |
| with torch.no_grad(): | |
| if 'input_ids' in inputs: | |
| outputs = self.text_encoder( | |
| input_ids=inputs['input_ids'], | |
| attention_mask=inputs.get('attention_mask', None) | |
| ) | |
| return outputs.last_hidden_state | |
| else: | |
| # 回退到简单的嵌入 | |
| batch_size = len(texts) | |
| return torch.randn(batch_size, 77, 768) | |
| def preprocess_batch(self, batch: List[Dict]) -> Dict: | |
| """预处理批次数据""" | |
| images = [item['image'] for item in batch] | |
| texts = [item['text'] for item in batch] | |
| # 预处理图像 | |
| image_tensors = self.image_preprocessor.batch_preprocess(images) | |
| # 编码文本 | |
| if self.text_encoder is not None: | |
| text_embeddings = self.batch_encode_text(texts) | |
| else: | |
| text_embeddings = None | |
| return { | |
| 'images': image_tensors, | |
| 'text_embeddings': text_embeddings, | |
| 'texts': texts, | |
| 'image_paths': [item.get('image_path', '') for item in batch] | |
| } | |
| def test_preprocessing(): | |
| """测试预处理""" | |
| import yaml | |
| from PIL import Image | |
| # 创建测试图像 | |
| test_image = Image.new('RGB', (512, 512), color='red') | |
| # 加载配置 | |
| with open('configs/data/laion_filtered.yaml', 'r') as f: | |
| config = yaml.safe_load(f) | |
| # 测试图像预处理 | |
| image_preprocessor = ImagePreprocessor(config) | |
| processed_image = image_preprocessor.preprocess_image(test_image) | |
| print(f"原始图像: {test_image.size}") | |
| print(f"处理后图像形状: {processed_image.shape}") | |
| # 测试文本预处理 | |
| text_preprocessor = TextPreprocessor(config) | |
| processed_text = text_preprocessor.preprocess_text("A red square image") | |
| print(f"文本处理结果: {processed_text}") | |
| # 测试数据预处理器 | |
| data_preprocessor = DataPreprocessor(config) | |
| test_batch = [ | |
| {'image': test_image, 'text': "A red square"}, | |
| {'image': test_image, 'text': "A blue circle"} | |
| ] | |
| processed_batch = data_preprocessor.preprocess_batch(test_batch) | |
| print(f"批次图像形状: {processed_batch['images'].shape}") | |
| print(f"文本嵌入形状: {processed_batch['text_embeddings'].shape if processed_batch['text_embeddings'] is not None else 'None'}") | |
| return processed_batch | |
| if __name__ == '__main__': | |
| test_preprocessing() |