模型训练-下篇(完结篇)

八、什么是训练/预训练/微调/轻量化微调

  • 模型训练(Training)
  • 预训练(Pre-Training)
  • 微调(Fine-Tuning)
  • 轻量化微调(Parameter Efficient Fine-Tuning, PEFT)

在这里插入图片描述

九、Transformer 结构简介

Transformer 是组成 LLM 的基本单元。

或者说,一个 LLM 就是一个 N N N 层 Transformer 网络,例如 GPT-3.5 是 96 层。

9.1、Transformer 内部解构简图

在这里插入图片描述

Self-Attention 的计算

在这里插入图片描述

全连接层:

  • 回忆上节课的全连接网络
  • 这里的激活函数一般是 GELU 或 Swish

为了严谨:在 Self-Attention 和全连接网络之间还有个残差和 LayerNorm,图中未展示

9.2、LM Head (选)

在这里插入图片描述

扩展阅读:
  • 更详细的Transformer网络拆解(Encoder-Decoder):https://jalammar.github.io/illustrated-transformer/
  • 更详细的GPT模型拆解:https://jalammar.github.io/illustrated-gpt2/

十、轻量化微调

在这里插入图片描述

  • 定义微调数据集加载器
  • 定义数据处理函数
  • 加载预训练模型:AutoModel.from_pretrained(MODEL_NAME_OR_PATH)
  • 在预训练模型上增加任务相关输出层 (如果需要)
  • 加载预训练 Tokenizer:AutoTokenizer.from_pretrained(MODEL_NAME_OR_PATH)
  • 定义注入参数的方法(见下文)
  • 定义各种超参
  • 定义 Trainer
  • 定义 Evaluation Metric
  • 开始训练

10.1、注入伪 Embedding 或 Hidden States(选)

1. Prompt Tuning
  • 在输入序列前,额外加入一组伪 Embedding 向量
  • 只训练这组伪 Embedding,从而达到参数微调的效果

在这里插入图片描述

2. P-Tuning
  • 用一个生成器生成上述伪 Embedding
  • 只有生成器的参数是可训练的

在这里插入图片描述

3. Prefix-Tuning
  • 伪造前面的 Hidden States
  • 只训练伪造的这个 Prefix
    在这里插入图片描述
划重点: 此类方法作为早期的 PEFT 方法,经过一段时间检验后,在通用性和最终效果上普遍不如 LoRA 类方法。目前只有 GLM 系列模型官方仍有延用 Prefix-Tuning。建议作为了解即可。

10.2、注入参数矩阵

1. LoRA
  • 在 Transformer 的参数矩阵上加一个低秩矩阵( A × B A\times B A×B
  • 只训练 A,B
  • 理论上可以把上述方法应用于 Transformer 中的任意参数矩阵,包括 Embedding 矩阵
  • 通常应用于 Query, Value 两个参数矩阵

在这里插入图片描述

2. QLoRA

什么是模型量化

在这里插入图片描述
在这里插入图片描述

更多参考: https://huggingface.co/blog/hf-bitsandbytes-integration

QLoRA 引入了许多创新来在不牺牲性能的情况下节省显存:

  • 4 位 NormalFloat(NF4),一种对于正态分布权重而言信息理论上最优的新数据类型
  • 双重量化,通过量化量化常数来减少平均内存占用
  • 分页优化器,用于管理内存峰值

原文实现:单个 48G 的 GPU 显卡上微调 65B 的参数模型,保持 16 字节微调任务的性能
#%% md

十一·、实战

基于 GLM 4, Llama 3 或 Qwen2, 微调一个同时具有 NLU 和问答能力对话机器人

11.1、数据源

酒店预订场景
https://github.com/thu-coai/CrossWOZ

酒店数据库
https://github.com/thu-coai/CrossWOZ/blob/master/data/crosswoz/database/hotel_db.json

11.2、数据增强

  • 从 CrossWOZ 数据集中抽取了只关于酒店的对话
  • 利用 ChatGPT 进行如下修改和补充
    • 对设施的描述更口语化
      • “找一家有国际长途电话的酒店” -> “找一家能打国际长途的酒店”
    • 补充一定比例的多轮问答,和结束语对话(p=0.3)
      • 针对只提及一个酒店时的问答:“这个酒店的电话是多少”
      • 针对推荐多个酒店时的对比问答:“哪个酒店评分更高”
      • 结束语:“好的,祝您入住愉快”
    • 补充按酒店名(简称)、价格上限查询的对话(原数据中没有这类说法)

数据增强的代码参考 data_augmentation.zip

最终按 8:1:1 拆分训练集、验证集和测试

原始样本


```json
[
  {
    "role": "user",
    "content": "你好,我出差想去酒店住宿。请帮我推荐一家公共区域和部分房间提供wifi服务,评分是4分以上的酒店。"
  },
  {
    "role": "search",
    "arguments": {
      "facilities": ["公共区域和部分房间提供wifi"],
      "rating_range_lower": 4.0
    }
  },
  {
    "role": "return",
    "records": [
      {
        "name": "北京龙鼎华鼎云酒店",
        "type": "舒适型",
        "address": "北京朝阳区潘家园东里18号",
        "subway": "劲松地铁站D口",
        "phone": "010-52001188",
        "facilities": [
          "公共区域和部分房间提供wifi",
          "宽带上网",
          "国际长途电话",
          "吹风机",
          "24小时热水",
          "中式餐厅",
          "会议室",
          "无烟房",
          "商务中心",
          "洗衣服务",
          "行李寄存",
          "叫醒服务"
        ],
        "price": -1.0,
        "rating": 4.3,
        "hotel_id": 24
      }
    ]
  },
  {
    "role": "assistant",
    "content": "那您去北京龙鼎华鼎云酒店住宿吧,酒店质量很好。"
  }
]

**增强后样本**
[
  {
    "role": "user",
    "content": "你好,我出差想去酒店住宿。请帮我推荐一家提供无线网络且评分在4分以上的酒店。"
  },
  {
    "role": "search",
    "arguments": {
      "facilities": ["无线网络"],
      "rating_range_lower": 4.0
    }
  },
  {
    "role": "return",
    "records": [
      {
        "name": "北京龙鼎华鼎云酒店",
        "type": "舒适型",
        "address": "北京朝阳区潘家园东里18号",
        "subway": "劲松地铁站D口",
        "phone": "010-52001188",
        "facilities": [
          "公共区域和部分房间提供wifi",
          "宽带上网",
          "国际长途电话",
          "吹风机",
          "24小时热水",
          "中式餐厅",
          "会议室",
          "无烟房",
          "商务中心",
          "洗衣服务",
          "行李寄存",
          "叫醒服务"
        ],
        "price": -1.0,
        "rating": 4.3,
        "hotel_id": 24
      }
    ]
  },
  {
    "role": "assistant",
    "content": "那您去北京龙鼎华鼎云酒店住宿吧,酒店质量很好。"
  },
  {
    "role": "user",
    "content": "这个酒店的评分是多少?"
  },
  {
    "role": "assistant",
    "content": "这个酒店的评分是4.3分。"
  },
  {
    "role": "user",
    "content": "好的,我决定入住北京龙鼎华鼎云酒店了。"
  },
  {
    "role": "assistant",
    "content": "好的,祝您入住愉快。"
  }
]

11.3、数据的基本拼接方式
在这里插入图片描述

11.4、多轮对话怎么拼

方法一:ChatGLM 2 的方式

user: 你好
assistant: 有什么可以帮您
user: 你喜欢什么颜色
assistant: 喜欢黑色
user: 为什么
assistant: 因为黑色幽默

按照轮次,上述对话将被拆分成 3 个单独的样本

  • 每个样本以之前的历史为输入
  • 当前轮的回复为输出

样本 1

输入:
[Round 0]\n
问: 你好\n
答:

输出:
有什么可以帮您

样本 2

输入:
[Round 0]\n
问: 你好\n
答: 有什么可以帮您\n
[Round 1]\n
问: 你喜欢什么颜色\n
答:

输出:
喜欢黑色

样本 3

输入:
[Round 0]\n
问: 你好\n
答: 有什么可以帮您\n
[Round 1]\n
问: 你喜欢什么颜色\n
答: 喜欢黑色\n
[Round 2]\n
问: 为什么\n
答:

输出:
因为黑色幽默

方法二:ChatGLM 3 / GLM4-9B 的方式

因为 CausalLM 是一直从左往右预测的,我们可以直接在多轮对话中标识出多段输出。
在这里插入图片描述

具体如下:

角色special token用于标识分隔出多轮对话,同时也可以防范注入攻击

  • <|system|> #系统提示词,指明模型可使用的工具等信息
  • <|user|> #用户输入,用户的指令
  • <|assistant|> #模型回复,或模型思考要做的事情
  • <|observation|> #工具调用、代码执行结果

注意:这里<|role|>这种是一个 token,而不是一串文本,所以不能通过tokenizer.encode('<role>')来得到

角色后跟随的是 metadata,对于 function calling 来说,metadata 是调用的函数和相应参数;对其他角色的对话,metadata 为空

  • 多轮对话 finetune 时根据角色添加 loss_mask
  • 在一遍计算中为多轮回复计算 loss

<|system|>你是一个名为 GhatGLM 的人工智能助手。你是基于智谱AI训练的语言模型 GLM-4 模型开发的,你的任务是针对用户的问题和要求提供适当的答复和支持。\n\n# 可用工具\n\n## function_name1\n\n{…}\n\n## function_name2\n\n{…}\n在调用上述函数时,请使用 Json 格式表示调用的参数。"

<|user|> 北京的天气怎么样?

<|assistant|> get_weather\n{“location”:“北京”}

<|observation|> {“temperature_c”: 12, “description”: “haze”}

<|assistant|> 根据天气工具的信息,北京的天气是:温度 12 摄氏度,有雾。

<|user|> 这样的天气适合外出活动吗?

<|assistant|> 北京现在有雾,气温较低,建议您考虑一下是否适合外出进行锻炼。

<|user|>


高亮部分为需要计算 loss 的 token。注意<|assistant|>后的内容和角色 token 都需要算 loss。

此部分可以参考 GLM4 的官方实现tokenizer 的代码中的 apply_chat_template 方法。

格式化后的数据样例


```json
{
  "messages": [
    {
      "role": "system",
      "content": "",
      "tools": [
        {
          "type": "function",
          "function": {
            "name": "search_hotels",
            "description": "根据用户的需求生成查询条件来查酒店",
            "parameters": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "酒店名称"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "豪华型",
                    "经济型",
                    "舒适型",
                    "高档型"
                  ],
                  "description": "酒店类型"
                },
                "facilities": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "酒店能提供的设施列表"
                },
                "price_range_lower": {
                  "type": "number",
                  "minimum": 0,
                  "description": "价格下限"
                },
                "price_range_upper": {
                  "type": "number",
                  "minimum": 0,
                  "description": "价格上限"
                },
                "rating_range_lower": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 5,
                  "description": "评分下限"
                },
                "rating_range_upper": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 5,
                  "description": "评分上限"
                }
              },
              "required": [
                
              ]
            }
          }
        }
      ]
    },
    {
      "role": "user",
      "content": "我想找一家叫瑞洁宾舍民宿的酒店"
    },
    {
      "role": "assistant",
      "content": "search_hotels\n{\"name\": \"瑞洁宾舍民宿\"}"
    },
    {
      "role": "observation",
      "content": "[{\"address\": \"北京西城区新农街17号\", \"facilities\": \"酒店提供的设施:公共区域和部分房间提供wifi;国际长途电话;吹风机;24小时热水;西式餐厅;无烟房;早餐服务;接站服务;接机服务;接待外宾;行李寄存;看护小孩服务;叫醒服务;收费停车位\", \"hotel_id\": 782, \"name\": \"北京瑞洁宾舍民宿\", \"phone\": \"010-83133338\", \"price\": 249, \"rating\": 4.5, \"subway\": \"珠市口地铁站D口\", \"type\": \"舒适型\"}]"
    },
    {
      "role": "assistant",
      "content": "为您查到北京瑞洁宾舍民宿,您要选择这家吗"
    }
  ]
}

划重点:
       1在 messages 字段中组织对话轮次
        2在 tools 的定义放在 system 角色中,描述 function 和 parameters 的定义
        3以 user 和 assistant 标识出用户输入与系统回复
      4在 function call 的角色为 assistant,格式为 function_name\n{"arg":"val", ...}
        5以 observation 标识出 function 的返回结果

11.5、如何在 Llama3/Qwen2 中实现类似 Function Calling 的效果

  1. 我们自定义 user、assistant、search、return 四个角色
    • 因为只有一个 function,我们直接把 function 标识成 search
  2. 每轮 assistant 和 search 前缀也由模型自动生成,我们以此判断是 function 还是文本回复
  3. 类似 GLM4-9b,我们以预留的特殊 token,来标识每个轮次的角色和轮次结束
    • 例如:<|start_header_id|>角色<|end_header_id|>内容 … …<|eot_id|>
    • 其中 <|start_header_id|><|end_header_id|><|eot_id|> 是 Llama 3 预留的特殊 token
Function Call 的样例

输入

<|start_header_id|>user<|end_header_id|>你好,请帮我推荐一个提供无烟房的舒适型酒店可以吗?<|eot_id|>

输出

<|start_header_id|>search<|end_header_id|>{“facilities”: [“无烟房”], “type”: “舒适型”}}<|eot_id|>

文本回复的样例

输入

<|start_header_id|>user<|end_header_id|>你好,请帮我推荐一个提供无烟房的舒适型酒店可以吗?<|eot_id|>

<|start_header_id|>search<|end_header_id|>{“facilities”: [“无烟房”], “type”: “舒适型”}}<|eot_id|>

<|start_header_id|>return<|end_header_id|>[{“name”: “北京红驿栈酒店”, “type”: “舒适型”, “address”: “北京朝阳区东直门外春秀路太平庄 10 号(主副楼在一幢建筑里)”, “subway”: “东直门地铁站 E 口”, “phone”: “010-64171066”, “facilities”: [“公共区域和部分房间提供 wifi”, “宽带上网”, “国际长途电话”, “吹风机”, “24 小时热水”, “暖气”, “无烟房”, “早餐服务”, “接待外宾”, “行李寄存”, “叫醒服务”], “price”: 344.0, “rating”: 4.7, “hotel_id”: 51}, {“name”: “维也纳国际酒店(北京广安门店)”, “type”: “舒适型”, “address”: “北京西城区白广路 7 号”, “subway”: “广安门内地铁站 C 口”, “phone”: “010-83539988”, “facilities”: [“酒店各处提供 wifi”, “宽带上网”, “吹风机”, “24 小时热水”, “中式餐厅”, “会议室”, “无烟房”, “商务中心”, “早餐服务”, “洗衣服务”, “行李寄存”, “叫醒服务”], “price”: 553.0, “rating”: 4.7, “hotel_id”: 56}]}]<|eot_id|>

输出

<|start_header_id|>assistant<|end_header_id|>没问题,推荐你去北京红驿栈酒店和维也纳国际酒店(北京广安门店),都挺好的。<|eot_id|>
#%% md

11.6、编写训练代码

11.7、训练后,在测试集上的参考指标

  1. 针对 Function Calling 的每个参数(即 Slot),我们评估准确率、召回率、F1 值
  2. 针对文本回复,我们评估输出文本与参考文本之间的 BLEU Score
  3. 如果本该是 Function Calling 的轮次,模型回复了文本,则所有指标为 0,反之 BLEU Score 为 0
ModelMethodBLEU-4SLOT-PSLOT-RSLOT-F1
GLM4-9BFunctional Call53.5382.7973.4777.85
QLoRA67.4195.9596.7496.34
Llama3.1-8BPrompt51.9492.5587.8490.19
QLoRA66.5397.2395.9896.60
Qwen2-7BPrompt23.4385.2188.9188.02
LoRA66.7096.4295.3495.87

十二、数据准备与处理

12.1、数据采集

  • 自然来源(如业务日志):真实数据
  • Web 抓取:近似数据
  • 人造

12.2、数据标注

  • 专业标注公司
    • 定标准,定验收指标
    • 预标注
    • 反馈与优化
    • 正式标注
    • 抽样检查:合格->验收;不合格->返工
  • 众包
    • 定标准,定检验指标
    • 抽样每个工作者的质量
    • 维系高质量标注者社区
  • 主动学习:通过模型选择重要样本,由专家标注,再训练模型
  • 设计产品形态,在用户自然交互中产生标注数据(例如点赞、收藏)

12.3、数据清洗

  • 去除不相关数据
  • 去除冗余数据(例如重复的样本)
  • 去除误导性数据(业务相关)

12.4、样本均衡性

  • 尽量保证每个标签(场景/子问题)都有足够多的训练样本
  • 每个标签对应的数据量尽量相当
    • 或者在保证每个标签样本充值的前提下,数据分布尽量接近真实业务场景的数据分布
  • 数据不均衡时的策略
    • 数据增强:为数据不够类别造数据:(1)人工造;(2)通过模板生成再人工标注;(3)由模型自动生成(再人工标注/筛选)
    • 数据少的类别数据绝对数量也充足时,Downsample 一般比 Upsample 效果好
    • 实在没办法的话,在训练 loss 里加权(一般不是最有效的办法)
  • 根据业务属性,保证其他关键要素的数据覆盖,例如:时间因素、地域因素、用户年龄段等

12.5、数据集构建

  • 数据充分的情况下
    • 切分训练集(训练模型)、验证集(验证超参)、测试集(检验最终模型+超参的效果)
    • 以随机采样的方式保证三个集合的数据分布一致性
    • 在以上三个集合里都尽量保证各个类别/场景的数据覆盖
  • 数据实在太少
    • 交叉验证
训练不同模型需要多少显存?
  • 占用显存的大头主要分为四部分:模型参数、前向计算过程中产生的中间激活、后向传递计算得到的梯度、优化器状态。
  • HuggingFace 官方推出一个在线工具,可以估算模型的显存使用情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值