3. 构建数据加载器
任务目标
本案例实现一个PyTorch数据加载器,用于端到端自动驾驶模仿学习。该加载器从磁盘高效批量读取前置摄像头图像及其对应的转向角、油门和刹车标签,并输出为模型训练可直接使用的张量批次。
数据集约定
假设数据按以下目录结构存放,每个样本包含一张图像和一个标签文件(JSON格式):
dataset/
├── images/
│ ├── 00001.jpg
│ ├── 00002.jpg
│ └── ...
└── labels/
├── 00001.json
├── 00002.json
└── ...
每个JSON文件内容示例:{"steer": 0.12, "throttle": 0.65, "brake": 0.0}
完整实现
以下代码实现了一个可配置的PyTorch Dataset与DataLoader组合,支持图像缩放、归一化、标签归一化,并使用多进程预取加速数据流水线。
import json
import os
from pathlib import Path
import torch
from torch.utils.data import Dataset, DataLoader
from PIL import Image
import torchvision.transforms as T
class DrivingDataset(Dataset):
"""端到端驾驶数据集:加载图像 + 转向/油门/刹车标签"""
def __init__(self, root_dir, img_size=(160, 320), normalize_labels=True):
self.img_dir = Path(root_dir) / "images"
self.label_dir = Path(root_dir) / "labels"
self.img_size = img_size
self.normalize_labels = normalize_labels
# 按文件名排序保证顺序一致
self.img_files = sorted(self.img_dir.glob("*.jpg"))
self.label_files = sorted(self.label_dir.glob("*.json"))
assert len(self.img_files) == len(self.label_files), \
f"图像({len(self.img_files)})与标签({len(self.label_files)})数量不匹配"
# 图像变换:缩放 + 转张量 + 归一化(ImageNet统计)
self.transform = T.Compose([
T.Resize(img_size),
T.ToTensor(),
T.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
def __len__(self):
return len(self.img_files)
def __getitem__(self, idx):
# 加载图像
img_path = self.img_files[idx]
image = Image.open(img_path).convert("RGB")
image = self.transform(image)
# 加载标签
label_path = self.label_files[idx]
with open(label_path, "r") as f:
label_data = json.load(f)
steer = torch.tensor(label_data["steer"], dtype=torch.float32)
throttle = torch.tensor(label_data["throttle"], dtype=torch.float32)
brake = torch.tensor(label_data["brake"], dtype=torch.float32)
if self.normalize_labels:
# 转向角归一化到[-1, 1](假设原始范围约[-1, 1])
steer = steer.clamp(-1.0, 1.0)
# 油门/刹车归一化到[0, 1](假设原始范围[0, 1])
throttle = throttle.clamp(0.0, 1.0)
brake = brake.clamp(0.0, 1.0)
# 拼接为单一标签向量 [steer, throttle, brake]
labels = torch.stack([steer, throttle, brake])
return image, labels
def create_dataloader(root_dir, batch_size=32, shuffle=True,
num_workers=4, img_size=(160, 320)):
"""创建训练/验证数据加载器"""
dataset = DrivingDataset(root_dir, img_size=img_size)
loader = DataLoader(
dataset,
batch_size=batch_size,
shuffle=shuffle,
num_workers=num_workers,
pin_memory=True, # GPU训练时加速主机到设备传输
drop_last=False
)
return loader
# 最小调用示例
if __name__ == "__main__":
loader = create_dataloader("./dataset", batch_size=8, num_workers=2)
images, labels = next(iter(loader))
print(f"图像批次形状: {images.shape}") # [8, 3, 160, 320]
print(f"标签批次形状: {labels.shape}") # [8, 3]
print(f"标签范围: steer [{labels[:,0].min():.2f}, {labels[:,0].max():.2f}]")
运行与验证
依赖安装:
pip install torch torchvision pillow
准备最小测试数据:创建dataset/images/与dataset/labels/,放入至少2对同名.jpg/.json文件即可运行。
执行验证:
python driving_loader.py
通过条件:程序无异常退出,输出图像批次形状为[batch_size, 3, 160, 320],标签批次形状为[batch_size, 3],且标签数值在合理范围内(steer∈[-1,1],throttle/brake∈[0,1])。
关键说明
- 图像尺寸默认
160×320(高×宽),可根据模型输入调整img_size参数。 - 标签归一化采用截断方式,不改变原始分布,仅保证数值范围有效。
- 若需加载灰度图或不同图像格式,修改
glob("*.jpg")与convert("RGB")即可。 - 实际训练时建议将
num_workers设为CPU核心数,并启用pin_memory=True以提升GPU传输效率。