
这篇博客文章的详细内容如下兄弟们你们在用PyTorch跑训练的时候有没有遇到过这样一个弹窗——“页面文件太小无法完成操作”我第一次遇到这个错误的时候是在一次yolov8训练自己的数据集的时候batch size设得稍微大了点数据加载刚跑到第三个epochWindows直接弹窗提示训练进程当场卡死终端里一堆内存相关的报错信息显卡利用率直接掉到0%。那一刻真的非常崩溃因为训练环境明明已经是32GB内存加上8GB显存的配置了怎么还会出现这种低级问题后来我花了半天时间把Windows的虚拟内存机制、PyTorch的DataLoader加载逻辑、还有显存与内存之间的数据交换链路全部过了一遍才彻底搞清楚这个报错的来龙去脉。这篇文章就把我的排查过程和解决方案完整分享出来包括虚拟内存到底解决什么问题、在什么情况下不能盲目调大、PyTorch侧又该怎么配合调整希望能帮同样踩坑的兄弟少走弯路。整个内容不兜圈子直接把经验捞干货讲清楚。1. 这个报错到底在向你传达什么信息1.1 页面文件并不是Windows独有的缓存它是物理内存的扩展很多做深度学习的人平时在服务器上用的是Linux对Windows的很多机制不太了解看到页面文件这几个字容易一头雾水。简单说页面文件就是Windows系统在磁盘上划出来的一块区域用来存放物理内存RAM里放不下的数据。当你的系统物理内存占用接近满额Windows会把你暂时不需要的数据挪到这块磁盘区域里腾出物理内存给当前需要快速访问的程序。这个机制本身没有好坏之分它存在的意义是为了防止程序一次性申请的内存超过物理内存就立刻崩溃。然而页面文件有一个致命弱点磁盘的读写速度和内存差了好几个数量级。如果你的训练程序经常需要访问被换出到页面文件里的数据程序的运行速度会断崖式下降就像本来在高速公路上飙车突然被迫开进泥地沼泽。1.2 为什么PyTorch训练会频繁撞上这个限制PyTorch训练过程中内存消耗的路径其实比大多数人想的要复杂得多数据加载与预处理DataLoader会从磁盘读取图像、文本或其他样本做归一化、增强等预处理然后打包成batch张量放进内存模型参数和优化器状态模型参数本身在显存里但优化器状态如Adam的一阶、二阶动量也要占显存当显存不够时一些PyTorch版本会将部分参数溢出到CPU内存中间梯度计算反向传播时每个中间激活值都要保存在内存或显存中batch越大、模型越深中间激活值越多评测与日志验证集推理、TensorBoard记录、checkpoint保存都会临时产生大量内存对象。以上这些环节只要有一个瞬间峰值超过物理内存总量Windows就会启动页面文件。如果你的页面文件设置得偏小系统就没有足够空间容纳超出的部分然后就会弹出页面文件太小无法完成操作。很多人以为这个报错是程序内部错误其实它是操作系统层面的拦路虎。1.3 容易混淆的概念显存不足、内存不足、页面文件不足这里必须把三个概念彻底理顺否则你会在错误的方向上浪费时间报错类型出现位置本质原因CUDA out of memoryPyTorch终端显卡显存不够张量放不进显存页面文件太小Windows弹窗物理内存不够且页面文件不够系统无法分配内存物理内存不足任务管理器曲线冲顶内存条几乎耗尽但不一定触发弹窗我踩过的坑就是当初把页面文件太小误当成需要调低batch size来解决的问题。调低batch size确实能降低显存和内存占用但如果页面文件本身设置得过小即便batch size已经降到很小数据在加载过程中依然可能出现内存分配失败。两个问题重叠在一起单纯靠调batch size是无法完全规避的。2. 动手排查判断是临时性内存抖动还是系统性内存不足2.1 三步确认当前系统内存状态遇到页面文件太小报错先不要急着去改设置先做三步基础排查确定问题属于哪种类型第一步查看任务管理器。按下Ctrl Shift Esc在性能标签页里查看内存一项重点看已缓存数值大小和虚拟内存提交量。如果物理内存占用率超过90%而虚拟内存的使用量已经接近当前页面文件上限那么问题就出在页面文件容量不够。第二步检查当前页面文件的配置大小。右键此电脑选择属性进入高级系统设置在高级选项卡中找到性能一栏点击设置再切到高级选项卡最下面的虚拟内存区域就能看到当前页面文件的配置。系统默认通常是自动管理所有驱动器的分页文件大小但很多人装的第三方精简版系统或手动配置过这一项会被改成一个较小的固定值比如2048MB或4096MB。如果只有4GB页面文件训练程序内存占用一飙必炸。第三步运行训练脚本实时监控内存和提交大小。推荐用微软官方工具Process Explorer替代任务管理器按Ctrl L可以查看每个进程的Private Bytes和Virtual Size变化曲线。与此同时观察任务管理器里的提交(Commit)数值变化。如果内存总量32GB、页面文件4GB提交量飙到20GB时就会出现分配失败的弹窗这个数值能帮你直观判断是训练代码本身申请内存太多还是其他后台程序占用了大量内存空间。2.2 日常最容易吃内存的几个隐形杀手排查过程中我注意到很多情况下页面文件报错并非训练脚本单独导致而是多个内存大户同时抢资源浏览器现在的浏览器就是内存吞噬怪兽开着几十个标签页加上各种插件常驻后台吃6~10GB内存轻轻松松Anaconda/Jupyter如果你是通过Jupyter Notebook训练模型基本环境本身就要占用3~5GB内存再叠加训练进程的内存消耗多个Python进程残留训练中断后之前的Python进程没有完全退出GPU显存被释放了但CPU内存还占着这种情况在长时间调试时特别常见显卡驱动与CUDA上下文CUDA上下文一旦初始化就会锁定几百MB到1GB的系统内存多个CUDA进程并行更是成倍占用。建议在训练前先关掉浏览器非必要标签页清理掉残留的Python进程保证系统在训练期间有最大可用内存。这些操作虽然简单但确实能有效降低触发页面文件报错的概率。2.3 判断是偶尔抖动还是频繁爆炸的经验法则结合我的实操经验可以按以下情况做个快速归纳训练前期一切正常训练到中后期偶尔出现一次页面文件报错这种情况通常是某个checkpoint保存或验证集评估时存在一个内存峰值页面文件偏小导致峰值过不去。解决方向是兼顾页面文件扩容和降低峰值内存占用训练一开始就报错甚至还没开始加载数据就报错大概率是系统整体内存已经被其他程序占满页面文件又太小。此时先用任务管理器杀掉非必要进程再考虑扩容页面文件每次迭代都报错且报错之后进程直接崩溃说明训练程序的内存申请量远超物理内存加页面文件之和调大页面文件只能缓解崩溃频率真正要做的是PyTorch侧的省内存优化后面我会详细讲。这个判断过程非常关键因为它直接决定了你接下来应该优先调设置还是优先改代码。3. 第一层解药正确调整页面文件治标且避免副作用3.1 图形界面调整的具体操作步骤如果你的排查结果是页面文件设置得太小那第一步就是把它调大。操作步骤如下右键此电脑选择属性点击左侧高级系统设置在高级选项卡的性能栏中点击设置按钮在弹出的性能选项窗口中切换到高级选项卡找到虚拟内存区域点击更改按钮取消勾选自动管理所有驱动器的分页文件大小选择D盘或其他非系统盘原因稍后讲点击自定义大小初始大小和最大值设置为相同数值这样可以避免页面文件频繁扩容缩容带来的性能波动点击设置按钮然后一路点击确定最后重启系统生效。关于数值的选择物理内存是32GB的机器建议初始大小和最大值都设为16384~32768MB16~32GB。如果你的物理内存是16GB页面文件至少设成16GB。需要特别注意的是页面文件不建议设得过大比如直接设成100GB因为它在磁盘上占据的是实际空间设得过大反而会让系统在管理时产生不必要的额外负担而如果设得太小训练过程中同样可能碰壁。3.2 为什么强烈建议把页面文件放在非系统盘不少人图省事直接在C盘上设置页面文件但我后来看了系统的性能分析才发现问题所在。Windows系统盘本身还要承担系统文件读写、软件安装更新、临时文件写入等大量IO操作如果页面文件也在C盘训练过程中的频繁内存换页会和系统IO抢占磁盘带宽导致磁盘卡顿明显训练速度进一步下降。因此如果你的机器有机械硬盘加固态硬盘的混合配置页面文件应该放在固态硬盘上如果是纯固态可以考虑放在数据盘或专门分区减少与系统盘的IO竞争。特别是对于深度学习场景强烈建议使用NVMe固态来承担页面文件读写性能要比SATA固态好很多更不用说机械硬盘了。3.3 调整页面文件时容易忽略的四个细节调整页面文件并不是设个16GB就完事我在实践中有几个细节想提醒你重启必须做修改页面文件设置后很多情况下需要重启系统才能生效否则下次训练时你会发现自己设置的数值根本没被系统采用不要在多个磁盘重复设置大量页面文件虽然Windows允许在不同磁盘上分别设置页面文件但多个磁盘同时承担内存换页可能会带来管理上的复杂度实际效果未必更好不要在存放训练数据的磁盘上设置页面文件如果页面文件和数据加载同时读写同一块磁盘IO竞争会非常激烈训练速度会明显下降我一般放在专门的数据盘或另一块独立SSD上别忽略系统盘的可用空间即使页面文件放在非系统盘C盘还是需要有充足剩余空间因为系统每次启动或运行时一些临时文件还是需要写入C盘。3.4 临时系统性应急方案用脚本动态释放内存如果训练已经跑了一半不想因为这个报错重启系统可以临时用PowerShell快速释放缓存和部分内存操作。不过要说明这类操作只能临时缓解不能根治问题应急用可以长期用建议还是按上面方案正确配置。我实际试过的一个方式是训练前用管理员权限打开PowerShell执行以下命令清理工作集# 清空系统工作集缓存谨慎使用 Write-Host 开始清理系统缓存... $EmptyMem using System; using System.Diagnostics; using System.Runtime.InteropServices; public class MemoryHelper { [DllImport(psapi.dll, SetLastError true)] public static extern bool EmptyWorkingSet(IntPtr hProcess); public static void Clear() { foreach (Process p in Process.GetProcesses()) { try { EmptyWorkingSet(p.Handle); } catch { } } } } Add-Type $EmptyMem [MemoryHelper]::Clear() Write-Host 缓存清理完成注意这个脚本会把所有进程的工作集强制清空让它们的内存数据换入页面文件虽然能腾出物理内存但当你切回其他程序时会感觉明显卡顿。建议只在训练前执行一次不要在训练过程中频繁调用。4. 第二层解药从PyTorch训练侧压缩内存治本4.1 DataLoader参数优化是最立竿见影的手段排查页面文件问题时我的最终结论是光靠调大虚拟内存只是把马路边拓宽真正要让训练跑得顺畅还是得让训练程序本身对内存更友好。PyTorch中DataLoader的配置就是最容易被忽略也最值得优化的地方。以下是我在实际项目中总结出来的一套DataLoader配置逻辑可以直接抄作业from torch.utils.data import DataLoader from torch.utils.data.distributed import DistributedSampler train_loader DataLoader( dataset, batch_size32, shuffleTrue, num_workers4, # 不是越大越好Windows下4~8比较合适 pin_memoryTrue, # 锁页内存能加速CPU向GPU传输数据 persistent_workersTrue, # 复用worker减少重复创建开销 prefetch_factor4, # 每个worker预取4个batch drop_lastTrue, # 去掉最后一个不完整batch减少波动 )几个参数值得重点说明num_workersWindows系统下每创建一个worker系统内存开销都会明显增加。我实测过在32GB内存的机器上把num_workers从8降到4内存占用峰值能降低约25%而训练速度几乎没有下降。如果你的数据集很小几百到几千张num_workers2就够用了多了反而增加调度开销pin_memory开启后会将数据放到锁页内存中加速与GPU的传输但这个操作会占用物理内存中不可换出的部分如果开启后内存紧张可以考虑关掉prefetch_factor默认值是2如果你的内存比较吃紧可以降到1减少预取队列对内存的占用drop_last丢弃最后一个不完整batch避免某些异常batch尺寸导致的额外内存分配波动。如果你在数据加载时使用了自定义的collate_fn还需要额外注意在处理变长序列或关键点标注时最终的batch张量内存分配是否合理不然容易出现训练中段突然报错。4.2 显存不够时别让PyTorch悄悄往内存里写数据这里有一个容易踩坑的点当batch size过大导致显存不足时PyTorch某些版本和扩展比如部分检测、分割模型会自动把部分中间张量放在CPU内存上等待后续计算再搬回GPU。这个行为在代码层面是无感知的但它会大幅增加CPU内存占用。如果内存和页面文件都吃紧系统就会直接报错。我的经验是如果报错信息中出现CUDA out of memory同时还伴随页面文件太小那就要优先考虑降低batch size或者使用梯度累积gradient accumulation来等效扩大batch size。梯度累积的参考实现如下accumulation_steps 4 # 模拟batch_size * 4的效果 optimizer.zero_grad() for i, (images, targets) in enumerate(train_loader): images images.cuda() targets [t.cuda() for t in targets] loss model(images, targets) loss loss / accumulation_steps # 归一化 loss.backward() if (i 1) % accumulation_steps 0: optimizer.step() optimizer.zero_grad()这样做的核心逻辑是每一小步只反向传播一部分batch的梯度内存峰值得以大幅下降最终的更新等效于一个大batch的效果。实际测试中我把batch size从32降到8、累积步数设为4显存峰值降低了约50%页面文件压力也显著减轻。4.3 torch.cuda内存池的释放时机另一个容易被忽视的内存消耗点是PyTorch的显存缓存机制。当你调低batch size或切换数据集后PyTorch并不会立刻把显存释放回操作系统而是保留在缓存池中复用这导致任务管理器看到的显存占用依然很高。对于CPU内存也一样PyTorch会缓存一部分分配过的内存块方便下次分配时快速复用。在长时间多轮训练之间如果你需要强制释放内存池可以在关键节点调用import gc import torch # 清空Python引用计数让垃圾回收器有机会回收临时对象 gc.collect() # 清空CUDA缓存释放显存回操作系统 torch.cuda.empty_cache() # 如果需要可以记录释放前后的显存占用 print(f当前显存占用: {torch.cuda.memory_allocated() / 1024**3:.2f} GB) print(f显存缓存池占用: {torch.cuda.memory_reserved() / 1024**3:.2f} GB)但这里要提醒一下torch.cuda.empty_cache()并不总是必要的。如果你在训练主循环里频繁调用它会导致显存分配器不断重新分配内存反而拖慢训练速度。合理做法是在验证或保存checkpoint之后的非高频路径调用或者在训练中途遇到显存不足的异常时再调用。4.4 模型侧的几个省内存小技巧按优先级排序如果上述优化做完之后训练进程仍然有较大内存压力可以围绕模型本身做进一步压缩使用混合精度训练AMPtorch.cuda.amp.autocast配合GradScaler不仅显存占用降低CPU内存的中间张量占用也会减少。对于支持半精度计算的GPU训练速度还能提升20%~40%减少优化器状态的内存占用AdamW优化器需要保存模型参数两倍左右的状态内存一阶动量和二阶动量如果你内存极度紧张可以考虑换用SGD或LARS这类优化器牺牲部分收敛速度换取更小的内存占用使用参数高效微调如果是在大模型上做lora训练那本身就是为了省内存但注意PEFT库加载基座模型时还是会先把整个模型参数加载到显存或内存只是训练时可训练的参数量变少内存节省主要在反向传播时的梯度保存上减少checkpoint保存频率保存checkpoint时会序列化模型参数如果你连续保存多个checkpoint而磁盘IO速度跟不上内存中会产生大量的临时缓存对象。建议每N个epoch保存一次保留最近几个最优版本就好用torch.utils.checkpoint做激活重计算对于深层次模型激活重计算可以大幅降低激活值的内存占用但会增加约30%的额外计算开销适合显存和内存真的很紧张时使用。我在实际操作中的体会是这五个技巧叠加使用能在不损失模型精度的情况下把训练过程中的内存峰值压缩40%~60%。对于原先频繁报页面文件太小的机器这样的改动往往才是真正解决问题的关键。5. 常见方案对比什么时候选什么方式更有性价比5.1 一张表看懂各方案的使用场景整理了这么多种手段下面用一个表格帮你快速对照实际项目中按需选两到三种组合使用就好解决方案操作成本对训练速度影响适用场景调大页面文件低几乎没有影响除非频繁换页所有Windows机器建议优先操作减少num_workers低轻微下降内存吃紧但数据加载速度还够的情况降低batch size 梯度累积中基本持平显存和内存同时紧张的核心选择混合精度训练中提升支持FP16/BF16的GPU激活重计算高明显下降模型太深、内存压力极大换Linux环境高提升长期做深度学习Windows频繁报错的根本解这个表格不是绝对的但大致给出了不同场景下的优先级顺序。我的建议是先调页面文件解决问题快再优化DataLoader和batch size降低内存峰值如果还不够再上混合精度和激活重计算。5.2 为什么Linux下很少遇到页面文件太小这个报错顺带解释一个大家常问的问题为什么在Linux服务器上训练从没见过这个报错因为Linux的内存管理机制与Windows不同它默认允许进程申请超过物理内存加swap容量的内存即overcommit策略程序申请内存时不一定会立刻失败而是在真正访问内存页时才触发OOM Killer。而Windows的内存管理器相对保守提交内存时如果发现物理内存加页面文件空间不足就会直接拒绝分配并向用户弹窗报错。这两种机制各有优劣Windows的报错更及时、更直观Linux则是把问题推迟到访问内存页的时刻。对于新手来说Windows的报错反而能更早暴露内存设计问题。理解了这一点你就明白为什么说页面文件太小更像是一个友好提示而不是系统故意刁难你。5.3 长时间训练下的内存回收习惯页面文件报错在长时间训练任务中出现的概率更高尤其是多轮训练、连续做实验的场景。分享几个我一直在用的内存回收习惯每次训练循环开始前先用nvidia-smi查看显存占用确保上一个任务彻底退出没有残留进程占用显存和系统内存实验结束后用psutil查看当前Python进程的内存占用判断是否正常回落。如果异常偏高检查代码里是否有未释放的全局变量或list一直在累积保存wandb或TensorBoard日志时不要一次性记录过多图像样本尤其是可视化batch数据时一张高分辨率图像在内存里可以占到几MB如果一次性记录几百张内存压力会瞬间上升多个实验并行时尽量串行执行而非并行在内存只有32GB的机器上并行跑两个训练任务内存峰值基本会翻倍页面文件报错的概率也会大幅上升。这些习惯不需要改核心代码但能在日常实验流程中帮你节省大量排查问题的时间。6. 如果以上都试过了还是报错最后几个冷门检查点6.1 检查系统账号权限与内存配额限制有一种比较冷门的情况如果你的Windows系统是企业版或者加入了域环境系统管理员可能通过组策略限制了你这个用户账号的最大提交内存配额。这种限制不会在任务管理器里直接显示但训练程序一旦超过限额就会出现内存分配失败。检查方式是在运行窗口输入gpedit.msc依次进入计算机配置 - Windows设置 - 安全设置 - 本地策略 - 用户权限分配查找调整进程的内存配额选项。如果发现自己账号没有这个权限或者配额设置异常就联系管理员调整。个人版Windows家庭版一般不会遇到这种问题但如果你用的是公司配发的电脑这个点值得留意。6.2 检查训练数据集的加载方式是否为懒加载还有一个容易踩坑的细节如果你的自定义Dataset在__init__阶段把整个数据集一次性读入内存比如把所有图像路径全部读进来或者把全部图像数据load成list那内存占用从一开始就会很高。我之前接过一个图像去噪项目代码里直接在__init__中做了self.images [cv2.imread(f) for f in file_list]几万张图像直接吃掉20多GB内存再加上模型训练页面文件必定爆炸。正确的做法是把文件读取和预处理放在__getitem__中只在读取时才载入单张图像配合DataLoader的worker机制做并行读取。这样即使数据集有一万张图单次待在内存里的也只有当前batch的几十张。这个改动对内存占用的影响是数量级的务必优先检查。6.3 32位Python和大量DLL占用导致的非典型内存问题再分享一个我会在讲座上提到的冷门场景如果你用的是32位版本的Python极少数兼容性原因导致那么单个Python进程的地址空间会被限制在4GB以内即使你的物理内存有64GB训练进程申请超过4GB内存时也会失败可能表现为模糊的内存错误也可能与Windows弹窗同时出现。判断方法很简单在Python中执行import platform; print(platform.architecture()[0])如果输出的是32bit建议尽快换用64位Python重新配置环境。现在Anaconda默认就是64位版本但保不准有人用的生产环境是当初图省事装的32位版本。6.4 系统盘空间不足导致的连带效应页面文件不管是自动管理还是手动指定它所在磁盘的可用空间必须大于等于设置的页面文件大小。如果C盘剩余空间不足500MB即使你的页面文件设置值是32GB系统实际也无法正常扩展到这么大启动时甚至会出现系统在未使用分页文件的情况下创建了临时分页文件的提示。我遇到过一次是因为conda环境的缓存区C:\Users\xxx\.conda或pip缓存膨胀到了几十GB把C盘塞满了导致页面文件根本无法正常扩展。建议定期清理conda和pip缓存conda clean --all和pip cache purge给C盘和页面文件留足空间。6.5 从任务管理器中找到被忽略的内存分配者最后如果你排查了好几轮还是找不出是谁在吃掉内存可以用Windows内置的资源监视器做一次精确分析。打开资源监视器切到内存标签页按提交(KB)排序列在最上面的进程就是内存消耗大户。你可能会看到一些意料之外的进程比如某个后台服务、驱动宿主进程它们不断累积内存把训练进程的内存空间默默占掉。有些系统驱动或第三方杀毒软件会在训练启动时做全盘扫描短时间占用大量内存和CPU资源导致训练刚启动就报页面文件错误。遇到这种情况可以先暂停这些后台扫描或者把训练目录和数据集目录加入白名单再重新启动训练。7. 一些关于硬件升级与使用习惯的碎碎念7.1 内存条容量选择与页面文件设置之间的平衡如果你的机器是16GB内存且日常除了训练还要开浏览器和其他工具那么单纯调大页面文件只能作为过渡方案。从实际体验来看16GB内存做中小型模型的PyTorch训练已经非常紧张升级到32GB内存条是最值得的投资之一。训练时内存条的价格不算高但换来的稳定性和训练速度提升是实打实的。在32GB内存条件下我推荐的页面文件方案是系统盘设置8~16GB数据盘再设置16GB这样既有足够的缓冲又不会因为需要频繁换页导致严重的性能损失。如果你用的是64GB甚至128GB内存页面文件可以系统自动管理或者只设置一个8GB左右的最小值作为兜底毕竟物理内存已经足够大大部分情况下都用不到页面文件。7.2 我在实际训练过程中形成的一套内存纪律最后分享几个我每次训练前都会执行一遍的固定动作时间成本不到一分钟但能避开大部分内存相关的坑打开任务管理器查看物理内存占用如果超过70%检查谁在占内存关掉所有非必要的浏览器标签页尤其是带视频或大量图片的页面用nvidia-smi确认没有僵尸GPU进程占着显存打开页面文件设置页面确认当前配置没有被系统自动改动过检查C盘剩余空间确保至少有页面文件大小两倍以上的空闲空间启动训练脚本后前30秒持续观察内存曲线如果快速爬升但不回落就要及时暂停排查。按照这套流程我在之后几百次Windows训练任务中基本没有因为页面文件太小这个报错中断过训练这个错误也从最初的玄学问题变成了套路清晰的确定性排查项。7.3 遇到问题别死磕单一路径关于这个报错网上很多教程会让你直接把虚拟内存调到系统管理或者一劳永逸地设一个很大的值。这种方法不能说完全没用但它只是让系统能挂得住而已。如果你的训练数据加载方式本身就有内存泄漏或者DataLoader的worker数设得完全不合理就算把页面文件调到64GB训练速度也会被磁盘换页拖累到无法接受。排查任何训练环境问题我的思路都是先解决系统层面的能不能跑再解决代码层面的跑得好不好最后才是怎么跑得更省。不要被单个方案绑架按自己的实际情况组合使用才能少走弯路。