基于LLaMA-Factory微调开源大模型

4/21/2024 LLaMA-Factory大模型微调多模态数据标注多模态大模型大模型训练经验大模型产品备案

# 1. 大模型基本介绍

# 1.1 大模型技术栈

# 1.1.1 大模型发展史

目前主流的大模型都是Transformer、MOE结构为基础进行构建,如果说Transformer结构使得模型突破到上亿参数量,MoE 稀疏混合专家结构使模型参数量产生进一步突破,达到数万亿规模。下图详细展示了AI大模型的发展历程:

大模型发展历程

可以说,Transformer 开创了继 MLP 、CNN和 RNN之后的第四大类模型。而基于Transformer结构的模型又可以分为Encoder-only、Decoder-only、Encoder-Decoder这三类。

  • 仅编码器架构(Encoder-only):自编码模型(破坏一个句子,然后让模型去预测或填补),更擅长理解类的任务,例如:文本分类、实体识别、关键信息抽取等。典型代表有:Bert、RoBERTa等。
  • 仅解码器架构(Decoder-only):自回归模型(将解码器自己当前步的输出加入下一步的输入,解码器融合所有已经输入的向量来输出下一个向量,所以越往后的输出考虑了更多输入),更擅长生成类的任务,例如:文本生成。典型代表有:GPT系列、LLaMA、OPT、Bloom等。
  • 编码器-解码器架构(Encoder-Decoder):序列到序列模型(编码器的输出作为解码器的输入),主要用于基于条件的生成任务,例如:翻译,概要等。典型代表有:T5、BART、GLM等。

# 1.1.2 LLM技术图谱

LLM 技术图谱(LLM Tech Map)从基础设施、大模型、Agent、AI 编程、工具和平台,以及算力几个方面,为开发者整理了当前 LLM 中最为热门和硬核的技术领域以及相关的软件产品和开源项目。

llm-tech-map

# 1.2 大模型基座选型

# 1.2.1 MoE模型组成与优势

混合专家模型(MoE)是基于 Transformer 架构的一种高效模型,具有显著的优势:能够在较少的计算资源下进行有效的预训练,从而扩展模型或数据集规模。在相同计算预算下,MoE 通常能更快地达到与稠密模型相同的质量。

MoE的核心由两部分组成:

  • 稀疏MoE层:代替了传统Transformer模型中的前馈网络层。MoE层包含若干“专家”,每个专家本身是一个独立的神经网络。在实际应用中,这些专家通常是前馈网络,但它们也可以是更复杂的网络结构,甚至可以是MoE层本身,从而形成层级式的MoE结构。
  • 门控网络:决定每个Token路由到哪个专家,通常一个Token会被发送到一个或多个专家。Token的路由方式是MoE使用中的一个关键点,因为路由器由学习的参数组成,并且与网络的其他部分一同进行预训练。

MoE模型结构

尽管 MoE 在预训练和推理速度上有优势,它仍面临一些挑战。首先,MoE 在微调阶段可能会出现过拟合,导致泛化能力较弱。其次,尽管 MoE 在推理时只使用部分专家,内存需求依然很高,因为所有参数都需要加载到内存中。

总的来说,MoE 提供了更高效的预训练和较快的推理速度,但在微调和内存管理上存在挑战。

# 1.2.2 是否需要推理模型

大模型发展至今,可以分为:通用大模型与推理大模型。

  • 通用大模型:适用于大多数任务,侧重于语言生成、上下文理解和自然语言处理,而不强调深度推理能力。此类模型通常通过对大量文本数据的训练,掌握语言规律并能够生成合适的内容,但缺乏像推理模型那样复杂的推理和决策能力。
  • 推理大模型:在传统模型基础上,强化推理、逻辑分析和决策能力。

推理模型与通用模型对比

思维链(Chain of Thought,CoT)通过要求/提示模型在输出最终答案之前,显式输出中间逐步的推理步骤这一方法来增强大模型的算数、常识和推理的性能。从该角度,可以将大模型的范式分为两类:概率预测(快速反应模型)和链式反应(慢速思考模型),前者适合快速反馈,处理即时任务,后者通过推理解决复杂问题。

概率预测与链式思考对比

# 1.2.3 开源基座模型选型

我们团队主要尝试过Qwen、DeepSeek、Baichuan、ChatGLM、LLaMA等主流的开源大模型。不同的开源大模型能力侧重点不同,综合来看,我们认为Qwen和DeepSeek系列模型是比较优秀的,目前团队内主要选择Qwen作为直接使用和微调训练的基座模型。

个人选择倾向排序:Qwen(模型效果很好且种类齐全)= DeepSeek(模型效果很好,且思维链模型R1接近SOTA)> Baichuan(模型效果还可以) > ChatGLM(模型效果还可以,但只开源了小参数的,大参数的商业授权很贵) > LLaMA(模型效果不好且没中文支持)

# 1.3 大模型相关产品备案

# 1.3.1 国家相关政策

在大模型的监管政策层面,主要参与监管的有三个部委:网信办,公安部,工信部。

利用生成式人工智能产品向公众提供服务前,应当按照《具有舆论属性或社会动员能力的互联网信息服务安全评估规定》向国家网信部门申报安全评估,并按照《互联网信息服务算法推荐管理规定》履行算法备案和变更、注销备案手续。

查看已经通过备案的大模型及相关产品:国家互联网信息办公室关于发布生成式人工智能服务已备案信息的公告 (opens new window)

# 1.3.2 哪些需要备案

根据《生成式人工智能服务管理暂行办法》的规定,以下类型的企业需要做大模型(生成式人工智能服务)备案:

  • 提供具有舆论属性或者社会动员能力的生成式人工智能服务的企业:这些企业包括但不限于开办论坛、博客、微博客、聊天室、通讯群组、公众账号、短视频、网络直播、信息分享、小程序等信息服务或者附设相应功能的企业。
  • 面向境内公众提供生成式人工智能服务的企业:无论是直接面向消费者(2C)还是间接面向消费者(B2B2C)的模式,只要企业向中华人民共和国境内公众提供生成式人工智能服务,都需要进行备案。
  • 具有舆论属性或者社会动员能力的深度合成服务提供者和技术支持者:根据《互联网信息服务深度合成管理规定》,这些企业也需要进行备案。
  • 规模达到一定量级的企业:虽然法规没有明确规定具体的规模标准,但建议规模较大或模型服务应用程序规模较大的企业优先进行备案。
  • 有实力或有意愿做大模型备案的企业:特别是那些以生成式人工智能为经营主业、需要做商业宣传使用企业。

不需要做大模型备案的企业:

  • 不具备舆论属性或者社会动员能力的生成式人工智能服务。
  • 调用已备案大模型API接口,面向境内公众服务的,只需做登记即可。
  • 企业、教育及科研机构、行业组织、公共文化机构等,服务未面向境内公众提供的类型。

# 1.3.3 应当如何备案

大模型及相关应用的备案流程是非常繁琐的,大致流程包含“提交内部评估材料——属地网信办初审——中央网信办复核”。

详细步骤见:2025大模型(生成式人工智能服务)备案 (opens new window)

大模型备案流程

# 2. 大模型微调概述

# 2.1 什么是大模型微调

大模型微调是一种迁移学习技术,通过在预训练模型的基础上进行额外训练,使其适应特定任务或领域。这一过程包括选择预训练模型,准备目标任务的数据,调整模型结构,进行微调训练,以及评估和部署。微调的优点在于节省时间和资源,提高性能,但也存在过拟合风险和模型选择与调整的复杂性。总体而言,它是一种强大的技术,特别适用于数据受限或计算资源有限的情况。

在 OpenAI 发布的 ChatGPT 中,就主要应用了大模型微调技术,从而获得了惊艳全世界的效果。

  • 第一阶段,根据问题库中的问题和人工标注的回答产生的数据集来监督学习微调模型,得到SFT模型。
  • 第二阶段,收集一组模型输出的比较数据集,由问题和不同的模型输出组成,标注者指出给定输入的情况下他们更偏好的输出。然后训练一个奖励模型,以预测人类偏好的输出。
  • 第三阶段,利用奖励模型的输出作为标量奖励,使用PPO算法微调SFT模型以优化这一奖励 其中,二三步可以迭代进行。

ChatGPT应用大模型微调技术

论文地址:https://proceedings.neurips.cc/paper_files/paper/2022/file/b1efde53be364a73914f58805a001731-Paper-Conference.pdf (opens new window)

# 2.2 大模型微调的方式

# 2.2.1 全量微调

大模型全量微调通过在预训练的大型模型基础上调整所有层和参数,使其适应特定任务。这一过程使用较小的学习率和特定任务的数据进行,可以充分利用预训练模型的通用特征,但可能需要更多的计算资源。

# 2.2.2 参数高效微调

PEFT技术旨在通过最小化微调参数的数量和计算复杂度,来提高预训练模型在新任务上的性能,从而缓解大型预训练模型的训练成本。这样一来,即使计算资源受限,也可以利用预训练模型的知识来迅速适应新任务,实现高效的迁移学习。因此,PEFT技术可以在提高模型效果的同时,大大缩短模型训练时间和计算成本,让更多人能够参与到深度学习研究中来。

  • Prefix Tuning:与full fine-tuning更新所有参数的方式不同,该方法是在输入token之前构造一段任务相关的virtual tokens作为Prefix,然后训练的时候只更新Prefix部分的参数,而Transformer中的其他部分参数固定。该方法其实和构造Prompt类似,只是Prompt是人为构造的“显式”的提示,并且无法更新参数,而Prefix则是可以学习的“隐式”的提示。同时,为了防止直接更新Prefix的参数导致训练不稳定的情况,他们在Prefix层前面加了MLP结构(相当于将Prefix分解为更小维度的Input与MLP的组合后输出的结果),训练完成后,只保留Prefix的参数。

  • Prompt Tuning:该方法可以看作是Prefix Tuning的简化版本,只在输入层加入prompt tokens,并不需要加入MLP进行调整来解决难训练的问题。随着预训练模型参数量的增加,Prompt Tuning的方法会逼近fine-tuning的结果。

  • P-Tuning:该方法主要是为了解决这样一个问题:大模型的Prompt构造方式严重影响下游任务的效果。P-Tuning将Prompt转换为可以学习的Embedding层,并用MLP+LSTM的方式来对prompt embedding进行一层处理。

  • P-Tuning v2:让Prompt Tuning能够在不同参数规模的预训练模型、针对不同下游任务的结果上都达到匹敌Fine-tuning的结果。相比Prompt Tuning和P-tuning的方法,P-Tuning v2方法在多层加入了Prompts tokens作为输入,带来两个方面的好处:

    1)带来更多可学习的参数(从P-tuning和Prompt Tuning的0.1%增加到0.1%-3%),同时也足够参数高效。

    2)加入到更深层结构中的Prompt能给模型预测带来更直接的影响。

  • Adapter Tuning:该方法设计了Adapter结构(首先是一个down-project层将高维度特征映射到低维特征,然后过一个非线形层之后,再用一个up-project结构将低维特征映射回原来的高维特征;同时也设计了skip-connection结构,确保了在最差的情况下能够退化为identity),并将其嵌入Transformer的结构里面,在训练时,固定住原来预训练模型的参数不变,只对新增的Adapter结构进行微调。同时为了保证训练的高效性(也就是尽可能少的引入更多参数)。

  • LoRA:在涉及到矩阵相乘的模块,引入A、B这样两个低秩矩阵模块去模拟full fine-tuning的过程,相当于只对语言模型中起关键作用的低秩本质维度进行更新。

# 2.3 常见的参数高效微调技术

# 2.3.1 LoRA技术

LoRA,全称Low-Rank Adaptation of Large Language Models,直译大语言模型的低阶适应,这是微软的研究人员为了解决大语言模型微调而开发的一项技术。

比如,GPT-3有1750亿参数,为了让它能干特定领域的活儿,需要做微调,但是如果直接对GPT-3做微调,成本太高太麻烦了。LoRA的做法是,冻结预训练好的模型权重参数,然后在每个Transformer(Transforme就是GPT的那个T)块里注入可训练的层,由于不需要对模型的权重参数重新计算梯度,所以,大大减少了需要训练的计算量。研究发现,LoRA的微调质量与全模型微调相当,就好比是大模型的一个小模型。

LoRA微调技术

# 2.3.2 Freeze技术

Freeze 方法,即参数冻结,对原始模型部分参数进行冻结操作,仅训练部分参数,以达到在单卡或不进行 TP 或 PP 操作,就可以对大模型进行训练。在语言模型模型微调中,Freeze 微调方法仅微调 Transformer 后几层的全连接层参数,而冻结其它所有参数。

# 2.3.3 P-Tuning v2技术

P-Tuning v2 通过在每一层加入Prompts tokens,实现了更多的可学习参数和更深层结构中的Prompt对模型预测的直接影响,提高了模型的灵活性和效率。

ChatGLM微调介绍

# 3. 大模型微调数据集标注

# 3.1 用于大模型微调的数据集

# 3.1.1 开源的微调数据集

对于文本类型,典型的数据集格式为{"instruction": "", "input": "", "output": ""},以下是一些开源数据集:

数据集 内容
COIG (opens new window) Chinese Open Instruction Generalist project
Stanford Alpaca (Chinese) (opens new window) Alpaca 数据集中文翻译(ChatGPT 辅助翻译)
BELLE (opens new window) BELLE 项目的中文数据集(ChatGPT 生成)
GuanacoDataset (opens new window) Guannaco 模型的对话数据集
WebQA(zh) (opens new window) 中文网络问答
pCLUE (opens new window) 基于提示的大规模预训练数据集,用于多任务学习和零样本学习

注:很多开源数据集里,问题放在 instruction 上,而 input 是空的,这是正常情况,数据字段也没有对应错。这是因为 instruction 与 input 是拼一起用的。比如:“翻译以下这段话:xxx”,翻译以下这段话是 instruction,xxx 是 input。

# 3.1.2 SemHash数据去重

SemHash是一个轻量且灵活的工具,用于通过语义相似性来去重数据集。它结合了Model2Vec的快速嵌入生成和Vicinity的高效ANN相似性搜索,支持单数据集去重(例如,清理训练集)和多数据集去重(例如,确保测试集和训练集之间没有重叠)。

# 3.2 LabelU多模态数据标注平台

实验环境:Macbook Pro 2021,M1 Pro芯片,16G内存,macOS Sonoma 14.5系统,Anaconda环境

# 3.2.1 LabelU平台简介

LabelU是一款综合性的数据标注平台,专为处理多模态数据而设计。该平台旨在通过提供丰富的标注工具和高效的工作流程,帮助用户更轻松地处理图像、视频和音频数据的标注任务,满足各种复杂的数据分析和模型训练需求。

功能特性:LabelU提供了多种标注工具和功能,支持图像、视频、音频标注。

  • 图像类:多功能图像处理工具,涵盖拉框、标点、标线、多边形、立体框等多种标注工具,协助完成图像的标识、注释和分析。
  • 音频类:高效精准的音频分析工具,可实现音频分割、音频分类、音频时间戳等功能,将复杂的声音信息更好进行标注。
  • 视频类:具备强大视频处理能力,可实现视频分割、视频分类、视频时间戳等功能,为模型训练提供优质标注数据。

LabelU-Kit工具包,支持图片2D框、点、线、多边形、立体框及混合标注工具,可用于标注平台开发集成,开箱即用。

LabelU-Kit工具包组件

# 3.2.2 LabelU平台搭建使用

安装过程:

[1] 方式一:快速启动(适合一般使用的情形)

LabelU平台的安装部署非常简单,只需要安装labelu包即可,没有什么坑儿。

$ conda create -n labelu python=3.11
$ conda activate labelu
$ pip3 install labelu
$ labelu
1
2
3
4

执行 labelu 命令即可一键启动。

LabelU本地启动

[2] 方式二:源码启动(适合二次开发情形)

$ conda create -n labelu python=3.11
$ conda activate labelu
$ export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890
$ git clone https://github.com/opendatalab/labelU.git
$ poetry install
1
2
3
4
5

使用poetry安装labelU的项目依赖

注:如果没有poetry环境的话,安装先将其安装一下。

  • pipx:类似于 macOS 上的 brew,pipx 依赖 pip 和 venv,它只能在 python 3.6+ 的 Python 版本中才能使用。
  • poetry:如果想使用pyproject.toml,并通过pyproject.toml进行依赖包管理,目前pip还不支持,所以poetry是首选。
$ brew install pipx
$ pipx --version
$ pipx install poetry
$ pipx ensurepath   // 自动更新环境变量
$ poetry --version  // 需要重新打开终端
1
2
3
4
5

然后从 labelu-kit 拉取前端编译好的dist静态包,启动项目。

$ sh ./scripts/resolve_frontend.sh true
$ uvicorn labelu.main:app --reload
1
2

注:resolve_frontend.sh脚本获取前端包的地址是写在 .VERSION 文件里的。

使用流程:

打开Chrome浏览器,访问 http://localhost:8000 即可访问 LabelU,注册登录后即可使用。

Step1:创建数据标注任务

LabelU创建标注任务

Step2:查看数据标注任务

LabelU数据标注列表

Step3:开始进行数据标注

LabelU开始数据标注

Step4:导出数据标注结果

# 3.2.3 LabelU平台数据标注

[1] 图像类数据

工具 使用方法
拉框标注 1. 选中拉框工具并配置标签,如轿车、公交车。 2. 单击鼠标左键标注第一点,画出框的范围之后再次单击左键,即可绘制出框。 3. 右键选中框之后,可以调整框的大小、有效性,也可以删除已标注的框。
标点标注 1. 选中标点工具并配置标签,如人体姿态14个关键点,包括头、脖子、左肩、右肩、左手肘、右手肘、左腕、右腕、左髋、右髋、左膝、右膝、左脚踝、右脚踝。 2. 单击鼠标左键标注指定关键点。 3. 右键选中点之后,可以调整点的位置、属性,也可以删除已标注的点。
多边形标注 1. 选中多边形工具并配置标签,如猫、狗。 2. 单击鼠标左键标注起始点,随后沿目标边缘多次单击左键,以此类推在围绕目标边缘右键连接最接近起始点后,即可绘制出多边形框。 3. 右键选中框之后,可以调整目标边缘关键点、线段、目标有效性,也可以删除已标注的多边形框。
标线标注 1. 选中线条工具并配置标签,如车道线。 2. 单击鼠标左键标注起始点,再次点击1+N后右键为终止点。 3. 操作Shift+左键则为垂直或水平线。
立体框标注 1. 选中立体框工具并配置标签,如汽车。 2. 单击鼠标左键标注第一点,画出框的范围之后再次单击左键,即可绘制出正面。 3. 移动鼠标带出尾面(与正面相同),点击鼠标左键完成。 4.点击更换正反面,更改更多朝向。 5.右键选中立体框之后,可以调整8个点的位置以及6条线的位置(正面四条线以及尾面两条立柱线),也可以删除已标注的立体框。

LabelU图片数据标注

[2] 音频类数据

工具 使用方法
片段分割 播放音频并找到你想要开始切割的点,按下暂停并点击确定起始点。可以直接在时间线上点击并拖动选择终止点,来选择你想要切割的音频部分。
时间戳 选中找到你想引用或高亮的时间点,如果你想标记音频的1小时10分钟30秒处,你应该点击01:10:30这个进度条的点。

LabelU音频数据标注

[3] 视频类数据

工具 使用方法
片段分割 播放视频并找到你想要开始切割的点,按下暂停并点击确定起始点。可以直接在时间线上点击并拖动选择终止点,来选择你想要切割的视频部分。
时间戳 选中找到你想引用或高亮的时间点,如果你想标记视频的1小时10分钟30秒处,你应该点击01:10:30这个进度条的点。

LabelU视频数据标注

# 3.3 LabelLLM数据标注平台

实验环境:Debian 11 x86_64 系统,8GB内存,160GB存储,2x Intel Xeon CPU,无GPU,带宽1 Gigabit

# 3.3.1 LabelLLM平台简介

LabelLLM是一个开源的数据标注平台,致力于优化对于大型语言模型(LLM)开发不可或缺的数据标注过程。LabelLLM的设计理念旨在成为独立开发者和中小型研究团队提高标注效率的有力工具。它的核心在于通过提供全面的任务管理解决方案和多样化的多模态数据支持,简化并增强模型训练的数据注释过程的效率。

# 3.3.2 LabelLLM搭建使用

[1] 搭建过程

Step1:拉取项目代码

拉取代码并修改配置,只要让中间件与后端的连接信息配套即可。

$ git clone https://github.com/opendatalab/LabelLLM.git
$ cd LabelLLM
1
2

Step2:修改配置文件

./docker-compose.yaml

version: "3.1"

services:

  redis:
    image: redis:5.0
    restart: always
    ports:
      - "16280:6379"
    volumes:
      - redis_data:/data

  mongo:
    image: mongo:4.2
    restart: always
    ports:
      - "16019:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: mongodb_password
    volumes:
      - mongo_data:/data/db
      
  minio:
    image: docker.io/bitnami/minio:2022
    ports:
      - '9000:9000'
      - '9001:9001'
    environment:
      - MINIO_ROOT_USER=minio_user
      - MINIO_ROOT_PASSWORD=minio_password
      - MINIO_DEFAULT_BUCKETS=label-llm
    volumes:
      - minio_data:/data
    
  backend:
    build: ./backend
    ports:
      - '16666:8080'

  frontend:
    build: ./frontend
    ports:
      - '8086:80'
    depends_on:
      - backend

volumes:
  redis_data:
  mongo_data:
  minio_data:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

./backend/.env

DEBUG = True
ENVIRONMENT=local 

MINIO_ACCESS_KEY_ID = minio_user
MINIO_ACCESS_KEY_SECRET = minio_password
MINIO_ENDPOINT = localhost:9000
MINIO_INTERNAL_ENDPOINT = minio:9000
MINIO_BUCKET = label-llm

MongoDB_DSN = mongodb://root:mongodb_password@mongo:27017
MongoDB_DB_NAME = label_llm


REDIS_DSN = redis://redis:6379/11

SECRET_KEY="?*hsbRq5c9gpjBp~:oHU+7s8,[email protected]:Oi_5oIYgw"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Step3:一键部署服务

修改完配置之后,执行如下命令即可一键部署。

$ docker-compose up -d
1

[2] 使用过程

使用Chrome浏览器访问地址即可访问,它分为运营端和标注端,账号自行注册。

  • 运营端:http://ip:8086/operator
  • 标注端:http://ip:8086/supplier

LabelLLM运营端主界面

LabelLLM分为运营端和标注端两部分,如何使用官方有很详细的文档,这里就不赘述了。

LabelLLM标注配置

# 4. 大模型高效微调

实验环境:租用的揽睿星舟的GPU服务器,NVIDIA RTX 4090 / 24GB,Python 3.10.6, CUDA 11.7

关于GPU服务器租用、Jupyter及HuggingFace的使用,这里就不赘述了,详见我的另一篇博客:常用深度学习平台的使用指南 (opens new window)

# 4.1 准备大模型微调环境

# 4.1.1 大模型微调开源项目

基于 PEFT 的高效 ChatGLM 微调,兼容 ChatGLM 与 ChatGLM-2 模型,支持 Full Tuning、LoRA、P-Tuning V2、Freeze等微调方式。

注:作者后来将各种大模型的高效微调,统一到了一个项目里:https://github.com/hiyouga/LLaMA-Factory (opens new window)

# 4.1.2 拉取仓库并安装依赖

拉取ChatGLM-Efficient-Tuning项目并安装依赖。

$ git clone https://github.com/hiyouga/ChatGLM-Efficient-Tuning.git
$ cd ChatGLM-Efficient-Tuning
$ pip3 config set global.index-url http://mirrors.aliyun.com/pypi/simple/
$ pip3 config set install.trusted-host mirrors.aliyun.com
$ pip3 install -r requirements.txt
1
2
3
4
5

注:我实验时还是早期版本,作者还没将各类大模型微调合并到一个项目里,现在建议用合并后的 LLaMA-Factory 项目。

# 4.2 单GPU微调训练模型

该项目支持使用多个 GPU 进行分布式微调,但我这里的服务器环境只有一个GPU,就不尝试了。

创建个checkpoint目录用于保存微调后的模型。

$ mkdir /home/user/checkpoint
1

制作测试数据集(复制一份alpaca_gpt4_data_zh.json数据集,就保留几条数据),然后在 /home/user/ChatGLM-Efficient-Tuning/data/dataset_info.json文件里再加一条配置。

  "alpaca_gpt4_zh_test": {
    "file_name": "alpaca_gpt4_data_zh_test.json",
    "file_sha1": "3eaa3bda364ccdd59925d7448a698256c31ef846"
  },
1
2
3
4

这里使用官方示例的数据集和参数运行(为了加快训练速度,使用刚刚制作的测试数据集,规模进行了阉割):

$ cd /home/user/ChatGLM-Efficient-Tuning
$ CUDA_VISIBLE_DEVICES=0 python3 src/train_bash.py \
  --stage sft \
  --model_name_or_path THUDM/chatglm2-6b \
  --do_train \
  --dataset alpaca_gpt4_zh_test \
  --finetuning_type lora \
  --output_dir /home/user/checkpoint \
  --per_device_train_batch_size 4 \
  --gradient_accumulation_steps 4 \
  --lr_scheduler_type cosine \
  --logging_steps 10 \
  --save_steps 1000 \
  --learning_rate 5e-5 \
  --num_train_epochs 3.0 \
  --plot_loss \
  --fp16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

注意事项:GPU服务器厂商提供的预训练模型在/home/user/imported_models/chatglm2-6b/huggingface/THUDM/chatglm2-6b目录下,但是这个模型亲测没法用。报错“ValueError: Please update the model files of ChatGLM2-6B.”。模型文件应该从 https://huggingface.co/THUDM/chatglm2-6b/tree/main (opens new window) 下载,我这里直接填写了 THUDM/chatglm2-6b,程序执行时会自动下载模型。

ChatGLM2的高效微调训练过程

常用参数说明:这里只解释官方命令里用到的,其余的自己看源码里的说明吧。

--stage sft:在训练过程中要执行的阶段。(默认值: sft,可选值:sft、rm、ppo)
--model_name_or_path:模型文件名称(例如THUDM/chatglm2-6b,从huggingface下载)或者本地路径
--do_train:是否进行训练。(默认值:False)
--dataset:要使用的提供的数据集名称,用逗号分隔多个数据集。(默认值:alpaca_zh)
--finetuning_type:要使用的微调方法。(默认值: lora,可选值:freeze,p_tuning,lora,full)
--output_dir:用于输出训练后模型checkpoint的目录。(默认值:None)
--per_device_train_batch_size:每个GPU/TPU核心/CPU用于训练的批量大小。(默认值:8)
--gradient_accumulation_steps:在执行向后/更新传递之前累积的更新步数。(默认值:1)
--lr_scheduler_type:要使用的调度程序类型。(默认值:linear,可选值:linear,cosine,cosine_with_restarts,polynomial,constant,constant_with_warmup,inverse_sqrt,reduce_lr_on_plateau)
--logging_steps:每X次更新步骤记录一次。应为整数或范围为[0,1)的浮点数。如果小于1,将被解释为总训练步骤的比例。(默认值:500)
--save_steps:每X次更新步骤保存一个检查点。应为整数或范围为[0,1)的浮点数。如果小于1,将被解释为总训练步骤的比例。(默认值:500)
--learning_rate:AdamW的初始学习率。(默认值:5e-05)
--num_train_epochs:执行的总训练时代数。(默认值:3.0)
--plot_loss:是否在微调后绘制训练损失。(默认值:False)
--fp16:是否使用fp16(混合)精度而不是32位。(默认值:False)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

训练过程:微调训练过程所需时间较长,具体取决于所选的基础大模型、微调方式、参数设置以及数据集规模。如果只是为了体验流程的话,可以专门弄个两三条的测试数据集,几秒就能跑完。

ChatGLM2的高效微调训练完毕

训练完的模型,被保存在 /home/user/checkpoint 目录,文件列表如下:

README.md                88 B
adapter_config.json      435 B
adapter_model.bin        7.45 MB
all_results.json         162 B
finetuning_args.json     254 B
train_results.json       162 B
trainer_log.jsonl        224 B
trainer_state.json       578 B
training_args.bin        3.23 KB
1
2
3
4
5
6
7
8
9

# 4.3 评估微调后的模型

# 4.3.1 对微调后的模型进行指标评估

指标评估(BLEU分数和汉语ROUGE分数),创建个eval_result目录用于保存微调模型的指标评估结果。

$ mkdir /home/user/eval_result
$ cd /home/user/ChatGLM-Efficient-Tuning
$ CUDA_VISIBLE_DEVICES=0 python3 src/train_bash.py \
    --stage sft \
    --model_name_or_path THUDM/chatglm2-6b \
    --do_eval \
    --dataset alpaca_gpt4_zh_test \
    --finetuning_type lora \
    --checkpoint_dir /home/user/checkpoint \
    --output_dir /home/user/eval_result \
    --per_device_eval_batch_size 8 \
    --max_samples 50 \
    --predict_with_generate
1
2
3
4
5
6
7
8
9
10
11
12
13

评估过程:因为我这里使用的是只有3条数据的测试数据集,所以评估过程也很快。

对微调后的模型进行指标评估

评估结果:保存的评估结果JSON文件也是这个内容。

***** eval metrics *****
  eval_accuracy           =        0.0
  eval_bleu-4             =     9.1284
  eval_rouge-1            =    36.5252
  eval_rouge-2            =    10.9344
  eval_rouge-l            =    22.3205
  eval_runtime            = 0:00:06.50
  eval_samples_per_second =      0.461
  eval_steps_per_second   =      0.154
1
2
3
4
5
6
7
8
9

# 4.3.2 使用微调后的模型进行预测

创建个predict_result目录用于保存微调模型的预测结果。

$ mkdir /home/user/predict_result
$ cd /home/user/ChatGLM-Efficient-Tuning
$ CUDA_VISIBLE_DEVICES=0 python3 src/train_bash.py \
    --stage sft \
    --model_name_or_path THUDM/chatglm2-6b \
    --do_predict \
    --dataset alpaca_gpt4_zh_test \
    --finetuning_type lora \
    --checkpoint_dir /home/user/checkpoint \
    --output_dir /home/user/predict_result \
    --per_device_eval_batch_size 8 \
    --max_samples 100 \
    --predict_with_generate
1
2
3
4
5
6
7
8
9
10
11
12
13

预测过程:因为我这里使用的是只有3条数据的测试数据集,所以预测过程也很快。

使用微调后的模型进行预测

预测结果,被保存在 /home/user/predict_result 目录,文件列表如下:

all_results.json                311 B
generated_predictions.jsonl     3.93 KB
predict_results.json            311 B
1
2
3

其中 generated_predictions.jsonl 的内容如下:

{
    "label": "以下是保持健康的三个提示:\n\n1. 保持身体活动。每天做适当的身体运动,如散步、跑步或游泳,能促进心血管健康,增强肌肉力量,并有助于减少体重。\n\n2. 均衡饮食。每天食用新鲜的蔬菜、水果、全谷物和脂肪含量低的蛋白质食物,避免高糖、高脂肪和加工食品,以保持健康的饮食习惯。\n\n3. 睡眠充足。睡眠对人体健康至关重要,成年人每天应保证 7-8 小时的睡眠。良好的睡眠有助于减轻压力,促进身体恢复,并提高注意力和记忆力。",
    "predict": "保持健康对于我们的身体和心理健康都非常重要。以下是三个保持健康的提示:\n\n1. 积极心态:积极心态可以帮助我们减轻压力,增强自信心,提高心理韧性,从而有助于保持心理健康。\n\n2. 均衡饮食:均衡饮食是保持健康的关键,我们应该吃各种颜色的蔬菜和水果,适量摄入优质蛋白质、健康脂肪和碳水化合物,避免过度食用或饮用高热量、高脂肪和高糖分的食物。\n\n3. 充足睡眠:睡眠是恢复身体和大脑功能的重要环节,我们应该尽量保证每晚7-8小时的睡眠时间,并创造一个安静、舒适的睡眠环境,避免过度使用电子设备、过度饮食和过度运动等不良睡眠习惯。"
}{
    "label": "三原色通常指的是红色、绿色和蓝色(RGB)。它们是通过加色混合原理创建色彩的三种基础颜色。在以发光为基础的显示设备中(如电视、计算机显示器、智能手机和平板电脑显示屏), 三原色可混合产生大量色彩。其中红色和绿色可以混合生成黄色,红色和蓝色可以混合生成品红色,蓝色和绿色可以混合生成青色。当红色、绿色和蓝色按相等比例混合时,可以产生白色或灰色。\n\n此外,在印刷和绘画中,三原色指的是以颜料为基础的红、黄和蓝颜色(RYB)。这三种颜色用以通过减色混合原理来创建色彩。不过,三原色的具体定义并不唯一,不同的颜色系统可能会采用不同的三原色。",
    "predict": "三原色是红色、绿色和蓝色,分别对应波长范围为约620-750纳米的红光、约495-570纳米的绿光和约450-495纳米的蓝光。在彩色显示、打印和电视技术中,我们通常使用红、绿、蓝三原色作为基本颜色,通过它们的不同组合可以产生各种颜色,这种方式也被称为“加色混合”。"
}{
    "label": "原子是物质的基本单位,它由三种基本粒子组成:质子、中子和电子。质子和中子形成原子核,位于原子中心,核外的电子围绕着原子核运动。\n\n原子结构具有层次性。原子核中,质子带正电,中子不带电(中性)。原子核非常小且致密,占据了原子总质量的绝大部分。电子带负电,通常围绕核运动,形成若干层次,称为壳层或电子层。电子数量与质子数量相等,使原子呈电中性。\n\n电子在每个壳层中都呈规律分布,并且不同壳层所能容纳的电子数也不同。在最里面的壳层一般只能容纳2个电子,其次一层最多可容纳8个电子,再往外的壳层可容纳的电子数逐层递增。\n\n原子核主要受到两种相互作用力的影响:强力和电磁力。强力的作用范围非常小,主要限制在原子核内,具有极强的吸引作用,使核子(质子和中子)紧密结合在一起。电磁力的作用范围较大,主要通过核外的电子与原子核相互作用,发挥作用。\n\n这就是原子的基本结构。原子内部结构复杂多样,不同元素的原子核中质子、中子数量不同,核外电子排布分布也不同,形成了丰富多彩的化学世界。",
    "predict": "原子由位于原子核中的质子与核外电子组成。质子带正电荷,电子带负电荷,质子与电子数目相等。因此,原子的结构可以用质子、中子、电子和核来描述。"
}
1
2
3
4
5
6
7
8
9
10

# 4.4 导出微调后的模型

创建个predict_result目录用于保存微调后的模型。

$ mkdir /home/user/export_models
$ cd /home/user/ChatGLM-Efficient-Tuning
$ python3 src/export_model.py \
    --model_name_or_path THUDM/chatglm2-6b \
    --finetuning_type lora \
    --checkpoint_dir /home/user/checkpoint \
    --output_dir /home/user/export_models
1
2
3
4
5
6
7

导出过程:大概耗时1min,将微调后的模型导出来了。

导出微调后的模型

导出模型,被保存在 /home/user/export_models 目录,文件列表如下:

config.json                          1.38 KB
configuration_chatglm.py             2.25 KB
generation_config.json               111 B
modeling_chatglm.py                  49.55 KB
pytorch_model-00001-of-00015.bin     932.95 MB
pytorch_model-00002-of-00015.bin     810.33 MB
pytorch_model-00003-of-00015.bin     777.67 MB
pytorch_model-00004-of-00015.bin     777.67 MB
pytorch_model-00005-of-00015.bin     777.67 MB
pytorch_model-00006-of-00015.bin     777.67 MB
pytorch_model-00007-of-00015.bin     777.67 MB
pytorch_model-00008-of-00015.bin     777.67 MB
pytorch_model-00009-of-00015.bin     777.67 MB
pytorch_model-00010-of-00015.bin     777.67 MB
pytorch_model-00011-of-00015.bin     777.67 MB
pytorch_model-00012-of-00015.bin     777.67 MB
pytorch_model-00013-of-00015.bin     777.67 MB
pytorch_model-00014-of-00015.bin     777.67 MB
pytorch_model-00015-of-00015.bin     827.70 MB
pytorch_model.bin.index.json         20.00 KB
quantization.py                      14.35 KB
special_tokens_map.json              3 B
tokenization_chatglm.py              9.84 KB
tokenizer.model                      994.54 KB
tokenizer_config.json                325 B
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 5. Qwen-VL多模态大模型微调

实验环境:租用的AutoDL的GPU服务器,NVIDIA RTX 4090D / 24GB,Ubuntu20.04,Python 3.10, CUDA 11.8

关于GPU服务器租用这里就不赘述了,详见我的另一篇博客:常用深度学习平台的使用指南 (opens new window)

# 5.1 Qwen-VL多模态大模型简介

# 5.1.1 Qwen-VL模型简介

Qwen-VL 是阿里云研发的大规模视觉语言模型(Large Vision Language Model, LVLM)。Qwen-VL 可以以图像、文本、检测框作为输入,并以文本和检测框作为输出。Qwen-VL 系列模型的特点包括:

  • 强大的性能:在四大类多模态任务的标准英文测评中(Zero-shot Caption/VQA/DocVQA/Grounding)上,均取得同等通用模型大小下最好效果;
  • 多语言对话模型:天然支持多语言对话,端到端支持图片里中英双语的长文本识别;
  • 多图交错对话:支持多图输入和比较,指定图片问答,多图文学创作等;
  • 首个支持中文开放域定位的通用模型:通过中文开放域语言表达进行检测框标注;
  • 细粒度识别和理解:相比于目前其它开源LVLM使用的224分辨率,Qwen-VL是首个开源的448分辨率的LVLM模型。更高分辨率可以提升细粒度的文字识别、文档问答和检测框标注。

项目地址:https://github.com/QwenLM/Qwen-VL (opens new window)

论文地址:Qwen-VL: A Versatile Vision-Language Model for Understanding, Localization, Text Reading, and Beyond (opens new window)

Qwen-VL

# 5.1.2 Qwen-VL模型权重

Qwen-VL-Chat最初是不支持在线量化的,后来ModelScope的v1.1.0版本支持了(早期版本也不行),各模型文件的下载地址如下:

支持在线量化的Qwen-VL-Chat模型

# 5.2 安装环境并部署推理服务

# 5.2.1 安装基础环境

安装conda环境

$ curl -O https://repo.anaconda.com/archive/Anaconda3-2019.03-Linux-x86_64.sh   // 从官网下载安装脚本
$ bash Anaconda3-2019.03-Linux-x86_64.sh           // 阅读协议确认安装,安装完成后再输入yes以便不需要手动将Anaconda添加到PATH
$ conda create -n conda_env python=3.10            // 安装虚拟环境,conda_env是给虚拟环境起的别名(任意即可)
$ source /root/miniconda3/etc/profile.d/conda.sh   // conda初始化
$ conda activate conda_env                         // 激活虚拟环境
1
2
3
4
5

安装其他版本的CUDA/cuDNN

$ conda search cudatoolkit
$ conda install cudatoolkit==11.8.0
$ conda list cudatoolkit
$ conda search cudnn --channel nvidia
$ conda install cudnn=8.9.2.26
$ conda list cudnn
1
2
3
4
5
6

注:默认镜像都内置了最原生的CUDA和cuDNN,如果您自己安装了cudatoolkits等,那么一般会默认优先使用conda中安装的cudatoolkits。

# 5.2.2 部署推理服务

[1] 部署全精度的推理服务

Step1:下载模型文件

安装huggingface_hub依赖:

$ pip3 install huggingface_hub
1

使用该脚本从HuggingFace下载Qwen-VL-Chat的全精度版本模型文件。

# -*- coding: utf-8 -*-

import os
from huggingface_hub import snapshot_download

# 模型仓库的标识
repo_id = "Qwen/Qwen-VL-Chat"

# 下载模型到指定目录
local_dir = "/root/autodl-tmp/Qwen-VL-Chat"

# 检查目录是否存在,如果不存在则创建
if not os.path.exists(local_dir):
    os.makedirs(local_dir)

snapshot_download(repo_id=repo_id, local_dir=local_dir)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Step2:部署API服务

安装依赖环境:

$ pip3 install flask flask-cors torch transformers
1

server.py

# -*- coding: utf-8 -*-

from flask import Flask, request
from flask_cors import cross_origin
import json
import argparse
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

parser = argparse.ArgumentParser()
parser.add_argument("--model-path", type=str, default="/root/autodl-tmp/Qwen-VL-Chat")
args = parser.parse_args()

tokenizer = AutoTokenizer.from_pretrained(args.model_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(args.model_path, device_map="cuda", trust_remote_code=True).eval()

app = Flask(__name__)


@app.route('/', methods=['POST'])
@cross_origin()
def batch_chat():
    global model, tokenizer

    data = json.loads(request.get_data())
    messages = data.get("messages")
    history = data.get("history")

    try:
        query = tokenizer.from_list_format(messages)
        response, history = model.chat(tokenizer, query=query, history=history)
        return {"response": response, "history": history, "status": 200}
    except Exception as e:
        return {"response": f"多模态大模型出错:{repr(e)}", "history": history, "status": 400}


if __name__ == '__main__':
    with torch.no_grad():
        app.run(host='0.0.0.0', port=6006)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

测试请求API服务:

image参数可以接受url或者path,如果没有图片就不传,如果多张图片就多个image。

# -*- coding: utf-8 -*-

import json
import requests

messages = [
            {'image': "https://www.eula.club/logo.png"},
            {'text': """这张图描述了什么"""}
        ]
data = {"messages": messages, "history": []}
response = requests.post("http://127.0.0.1:6006", json=data)
response = json.loads(response.content)
print("#> response: ", response['response'])
print("#> history: ", response['history'])
1
2
3
4
5
6
7
8
9
10
11
12
13
14

全精度Qwen-VL-Chat的输出及显存占用

[2] 部署在线量化的推理服务

Step1:下载模型文件

想要使用在线量化,目前模型必须使用ModelScope的v1.1.0版本,用原来在HuggingFace下载的全精度模型会出现如下报错:

RuntimeError('Input type (torch.cuda.ByteTensor) and weight type (torch.cuda.HalfTensor) should be the same')
1

安装modelscope依赖:

$ pip3 install modelscope
1

使用该脚本从ModelScope下载Qwen-VL-Chat的v1.1.0版本全精度版本模型文件。

# -*- coding: utf-8 -*-

import os
from modelscope import snapshot_download

# 模型仓库的标识
model_id = "qwen/Qwen-VL-Chat"
revision = 'v1.1.0'

# 下载模型到指定目录
cache_dir = "/root/autodl-tmp/Qwen-VL"

# 检查目录是否存在,如果不存在则创建
if not os.path.exists(cache_dir):
    os.makedirs(cache_dir)

# 下载模型
snapshot_download(model_id=model_id, revision=revision, cache_dir=cache_dir)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Step2:部署在线量化的API服务

安装 bitsandbytes 依赖以支持在线量化

$ pip3 install bitsandbytes
1

quantitative_server.py

# -*- coding: utf-8 -*-

import argparse
import json
from flask import Flask, request
from flask_cors import cross_origin
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig

# 解析命令行参数
parser = argparse.ArgumentParser()
parser.add_argument("--model_name_or_path", type=str, default="/root/autodl-tmp/Qwen-VL/qwen/Qwen-VL-Chat")
parser.add_argument('--quantization_bit', type=int, default=-1)
args = parser.parse_args()

# 根据量化参数配置模型
quantization_config = None
if args.quantization_bit == 8:  # 8位量化
    quantization_config = BitsAndBytesConfig(load_in_8bit=True, llm_int8_skip_modules=['lm_head', 'attn_pool.attn'])
elif args.quantization_bit == 4:  # 4位量化
    quantization_config = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_compute_dtype=torch.float16, bnb_4bit_quant_type='nf4', bnb_4bit_use_double_quant=True, llm_int8_skip_modules=['lm_head', 'attn_pool.attn'])

# 加载模型和分词器
model_dir = args.model_name_or_path
tokenizer = AutoTokenizer.from_pretrained(model_dir, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_dir, device_map="auto", trust_remote_code=True, fp16=True, quantization_config=quantization_config).eval()

# 初始化Flask应用
app = Flask(__name__)

@app.route('/', methods=['POST'])
@cross_origin()
def batch_chat():
    global model, tokenizer
    data = json.loads(request.get_data())
    messages = data.get("messages")
    history = data.get("history")
    try:
        query = tokenizer.from_list_format(messages)
        response, history = model.chat(tokenizer, query=query, history=history)
        return {"response": response, "history": history, "status": 200}
    except Exception as e:
        return {"response": f"多模态大模型出错: {repr(e)}", "history": history, "status": 400}

if __name__ == '__main__':
    with torch.no_grad():
        app.run(host='0.0.0.0', port=6006)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

部署INT4在线量化的版本,并使用之前的测试脚本请求。

$ python3 quantitative_server.py --quantization_bit 4
1

INT4精度Qwen-VL-Chat的输出及显存占用

部署INT8在线量化的版本,并使用之前的测试脚本请求。

$ python3 quantitative_server.py --quantization_bit 8
1

INT8精度Qwen-VL-Chat的输出及显存占用

# 5.3 Qwen-VL多模态模型微调

Qwen-VL-Chat模型支持 Full-parameter finetuning、LoRA、Q-LoRA这三种微调方式,显存占用及训练速度如下表所示:

MethodSequence Length
38451210242048
LoRA (Base)37.1G / 2.3s/it37.3G / 2.4s/it38.7G / 3.6s/it38.7G / 6.1s/it
LoRA (Chat)23.3G / 2.2s/it23.6G / 2.3s/it25.1G / 3.5s/it27.3G / 5.9s/it
Q-LoRA17.0G / 4.2s/it17.2G / 4.5s/it18.2G / 5.5s/it19.3G / 7.9s/it

# 5.3.1 准备微调数据集

需要将所有样本数据放到一个列表中并存入JSON文件中。每个样本对应一个字典,包含id和conversation,其中后者为一个列表。

data.json

[
  {
    "id": "identity_0",
    "conversations": [
      {
        "from": "user",
        "value": "你好"
      },
      {
        "from": "assistant",
        "value": "我是Qwen-VL,一个支持视觉输入的大模型。"
      }
    ]
  },
  {
    "id": "identity_1",
    "conversations": [
      {
        "from": "user",
        "value": "Picture 1: <img>https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg</img>\n图中的狗是什么品种?"
      },
      {
        "from": "assistant",
        "value": "图中是一只拉布拉多犬。"
      },
      {
        "from": "user",
        "value": "框出图中的格子衬衫"
      },
      {
        "from": "assistant",
        "value": "<ref>格子衬衫</ref><box>(588,499),(725,789)</box>"
      }
    ]
  },
  { 
    "id": "identity_2",
    "conversations": [
      {
        "from": "user",
        "value": "Picture 1: <img>assets/mm_tutorial/Chongqing.jpeg</img>\nPicture 2: <img>assets/mm_tutorial/Beijing.jpeg</img>\n图中都是哪"
      },
      {
        "from": "assistant",
        "value": "第一张图片是重庆的城市天际线,第二张图片是北京的天际线。"
      }
    ]
  }
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

对数据格式的解释:

  • 为针对多样的VL任务,增加了一下的特殊tokens: <img> </img> <ref> </ref> <box> </box>
  • 对于带图像输入的内容可表示为 Picture id: <img>img_path</img>\n{your prompt},其中id表示对话中的第几张图片。"img_path"可以是本地的图片或网络地址。
  • 对话中的检测框可以表示为<box>(x1,y1),(x2,y2)</box>,其中 (x1, y1)(x2, y2)分别对应左上角和右下角的坐标,并且被归一化到[0, 1000)的范围内. 检测框对应的文本描述也可以通过<ref>text_caption</ref>表示。

# 5.3.2 对模型进行LoRA微调

这里使用官方项目里提供的微调脚本进行LoRA微调测试,模型采用HuggingFace下载的那个全精度模型,数据采用上面的示例数据。

finetune_lora_single_gpu.sh

#!/bin/bash

export CUDA_DEVICE_MAX_CONNECTIONS=1
DIR=`pwd`

MODEL="/root/autodl-tmp/Qwen-VL-Chat"
DATA="/root/autodl-tmp/data.json"

export CUDA_VISIBLE_DEVICES=0

python3 finetune.py \
    --model_name_or_path $MODEL \
    --data_path $DATA \
    --bf16 True \
    --fix_vit True \
    --output_dir output_qwen \
    --num_train_epochs 5 \
    --per_device_train_batch_size 1 \
    --per_device_eval_batch_size 1 \
    --gradient_accumulation_steps 8 \
    --evaluation_strategy "no" \
    --save_strategy "steps" \
    --save_steps 1000 \
    --save_total_limit 10 \
    --learning_rate 1e-5 \
    --weight_decay 0.1 \
    --adam_beta2 0.95 \
    --warmup_ratio 0.01 \
    --lr_scheduler_type "cosine" \
    --logging_steps 1 \
    --report_to "none" \
    --model_max_length 600 \
    --lazy_preprocess True \
    --gradient_checkpointing \
    --use_lora
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

注意事项:

  • 需要修改脚本中的MODEL、DATA参数,将其换成实际的模型和数据地址。
  • 需要修改脚本里的model_max_length参数,默认是2048,这需要27.3GB的显存,租用的服务器显存不够,这里将其设置为600,是可以微调成功的。

对Qwen-VL-Chat模型进行LoRA微调

# 6. 使用优化算法降低全量微调的显存

实验环境:租用的AutoDL的GPU服务器,NVIDIA RTX 4090D / 24GB,Ubuntu20.04,Python 3.10, CUDA 11.8

数据盘额外扩容了100GB,关于GPU服务器租用这里就不赘述了,详见我的另一篇博客:常用深度学习平台的使用指南 (opens new window)

# 6.1 背景介绍及技术概述

# 6.1.1 背景介绍

LLM 训练通常需要比较大的显存,主要是模型权重和优化器状态。节约显存常见的方法有 LoRA,然而其往往用于微调阶段,或需要满秩热启动,导致预训练依旧需要很大的显存。梯度低秩投影(GaLore)这是一种允许全参数学习的训练策略,但比常见的 LoRA 等方案更省显存。可以减少多达 65.5% 的显存。此方案可以在更省显存的同时基本不影响模型效果,但是训练时间会变得很长。

与 8-bit Adam 结合,8-bit GaLore 可以进一步减少高达 82.5% 的优化器内存和 63.3% 的总训练内存。甚至实现了在 24GB 显存的消费级 GPU(如 NVIDIA RTX 4090)上训练 7B 模型,而无需模型并行,Checkpointing 和 Offload 策略。

# 6.1.2 GaLore技术概述

GaLore 将低秩投影应用到模型训练的梯度上,可以大幅节约显存占用,为消费级显卡全量微调训练大模型提供了一种可能。

  • 梯度低秩投影(GaLore)是一种全量参数学习的训练策略,但比常见的低秩自适应方法(如LoRA)更节省显存。其关键思想是利用权重矩阵 W 的梯度缓慢变化的低秩结构,而不是试图将权重矩阵本身近似为低秩。
  • 作为一种梯度投影方法,GaLore 与优化器的选择无关,只需两行代码即可轻松插入现有优化器,GaLore目前实现了GaLoreAdamW, GaLoreAdamW8bit, GaLoreAdafactor 三种优化器。

项目地址:https://github.com/jiaweizzhao/GaLore (opens new window)

论文地址:GaLore: Memory-Efficient LLM Training by Gradient Low-Rank Projection (opens new window)

GaLore

注:目前GaLore仅支持单GPU训练,该技术还处在开发阶段,官方说正式版将会支持多GPU训练。

# 6.1.3 BAdam技术概述

基本介绍:BAdam的核心思想是依次求解块坐标优化子问题。从实现的角度来看,该算法在参数的一小部分(通常是一个 Transformer 层)上运行 Adam 的更新,因此与全参数 Adam 微调相比,需要的显存要少得多。使用 BAdam 只需要对原始代码进行一行修改。

# 6.2 准备测试环境

# 6.2.1 安装基础环境

安装conda环境

$ curl -O https://repo.anaconda.com/archive/Anaconda3-2019.03-Linux-x86_64.sh   // 从官网下载安装脚本
$ bash Anaconda3-2019.03-Linux-x86_64.sh           // 阅读协议确认安装,安装完成后再输入yes以便不需要手动将Anaconda添加到PATH
$ conda create -n fine_tuning_env python=3.10      // 安装虚拟环境,fine_tuning_env是给虚拟环境起的别名(任意即可)
$ source /root/miniconda3/etc/profile.d/conda.sh   // conda初始化
$ conda activate fine_tuning_env                   // 激活虚拟环境
1
2
3
4
5

安装其他版本的CUDA/cuDNN

$ conda search cudatoolkit
$ conda install cudatoolkit==11.8.0
$ conda list cudatoolkit
$ conda search cudnn --channel nvidia
$ conda install cudnn=8.9.2.26
$ conda list cudnn
1
2
3
4
5
6

注:默认镜像都内置了最原生的CUDA和cuDNN,如果您自己安装了cudatoolkits等,那么一般会默认优先使用conda中安装的cudatoolkits。

# 6.2.2 下载模型文件

全量微调需要大量的计算资源,用了GaLore也是用时间来换取的,因此这里使用了参数量较小的 Qwen1.5-0.5B 作为实验模型。

安装huggingface_hub依赖:

$ pip3 install huggingface_hub
1

download_model.py

# -*- coding: utf-8 -*-

import os
from huggingface_hub import snapshot_download

# 模型仓库的标识
repo_id = "Qwen/Qwen1.5-0.5B"

# 下载模型到指定目录
local_dir = "/root/autodl-tmp/Qwen-1.5-0.5B"

# 检查目录是否存在,如果不存在则创建
if not os.path.exists(local_dir):
    os.makedirs(local_dir)

snapshot_download(repo_id=repo_id, local_dir=local_dir)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 6.2.3 准备微调代码

LLaMA-Factory 是一个易于使用的大模型微调框架,旨在简化大型语言模型的微调过程,提供了一套完整的工具和接口,使得用户能够轻松地对预训练的模型进行定制化的训练和调整,以适应特定的应用场景。

LLaMA-Factory具备以下特性:

  • 多种模型:LLaMA,LLaVA,Mistral,Mixtral-MoE,Qwen,Yi,Gemma,Baichuan,ChatGLM,Phi。
  • 集成方法:(Continuous) pre-training, (multimodal) supervised fine-tuning, reward modeling, PPO, DPO and ORPO。
  • 多种精度:32-bit full-tuning, 16-bit freeze-tuning, 16-bit LoRA,2/4/8-bit QLoRA via AQLM/AWQ/GPTQ/LLM.int8。
  • 先进算法:GaLore,BAdam,DoRA,LongLoRA,LLaMA Pro,Mixture-of-Depths,LoRA+,LoftQ,Agent tuning。
  • 实用技巧:FlashAttention-2,Unsloth,RoPE scaling,NEFTune,rsLoRA。
  • 实验监控:LlamaBoard,TensorBoard,Wandb,MLflow。
  • 极速推理:OpenAI风格的API,Gradio UI和CLI、vLLM支持。

LLaMA-Factory项目已经集成支持了GaLore、BAdam等先进的优化算法。

LLaMA-Factory支持GaLore及BAdam

拉取代码并安装依赖:

$ git clone https://github.com/hiyouga/LLaMA-Factory.git
$ cd /root/LLaMA-Factory
$ pip3 install -r requirements.txt
1
2
3

该项目下的 data 目录自带了大量的开源数据集,以下均采用 oaast_sft_zh.json 数据集进行测试。

oaast_sft_zh测试数据集

# 6.2.4 显存监控脚本

由于需要监控微调过程的显存占用,这里简单写了个Python脚本去实现。

$ pip3 install nvidia-ml-py matplotlib
1

monitor.py

# -*- coding: utf-8 -*-

import pynvml
import matplotlib.pyplot as plt
import datetime
import time
import signal

# 初始化NVML
pynvml.nvmlInit()
# 获取第一个GPU的句柄
handle = pynvml.nvmlDeviceGetHandleByIndex(0)
memory_usage = []
times = []
running = True

def signal_handler(sig, frame):
    global running
    running = False

# 注册信号处理器,以便于接收到Ctrl+C时能够停止收集数据
signal.signal(signal.SIGINT, signal_handler)

print("开始监控GPU显存使用情况,按Ctrl+C停止...")

# 收集数据,直到接收到停止信号
try:
    while running:
        info = pynvml.nvmlDeviceGetMemoryInfo(handle)
        memory_used = round(info.used / 1024 ** 2, 2)
        memory_usage.append(memory_used)
        times.append(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
        time.sleep(1)
finally:
    pynvml.nvmlShutdown()

# 计算最大值
max_usage = max(memory_usage, default=0)
max_index = memory_usage.index(max_usage) if memory_usage else -1

# 创建图表
plt.figure(figsize=(10, 5))
plt.plot(times, memory_usage, label='Memory Usage (MB)', linestyle='-', color='gray')
# 突出显示开始、最大值、结束的点
highlight_indices = [0, max_index, len(memory_usage) - 1]
highlight_times = [times[i] for i in highlight_indices]
highlight_usages = [memory_usage[i] for i in highlight_indices]
plt.scatter(highlight_times, highlight_usages, color='red', s=100, zorder=5)
# 添加特定时间点的标注
plt.annotate(f'Start: {memory_usage[0]:.2f} MB', (times[0], memory_usage[0]),
             textcoords="offset points", xytext=(0,10), ha='center', va='bottom')
plt.annotate(f'Max: {max_usage:.2f} MB', (times[max_index], max_usage),
             textcoords="offset points", xytext=(0,10), ha='center', va='bottom')
plt.annotate(f'End: {memory_usage[-1]:.2f} MB', (times[-1], memory_usage[-1]),
             textcoords="offset points", xytext=(0,-15), ha='center', va='top')

# 设置x轴仅显示开始、最大、结束的时间点
plt.xticks(highlight_times, rotation=45)
plt.legend()
plt.tight_layout()

# 保存图表
plt.savefig('/root/autodl-tmp/gpu_memory_usage.png')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

# 6.3 三种方式单卡全量微调

# 6.3.1 不使用优化算法单卡全量微调

$ cd /root/LLaMA-Factory/examples/full_multi_gpu
1

这里不使用 DeepSpeed 分布式训练技术,修改 single_node.sh 脚本的内容如下,并执行。

#!/bin/bash

CUDA_VISIBLE_DEVICES=0 python3 ../../src/train_bash.py \
    --stage sft \
    --do_train \
    --model_name_or_path /root/autodl-tmp/Qwen-1.5-0.5B \
    --dataset oaast_sft_zh \
    --dataset_dir ../../data \
    --template default \
    --finetuning_type full \
    --output_dir /root/autodl-tmp/Qwen-1.5-0.5B/full \
    --overwrite_cache \
    --overwrite_output_dir \
    --cutoff_len 1024 \
    --preprocessing_num_workers 16 \
    --per_device_train_batch_size 1 \
    --per_device_eval_batch_size 1 \
    --gradient_accumulation_steps 1 \
    --lr_scheduler_type cosine \
    --logging_steps 10 \
    --warmup_steps 20 \
    --save_steps 100 \
    --eval_steps 100 \
    --evaluation_strategy steps \
    --learning_rate 5e-5 \
    --num_train_epochs 3.0 \
    --max_samples 3000 \
    --val_size 0.1 \
    --plot_loss \
    --fp16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

训练过程中的显存占用:最大显存 11.64GB

不使用优化算法单卡全量微调的显存占用

训练结束的日志信息:训练耗时 6min44s

不使用优化算法单卡全量微调的日志

# 6.3.2 使用GaLore单卡全量微调

$ cd /root/LLaMA-Factory/examples/extras/galore
$ pip3 install galore_torch
1
2

修改 sft.sh 脚本的内容如下,并执行。

#!/bin/bash

CUDA_VISIBLE_DEVICES=0 python3 ../../../src/train_bash.py \
    --stage sft \
    --do_train \
    --model_name_or_path /root/autodl-tmp/Qwen-1.5-0.5B \
    --dataset oaast_sft_zh \
    --dataset_dir ../../../data \
    --template default \
    --finetuning_type full \
    --use_galore \
    --galore_layerwise \
    --galore_target mlp,self_attn \
    --galore_rank 128 \
    --galore_scale 2.0 \
    --output_dir /root/autodl-tmp/Qwen-1.5-0.5B/galore_full \
    --overwrite_cache \
    --overwrite_output_dir \
    --cutoff_len 1024 \
    --preprocessing_num_workers 16 \
    --per_device_train_batch_size 1 \
    --per_device_eval_batch_size 1 \
    --gradient_accumulation_steps 1 \
    --lr_scheduler_type cosine \
    --logging_steps 10 \
    --warmup_steps 20 \
    --save_steps 100 \
    --eval_steps 100 \
    --evaluation_strategy steps \
    --load_best_model_at_end \
    --learning_rate 5e-5 \
    --num_train_epochs 3.0 \
    --max_samples 3000 \
    --val_size 0.1 \
    --plot_loss \
    --pure_bf16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

训练过程中的显存占用:最大显存 8.24GB

使用GaLore单卡全量微调的显存占用

训练结束的日志信息:训练耗时 7min48s

使用GaLore单卡全量微调的训练日志

# 6.3.3 使用BAdam单卡全量微调

$ cd /root/LLaMA-Factory/examples/extras/badam
$ pip3 install badam
1
2

修改 sft.sh 脚本的内容如下,并执行。

#!/bin/bash

CUDA_VISIBLE_DEVICES=0 python3 ../../../src/train_bash.py \
    --stage sft \
    --do_train \
    --model_name_or_path /root/autodl-tmp/Qwen-1.5-0.5B \
    --dataset oaast_sft_zh \
    --dataset_dir ../../../data \
    --template default \
    --finetuning_type full \
    --use_badam \
    --badam_switch_mode descending \
    --badam_switch_block_every 50 \
    --badam_verbose 2 \
    --output_dir /root/autodl-tmp/Qwen-1.5-0.5B/badam_full \
    --overwrite_cache \
    --overwrite_output_dir \
    --cutoff_len 1024 \
    --preprocessing_num_workers 16 \
    --per_device_train_batch_size 1 \
    --per_device_eval_batch_size 1 \
    --gradient_accumulation_steps 1 \
    --lr_scheduler_type cosine \
    --logging_steps 10 \
    --warmup_steps 20 \
    --save_steps 100 \
    --eval_steps 100 \
    --evaluation_strategy steps \
    --load_best_model_at_end \
    --learning_rate 5e-5 \
    --num_train_epochs 3.0 \
    --max_samples 3000 \
    --val_size 0.1 \
    --plot_loss \
    --pure_bf16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

训练过程中的显存占用:最大显存 8.30GB

使用BAdam单卡全量微调的显存占用

训练结束的日志信息:训练耗时 2min56s

使用BAdam单卡全量微调的训练日志

# 7. 大模型效果评估

# 7.1 如何评估大模型

# 7.1.1 影响效果的因素

OpenAI的论文Scaling Laws for Neural Language Models中列举了影响模型性能最大的三个因素:计算量、数据集大小、模型参数量。也就是说,当其他因素不成为瓶颈时,计算量、数据集大小、模型参数量这3个因素中的单个因素指数增加时,loss会线性的下降。

除了以上的因素之外,还有一个比较大的影响因素就是数据质量。在微软的论文Instruction Tuning with GPT-4中指出,同样基于LLaMA模型,使用GPT3和GPT4产生的数据,对模型进行Instruction Turing,可以看到GPT4的数据微调过的模型效果远远好于GPT3数据微调的模型,可见数据质量带来的影响。

# 7.1.2 评估水平的维度

不同种类的大模型评测应该有不同的方法:

  • 基础语言模型不具备指令理解能力。
  • SFT模型和RL模型可以完成多种任务、要能够服从人类指令。

大模型的不同阶段

要评估一个大型语言模型的水平,可以从以下几个维度提出具有代表性的问题。

  • 理解能力:提出一些需要深入理解文本的问题,看模型是否能准确回答。
  • 语言生成能力:让模型生成一段有关特定主题的文章或故事,评估其生成的文本在结构、逻辑和语法等方面的质量。
  • 知识面广度:请模型回答关于不同主题的问题,以测试其对不同领域的知识掌握程度。这可以是关于科学、历史、文学、体育或其他领域的问题。一个优秀的大语言模型应该可以回答各种领域的问题,并且准确性和深度都很高。
  • 适应性:让模型处理各种不同类型的任务,例如:写作、翻译、编程等,看它是否能灵活应对。
  • 长文本理解:提出一些需要处理长文本的问题,例如:提供一篇文章,让模型总结出文章的要点,或者请模型创作一个故事或一篇文章,让其有一个完整的情节,并且不要出现明显的逻辑矛盾或故事结构上的错误。一个好的大语言模型应该能够以一个连贯的方式讲述一个故事,让读者沉浸其中。
  • 长文本生成:请模型创作一个故事或一篇文章,让其有一个完整的情节,并且不要出现明显的逻辑矛盾或故事结构上的错误。一个好的大语言模型应该能够以一个连贯的方式讲述一个故事,让读者沉浸其中。
  • 多样性:提出一个问题,让模型给出多个不同的答案或解决方案,测试模型的创造力和多样性。
  • 情感分析和推断:提供一段对话或文本,让模型分析其中的情感和态度,或者推断角色间的关系。
  • 情感表达:请模型生成带有情感色彩的文本,如描述某个场景或事件的情感、描述一个人物的情感状态等。一个优秀的大语言模型应该能够准确地捕捉情感,将其表达出来。
  • 逻辑推理能力:请模型回答需要进行推理或逻辑分析的问题,如概率或逻辑推理等。这可以帮助判断模型对推理和逻辑思考的能力,以及其在处理逻辑问题方面的准确性。例如:“所有的动物都会呼吸。狗是一种动物。那么狗会呼吸吗?”
  • 问题解决能力:提出实际问题,例如:数学题、编程问题等,看模型是否能给出正确的解答。
  • 道德和伦理:测试模型在处理有关道德和伦理问题时的表现,例如:“在什么情况下撒谎是可以接受的?”
  • 对话和聊天:请模型进行对话,以测试其对自然语言处理的掌握程度和能力。一个优秀的大语言模型应该能够准确地回答问题,并且能够理解人类的语言表达方式。

# 7.2 大模型评估方式

大模型效果评估的方式主要包含指标自动评估、人工评估两类。

大模型评测分类维度

# 7.2.1 指标自动评估

[1] 指标评估:自动评估方法在大型语言模型的评估中变得不可或缺,促进了各种任务和领域中的高效、客观和标准化的评估过程。

常见指标 概览 其他
Accuracy 正确预测数/预测总数
EM(exact match) 预测值和答案是否完全一样
BLEU(Bilingual Evaluation Understudy) 将模型给的结果A与标准结果GT进行对比,句子长度为n,A中有m个单词出现在GT,m/n就是bleu的1-gram的计算公式 用于评估机器翻译结果质量,主要侧重于衡量机器翻译输出与参考翻译之间的相似程度,着重于句子的准确性和精确匹配
ROUGE(Recall-Oriented Understudy for Gisting Evaluation) ROUGE通过计算N-gram的共现情况,来评估机器生成的摘要的召回率(Recall),只能在单词、短语的角度衡量两个句子的形似度 用于评估文本摘要(或其他自然语言处理任务)质量的指标
Regex patterns 正则表达式、用于检测是否存在电话号码、信用卡号等敏感信息
BERTScore 计算生成文本和参考文本之间的余弦相似度。 用于文本生成
Perplexity 困惑度一般来说是用来评价语言模型好坏的指标,一个语言概率模型可以看成是在句子或者文段上的概率分布,给测试集的句子赋予较高概率值的语言模型较好
F1-score 召回和精准的调和平均值,分别计算准确率与召回率

[2] 使⽤GPT-4 的反馈进⾏⾃动评估:Vicuna、Phoenix、Chimera、BELLE。

# 7.2.2 人工评估

人工评估主要需要考虑以下三种因素:评估人员的数量、评估标准、评估者的专业水平。它主要可分为两种方式:单一答案评分和相对排序。

[1] 单一答案评分:这种方法简单易用,评价不同语言模型时易于标准化、比较和分析,但该方法也存在一些缺陷,如不同评测员对不同等级、不同指标的理解存在一定主观性偏颇等。

单一答案评分

[2] 相对排序:如果直接要求参与者对待评估内容给出评估得分,那么得分的取值范围很容易受到人的主观因素影响,不同的参与者对同样质量的文本,可能会给出相差很大的评分结果。为了规避这一问题,可以采取相对排序的方法。首先,将参与比较的语言模型针对同一输入的回复作为一组提供给评测员;然后,评测员依次通过两两比较得出哪一个语言模型的回复更好,直至可以得到针对该输入,每个模型的回复的最终排名;最后,根据不同语言模型在多轮评估后的平均排名比较不同模型的性能差异。

相对排序评估

# 7.3 大模型评估工具

[1] HELM(斯坦福,2022):提出了语言模型的整体评估,以提高语言模型的透明度“场景、任务、指标”。

HELM

[2] AGI-EVAL(微软 2023.4):专门用于评估基础模型在标准化考试,如高考、公务员考试、 法学院入学考试、数学竞赛和律师资格等考试中的表现。

AGI-EVAL

[3] C-EVAL(上交、清华 2023.5):旨在评估基础模型先进知识和推理能力的首个全面的中文评测套件。

C-Eval

[4] FlagEval是一个面向AI基础模型的评测工具包,目标是探索和集合科学、公正、开放的基础模型评测基准、方法及工具,对多领域(如语言、语音、视觉及多模态)的基础模型进行多维度(如准确性、效率、鲁棒性等)的评测。希望通过对基础模型的评测,加深对基础模型的理解,促进相关的技术创新及产业应用。

Flag-EVAL

[5] PandaLM(2023.6 北大):专门用于评估大模型性能的裁判大模型。

PandaLM

[6] FastChat是一个开放平台,用于训练、服务和评估基于LLM的聊天机器人。它的一项主要功能是支持Chatbot Arena,通过横向对比大量的LLM,并根据用户投票来生成在线LLM Elo排行榜。

FastChat

# 8. 大模型微调经验

以上大模型微调过程只是作为demo演示基本训练过程,实际上真正要训练出一个可用的微调模型并没有这么简单,训练过程很容易导致其他通用能力的受损,出现捡了芝麻丢了西瓜的情况。

注:如果微调模型的领域能力已经提高上来了,但同时模型通用能力受损,急着要上线发布的话,有个临时取巧的方案。先用原始基座模型对用户问题做个意图判断,如果是领域相关的,调用微调后的模型去回复;如果领域不相关,则调用原始基座模型去回复。

# 8.1 大模型微调经验总结

以下是一些模型训练经验,可以用来解决模型通用能力受损、模型遗忘问题、重复生成问题等。

# 8.1.1 调整训练数据集

[1] 加入一定通用SFT数据:除了领域数据之外,再混入一定量的通用SFT数据做能力维持(很有必要,加入SFT数据之后,模型通用能力有了很大改善)

[2] 选择性语言建模:对低质量预训练文本进行自适应过滤。然后多样化领域问题和回复,调整问题的数量(领域知识数量少,记不住;而过多,又回导致其他通用能力受损)

# 8.1.2 调整训练策略

[1] 微调层数:微调模型的哪些层参数(视情况而定,有时仅对模型的部分层进行微调,保持其他层的参数不变,可以减少遗忘现象)

  • Freeze微调:只对最后四层进行微调。通过保持多数层的参数,从而减轻遗忘问题。
  • Stack式增量参数微调:为进一步减轻遗忘问题,freeze原有7B参数,通过每隔8层新增一层的方式,将参数增加到8B规模,并且只训练新增参数。
  • MoE式增量参数微调:为适配多任务微调,采用目前主流的MoE大模型架构,在1B的增量参数预算下,采用Sparse MoE架构,只将其中的4层重构为MoE层,采用复制的方式增加额外两个专家。

[2] DPO训练:利用偏好数据集,通过DPO算法训练模型生成符合人类偏好的响应,可以有效解决回复重复的问题。(注意调整学习率,要注意观察训练loss,之前我们遇到过loss不咋降的情况,训练出来的模型很差)。

[3] 模型蒸馏:使用预训练的大型语言模型作为教师模型,通过知识蒸馏技术将其知识转移到较小的模型中。这样,小模型可以更高效地学习和生成高质量的内容。

# 8.2 大模型微调策略概述

# 8.2.1 数据筛选策略

通过对初步采样的数据进行微调并计算每条数据的IFD分数,筛选出IFD分数较高的数据作为高质量微调数据集;然后定义一个任务集,计算微调后模型在任务集上的表现;通过近似评估每条数据对任务集表现的提升,将提升显著的数据纳入高质量微调数据集,从而形成一套优化数据筛选策略。

数据筛选策略

# 8.2.2 模型蒸馏策略

教师模型:DeepSeek-V3、gpt4o这种在当前处于SOTA地位的大模型

方法步骤:

  1. 构建一个问题数据集。我们从alpaca_gpt4_data_zh数据集中抽取了1k条问题。

  2. 获取学生模型和教师模型对问题的输出。

  3. 再让教师模型作为评判员来评判学生模型回答的好坏,得到一个分数。

  4. 根据这个分数,区分出hard问题和easy问题。

  • Easy问题:{"instruction": "用三个形容词描述红色。", "input": "", "assist1": "鲜艳、热情、醒目。", "assist2": "热烈、明亮、温暖。", "assist1_score": 9.0, "assist2_score": 9.0, "review_score_diff": 0.0}
  • Hard问题:{"instruction": "将数字22,000转换为文字。", "input": "", "assist1": "数字22,000转换为文字是:二万二千。", "assist2": "22,000转换为文字为“二十二万”。", "assist1_score": 9.0, "assist2_score": 2.5, "review_score_diff": 6.5}
  1. 让教师模型根据hard问题重新生成类似的hard问题(中间有评判问题质量的代码,可以尽量避免问题重复)。比如:
  • 美国没有标示速度限制的高速公路的最高限速是多少?=>在美国,没有明确标示速度限制的高速公路上,驾驶员应遵守的默认最高速度是多少?
  • 38度华氏温度转换成摄氏温度是多少?=>将95华氏度转换为摄氏温度是多少?
  1. 将包含教师模型回复的hard问题和新生成的hard问题组成一个新的指令微调数据集,让学生模型在上面做sft。

# 8.2.3 选择性语言建模

原因动机:预训练数据中包含低质量Token,容易影响模型性能;在微调时,Cot数据中的解析和答案混合存在,直接训练难以突出答案及解析中的关键部分。如果对所有Token采用相同的损失函数,则会在无意义Token上浪费计算资源,并可能降低模型效果。

解决方案:首先在高质量语料上训练一个参考模型,计算每个Token的参考损失。然后,通过比较参考损失和训练模型的损失,选择那些超额损失高的Token进行训练,使模型重点关注对性能提升有帮助的Token,提升训练效率和模型表现。

# 8.2.4 DPO人类偏好对齐

DPO(Direct Preference Optimization)是一种用于后训练阶段的直接偏好优化方法,主要用于图像处理中的扩散模型,通过最大化奖励函数并最小化KL散度来优化模型,使其更符合人类偏好。

DPO人类偏好对齐

# 9. 参考资料

[1] LLM 技术图谱 from Gitee (opens new window)

[2] ancient_text_generation_LLM 现代汉语转古文大模型 from Github (opens new window)

[3] LLM 大模型训练之路 from 鹤啸九天 (opens new window)

[4] LLaMA-Factory:大模型微调框架 from Github (opens new window)

[5] 推算LLM训练的GPU内存需求 from 微信公众号 (opens new window)

[6] 大模型多轮对话开源标注工具Label-LLM安装部署教程 from YouTube (opens new window)

[7] LabelLLM常见问题 from Github (opens new window)

[8] 大模型训练数据集介绍 from 知乎 (opens new window)

[9] LLM时代,数据为王,19个开源数据集下载网站汇总 from 吃果冻不吐果冻皮 (opens new window)

[10] 使用LLaMa-Factory简单高效微调大模型 from 知乎 (opens new window)

[11] 中国大模型列表 from Github (opens new window)

[12] 调查论文“大型语言模型调查” from Github (opens new window)

[13] 中文通用大模型开放域多轮测评基准 from Github (opens new window)

[14] 开源大语言模型完整列表 from Gitee (opens new window)

[16] SFT 监督微调、LoRA 微调方法、P-tuning v2 微调方法、Freeze 监督微调方法 from 稀土掘金 (opens new window)

[17] 大模型LLM-微调经验分享&总结 from 知乎 (opens new window)

[18] 大模型参数高效微调技术原理综述(一)-背景、参数高效微调简介 from 吃果冻不吐果冻皮 (opens new window)

[19] 大模型实践总结 from 吃果冻不吐果冻皮 (opens new window)

[20] 分享大模型相关技术原理以及实战经验 from 吃果冻不吐果冻皮 (opens new window)

[21] 大模型微调方法综述 from 古月居 (opens new window)

[22] 落地领域大模型应知必会 (1) :主要微调方法总览 from 稀土掘金 (opens new window)

[23] 多模态大型语言模型的最新论文和数据集 from Github (opens new window)

[24] 大语言模型(LLM)微调技术笔记 from Github (opens new window)

[25] LLM大模型 指令微调、peft高效参数微调 from CSDN (opens new window)

[26] 人工智能大语言模型微调技术:SFT 、LoRA 、Freeze 监督微调方法 from Bilibili (opens new window)

[27] Qwen-VL预训练大语言视觉模型 from Github (opens new window)

[28] 通义千问-VL-Chat from ModelScope (opens new window)

[29] 可以成功Lora微调的Qwen-VL模型 from Github (opens new window)

[30] Qwen-VL模型微调 from Github (opens new window)

[31] GaLore:梯度低秩投影,消费级显卡训练 LLaMA-7B from 微信公众号 (opens new window)

[32] GaLore:通过梯度低秩投影进行内存高效的 LLM 训练 from Github (opens new window)

[33] BAdam:一种用于大型语言模型的内存高效全参数训练方法 from Github (opens new window)

[34] 大语言模型评测 from 复旦大学张奇 (opens new window)

[35] 大模型评测方法简述 from 知乎 (opens new window)

[36] DPO:人类偏好对齐技术——大模型训练的最后一公里 from Bilibili (opens new window)

[37] 人类偏好对齐训练技术解析 from 53AI知识库 (opens new window)

[38] 选择需要的Token参与训练:微软重塑语言模型训练法则,准确率飙升30% from 领研 (opens new window)

[39] 去重神器SemHash:每秒2.2万条,快如闪电!已开源 from 微信公众号 (opens new window)

[40] SemHash:快速实现语义文本去重,提升数据清理效率 from 首席Al分享圈 (opens new window)

[41] 什么是混合专家模型(MoE)?from Bilibili (opens new window)

[42] 混合专家模型 (MoE) 详解 from Huggingface (opens new window)

[43] Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity from arxiv (opens new window)

[44] 大模型混入0.001%假数据就「中毒」,成本仅5美元!NYU新研究登Nature子刊 from 微信公众号 (opens new window)

[45] 7B模型搞定AI视频通话,阿里最新开源炸场,看听说写全模态打通,开发者企业免费商用 from 微信公众号 (opens new window)

[46] 一文了解DeepSeek及应用场景 from 吃果冻不吐果冻皮 (opens new window)

[47] 大模型应用上线前如何做好合规和备案 from 知乎 (opens new window)

[48] 精心整理|2025大模型(生成式人工智能服务)备案 from 知乎 (opens new window)

[49] 万字长文,深度解析大模型备案全流程 from 网易易盾 (opens new window)

[50] 算法备案和大模型备案区别是什么?from 网易易盾 (opens new window)

Last Updated: 4/30/2025, 2:55:19 PM