大模型微调踩坑记录 - 基于Alpaca-LLaMa+Lora

[复制链接]
查看1043 | 回复0 | 2023-8-10 13:24:01 | 显示全部楼层 |阅读模式
前言

为了使用开放权重的LLM(大语言模型),基于自己的训练集,微调模型,会涉及到如下一些技术要点:


  • 配置运行环境
  • 下载、加载基础模型
  • 收集语料、微调训练
  • 检验训练效果
在实施过程中,遇到不少困难,因此写下这篇文档,做为记录。
环境配置

1. 模型加载 - icetk报错

(1) 问题描述

在huggingface的模型库中,大模型会被分散为多个bin文件,在加载这些原始模型时,有些模型(如Chat-GLM)需要安装icetk。
这里遇到了第一个问题,使用pip安装icetk和torch两个包后,使用from_pretrained加载模型时会报缺少icetk的情况。
但实际情况是这个包已经安装了。
   查资料的过程中,有人说出现该错误的原因是icetk依赖cpu版torch2.0,需要先装icetk再装GPU版torch。但我在服务器上尝试该方案后,并不成功。
之后在github上看到一条相同的issue,下方的回答中提到的方法,成功解决了我的问题。
  (2) 解决方案



  • 启动python环境,执行import icetk会报错,并指向lzma.py中的引用错误
  • 根据错误提示,定位到lzma.py文件及相关代码的位置
  • 按如下方式修改源代码
修改前
  1. from _lzma import *
  2. from _lzma import _encode_filter_properties, _decode_filter_properties
复制代码
修改后
  1. try:
  2.         from _lzma import *
  3.         from _lzma import _encode_filter_properties, _decode_filter_properties
  4. except ImportError:
  5.         from backports.lzma import *
  6.         from backports.lzma import _encode_filter_properties, _decode_filter_properties
复制代码
2. 安装指定版本 - transformers安装

(1) 问题描述

截止到目前(2023-04-06),Meta官方仍未对LLaMa模型开源,网上流传的为泄露版。因此,正式版transformers没有该模型的Model或Tokenizer库。
   在查找资料的过程中,有人说使用pip install git+某版本transformer安装dev版transformers。
但由于当我使用该指令安装时,这里指定的分支已经被删除了,所以无法安装成功。
  (2) 解决方案

因为无法通过pip install的方式安装,所以这里考虑用编译源码的方式安装。
  1. git clone https://github.com/huggingface/transformers.git
  2. pip install -e .
复制代码
  因为模型目前还未开源,所以使用这种方式解决。未来Meta正式开源该模型后,transformers肯定会提供支持。
届时,直接使用pip install transformers即可。
  微调训练

1. 并行计算 - bitsandbytes报错

(1) 问题描述

安装bitsandbytes,可以在加载模型时,设置load_in_8bit=True, device_map='auto'降低显存,并将模型分布到GPU上计算。
但在引用时会出现警告: UserWarning: The installed version of bitsandbytes was compiled without GPU support.。
并在模型加载时会报有关"libsbitsandbytes_cpu.so"的编译错误。
(2) 解决方案


  • 使用pip install bitsandbytes正常安装库
  • 切换到bitsandbytes所在lib目录,例如: xxx/venv/lib/python3.9/site-packages/bitsandbytes/cuda_setup
  • 使用vim指令或其他方式编辑main.py文件
  • 定位到if not torch.cuda.is_available(): return 'libsbitsandbytes_cpu.so', None, None, None, None,将其替换为if torch.cuda.is_available(): return 'libbitsandbytes_cuda116.so', None, None, None, None 。(并非一定要使用cuda116,只需大于等于自身显卡cuda版本即可)
  • 定位到self.lib = ct.cdll.LoadLibrary(binary_path),会找到两处,把两处都替换为self.lib = ct.cdll.LoadLibrary(str(binary_path))
   可能是因为Nvidia针对浮点型计算进行过优化,使用load_in_8bit=True让参数以整型进行计算,反而会降低计算速度。
因此,在显存足够时,建议不使用该设置。
  2. 模型微调 - 使用PEFT

Lora技术提出之后,huggingface提供了PEFT框架支持,可通过pip install peft安装。
使用时分为如下步骤:

  • 参数设置 - 配置Lora参数,通过get_peft_model方法加载模型。
  • 模型训练 - 此时只会微调模型的部分参数、而其他参数不变。
  • 模型保存 - 使用model.save_pretrained("output_dir")方法保存模型。
  • 模型加载 - 读取保存peft模型及相关配置,使用PeftModel.from_pretrained(model, peft_model_id)方法加载模型。
(1) 模型训练、保存

  1. from transformers import AutoModel
  2. from peft import get_peft_model, LoraConfig
  3. # Lora参数设置
  4. peft_config = LoraConfig(
  5.         r=lora_r,
  6.         lora_alpha=lora_alpha,
  7.         target_modules=lora_target_modules,
  8.         lora_dropout=lora_dropout,
  9.         bias="none",
  10.         task_type="CAUSAL_LM",
  11. )
  12. # 模型加载
  13. model = AutoModel.from_pretrained("model_name_or_path")
  14. model = get_peft_model(model, peft_config)
  15. # output: trainable params: 4194304 || all params: 6742609920 || trainable%: 0.06220594176090199
  16. model.print_trainable_parameters()
  17. # 模型训练
  18. ...
  19. # 模型保存
  20. model.save_pretrained("output_dir")
复制代码
(2) 模型加载、计算

以下是官方给出的样例,但是按该方式加载模型,在计算时会出现AttributeError: 'NoneType' object has no attribute 'device'报错。
  1. from transformers import AutoModel, AutoTokenizer
  2. from peft import PeftModel, PeftConfig
  3. # 加载peft配置
  4. peft_model_id = "output_dir"
  5. peft_config = PeftConfig.from_pretrained(peft_model_id)
  6. # 加载tokenizer
  7. tokenizer = AutoTokenizer.from_pretrained(peft_config.base_model_name_or_path)
  8. # 结合基础模型和微调结果,加载模型
  9. model = AutoModel.from_pretrained(peft_config.base_model_name_or_path)
  10. model = PeftModel.from_pretrained(model, peft_model_id)
  11. model = model.to(device)
  12. model.eval()
  13. # 模型计算
  14. ...
复制代码
在查找资料后,发现这是PEFT在遇到GPU加载模型时会产生的bug。
根据使用环境是单GPU或多GPU,分别按以下两种方式使用,即可解决上述问题。
解决方案 - 单GPU

  1. # 加载基础模型
  2. base_model = AutoModel.from_pretrained(
  3.     model_path,
  4.     load_in_8bit=True,
  5.     torch_dtype=torch.float16,
  6.     # device_map="{'': 0}"
  7.     device_map="auto"
  8. )
  9. peft_model = PeftModel.from_pretrained(
  10.     base_model,
  11.     peft_model_id,
  12.     torch_dtype=torch.float16,
  13.     # device_map="{'': 0}"
  14.     device_map="auto"
  15. )
复制代码
解决方案 - 多GPU

  1. # 加载基础模型
  2. base_model = AutoModel.from_pretrained(
  3.     model_path,
  4.     load_in_8bit=True,
  5.     torch_dtype=torch.float16,
  6.     device_map="auto"
  7. )
  8. # 获取模型参数分配表
  9. device_map = {f"base_model.model.{k}": v for k, v in model.hf_device_map.items()}
  10. # 按相同的参数分配加载peft model
  11. peft_model = PeftModel.from_pretrained(
  12.     base_model,
  13.     peft_model_id,
  14.     device_map=device_map,
  15.     torch_dtype=torch.float16
  16. )
复制代码
  在查找资料的过程中,我先看到的是单GPU的解决方案,但是该方案对多GPU不适用。
应该是因为自动分配参数时,无法保证peft_model和base_model的分配方法一致。
因此,在多GPU环境下,需通过device_map绑定二者参数的分配方式。
  吐槽

在查资料的过程中,建议大家多用谷歌、少用百度
上述这几个问题,都在github对应仓库的issue里有人提出过,我也都是从下方的答复中找到的解决方案。
但是在用百度和谷歌搜报时,百度却从没给出github上的回答。
百度谷歌 参考文档

   icetk报错 - https://github.com/THUDM/ChatGLM-6B/issues/323
bitsandbytes报错 - https://github.com/oobabooga/text-generation-webui/issues/147#issuecomment-1456040134
peft使用说明 - https://huggingface.co/blog/peft
peft加载模型 - https://github.com/tloen/alpaca-lora/issues/77

来源:https://blog.csdn.net/anycall201/article/details/129959567
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

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

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则