Lumina_Dev_Legacy / src /data /preprocessing.py
TAI Research
Initial commit: Lumina_Dev_Legacy (archived)
29691f6
Raw
History Blame Contribute Delete
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()