【大模型微调】LoRA — 其实大模型微调也没那么难!

在这里插入图片描述


文章目录
  • 前言
  • 一、什么是微调?
  • 二、LORA 简介
    • 2.1 微调作为参数变化
    • 2.2 参数变化压缩
  • 三、LoRA微调的流程
  • 四、LoRA在Transformer中的应用
  • 五、LoRA Rank
  • 六、Python代码实现
    • 6.1 安装依赖库:
    • 6.2 加载预训练模型
    • 6.3 设置LoRA
    • 6.4 检查内存的节省
    • 6.5 加载微调数据集
    • 6.6 使用LoRA在SQUAD上进行微调
    • 6.7 检查LoRA大小
    • 6.8 测试

前言

直接上干货:大语言模型可以做什么?
在这里插入图片描述


LoRA的核心思想:
**加粗样式**

提示:后面是主要内容:


一、什么是微调?

随着机器学习的最新发展,对模型性能的期望也在增加,需要更复杂的机器学习方法来满足对性能的需求。在机器学习的早期阶段,构建一个模型并在单次训练中训练它是可行的。

在这里插入图片描述

训练,在其最简单的意义上。您将一个未经训练的模型,提供给它数据,并获得一个高性能的模型。

对于简单问题来说,这仍然是一种流行的策略,但对于更复杂的问题,将训练分为两个部分,即“预训练”和“微调”,可能会很有用。总体思路是在一个大规模数据集上进行初始训练,并在一个定制的数据集上对模型进行优化
在这里插入图片描述

这种“预训练”然后“微调”的策略可以让数据科学家利用多种形式的数据,并使用大型预训练模型来完成特定任务。因此,预训练然后微调是一种常见且非常强大的范例。

最基本的微调形式 是使用与预训练模型相同的过程来微调新数据上的模型。例如,您可以在大量的通用文本数据上训练模型,然后使用相同的训练策略,在更具体的数据集上微调该模型。

以上策略十分昂贵 。LLMs绝对庞大,需要足够的内存来存储整个模型,以及模型中每个参数的梯度(梯度是让模型知道调整参数方向的东西)。参数和梯度都需要存在于GPU上,这就是为什么训练LLMs需要如此多的GPU显存的。

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

二、LORA 简介

“低秩适应”(LoRA)是一种“参数高效微调”(PEFT)的形式,它允许使用少量可学习参数对大型模型进行微调 。LoRA改善微调的几个点:

  1. 将微调视为学习参数的变化(▲w),而不是调整参数本身(w)。

  2. 通过删除重复信息,将这些变化压缩成较小的表示。

  3. 通过简单地将它们添加到预训练参数中来“加载”新的变化。

2.1 微调作为参数变化

正如之前讨论的,微调的最基本方法是迭代地更新参数。就像正常的模型训练一样,你让模型进行推理,然后根据推理的错误程度更新模型的参数。

与其将微调视为学习更好的参数,LoRA 将微调视为学习参数变化:冻结模型参数,然后学习使模型在微调任务中表现更好所需的这些参数的变化。

类似于训练,首先让模型推理,然后根据error进行更新。但是,不更新模型参数,而是更新模型参数的变化。

在这里插入图片描述

在LoRA中,我们冻结模型参数,并创建一组描述这些参数变化的新值。然后,我们学习必要的参数变化,以在微调任务上表现更好。LoRA添加更多的数据和额外的步骤,如何使微调变得更小、更快?

2.2 参数变化压缩

**加粗样式**

矩阵的秩是为了量化矩阵中的线性独立性。我们可以将一个矩阵分解为一些线性独立的向量;这种矩阵的形式被称为“行阶梯形式”。

在这里插入图片描述

因此,矩阵可以包含一定程度的“重复信息”,即线性相关性。如果你有一个大矩阵,具有显著的线性相关性(因此秩较低),可以将该矩阵表示为两个相对较小的矩阵的乘积。这种分解的思想使得LoRA占用了如此小的内存空间。

三、LoRA微调的流程

首先冻结模型参数。使用这些参数进行推理,但不会更新它们。然后创建两个矩阵,当它们相乘时,它们的大小将与我们正在微调的模型的权重矩阵的大小相同。在一个大型模型中,有多个权重矩阵,为每个权重矩阵创建一个这样的配对。
在这里插入图片描述

LoRA将这些矩阵称为矩阵“A”和“B”。这些矩阵一起代表了LoRA微调过程中的可学习参数。
在这里插入图片描述

然后将输入通过冻结的权重和变化矩阵传递:
在这里插入图片描述

根据两个输出的组合计算损失,然后根据损失更新矩阵A和B:
在这里插入图片描述

这些变化矩阵是即时计算的,从未被存储,这就是为什么LoRA的内存占用如此小的原因。实际上,在训练期间只存储模型参数、矩阵A和B以及A和B的梯度。

我们执行此操作,直到我们优化了变化矩阵的因素以进行微调任务。更新矩阵A和B的反向传播步骤比更新完整模型参数集的过程要快得多,因为A和B要小得多。这就是为什么尽管训练过程中有更多的操作,LoRA仍然通常比传统微调更快的原因。

当我们最终想要使用这个微调模型进行推断时,我们只需计算变化矩阵,并将变化添加到权重中。这意味着LoRA不会改变模型的推断时间:

在这里插入图片描述

四、LoRA在Transformer中的应用

实际上许多大模型具有复杂的结构,不是一个整体结构。像Transformer这样的模型中的参数,如何应用LoRA?
在这里插入图片描述

对于Transformer模型,有两个要注意的事项:

  1. 通常,在Transformer的多头自注意力层中,密集网络(用于构建查询、键和值)的深度只有1。也就是说,只有一个输入层和一个由权重连接的输出层。

  2. 这些浅层密集网络是Transformer中大部分可学习参数,非常非常大。可能有超过100,000个输入神经元连接到100,000个输出神经元,这意味着描述其中一个网络的单个权重矩阵可能有10B个参数。因此,尽管这些网络的深度只有1,但它们非常宽,因此描述它们的权重矩阵非常大。

LoRA在Transformer模型上,要学习每个非常大但浅层的密集层的分解变化。

五、LoRA Rank

LoRA有一个超参数,称为Rank,它描述了用于构建之前讨论的变化矩阵的深度。较高的值意味着更大的和矩阵,这意味着它们可以在变化矩阵中编码更多的线性独立信息。
在这里插入图片描述

“r"参数可以被视为"信息瓶颈”。较小的r值意味着A和B可以用更小的内存占用编码较少的信息。较大的r值意味着A和B可以编码更多的信息,但内存占用更大
在这里插入图片描述

一个具有r值等于1和2的LoRA的概念图。在这两个例子中,分解的A和B矩阵导致相同大小的变化矩阵,但是r=2能够将更多线性独立的信息编码到变化矩阵中,因为A和B矩阵中包含更多信息。

事实证明,LoRA论文所做的核心假设,即模型参数的变化具有低隐式秩,是一个相当强的假设。微软(LoRA的出版商)的人员尝试了一些值,并发现即使是秩为一的矩阵也表现出色。

在这里插入图片描述

LoRA论文中建议:当数据与预训练中使用的数据相似时,较低的r值可能就足够了。当在非常新的任务上进行微调时,可能需要对模型进行重大的逻辑更改,这时可能需要较高的r值

六、Python代码实现

下面以HuggingFace的一个模块做例子,用LoRA对一个预训练模型进行微调,用于问题回答。代码 链接在这里:

6.1 安装依赖库:

pip install -q bitsandbytes datasets accelerate loralib
pip install -q git+https://github.com/huggingface/peft.git git+https://github.com/huggingface/transformers.git

# bitsandbytes:用于使用较小的数据类型表示模型,节省内存。
# datasets:用于下载数据集
# accelerate:一些模块的机器学习互操作性所需的依赖项
# loralib:LoRA实现
# peft:一般的“参数高效微调”模块,与LoRA的接口
# transformers:用于下载和使用HuggingFace的预训练transformers

6.2 加载预训练模型

我们将使用BLOOM,这是一个开源且许可证宽松的语言模型。我们将使用5.6亿参数版本以节省内存,也可以将相同的策略应用于更大版本的BLOOM。

"""导入依赖项并下载预训练的bloom模型
"""

import torch
import torch.nn as nn
import bitsandbytes as bnb
from transformers import AutoTokenizer, AutoConfig, AutoModelForCausalLM

# 加载模型
model = AutoModelForCausalLM.from_pretrained(
    # "bigscience/bloom-3b",
    # "bigscience/bloom-1b1",
    "bigscience/bloom-560m",
    torch_dtype=torch.float16,
    device_map='auto',
)

# 加载用于此模型的分词器(将文本转换为模型的输入)
tokenizer = AutoTokenizer.from_pretrained("bigscience/tokenizer")


6.3 设置LoRA

"""使用参数高效微调设置LoRA"""

from peft import LoraConfig, get_peft_model

# 定义LoRA在这个特定示例中的工作方式
config = LoraConfig(
    r=8,                                 # LoRA的层数,即A和B矩阵的秩
    lora_alpha=8,                        # LoRA的alpha值,可视为缩放因子,默认等于r
    target_modules=["query_key_value"],  # 目标模块列表,即LoRA优化的模型部分
    lora_dropout=0.05,                   # LoRA的dropout率
    bias="none",                         # 偏置类型。例子中,我们只训练权重
    task_type="CAUSAL_LM"                # 任务类型
)

# 实际上,这会覆盖内存中的模型,所以重命名仅用于可读性。
peft_model = get_peft_model(model, config)  # 获取经过参数高效微调的LoRA模型


6.4 检查内存的节省

LoRA训练参数数量明显较少,即节省了内存消耗。

"""比较LoRA之前和之后的参数"""

trainable_params = 0  # 可训练参数的数量
all_param = 0  # 所有参数的数量

# 遍历所有参数
for _, param in peft_model.named_parameters():
    all_param += param.numel()  # 将参数数量添加到总数中
    if param.requires_grad:  # 如果参数需要梯度
        trainable_params += param.numel()  # 将参数数量添加到可训练参数中

# 打印结果
print(f"可训练参数数量:{trainable_params}")
print(f"所有参数数量:{all_param}")
print(f"可训练参数占比:{100 * trainable_params / all_param:.2f}%")

在这里插入图片描述

6.5 加载微调数据集

将使用SQUAD数据集 来提高语言模型在问答任务上的性能。斯坦福问答数据集(SQUAD)是一个高质量、常用且许可证宽松的数据集。

from datasets import load_dataset  # 导入load_dataset函数,用于加载数据集
qa_dataset = load_dataset("squad_v2")  # 使用load_dataset函数加载SQUAD数据集,并将结果赋值给qa_dataset变量

模型将在特定的数据结构上,对语言模型进行微调。模型将期望以以下一般形式的文本作为输入:

# 代码注释
'''
CONTEXT: 上下文
{context}

QUESTION: 问题
{question}

ANSWER: 答案
{answer}</s>
'''

们将向模型提供上下文和问题,模型将被期望向我们提供答案。因此,我们将重新格式化SQUAD中的数据以符合此格式。

"""
"""Reformatting SQUAD to respect our defined structure
"""  # 定义一个函数用于重新格式化SQUAD数据集

# 定义一个函数用于重新格式化
def create_prompt(context, question, answer):
  if len(answer["text"]) < 1:  # 如果答案的文本长度小于1
    answer = "Cannot Find Answer"  # 将答案设置为"Cannot Find Answer"
  else:
    answer = answer["text"][0]  # 否则将答案设置为答案文本的第一个元素
  prompt_template = f"CONTEXT:\n{context}\n\nQUESTION:\n{question}\n\nANSWER:\n{answer}</s>"  # 创建一个模板,包含上下文、问题和答案
  return prompt_template  # 返回模板

# 将重新格式化的函数应用于整个数据集
mapped_qa_dataset = qa_dataset.map(lambda samples: tokenizer(create_prompt(samples['context'], samples['question'], samples['answers'])))
"""

6.6 使用LoRA在SQUAD上进行微调

# 导入transformers库
import transformers

# 定义trainer,用于训练模型
trainer = transformers.Trainer(
    model=peft_model, # 模型
    train_dataset=mapped_qa_dataset["train"], # 训练集
    args=transformers.TrainingArguments(
        per_device_train_batch_size=4, # 每个设备的训练批次大小
        gradient_accumulation_steps=4, # 梯度累积步数
        warmup_steps=100, # 热身步数
        max_steps=100, # 最大步数
        learning_rate=1e-3, # 学习率
        fp16=True, # 是否使用半精度浮点数
        logging_steps=1, # 日志记录步数
        output_dir='outputs', # 输出目录
    ),
    data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False) # 数据收集器
)

peft_model.config.use_cache = False  # 禁用缓存,以消除警告。在推理时请重新启用!
trainer.train() # 开始训练

在这里插入图片描述

6.7 检查LoRA大小

这个例子中,我们训练了100步,虽然在每一步中损失有一些随机变化,但总体上损失在训练过程中逐渐降低

"""将LoRA微调的模型保存到本地
"""
# 定义模型的标识符
model_id = "BLOOM-560m-LoRA"
# 将微调后的模型保存到指定的路径
peft_model.save_pretrained(model_id)

然后检查文件在我们的文件系统中的大小:

# 使用ls命令查看指定目录下的文件和文件夹的详细信息
!ls -lh {model_id}

在这里插入图片描述

6.8 测试

我们有一个经过微调的 LoRA 模型,让我们问它几个问题。首先,我们将定义一个辅助函数,该函数将接受一个上下文和问题,运行预测,并生成一个回答

"""用于比较结果的辅助函数"""

from IPython.display import display, Markdown

def make_inference(context, question):

    # 将输入转换为标记
    batch = tokenizer(f"**CONTEXT:**\n{context}\n\n**QUESTION:**\n{question}\n\n**ANSWER:**\n", return_tensors='pt', return_token_type_ids=False)
    # 将标记移动到GPU上进行推理
    batch = batch.to(device='cuda')

    # 使用经过微调的模型和原始模型进行推理
    with torch.cuda.amp.autocast():
        # 如果应用了LoRA,推理时间可能会更快,
        # 但是不应用LoRA可以让我同时进行微调前后的实验

        # 原始模型
        peft_model.disable_adapter_layers()
        output_tokens_raw = model.generate(**batch, max_new_tokens=200)

        # LoRA模型
        peft_model.enable_adapter_layers()
        output_tokens_qa = peft_model.generate(**batch, max_new_tokens=200)

    # 显示结果
    display(Markdown("# 原始模型\n"))
    display(Markdown((tokenizer.decode(output_tokens_raw[0], skip_special_tokens=True))))
    display(Markdown("\n# QA模型\n"))
    display(Markdown((tokenizer.decode(output_tokens_qa[0], skip_special_tokens=True))))

让我们来看几个例子:

context = "You are a monster, and you eat yellow legos."  # 定义一个字符串变量context,表示上下文信息
question = "What is the best food?"  # 定义一个字符串变量question,表示问题信息

make_inference(context, question)  # 调用make_inference函数,传入上下文信息和问题信息作为参数

在这里插入图片描述

示例2:

context = "you are a math wizard"  # 定义上下文,表示你是一个数学奇才
question = "what is 1+1 equal to?"  # 定义问题,询问1+1等于多少

make_inference(context, question)  # 调用make_inference函数进行推理

在这里插入图片描述
  因为只使用了一个560M参数的模型,所以它在基本推理方面并不出色。

context = "Answer the riddle"  # 上下文是一个谜语的问题
question = "What gets bigger the more you take away?"  # 问题是:越拿越多的东西会变得更大吗?

make_inference(context, question)  # 调用make_inference函数进行推理

在这里插入图片描述

一、大模型风口已至:月薪30K+的AI岗正在批量诞生

在这里插入图片描述

2025年大模型应用呈现爆发式增长,根据工信部最新数据:

国内大模型相关岗位缺口达47万

初级工程师平均薪资28K(数据来源:BOSS直聘报告)

70%企业存在"能用模型不会调优"的痛点

真实案例:某二本机械专业学员,通过4个月系统学习,成功拿到某AI医疗公司大模型优化岗offer,薪资直接翻3倍!

在这里插入图片描述

二、如何学习大模型 AI ?

🔥AI取代的不是人类,而是不会用AI的人!麦肯锡最新报告显示:掌握AI工具的从业者生产效率提升47%,薪资溢价达34%!🚀

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

1️⃣ 提示词工程:把ChatGPT从玩具变成生产工具
2️⃣ RAG系统:让大模型精准输出行业知识
3️⃣ 智能体开发:用AutoGPT打造24小时数字员工

📦熬了三个大夜整理的《AI进化工具包》送你:
✔️ 大厂内部LLM落地手册(含58个真实案例)
✔️ 提示词设计模板库(覆盖12大应用场景)
✔️ 私藏学习路径图(0基础到项目实战仅需90天)

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

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

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

  • 大模型 AI 能干什么?
  • 大模型是怎样获得「智能」的?
  • 用好 AI 的核心心法
  • 大模型应用业务架构
  • 大模型应用技术架构
  • 代码示例:向 GPT-3.5 灌入新知识
  • 提示工程的意义和核心思想
  • Prompt 典型构成
  • 指令调优方法论
  • 思维链和思维树
  • Prompt 攻击和防范

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

  • 为什么要做 RAG
  • 搭建一个简单的 ChatPDF
  • 检索的基础概念
  • 什么是向量表示(Embeddings)
  • 向量数据库与向量检索
  • 基于向量检索的 RAG
  • 搭建 RAG 系统的扩展知识
  • 混合检索与 RAG-Fusion 简介
  • 向量模型本地部署

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

  • 为什么要做 RAG
  • 什么是模型
  • 什么是模型训练
  • 求解器 & 损失函数简介
  • 小实验2:手写一个简单的神经网络并训练它
  • 什么是训练/预训练/微调/轻量化微调
  • Transformer结构简介
  • 轻量化微调
  • 实验数据集的构建

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

  • 硬件选型
  • 带你了解全球大模型
  • 使用国产大模型服务
  • 搭建 OpenAI 代理
  • 热身:基于阿里云 PAI 部署 Stable Diffusion
  • 在本地计算机运行大模型
  • 大模型的私有化部署
  • 基于 vLLM 部署大模型
  • 案例:如何优雅地在阿里云私有部署开源大模型
  • 部署一套开源 LLM 项目
  • 内容安全
  • 互联网信息服务算法备案

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值