AI 文摘

两行代码开启 LoRA 微调 && LLM 情感实体抽取实践





作者: AINLP  来源: [AINLP](https://mp.weixin.qq.com/s/F5SZIvYocwimopOCghpU1Q)

Huggingface 开源的 PEFT 大模型高效微调工具包,让普通老百姓玩起大模型不再是梦。下面介绍了笔者的一个 github 仓库,对代表性中文大模型进行 LoRA 微调,只要你有训练数据,然后本地下载好大模型的checkpoint,就可以最少只需 2 行代码 就可以微调你自己的 LLM。然后,介绍了我使用这个工具,进行 LLM 情感实体抽取 的一个实践。

仓库地址: *https://github.com/beyondguo/LLM-Tuning *

目前支持:

  • 清华 ChatGLM-6B 的 LoRA 微调
  • 百川智能 baichuan-7B 的 LoRA 微调
  • 更多模型持续更新中…

两行代码开启训练

  • 数据集分词预处理:sh tokenize.sh ,对比不同的 LLM,需在 tokenize.sh 文件里切换 model_checkpoint 参数
  • 开启 LoRA 微调:sh train.sh ,对于不同的 LLM,需切换不同的 python 文件来执行:
    • ChatGLM-6B 应使用 chatglm_lora_tuning.py

    • baichuan-7B 应使用 baichuan_lora_tuning.py

环境准备
pip install transformers datasets accelerate sentencepiece tensorboard peft

目前测试的环境为:

- torch, Version: 2.0.1  
- transformers, Version: 4.29.1  
- datasets, Version: 2.12.0  
- accelerate, Version: 0.19.0  
- peft, Version: 0.3.0  
- sentencepiece, Version: 0.1.99  
- tensorboard, Version: 2.13.0  

教程:

下面的教程以及代码使用 ChatGLM-6B 作为例子,如果更换其他模型,可能需要略微修改具体文件代码。

1. 指令微调数据准备

① 原始文件的准备

指令微调数据一般有输入和输出两部分,输入是特定的content加上instruction,这里我们将二者直接拼在一起,不单独区分;输出则是希望模型的回答。我们统一使用json 的格式在整理数据,可以自定义输出输出的字段名,例如下面的例子中我使用的是q 和a 代表模型的输入和输出:

{"q": "请计算:39 * 0 = 什么?", "a": "这是简单的乘法运算,39乘以0得到的是0"}  
{"q": "题目:51/186的答案是什么?", "a": "这是简单的除法运算,51除以186大概为0.274"}  
{"q": "鹿妈妈买了24个苹果,她想平均分给她的3只小鹿吃,每只小鹿可以分到几个苹果?", "a": "鹿妈妈买了24个苹果,平均分给3只小鹿吃,那么每只小鹿可以分到的苹果数就是总苹果数除以小鹿的只数。\n24÷3=8\n每只小鹿可以分到8个苹果。所以,答案是每只小鹿可以分到8个苹果。"}  
...  

整理好数据后,保存为.json 或者.jsonl 文件,然后放入目录中的data/ 文件夹中。

② 对数据集进行分词

为了避免每次训练的时候都要重新对数据集分词,我们先分好词形成特征后保存成可直接用于训练的数据集。

例如,

  • 我们的原始指令微调文件为:data/ 文件夹下的 simple_math_4op.json 文件

  • 输入字段为q ,输出字段为a

  • 希望经过 tokenize 之后保存到 data/tokenized_data/ 下名为 simple_math_4op 的文件夹中

  • 设定文本最大程度为 2000

则我们可以直接使用下面这段命令 (即tokenize.sh 文件) 进行处理:

CUDA_VISIBLE_DEVICES=0,1 python tokenize_dataset_rows.py \  
    --model_checkpoint THUDM/chatglm-6b \  
    --input_file simple_math_4op.json \  
    --prompt_key q \  
    --target_key a \  
    --save_name simple_math_4op \  
    --max_seq_length 2000 \  
    --skip_overlength False  

处理完毕之后,我们会在 data/tokenized_data/ 下发现名为 simple_math_4op 的文件夹,这就是下一步中我们可以直接用于训练的数据。

2. 使用 LoRA

微调

得到 tokenize 之后的数据集,就可以直接运行 chatglm_lora_tuning.py 来训练 LoRA 模型了,具体可设置的主要参数包括:

  • tokenized_dataset , 分词后的数据集,即在 data/tokenized_data/ 地址下的文件夹名称
  • lora_rank , 设置 LoRA 的秩,推荐为4或8,显存够的话使用8
  • per_device_train_batch_size , 每块 GPU 上的 batch size
  • gradient_accumulation_steps , 梯度累加,可以在不提升显存占用的情况下增大 batch size
  • max_steps , 训练步数
  • save_steps , 多少步保存一次
  • save_total_limit , 保存多少个checkpoint
  • logging_steps , 多少步打印一次训练情况(loss, lr, etc.)
  • output_dir , 模型文件保存地址

例如我们的数据集为 simple_math_4op,希望保存到 weights/simple_math_4op ,则执行下面命令(即train.sh 文件):

CUDA_VISIBLE_DEVICES=2,3 python chatglm_lora_tuning.py \  
    --tokenized_dataset simple_math_4op \  
    --lora_rank 8 \  
    --per_device_train_batch_size 10 \  
    --gradient_accumulation_steps 1 \  
    --max_steps 100000 \  
    --save_steps 200 \  
    --save_total_limit 2 \  
    --learning_rate 1e-4 \  
    --fp16 \  
    --remove_unused_columns false \  
    --logging_steps 50 \  
    --output_dir weights/simple_math_4op  

训练完之后,可以在 output_dir 中找到 LoRA 的相关模型权重,主要是adapter_model.bin 和adapter_config.json 两个文件。

如何查看 tensorboard:

  • 在 output_dir 中找到 runs 文件夹,复制其中日期最大的文件夹的地址,假设为 your_log_path

  • 执行 tensorboard –logdir your_log_path 命令,就会在 http://localhost:6006/ 上开启tensorboard

  • 如果是在服务器上开启,则还需要做端口映射到本地。推荐使用 VSCode 在服务器上写代码,可以自动帮你进行端口映射。

  • 如果要自己手动进行端口映射,具体方式是在使用 ssh 登录时,后面加上 -L 6006:127.0.0.1:6006 参数,将服务器端的6006端口映射到本地的6006端口。

3. 拿走 LoRA 小小的文件,到你本地的大模型上加载并推理

我们可以把上面的 output_dir 打包带走,假设文件夹为 weights/simple_math_4op , 其中(至少)包含 adapter_model.bin 和 adapter_config.json 两个文件,则我们可以用下面的方式直接加载,并推理

from peft import PeftModel  
from transformers import AutoTokenizer, AutoModel  
import torch  
  
device = torch.device(1)  
# 加载原始 LLM  
model_path = "THUDM/chatglm-6b"  
model = AutoModel.from_pretrained(model_path, trust_remote_code=True).half().to(device)  
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)  
model.chat(tokenizer, "你好", history=[])  
  
  
# 给原始 LLM 安装上你的 LoRA tool  
model = PeftModel.from_pretrained(model, "weights/simple_math_4op").half()  
model.chat(tokenizer, "你好", history=[])  

理论上,可以通过多次执行 model = PeftModel.from_pretrained(model, “weights/simple_math_4op”).half() 的方式,加载多个 LoRA 模型,从而混合不同Tool的能力,但实际测试的时候,由于暂时还不支持设置不同 LoRA weights的权重,往往效果不太好,存在覆盖或者遗忘的情况。

情感实体抽实践 | 效果展示

我最近对使用 LLM 做信息抽取比较感兴趣,因此我微调的目的是让模型做某些特殊场景下的复杂抽取。比如对于下面这段文本(我个人胡编乱造 的新闻):

ChatGPT的提出对谷嘎、万度的搜索业务产生巨大打击,传统搜索引擎的作用性降低了。  
与此同时,OChat,Linguo等新兴语义搜索公司,迅速推出自己的类ChatGPT模型,并结合进自家搜索引擎,受到了很多用户的青睐。  
腾势、艾里等公司表示会迅速跟进ChatGPT和AIGC的发展,并预计在年底前推出自己的大模型。  
大型图片供应商视觉中国称ChatGPT对公司业务暂无影响,还在观望状态。  
(本文图片来自视觉中国,上观新闻为您报道。)  
更多报道:  
- 亚牛逊公司关于AIGC的表态  
- 巨硬公司昨日在A股上市  

在情感分析中,我们不仅想知道新闻的情感,更想知道其中涉及的各种实体的情感,以及对应的原因。 这类似于“方面情感分析”任务,然而LLM时代前的模型很难做好这个任务。

我设计了这样的 instruction:

请从上文中抽取出所有公司,以及对应的在本文中的情感倾向(积极、消极、中性)以及原因。  
请用这样的格式返回:  
{"ORG":..., "sentiment":..., "reason":...}  

然后交给ChatGPT来做,下面是 ChatGPT 的输出:

{"ORG": "谷嘎", "sentiment": "消极", "reason": "ChatGPT的提出对谷嘎的搜索业务产生巨大打击"}  
{"ORG": "万度", "sentiment": "消极", "reason": "ChatGPT的提出对万度的搜索业务产生巨大打击"}  
{"ORG": "OChat", "sentiment": "积极", "reason": "新兴语义搜索公司迅速推出自己的类ChatGPT模型,并受到很多用户的青睐"}  
{"ORG": "Linguo", "sentiment": "积极", "reason": "新兴语义搜索公司迅速推出自己的类ChatGPT模型,并受到很多用户的青睐"}  
{"ORG": "腾势", "sentiment": "积极", "reason": "表示会迅速跟进ChatGPT和AIGC的发展,并预计在年底前推出自己的大模型"}  
{"ORG": "艾里", "sentiment": "积极", "reason": "表示会迅速跟进ChatGPT和AIGC的发展,并预计在年底前推出自己的大模型"}  
{"ORG": "视觉中国", "sentiment": "中性", "reason": "称ChatGPT对公司业务暂无影响,还在观望状态"}  
{"ORG": "亚牛逊", "sentiment": "中性", "reason": "没有提及具体的情感倾向和原因"}  
{"ORG": "巨硬", "sentiment": "中性", "reason": "没有提及具体的情感倾向和原因"}  

可见效果还是非常好的。

然后我们直接试试 ChatGLM-6B 呢:

{"ORG": "谷嘎", "sentiment": "消极", "reason": "ChatGPT的提出对谷嘎、万度的搜索业务产生巨大打击,传统搜索引擎的作用性降低了。"}  
{"ORG": "万度", "sentiment": "消极", "reason": "ChatGPT的提出对谷嘎、万度的搜索业务产生巨大打击,传统搜索引擎的作用性降低了。"}  
{"ORG": "OChat", "sentiment": "中性", "reason": "新兴语义搜索公司,迅速推出自己的类ChatGPT模型,并结合进自家搜索引擎,受到了很多用户的青睐。"}  
{"ORG": "腾势", "sentiment": "中性", "reason": "会迅速跟进ChatGPT和AIGC的发展,并预计在年底前推出自己的大模型。"}  
{"ORG": "艾里", "sentiment": "中性", "reason": "大型图片供应商视觉中国称ChatGPT对公司业务暂无影响,还在观望状态。"}  
{"ORG": "亚牛逊", "sentiment": "消极", "reason": "关于AIGC的表态。"}  

乍一眼好像还行,但仔细一看,存在情感理解错误、漏掉实体 等情况,例如“亚牛逊”应该是中性,“腾势”、“艾里”应该是积极的,漏掉了"视觉中国”。

然后,我使用 ChatGPT 的 API(gpt-3.5-turbo) 标注了大概一万多条数据,使用我的仓库进行微调,下面是微调后的效果,姑且称之为 ChatGLM-6B-lora

{"ORG": "谷嘎", "sentiment": "消极", "reason": "搜索业务受到打击"}  
{"ORG": "万度", "sentiment": "消极", "reason": "搜索业务受到打击"}  
{"ORG": "OChat", "sentiment": "积极", "reason": "迅速推出自己的类ChatGPT模型,并结合进自家搜索引擎,受到了很多用户的青睐"}  
{"ORG": "Linguo", "sentiment": "积极", "reason": "迅速推出自己的类ChatGPT模型,并结合进自家搜索引擎,受到了很多用户的青睐"}  
{"ORG": "腾势", "sentiment": "积极", "reason": "会迅速跟进ChatGPT和AIGC的发展,并预计在年底前推出自己的大模型"}  
{"ORG": "艾里", "sentiment": "积极", "reason": "会迅速跟进ChatGPT和AIGC的发展,并预计在年底前推出自己的大模型"}  
{"ORG": "视觉中国", "sentiment": "中性", "reason": "表示ChatGPT对公司业务暂无影响,还在观望状态"}  
{"ORG": "亚牛逊", "sentiment": "中性", "reason": "关于AIGC的表态"}  
{"ORG": "巨硬", "sentiment": "中性", "reason": "昨日在A股上市"}  

不解释了,非常不错!

对于百川大模型(baichuan-7B) ,这其实是一个基座模型,没有Chat能力,所以原始模型对这样的 instruction 根本无法输出,经过我的微调之后,也具备了听从instruction的能力,称之为 baichuan-7B-lora 吧:

{"ORG":"谷歌","sentiment":"消极","reason":"受到ChatGPT的影响,其作用性下降"}  
{"ORG":"百度","sentiment":"消极","reason":"同样受到ChatGPT的影响,其作用性下降"}  
{"ORG":"腾讯","sentiment":"中性","reason":"没有明确表达态度或情绪"}  
{"ORG":"阿里巴巴","sentiment":"中性","reason":"没有明确表达态度或情绪"}  
{"ORG":"视觉中国","sentiment":"中性","reason":"目前还没有明显表现出来受ChatGPT的影响,但仍在观察之中"}  
{"ORG":"亚马逊","sentiment":"中性","reason":"该公司对于AIGC的态度是谨慎而保守的,并没有给出具体评价"}  
{"ORG":"微软","sentiment":"中性","reason":"该公司的股价已经上涨到历史新高,并且宣布将在国内市场进行二次上市,因此对其未来的发展持乐观态度"}  

这里有点搞笑,我明明给的新闻都是“谷嘎”,“万度”,“腾势”这样的我编造的公司(虽然大家肯定知道我改编自是哪家公司)这个百川居然都给我掰回来了。。。甚至,“巨硬”它都能给我掰回来成“微软”。。。真不知道是该夸它呢,还是该骂它。

不过,至少也让一个从来都不会听 instruction 的基座模型,听从我的抽取指示进行十分规范地抽取了。

以上。

欢迎大家使用我的这个仓库:

*https://github.com/beyondguo/LLM-Tuning *

Acknowledgement

对这些优秀开源项目表示感谢!

**进技术交流群请添加AINLP小助手微信(id: ainlp2)**   


**请备注具体方向+所用到的相关技术点** ![](/images/20230623/8930214020137928598.webp)

**关于AINLP** 

AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLP小助手微信(id:ainlp2),备注工作/研究方向+加群目的。

  


  


![](/images/20230623/3932909149814453281.webp)

**阅读至此了,分享、点赞、在看三选一吧🙏** 
可关注我们的公众号:每天AI新工具