【错误记录】llamafactory-train的时候-跑到50%左右-OOM [复制链接]
发表于 2025-8-31 03:49:33 | 显示全部楼层 |阅读模式
两张V100*32G
训练LLaMA3-8B-Chat
从硬件设施上来看训这个8B应该是绰绰有余不至于OOM

原来的运行命令:
  1. #!/bin/bash
  2. CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.run --nproc_per_node=2 src/train.py\
  3.     --stage sft \
  4.     --do_train True \
  5.     --model_name_or_path {模型路径} \
  6.     --preprocessing_num_workers 16 \
  7.     --finetuning_type lora \
  8.     --template llama3 \
  9.     --flash_attn auto \
  10.     --dataset_dir data \
  11.     --dataset {数据集} \
  12.     --cutoff_len 100000 \
  13.     --learning_rate 5e-05 \
  14.     --num_train_epochs 3.0 \
  15.     --max_samples 100000 \
  16.     --per_device_train_batch_size 1 \
  17.     --gradient_accumulation_steps 4 \
  18.     --gradient_checkpointing true   \
  19.     --lr_scheduler_type cosine \
  20.     --max_grad_norm 1.0 \
  21.     --logging_steps 5 \
  22.     --save_steps 100 \
  23.     --warmup_steps 0 \
  24.     --packing False \
  25.     --report_to all \
  26.     --output_dir {输出路径} \
  27.     --fp16 True \
  28.     --plot_loss True \
  29.     --ddp_timeout 180000000 \
  30.     --optim adamw_torch \
  31.     --lora_rank 8 \
  32.     --lora_alpha 16 \
  33.     --lora_dropout 0 \
  34.     --lora_target all
复制代码
修改后:
  1. PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128 \
  2. ...
  3. --cutoff_len 4096 \
  4. ...
  5. --gradient_accumulation_steps 8 \
  6. ...
复制代码
先说问题缘故起因:

1. 重要问题是--cutoff_len 100000 \引起的

  • 由于我的pormpt和input字段都很长,所以直击把llama factory里的截断长度拉到最大了(应该是13万多),这个导致了内存溢出,实际上改成4096就可以运行了。
2. 在来一套组合拳,就可以确保不会内存溢出

  • PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128
  • –gradient_accumulation_steps 8
  1. #!/bin/bash
  2. PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128 \
  3. CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.run --nproc_per_node=2 src/train.py\
  4.     --stage sft \
  5.     --do_train True \
  6.     --model_name_or_path {模型路径} \
  7.     --preprocessing_num_workers 16 \
  8.     --finetuning_type lora \
  9.     --template llama3 \
  10.     --flash_attn auto \
  11.     --dataset_dir data \
  12.     --dataset {数据集} \
  13.     --cutoff_len 4096 \
  14.     --learning_rate 5e-05 \
  15.     --num_train_epochs 3.0 \
  16.     --max_samples 100000 \
  17.     --per_device_train_batch_size 1 \
  18.     --gradient_accumulation_steps 8 \
  19.     --gradient_checkpointing true   \
  20.     --lr_scheduler_type cosine \
  21.     --max_grad_norm 1.0 \
  22.     --logging_steps 5 \
  23.     --save_steps 100 \
  24.     --warmup_steps 0 \
  25.     --packing False \
  26.     --report_to all \
  27.     --output_dir {输出路径} \
  28.     --fp16 True \
  29.     --plot_loss True \
  30.     --ddp_timeout 180000000 \
  31.     --optim adamw_torch \
  32.     --lora_rank 8 \
  33.     --lora_alpha 16 \
  34.     --lora_dropout 0 \
  35.     --lora_target all
复制代码
为什么修改起作用

我在用llama factory微调模型,为什么–cutoff_len 100000会导致OOM,截断长度不是对每条数据而言吗?我的每条数据是远远小于100000的?
内存分配计谋:即使你的单条数据远小于100000,但在训练过程中,PyTorch会动态地为模型分配内存。假如--cutoff_len设置得过高,可能会导致PyTorch实验为更大的序列分配内存,从而超出GPU的显存容量。
为了解决这个问题,你可以实验:

  • 降低--cutoff_len的值:将其设置为更接近你数据实际必要的最大长度,减少不须要的内存分配。

{批量大小和梯度累积:--per_device_train_batch_size 和--gradient_accumulation_steps 参数共同决定了实际的批量大小。}
你说的这句话我很认同,但是我不明白这两参数的作用,以及他们的合适大小,和大小关系,我设置的是
–per_device_train_batch_size 1
–gradient_accumulation_steps 4
1. 参数解释


  • --per_device_train_batch_size
    每个设备(如每块GPU)在一次前向传播中处理的样本数,称为每设备批量大小。
    假如你有多个设备,总批量大小还必要乘以设备数量。
  • --gradient_accumulation_steps
    梯度累积步数,即在更新模型参数前,累积的前向和后向传播的次数。
    这样,即使你的显存不足以直接处理一个大批量,你也可以通过累积多个小批量的梯度,来模仿更大的批量大小。

2. 实际批量大小的计算

实际总批量大小可以用公式表示为:

根据你的设置:

  • 每设备批量大小:1
  • 梯度累积步数:4
  • 假设你只有一个设备:设备数量为 1。
因此:

这意味着,在每次参数更新时,你的模型实际上使用的是一个大小为 4 的总批量。

3. 参数的作用


  • --per_device_train_batch_size 控制单次前向传播的内存使用量。假如显存不足,可以设置较小值。
  • --gradient_accumulation_steps 用于在显存受限的情况下实现较大的有效批量大小。通过梯度累积,模型会在 gradient_accumulation_steps 次前向传播后才更新参数。

4. 怎样选择合适的大小

选择这些参数时,必要思量以下几点:

  • 显存限定

    • 假如显存较小,建议降低 --per_device_train_batch_size,同时增大 --gradient_accumulation_steps。
    • 比方:显存不足以支持批量大小为 16,可以设置 --per_device_train_batch_size 2 和 --gradient_accumulation_steps 8。

  • 总批量大小的效果

    • 理论上,总批量大小越大,模型训练的稳定性越高,得当较大的学习率。
    • 太小的批量可能导致训练不稳定。

  • 设备数量的影响

    • 假如你有多块 GPU,总批量大小会随着 GPU 数量增加。比如,4 块 GPU 的设置中,--per_device_train_batch_size 4,--gradient_accumulation_steps 2,总批量大小为:



5. 推荐设置

以下是几个常见情况的建议:

  • 显存紧张

    • 减小 --per_device_train_batch_size。
    • 增大 --gradient_accumulation_steps。

  • 显存充裕

    • 尽量增大 --per_device_train_batch_size,减少梯度累积次数,提高训练速率。

  • 实际测试

    • 确保总批量大小得当模型和使命。常见的总批量大小在 16 到 128 之间,具体取决于模型规模和数据量。


PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128,这条命令起什么作用?以及为什么在OOM的时候有效
1. 命令的作用

PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128 是 PyTorch 提供的一种控制 GPU 内存分配计谋的情况变量配置。它重要用于调治内存分配器在分配显存时的最大分块大小

  • max_split_size_mb
    指定内存分配器在分配显存时的单个分块的最大大小(单位是 MB)。默认值是相对较大的块(比方 2GB 左右,具体取决于显存和场景)。
    当这个值被设置得较小(比方 128MB),内存分配器会将较大的内存需求拆分成多个小块,从而减少大块内存分配失败的风险。

2. 为什么在 OOM(Out of Memory) 时有效

当显存不足时,OOM 的重要缘故起因是内存碎片化问题:

  • PyTorch 在显存管理上接纳了内存池机制来提拔性能,即提前申请大块显存池供程序使用。
  • 假如显存已经分配了一些块,但由于碎片化导致没有足够大的连续内存块满意分配需求,就会报 OOM 错误,即使总剩余显存量足够。
通过设置 max_split_size_mb:

  • 将大的内存哀求拆分为多个小块,降低对连续大内存块的需求。
  • 通过这种方式,有助于更好地利用显存碎片,从而减少 OOM 发生。

3. 应用场景


  • 大模型训练时

    • 假如显存紧张或大模型的激活函数占用大量显存,设置 max_split_size_mb 可以有效减少 OOM。

  • 显存碎片化严重

    • 在多次模型训练或推理使命中,由于显存频仍分配和开释,可能导致碎片化。

  • 动态计算图

    • 在使用诸如 torch.nn.DataParallel 或动态计算图时,大块显存可能分配失败,此配置可以帮助更高效地分配小块显存。


4. 使用建议


  • 推荐值

    • max_split_size_mb:128 是常见的推荐值,但也可以根据实际需求调整,比方 64MB 或 256MB。
    • 假如内存需求较小,可以选择更小的值;对于显存较大的场景,可以稍微调大。

  • 怎样设置

    • 在运行程序前,设置情况变量:
      1. export PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128
      复制代码
    • 或者在代码中动态设置:
      1. import os
      2. os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:128'
      复制代码

  • 调试 OOM 问题

    • 假如设置此值仍无法解决 OOM,可以实验进一步减少 batch_size 或启用 torch.cuda.empty_cache() 在关键点开释显存。


5. 总结

PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128 的焦点作用是优化显存分配,通过限定单块内存分配的最大大小来减少碎片化和大块分配失败。对于显存紧张或碎片化严重的场景,这是一种简单而有效的优化方法。
也就是我的分块大小最大是128MB是吗?这样就不会出现大块内存占用只剩下小块?
是的,你理解得非常正确!设置 PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128 后,分块大小的上限变成了 128MB,从而优化显存的利用效率,减少 OOM 的风险。以下是进一步解释:

1. 为什么分块大小设置为 128MB 有帮助?

显存的 OOM 问题通常源于内存碎片化,即固然显存总量足够,但由于内存被分割成了不连续的小块,大的内存分配哀求(如 512MB 或 1GB)无法找到足够大的连续区域。
通过限定分块大小:

  • 大内存哀求会被拆分成多个小块(如 128MB 的小块),这样即使显存存在碎片,也更容易分配乐成。
  • 减少“剩下很多小块但无法利用”的情况,由于拆分后的小块能够拼接到碎片中更高效地利用。

2. 分块最大为 128MB的实际效果


  • 每次分配显存时,假如需求超过 128MB,大内存分配会拆成多个 128MB 块。比方:

    • 假如必要分配 512MB,则会拆成 4 个 128MB 块。

  • 显存中的碎片被更高效地利用:

    • 假设显存碎片分别为 200MB、150MB 和 300MB,假如不限定分块大小,分配一个 512MB 内存可能失败(OOM)。
    • 但假如拆分成 128MB 小块,则内存分配可以乐成(150 + 150 + 150 + 62 = 512MB)。


3. 有了限定后还会 OOM 吗?

限定分块大小后,仍可能出现以下情况导致 OOM:

  • 显存总量确实不足:即使通过拆分,也没有足够的小块总量。
  • 梯度累积或激活占用过多:显存中必要同时保存前向、后向计算的中间效果(尤其是多层 Transformer 模型),假如这些效果太多或太大,显存仍然会耗尽。
解决方法

  • 进一步减少 --per_device_train_batch_size。
  • 启用显存优化选项,比方 PyTorch 的 torch.utils.checkpoint 或模型并行。
  • 思量混淆精度训练(使用 torch.cuda.amp 或 transformers 提供的 fp16 支持)。

4. 设置分块大小的留意事项


  • 分块大小不能太小
    太小的分块会导致分配和开释显存的频率增加,从而降低性能。128MB 是一个合理的折中值。
  • 显存足够时可以调大
    假如显存充裕,比方使用 32GB 显存的 GPU,可以实验将 max_split_size_mb 设置为更大的值(如 256MB 或 512MB),以减少分块分配的开销。

5. 总结

通过设置 max_split_size_mb:128,PyTorch 内存分配器可以避免由于找不到大块连续内存而导致的 OOM,大大提高了显存碎片化情况下的利用率。这种方法对于显存紧张或碎片化严重的场景非常有效。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表