大数跨境

每个训模型的人,都该学会跟自己的显存和解

每个训模型的人,都该学会跟自己的显存和解 AI驱动数字化转型
2026-09-08
5
导读:8G显存,八次僵死。这一趟从数据准备训到DPO上线,我没少跟这块卡较劲。每回都不是显存真不够,是显存明明还够,训练却死给你看。

8G显存,八次僵死。这一趟从数据准备训到DPO上线,我没少跟这块卡较劲。每回都不是显存真不够,是显存明明还够,训练却死给你看。GPU占用是满的,功耗掉下来,卡在一动不动。满转不干活,就是僵死的样子。

我要训的,是给写作用的综合体模型,基座Qwen3.5-4B,一个模型同时覆盖公众号、报告、科研学术三种文体,靠system里的文体指令加温度切换。先SFT把三种文体的口味学一遍,再用DPO做偏好对齐,让模型知道哪些话该说、哪些该避开。训练目标是一套291对的偏好数据,跑73步。听着不多,在8G卡上却差点没跑完。

显存这事,得分清两种账。大多数人看nvidia-smi的总占用,或者看还剩几个G,这俩都不够。PyTorch管显存分两层,一层叫allocated,是你此刻真正用上的那部分,另一层叫reserved,是PyTorch从驱动手里先圈下来囤着备用的。训练越跑,reserved越往上涨,哪怕暂时用不到也不肯放。等你真需要一大块连续显存时,可用的早被reserved占光,剩下的缝隙又拼不出一块整的,它就在内存池里翻来找去,找不到就一直耗。GPU占用看着是满的,其实是它空转着找空间,不是真在算,功耗自然掉下来。显存还有,训练却死了,这就是僵死。

僵死反复出现,我一开始无从下手。后来查到PyTorch有个环境变量PYTORCH_CUDA_ALLOC_CONF,里面一个参数叫garbage_collection_threshold,我设成0.8。意思是显存用到八成,分配器就主动回收一遍那些圈着没用的大块,别等快满了才去翻。显存跟内存一个德行,多数时候不是不够,是没人在中间清,定期清一次比临到关头手忙脚乱强。0.8是我试出来的,设太低清理太勤,每一步都腾地方,训练肉眼可见地慢,设太高等于没设,照样囤到满。八成是个平衡点,既不太勤拖慢,又能在堵死前收拾利索。

光靠这一行还不够,我又写了个VramManager的回调,每隔几步手动empty_cache一次,顺带清内存,每个训练步打印一行实时状态,allocated、reserved、free、碎片全打出来。排查僵死只看这几个数,不看nvidia-smi的总量,这条规矩后面救了我好几回。

跟显存和解,有时得先承认选错了工具。SFT那回就先卡在工具链上,Qwen3.5本身是多模态架构,我图省事拿视觉那套工具链去训纯文本,跑了300多步卡死。查allocated、reserved、free都正常,就是不往前走。后来才反应过来,压根不是显存的事,是工具链选错了。那个数据整理器是给图像模型用的,我拿它喂纯文本,源码里白纸黑字写着只用于图像模型,硬来能不卡。换成text_only只加载文本骨干,绕开视觉模块,那个卡死点再没出现,SFT完整跑完,789条样本,2367步,3个epoch,loss降到1.7,收尾时alloc 3.08G、reserved 3.28G,全程钉在3G上下没涨到8G,0.8加定时清理起作用了。

DPO这关更难缠。头一种我手写个省显存的DPO,用no_grad先算好参考模型的logp再单模型跑,想省显存。跑到第40步loss卡在0.6929不动,这个值本身正常,DPO从chosen和rejected等概率起步,logloss就该是ln2约0.69,正常该一路降下去,可它再也不动。一看显存,allocated 5.24G,reserved涨到7.56G,free归零,参考logp全缓存把reserved顶到天花板,可用显存被挤光,梯度无处落,loss自然学不动,这条路弃了。

后一种用最新的trl1.10,装上一跑就报错,缺个FSDPModule得升torch,升完又报接口变了,等全修好进度条卡在百分之零,第一步都迈不出去,trl1.10在8G上连smoke都跑不完。这里我学到一个,框架的新不等于能跑,反而社区里骂声一片的旧版更稳。后来查到Unsloth官方支持DPO,打上补丁用PatchDPOTrainer,版本锁在trl0.24,配上它的梯度卸载,把暂时不用的梯度先挪回内存,用时间换空间,才在8G上把DPO塞进去。smoke先跑一对样本,一步loss 0.6931正好是ln2,说明chosen和rejected等概率,是个干净起点,过了smoke我估了下单步耗时,全量73步一个多小时,心里有底了。

真正学会和解,是第一次全量僵死在第48步那回。smoke过了不代表全量能成,跑到第48步loss突然爆到九,梯度范数冲到七十二,单步耗时飙到340秒,发散得厉害,我看跑废直接杀掉。这一杀暴露了更要命的问题,我的脚本压根没开中途保存,save_strategy是no,不管跑到第几步,只要没跑完前面的算力全白费,48步清零。僵死可怕就在这,它不一定在开头死,可能在最后一步前死,你熬40多分钟它撂挑子一分没存,只能从头再来。这已经不是显存问题,是容灾问题。我把checkpoint加上每十步存一次,加了resume启动自动扫最近存档接着跑,就算再僵死最多白跑十步,不伤筋动骨。

第二回全量才算真跟它和解。加完checkpoint,我做了一件早该做对的事,独占显卡,之前有几回僵死其实是我自己作,一边训DPO一边开别的推理抢显存,两头打架把训练挤死在资源竞争里。这回独占加checkpoint从头跑,43分钟73步一次跑完没僵一次,整段盯着显存看,alloc 3.06G、reserved 4.14G、free 2.73G从头到尾不变。跟首跑reserved飙到7.6G一比,是0.8、定时清理、独占显卡一起把reserved压住了。

还有个观察值得说,二跑也到第48步,loss短暂跳到八点多,跟首跑爆掉的九几乎一样,但这次我没杀它,让它接着跑,第49步自己落回个位数一路收敛,loss掉到10的负8次方。这说明第48步那个尖峰大概率是某个噪声样本的梯度冲了一下,不是训练真发散,单步loss跳高不一定要kill,给它续跑的机会,这正是checkpoint加resume的意义。学着在它发抖的时候别急着下手。

模型训完,合并成bf16转GGUF量化Q4_K_M,八个多G压到2.7G导入Ollama,模型名沿用writer-agent,同名覆盖SFT旧版,所有调用处一行不用改。评测用同一套集,三文体各十题四个模型一起比,我的DPO版、SFT旧版、本地通用模型ornith、云端DeepSeek,请DeepSeek当裁判,按作者声口、文体规范、内容、整体四维打分。结果DPO版十二项全面压过SFT,报告体从3.8涨到6.8,voice从5.2涨到7.6,追平甚至反超云端DeepSeek,一个8G卡训出的4B小模型在写作这个垂类上咬住了云端大模型。

跟显存和解,其实是跟自己和解。显存要看reserved不是allocated,卡死要看功耗别只看占用,框架要选能跑的别选最新的,训练要留后路别一把梭,还有那行0.8。单拎哪条都不稀奇,凑一起才把一块8G卡的算力榨出来。学会和解的人不是显存多,是懂得在它囤积时替它清一清,在它发抖时给它留口气,你肯这么对它,那块卡能给的可能比你想象多。


【声明】内容源于网络
0
0
AI驱动数字化转型
专注AI,促进智造行业数据衍生,服务智能制造企业的数字化、智能化,聚焦大模型私域部署、大模型微调、数据清洗、AI模型训练、私域知识库及agent技术延展等。行业智能,落地为先。
内容 1084
粉丝 1
AI驱动数字化转型 专注AI,促进智造行业数据衍生,服务智能制造企业的数字化、智能化,聚焦大模型私域部署、大模型微调、数据清洗、AI模型训练、私域知识库及agent技术延展等。行业智能,落地为先。
总阅读10.9k
粉丝1
内容1.1k