ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

无GPU环境下深度学习实战:CPU训练方案与PyTorch配置全指南

无GPU环境下深度学习实战:CPU训练方案与PyTorch配置全指南 上个月有个师弟在群里问我“实验室分到的那台机器只有CPU论文方向又被定成了深度学习怎么办”这问题我太熟了。我读研那会儿整个实验室就一块老旧的GPU还是隔壁组导师借来用的排队排得比食堂打饭还难受。后来没办法我硬是靠着纯CPU环境把毕业论文的实验做完了虽然速度确实感人但该学的东西一样没落。写了这篇内容就是想给同样被硬件卡的同学们一点实际参考无GPU环境下深度学习到底能不能做、怎么做、哪些坑千万别踩。文章会先帮你判断哪些任务在CPU上能跑、哪些必须想别的办法再给出一套完整的CPU版PyTorch配置流程分享几个提升训练效率的实测技巧最后整理一些我踩过的坑和排查思路。不管你是准备课程作业、应付毕业设计还是真的想入门深度学习这篇都能用得上。1. 没有GPU到底还能不能做深度学习1.1 先搞清楚真实需求你是要“学会”还是要“训出”每次看到“实验室没有GPU怎么办”这个问题我的第一反应不是直接回答能或不能而是反过来问一句你到底想干什么想清楚这个问题特别重要因为“深度学习”这四个字在不同人嘴里指的是完全不同的工作量。如果你是跟着李沐的《动手学深度学习》跑代码或者在看那本被很多人叫“鱼书”的《深度学习入门基于Python的理论与实现》又或者在做吴恩达课程的课后习题那么我可以负责任地说纯CPU完全够用。这些课程里的MNIST手写数字识别、CIFAR-10图像分类、简单的情感分类模型规模都很小在普通CPU上训练一个epoch也就是几十秒到几分钟的事。我当年就是拿一台连独立显卡都没有的笔记本电脑把这些课的代码都跑通的一点问题没有。如果你是在跑自己的毕业设计或者导师给的方向涉及ViT、GAN、大语言模型微调这些东西那结论就不一样了。这些任务的计算量是课程代码的几百倍上千倍CPU硬扛不是不行但训练一次要几小时甚至几天实验迭代一轮下来整个人都麻了。所以判断的标准不是“有没有GPU”而是“任务的计算量在不在CPU能承受的范围内”。这里我建议你先做一个简单估算看一眼你的模型参数量再跑一次单batch的forward和backward记下耗时。如果单次迭代超过5秒而你的训练集有几万张图片那这个任务在CPU上做到毕业基本不现实。趁早做两手准备要么换小模型要么去云上租GPU。1.2 一个残酷但清醒的结论有些任务确实不该在CPU上跑我也不想给你灌鸡汤。深度学习里有一类任务天生不适合CPU比如从头预训练GPT这样的大语言模型、训练高分辨率GAN、做视频理解、跑NeRF三维重建。它们的共同特点是要么显存占用量大得离谱要么计算密度极高。CPU的内存带宽和浮点算力就摆在那里不是靠优化代码就能翻盘的。这就好比让一辆家用轿车去拉几十吨的重卡货你再怎么换轮胎、调发动机物理上限摆着。但“不能跑重任务”不代表“没得学”。深度学习的核心原理——反向传播、过拟合、正则化、卷积的计算过程——用小模型、小数据同样能看得清清楚楚。我在CPU上跑过带L2正则化的PyTorch代码明明就是很简单的两层卷积网络加一个weight decay参数但把不同正则化强度的训练曲线对比画出来那种“过拟合被压住”的感觉比看十遍理论都直观。而且实话实说无GPU环境反而能逼你养成一些好习惯。因为慢你会珍惜每一次训练机会动手之前先想清楚模型结构、先在小规模数据上做冒烟测试也会更早接触迁移学习、特征复用这些工程技巧。这些习惯等你真正有了GPU之后一样是受益终身的。2. 方案选型四类可行路子按需取用2.1 场景一CPU硬扛——适合教学、入门和小规模实验无GPU条件下最朴素也最可控的方案就是直接在本地CPU上跑。它不需要额外花钱不依赖外部网络环境代码改起来也快。适合的场景我帮你列一下经典机器学习算法MLP、逻辑回归、决策树、SVM这些完全用不到GPUCPU跑得飞快小规模CNNLeNet、简单的两层卷积网络在MNIST上普通CPU一个epoch几十秒到两三分钟迁移学习只训练分类层用现成的ResNet18提取特征保存下来只训练最后那个全连接层计算量非常小代码逻辑验证、教学演示、算法课程作业。CPU硬扛的心理预期要放低。一个常见的参考数据是batch size为32、输入分辨率224x224的ResNet18在普通i5或i7处理器上一次forward加backward大概需要几秒到十几秒。这跟A100比确实没法看但如果是小数据集咬咬牙也能出结果。我当年做毕业论文最核心的那几张对比表格就是用CPU反复跑出来的虽然时间跨度拉了一个月但最后提交的时候数据是完整的、可复现的。2.2 场景二免费云GPU平台——学生党的救命稻草如果你的任务中等规模又实在没钱买卡免费的云GPU平台是值得认真考虑的选项。Kaggle的Notebook每周赠送30小时的GPU额度Google Colab也有免费额度虽然有时候排队排到天荒地老但胜在不要钱。国内一些面向高校的AI实训平台、竞赛平台也会提供免费算力具体资源随时间会变但思路是一样的多注册几个平台把它们的免费额度用起来。我个人的经验是在这些免费平台上跑中等规模的任务比如CIFAR-10、SVHN这类图像分类或者中小型NLP模型的微调是完全可以接受的。它们的GPU型号不算新但比CPU强太多了一个epoch几十秒就能跑完实验节奏一下子从“半天跑一次”变成“十分钟跑一次”。这类平台有一个通病Session会被回收。你要是把训练任务挂那不管隔几个小时回来看可能连接都没了。解决办法是老生常谈但必须做——定时保存checkpoint每跑完一个指定轮数就把模型权重和优化器状态存下来。断线了重新连上去接着跑就行不用从头再来。2.3 场景三按量租用GPU服务器——重任务的性价比之选如果任务真的很重比如连续训练十几个小时以上那就别在免费平台耗了直接按量租GPU。现在主流的云服务商和垂直算力平台都支持按小时计费用多久付多久没有硬件维护成本。我身边学生用得比较多的是AutoDL、恒源云这类面向高校的算力平台也有直接用阿里云、腾讯云的GPU实例的大家按自己预算和习惯选就行。租用有几个注意点第一选配置的时候看清楚GPU型号和显存大小显存决定你能跑多大的模型第二关注计费方式按时计费而非包月用完了记得关机不然分分钟扣钱第三选有数据盘或网盘挂载功能的平台上传下载数据集会方便很多。从我实际体验看一张消费级显卡租一天的价格比很多人想象得便宜而且省去了一堆环境配置的功夫对赶时间的人来说很值。2.4 场景四换个思路——用更轻的模型和技巧绕过算力瓶颈无GPU环境下选模型比调代码重要得多。同样一个图像分类任务你用ResNet152和用MobileNetV3体验完全是两个世界。轻量级模型不是不能打很多场景下精度差距很小但速度能快十几倍。给你几个我实测过的组合图像分类MobileNet、EfficientNet-Lite、ShuffleNet这类轻量CNN在CIFAR-10上CPU训练完全可行自然语言处理直接用DistilBERT等蒸馏过的预训练小模型比BERT-base轻一半以上很多任务上精度损失很小迁移学习加载别人预训练好的模型权重冻结大部分层只训练最后几层这是无GPU环境最划算的方案。还有一个思路是知识蒸馏先在大模型上算出teacher的预测分布再用这个分布去训练一个小模型。这个操作在无GPU环境下做有点难度因为你自己得先有个大模型来蒸馏但如果你能从公开渠道拿到蒸馏数据那效果是立竿见影的。3. 环境配置实操手把手跑通CPU版PyTorch3.1 理清版本差异别再把GPU版装进无GPU的机器很多第一次配环境的人在无GPU机器上直接执行pip install torch结果装出来的包有几百MB甚至几个GB运行的时候还老提示CUDA不可用。根子就在于PyTorch默认的pip包是带CUDA依赖的GPU版本你虽然没显卡但它把CUDA相关的库一并装上了。在无GPU机器上这倒不会报大错但白白占了一堆硬盘空间而且某些场景下启动会变慢。正确的做法是安装时要明确指定CPU版本的安装源。PyTorch官方提供了CPU版本的whl包安装命令是pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu如果你用的是Anaconda还可以用conda直接安装CPU版conda create -n dl python3.10 conda activate dl conda install pytorch torchvision torchaudio cpuonly -c pytorch这两条命令我都实测过装出来的PyTorch是纯CPU版本体积小运行稳定。conda方式适合你已经依赖conda管理环境的情况pip方式更轻量随便一个Python虚拟环境都能用。3.2 完整安装流程以Anaconda为例装CPU版PyTorch下面这套流程我在好几台机器上跑过照着做基本不会出问题。第一步装Anaconda或Miniconda。Anaconda自带Python和一堆常用库Miniconda更精简看个人喜好。装完之后打开终端或者Anaconda Prompt输入conda --version检查是否装好了。第二步创建独立的深度学习环境。这一步强烈推荐不要直接在base环境里干活因为不同项目依赖的Python版本和包版本可能冲突独立环境可以隔离脏乱差。conda create -n lab python3.10 -y conda activate lab第三步安装CPU版PyTorch。这一步我建议优先用conda命令因为conda会自动处理依赖关系conda install pytorch torchvision torchaudio cpuonly -c pytorch如果你在下载时遇到速度很慢的问题可以先配置国内镜像源比如清华源或者阿里源然后再执行上面的安装命令速度会快很多。conda配置镜像的方法网上有很多我在这里就不赘述了总之是改一个.condarc配置文件的事。第四步验证环境。安装完成后在Python里执行下面这行命令import torch print(torch.__version__) print(torch.backends.mkldnn.enabled)如果输出版本号类似2.3.0而mkldnn那一行输出True说明CPU版PyTorch装好了而且底层已经开启了MKL-DNNoneDNN加速这个对CPU训练提速非常重要。3.3 代码兼容性CPU版也能无缝切换配好环境之后还有一个很重要的习惯要养成就是写代码的时候做好设备抽象。我在帮师弟改代码时经常看到他们把设备写死成cudadevice torch.device(cuda)这么写在无GPU机器上肯定报错。更稳妥的写法是device torch.device(cuda if torch.cuda.is_available() else cpu)这样你的代码在无GPU机器上自动使用CPU在云上租了GPU之后又能自动切到CUDA。模型、数据、损失函数统一用.to(device)移到对应设备就行。我自己的经验是所有训练脚本都按这个模板写后面换环境几乎零成本。另外还有一个小细节如果要从GPU机器上把训练好的权重拿回CPU机器做推理或继续训练加载模型时要指定map_locationcheckpoint torch.load(model.pth, map_locationcpu)不然大概率会遇到运行设备不匹配的报错。4. 无GPU环境下的训练提速技巧4.1 从源头上减负小batch、小模型、小数据集CPU训练最怕的就是贪大。GPU上轻松跑64或128的batch size在CPU上最好降到16或32。小batch除了减少单次运算量还有一个额外的好处梯度更“吵”反而带来一定的正则化效果有些任务上最终精度并不差。我实测过MNIST上一个简单的CNNbatch 32和batch 128相比训练轮数要更多一些但每轮耗时少一大截综合起来时间差不多而显存或者说内存占用却低很多。模型层面的减负同样重要。刚开始做实验的时候别一上来就上ResNet先用一个两三层的小卷积网络把代码逻辑跑通。等确认forward、backward、精度计算这些环节都没问题了再换大模型。我在无GPU环境下的习惯是先在1000张图片的子集上做冒烟测试确保能正常收敛再决定是扩大数据量还是在本地硬跑。数据集方面建议把数据提前预处理成内存中的numpy数组而不是每次训练都从磁盘读图、解码、做随机裁剪。后者在CPU环境下的开销非常大很可能你训练代码本身只要1秒钟但数据加载要5秒钟最终一个epoch下来大部分时间花在等数据上。4.2 用线程和内存优化把CPU性能榨干CPU训练中一个常被忽略的瓶颈是数据加载。DataLoader默认的num_workers等于0意思是数据加载在主进程里串行执行模型算完一个batch还得等着加载下一个浪费大量时间。把num_workers调成4或8让数据读取和模型计算在多个进程里并行提速效果立竿见影。dataloader DataLoader(dataset, batch_size32, num_workers4, shuffleTrue)不过Windows系统下num_workers大于0可能会有一些小问题比如报DataLoader worker的错如果遇到了可以先用0跑通再想办法优化。Linux和macOS基本没这个问题。线程数的设置也值得调一调。PyTorch默认会用满所有CPU核心但超线程带来的收益其实是递减的盲目把线程设到很大反而可能因为上下文切换浪费性能。我一般用物理核心数而不是逻辑核心数import torch torch.set_num_threads(8)如果你的CPU是8核16线程设8通常比设16更快。这个可以自己试跑一个epoch对比一下时间就知道了。再说一个内存相关的小技巧pin_memory这个参数是给GPU用的在CPU环境下设不设都无所谓。但如果你同时开了很多个DataLoader进程内存占用会迅速爬升小内存机器要小心。内存不够的时候系统会疯狂swap那速度才叫一个惨烈。4.3 梯度累积与检查点策略省时间的关键操作如果你用小batch还是嫌慢可以考虑梯度累积。这个技巧的原理是不每个batch都更新参数而是攒几个batch的梯度再一起更新等效于用一个更大的batch size但每次计算量不变。accumulation_steps 4 optimizer.zero_grad() for i, (inputs, labels) in enumerate(dataloader): outputs model(inputs) loss criterion(outputs, labels) loss.backward() if (i 1) % accumulation_steps 0: optimizer.step() optimizer.zero_grad()这样做的好处是减少了优化器的更新频率也就减少了一些同步和调度开销在CPU环境下实测能稳住训练速度。需要注意的是梯度累积会影响BatchNorm的行为用的时候要留意一般建议在不用BatchNorm的模型上使用或者配合梯度缩放一起处理。检查点策略的重要性怎么强调都不为过。CPU训练很慢一次训练可能跑好几个小时如果中途断电或者手动误操作不保存就全废了。我一般每个epoch结束就保存一次checkpoint里面包含模型权重、优化器状态、当前epoch编号甚至还包括随机数生成器的状态这样就能在出错时精确恢复现场torch.save({ epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), }, fcheckpoint_epoch_{epoch}.pth)恢复训练时加载这批状态之后继续循环省去重头训练的时间。这招在免费云GPU平台上特别管用session断了也不慌。还有一个我踩过坑才学乖的经验CPU环境下验证不要每个epoch都做。验证集的完整跑一遍在训练里也占不少时间。我在无GPU环境下经常每隔5到10个epoch才验证一次或者只抽验证集的一个子集来做省下来的时间能多跑好几轮训练。5. 常见问题与排查技巧实录5.1 装了CPU版却报错CUDA不可用这是无GPU环境下最常遇到的报错之一。你明明看到自己装的是CPU版PyTorch但代码一运行就提示CUDA不可用。这种情况百分之九十是代码里硬编码了cuda设备比如model torch.nn.DataParallel(model.cuda())或者更直接的tensor tensor.cuda()解决办法就是把设备相关逻辑改成上一节说的自适应写法。还有一个排查思路用pip list或pip show torch看一下已安装PyTorch的版本号如果版本号后面带cu后缀比如2.3.0cu118说明装的确实是GPU版需要卸载重装CPU版。如果有人是从GPU机器拷贝过来的代码加载权重时也可以用map_location解决。需要注意的是PyTorch在CPU上加载GPU权重有时会提示缺少什么键那多半是模型结构定义和保存时的结构不一致跟设备无关要查模型定义。5.2 CPU训练慢到崩溃先看这几处配置遇到训练慢第一件事不是猜而是测。把一次完整迭代拆开看数据加载耗时多少、模型计算耗时多少。一个简单的办法是在训练循环里手动记录时间戳import time t0 time.time() data_start time.time() inputs, labels next(iter(dataloader)) print(f数据加载耗时: {time.time() - data_start:.2f}s) t_start time.time() outputs model(inputs) print(f模型计算耗时: {time.time() - t_start:.2f}s)如果数据加载耗时占了很大比例就按前面说的预处理和num_workers方案优化。如果模型计算是瓶颈那就得从源头减负了减小batch size、换更小的模型、降低输入分辨率。还有检查一下是不是所有核心都在满负荷工作。打开任务管理器或htop看看CPU占用率。如果某个进程只用一个核心但你机器是八核的说明torch.set_num_threads或者环境变量设置出了问题。有些情况下需要设置环境变量OMP_NUM_THREADS因为PyTorch后端也依赖OpenMP的线程池export OMP_NUM_THREADS85.3 内存OOM与硬盘空间告急CPU训练也会OOM只不过爆的是内存而不是显存。最常见的报错是RuntimeError: DataLoader worker (pid(s) ...) is killed这就是内存爆了系统直接把进程给杀了。处理思路从易到难先减小DataLoader的num_workers哪怕设为0也行让数据加载只占一个进程再减小batch size如果数据集本身太大比如几十GB就要考虑增量加载每次只把当前要用的一批数据读进内存用完就释放。硬盘空间的坑也不小特别是数据集和频繁保存的checkpoint。我遇到过训练到一半硬盘满了的悲剧训练中断不说之前的日志和部分权重也没了。建议在训练脚本里加一个简单的硬盘可用空间检查低于阈值就主动暂停并释放一些不需要的文件。控制checkpoint数量也是好习惯保留最新的两个滚动覆盖就行别每个epoch都留着一个模型权重动辄几百MB几个epoch下来硬盘就吃紧了。写在后面我个人的体会是无GPU环境不是灾反而是一层滤镜把深度学习里那些“靠堆硬件就能解决”的幻觉滤掉了逼着你去理解模型、数据和计算流程的细节。因为等不起我学会了先跑小实验再放大规模因为资源紧张我学会了保存所有中间状态而不是只留最后的权重因为换卡困难我学会了写设备无关的代码后来真拿到GPU卡的时候迁移过程只改了一行。最后再分享一个很实用的小细节把你验证通过的安装命令、依赖版本、训练脚本里那些微调过的参数都写进项目根目录的README或者requirements文件里。团队里其他人换电脑、换云平台的时候照着走能少踩一大半我踩过的坑。这种“穷环境逼出来的配置文档”往往是整个项目最值钱的部分之一。
返回列表