dataloader加载大图易oom,应改用滑动窗口切片dataset:预存切片坐标,__getitem__返回块及位置,collate_fn需统一尺寸,拼接时推荐加权平均。

大图直接送进DataLoader会爆显存?
PyTorch的DataLoader默认按batch加载完整图像,如果单张图尺寸是4000×3000 RGB,哪怕batch_size=1,也可能瞬间占满16GB显存——尤其模型本身参数量不小的时候。这不是数据加载慢的问题,是根本加载不进去。
根本原因在于:显存消耗 ≈ 图像张量大小 × 模型中间激活 × batch_size。大图会让tensor本身体积爆炸,比如torch.float32下一张4000×3000×3图就占约135MB,还没算梯度和特征图。
- 别指望靠调小
batch_size=1硬扛——单图太大,依然OOM - 别在
Dataset.__getitem__里用cv2.resize暴力缩放——会破坏细节,尤其医学/遥感场景不可接受 - 切片必须在CPU上完成,GPU只负责小块推理/训练;否则切片操作本身也吃显存
怎么写一个支持滑动窗口切片的Dataset?
核心是把“读整图→切块→返回块”逻辑封装进Dataset,让DataLoader看到的仍是标准的__len__和__getitem__接口,但背后已不是原始图像粒度。
关键点:切片索引要映射回原图坐标,方便后续拼接或loss计算(比如分割任务需还原mask位置)。
- 在
__init__中预计算所有切片的(x_start, y_start, x_end, y_end),存为self.patch_list -
__len__返回len(self.patch_list),不是原图数量 -
__getitem__根据index取出对应区域,用img[y_start:y_end, x_start:x_end]切片(注意PIL/CV2通道顺序) - 记得在返回时附带位置信息,比如
return patch_tensor, (x_start, y_start)
示例片段:
patch = img[:, y:y+h, x:x+w] # 假设img是C×H×W tensor
collate_fn怎么避免切片后shape不一致?
不同位置的切片可能因原图边缘被截断而尺寸略小(比如最后几块不足patch_size),default_collate会直接报错stack expects each tensor to be equal size。
- 要么在切片时强制补零/补边(
F.pad),保证所有块严格等尺寸 - 要么自定义
collate_fn,用torch.stack前先检查并统一裁剪/填充 - 更稳妥的做法:切片时避开边缘,即只取能完整容纳
patch_size的区域,用step = patch_size而非step = 1 - 如果任务允许,也可改用
torchvision.transforms.CenterCrop做中心裁剪,但会丢失大量上下文
训练完如何把小块结果拼回原图?
切片推理容易,拼回去才是坑多的地方:重叠区域怎么融合?边界伪影怎么抑制?类别概率图怎么加权平均?
- 最简方式:非重叠切片 + 简单拼接(
torch.cat沿H/W维度),适用于检测框回归等任务 - 重叠切片推荐用“加权平均”,给每个像素赋予权重(如高斯衰减),再用
torch.scatter_add累加预测值和权重 - 千万别直接覆盖拼接——边缘块的预测质量通常较差,硬拼会产生明显接缝
- 验证时建议先用一张图全程CPU跑通拼接逻辑,再上GPU加速,避免显存错误掩盖逻辑问题
真正麻烦的从来不是切,而是怎么让切出来的每一块,在语义上跟邻居对得上——这取决于你的模型感受野是否覆盖了patch边界,以及loss是否对边缘区域做了特殊处理。











