共计 3415 个字符,预计需要花费 9 分钟才能阅读完成。
背景痛点分析
CelebA 数据集作为人脸识别领域的基准数据集,包含超过 20 万张名人面部图像。但在实际使用中,开发者常遇到以下问题:

- 下载速度慢:官方源位于香港中文大学服务器,国内直连速度常低于 100KB/s
- 文件校验失败:10GB 的 img_align_celeba.zip 文件在传输中易出现损坏
- 目录结构混乱:原始压缩包解压后包含 3 个子文件夹和 5 个标注文件,手动整理耗时
- 内存瓶颈:直接加载全部图像会导致 32GB 内存机器 OOM(内存不足)
下载方案对比
官方源 vs 镜像源
- 官方源:
- URL:http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html
- 优点:数据版本最新
-
缺点:单线程限速,无断点续传
-
国内镜像:
- 阿里云镜像:https://mirror.alibaba.com/celeba
- 百度网盘:提取码需关注公众号获取
- 优点:下载速度可达 10MB/s+
- 缺点:可能存在版本滞后问题
下载工具性能测试(1GB 测试文件)
| 工具 | 平均速度 | 断点续传 | 线程支持 |
|---|---|---|---|
| wget | 2.1MB/s | 支持 | 单线程 |
| aria2c | 8.7MB/s | 支持 | 16 线程 |
| IDM | 6.4MB/s | 支持 | 8 线程 |
核心实现
带校验的下载脚本
import hashlib
import requests
from pathlib import Path
from tqdm import tqdm
def download_with_checksum(url: str, target_path: Path, md5: str = None):
"""
带 MD5 校验的下载函数
:param url: 下载链接
:param target_path: 保存路径
:param md5: 预期 MD5 值(可选)"""
try:
# 创建父目录
target_path.parent.mkdir(parents=True, exist_ok=True)
# 断点续传
if target_path.exists():
current_size = target_path.stat().st_size
headers = {'Range': f'bytes={current_size}-'}
else:
current_size = 0
headers = {}
with requests.get(url, stream=True, headers=headers) as r, \
open(target_path, 'ab') as f, \
tqdm(unit='B', unit_scale=True, unit_divisor=1024,
initial=current_size, total=int(r.headers.get('content-length', 0)) + current_size) as pbar:
for chunk in r.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
pbar.update(len(chunk))
# MD5 校验
if md5:
file_md5 = hashlib.md5(open(target_path,'rb').read()).hexdigest()
assert file_md5 == md5, f"MD5 校验失败: {file_md5} != {md5}"
except Exception as e:
if target_path.exists():
target_path.unlink()
raise RuntimeError(f"下载失败: {str(e)}")
人脸 ROI 裁剪示例
import cv2
import numpy as np
# CelebA 标注格式: [x1, y1, width, height]
def crop_face(image_path: str, bbox: list, margin: float = 0.2):
"""
带边缘扩展的人脸裁剪
:param image_path: 图像路径
:param bbox: [x, y, w, h]格式的边界框
:param margin: 扩展比例(0.2 表示扩展 20%):return: 裁剪后的 RGB 图像
"""
img = cv2.imread(image_path)
if img is None:
raise ValueError(f"无法读取图像: {image_path}")
h, w = img.shape[:2]
x, y, bw, bh = bbox
# 计算扩展后的区域
new_x = max(0, x - margin * bw)
new_y = max(0, y - margin * bh)
new_w = min(w - new_x, bw * (1 + 2 * margin))
new_h = min(h - new_y, bh * (1 + 2 * margin))
cropped = img[int(new_y):int(new_y + new_h),
int(new_x):int(new_x + new_w)]
return cv2.cvtColor(cropped, cv2.COLOR_BGR2RGB)
性能优化
存储格式对比
| 格式 | 加载速度 | 磁盘占用 | 随机访问 | 适用场景 |
|---|---|---|---|---|
| 原始 JPEG | 慢 | 小 | 差 | 初步实验 |
| HDF5 | 快 | 中 | 优秀 | 中型数据集 |
| TFRecord | 最快 | 大 | 优秀 | TensorFlow 训练 |
| LMDB | 快 | 小 | 优秀 | 超大规模数据集 |
多进程预处理模板
from multiprocessing import Pool
from functools import partial
def process_single_image(args, config):
"""单个图像的处理函数"""
img_path, bbox = args
try:
img = crop_face(img_path, bbox)
# 其他预处理...
return img
except Exception as e:
print(f"处理失败 {img_path}: {str(e)}")
return None
def batch_processing(image_list: list, workers: int = 8):
"""批量处理入口"""
with Pool(workers) as p:
# 使用 partial 固定配置参数
processor = partial(process_single_image, config={"size": 256})
results = list(tqdm(p.imap(processor, image_list), total=len(image_list)))
return [r for r in results if r is not None]
避坑指南
标注文件编码问题
CelebA 的 list_attr_celeba.txt 使用 UTF- 8 编码,但部分操作系统可能误判为 ASCII。解决方案:
# 正确读取方式
with open('list_attr_celeba.txt', 'r', encoding='utf-8') as f:
header = f.readline() # 第一行是标题
for line in f:
parts = line.strip().split()
filename = parts[0]
attrs = list(map(int, parts[1:])) # 属性值为 1 或 -1
内存优化技巧
-
生成器加载:
def image_generator(file_list, batch_size=32): """分批生成图像数据""" for i in range(0, len(file_list), batch_size): batch = file_list[i:i + batch_size] yield [load_image(f) for f in batch] -
内存映射文件:
import numpy as np # 将预处理好的数据保存为内存映射格式 arr = np.memmap('dataset.dat', dtype='float32', mode='w+', shape=(202599, 256, 256, 3)) for i, img in enumerate(image_generator()): arr[i] = img
结语
通过本文介绍的方法,应该能帮助大家高效完成 CelebA 数据集的下载和预处理工作。在实际项目中,建议根据硬件条件和框架特性选择合适的存储格式。例如使用 PyTorch 时,可以优先考虑 LMDB 格式;而 TensorFlow 项目则更适合 TFRecord。
如果你有更好的预处理技巧或遇到文中未覆盖的问题,欢迎在评论区分享你的实践经验。对于大规模人脸识别项目,推荐进一步研究以下优化方向:
- 使用 DALI 等 GPU 加速数据管道
- 实现实时数据增强
- 构建缓存机制避免重复预处理
正文完
