介绍
在过去几年中,语言模型领域经历了一场巨大的演变,特别是随着大规模语言模型(LLMs)的出现。这些模型具备数十亿个参数和对自然语言的深刻理解,对于改变人工智能领域起到了关键作用。今天,我们将探索这场革命,重点介绍从闭源到开源LLMs的转变,精细调整的重要性以及最近出现的高效调整技术的发展。
学习目标:
- 了解闭源和开源LLMs的区别。
- 了解LLMs中的传统和参数高效调整。
- 探索不同的参数高效调整策略。
- 学习使用Ludwig进行高效调整。
闭源vs开源LLMs:选择正确的方法
语言模型领域存在着闭源模型(如OpenAI的ChatGPT、GPT 3.5和GPT 4)和开源变种(如Meta、Google和各种研究实验室提供的)之间的两极分化。闭源LLMs由于其管理基础设施和快速概念验证能力,成为一个引人注目的起点。这些模型提供高质量的预训练数据集,并且无需设置基础设施,使得那些探索LLMs能力的人可以轻松入门。
然而,尽管闭源LLMs易于获取,但它们存在根本性的局限性。它们缺乏模型所有权和极少的自定义能力,特别是对于数据隐私和模型控制至关重要的领域,这使得闭源LLMs不太适合长期投资。相比之下,开源LLMs提供了一个有希望的替代方案。它们使得完全拥有模型和自定义成为可能,并便利地获得开源空间中的创新发展。而付出的代价则是主机费用和困难。
传统微调和参数高效微调
微调成为了最大化LLMs潜力的关键过程,特别是考虑到特定领域任务的情况下。闭源模型常常缺乏所需的灵活性进行微调,而开源模型则可以完全控制这个过程。微调允许通过更新模型权重将预训练的LLMs适应于特定任务,从而提高性能。这是将这些通用模型个性化为专用应用的手段,为独特任务优化性能。
关于微调和类似检索增强生成(RAG)模型之间的辩论,重点在于是否需要针对具体任务进行定制的模型,而非通用智能模型。开源LLMs的性质允许自定义和高效微调以实现卓越的任务特定性能。
传统微调涉及更新所有模型参数,这一过程已被证明是资源密集型、耗时且不总能获得最佳的任务特定性能。然而,参数高效微调的最新创新取得了突破。通过冻结预训练LLM并仅训练一小部分特定任务层(不到总模型权重的1%),高效微调变得既节约资源又更有效。
向参数高效微调的转变显著影响了LLMs如何适应特定任务。通过仅关注训练少量特定任务层,这个过程变得更具成本效益和高效性。这种创新方法在较小数据集上实现了最佳任务特定性能,展示了开源LLMs相对于闭源模型的潜力。
Meta等人的LIMA论文等研究支持了在较小数据集上进行微调可以超越GPT 4等闭源模型性能的观点。这种通过较少数据实现更多的概念的概念突出了开源LLMs在适当微调下的效率和效果。
理解高效训练策略
在利用预训练模型进行特定任务时,LoRA(低秩自适应)和QLoRA(量化低秩自适应)已经成为有效微调大型语言模型(LLMs)的创新方法。这些方法对于将预训练模型定制为专用任务而最小化附加参数非常重要。
LoRA:对体系结构的深入研究
LoRA的体系结构涉及低秩分解,通过将变压器架构中的大型权重矩阵分解为较小矩阵来实现。在变压器的上下文中,LoRA专注于查询,键和值线性投影。
通常,这些线性投影具有大的权重矩阵,例如1024×1024,LoRA将其分解为较小的矩阵,例如1024×8和8×1024。这些较小的矩阵相乘,可以产生原始的维度。这种压缩大大减少了可调参数的数量,约为总LLM参数的一半到1%。
在变压器体系结构的上下文中,LoRA为键和查询投影层集成了适配器模块。这些通过低秩分解构造的适配器保持了原始形状,同时使其能够插入到变压器层中。基本层保持冻结状态,只有适配器权重是可训练的。
QLoRA:量化低秩自适应
QLoRA是LoRA的一种扩展,创新地使用4位精度表示模型权重,而不是标准的32位浮点表示。通过使用4位值,压缩了每个参数的权重,显著减小了模型的大小。QLoRA的高效性使得即使在内存资源较少的平台(如Colab)上也能对庞大的模型进行精细调整。
这种量化方法极大地减少了进行精细调整所需的内存,使得即使在计算资源有限的情况下,也能对大型模型进行精细调整,如T4 GPU。
LoRA vs. QLoRA
LoRA和QLoRA为精细调整大型语言模型提供了不同的途径。LoRA主要通过低秩分解来操作,可以有效地修改预训练模型并减少参数。另一方面,QLoRA是经过改进的版本,引入了量化来显著压缩权重,从而减小模型的内存占用。LoRA和QLoRA在LLM的参数高效精细调整领域中都起着重要作用。
Ludwig:一种声明式机器学习方法
在探索开源LLM的领域中,Ludwig是一个重要的参与者。Ludwig提供了一种声明式机器学习方法,通过提供可访问的界面来控制和自定义模型,无需大量编码。其基于YAML的配置使用户能够高效地管理不同的输入特征和输出任务。Ludwig的多模态能力使其能够处理各种数据类型,在LLM领域中成为一种多功能且用户友好的工具。
通过将AutoML的便利性与低级API的灵活性结合,Ludwig弥合了两者之间的差距,提供了可定制的模型,无需进行大量编码。其模块化架构使深度学习实验更加简单和易于访问,为用户提供了一个方便的平台,探索LLM的潜力。
实现LoRA进行精细调整
利用LoRA需将适配器模块集成到变压器层中,从而实现特定的精细调整,同时保持基本层冻结。LoRA的低秩分解将可调参数压缩到原始LLM大小的一小部分。这种方法有助于将预训练模型适应到自定义任务,而不需要对基本架构进行大量修改。
Ludwig提供了一种易于访问的方法来配置基于LoRA的语言模型的精细调整。通过利用Ludwig,用户可以设置模型架构,定义输入和输出特征,并通过基于YAML的配置应用LoRA或QLoRA配置。
这些配置简化了实现基于LoRA的精细调整的过程,例如模型类型(LLM),基本模型选择以及为预期任务指定输入和输出特征。
代码:
# 安装Ludwig和Ludwig的LLM相关依赖。
!pip uninstall -y tensorflow –quiet
!pip install ludwig –quiet
!pip install ludwig[llm] –quiet
# 启用文本自动换行,以避免水平滚动,并创建刷新CUDA缓存的函数。
from IPython.display import HTML, display
def set_css():
display(HTML(“’
<style>
pre {
white-space: pre-wrap;
}
</style>
“’))
get_ipython().events.register(‘pre_run_cell’, set_css)
def clear_cache():
if torch.cuda.is_available():
torch.cuda.empty_cache()
# 设置您的HuggingFace令牌
import getpass
import locale; locale.getpreferredencoding = lambda: “UTF-8”
import logging
import os
import torch
import yaml
from ludwig.api import LudwigModel
os.environ[“HUGGING_FACE_HUB_TOKEN”] = getpass.getpass(“令牌:”)
assert os.environ[“HUGGING_FACE_HUB_TOKEN”]
# 导入代码生成数据集
from google.colab import data_table; data_table.enable_dataframe_formatter()
import numpy as np; np.random.seed(123)
import pandas as pd
df = pd.read_json(“https://raw.githubusercontent.com/sahil280114/codealpaca/master/data/code_alpaca_20k.json”)
# 我们将创建一个名为`split`的新列,其中:
# 90%将分配值0 -> 训练集
# 5%将分配值1 -> 验证集
# 5%将分配值2 -> 测试集
# 计算每个分割值的行数
total_rows = len(df)
split_0_count = int(total_rows * 0.9)
split_1_count = int(total_rows * 0.05)
split_2_count = total_rows – split_0_count – split_1_count
# 根据计数创建一个具有分割值的数组
split_values = np.concatenate([
np.zeros(split_0_count),
np.ones(split_1_count),
np.full(split_2_count, 2)
])
# 打乱数组以确保随机性
np.random.shuffle(split_values)
# 将‘split’列添加到DataFrame中
df[‘split’] = split_values
df[‘split’] = df[‘split’].astype(int)
# 对于此网络研讨会,我们将仅使用这个数据集的500行。
df = df.head(n=1000)
Ludwig中的高级配置和微调参数
在我们探索Ludwig及其在自然语言处理任务中的能力的过程中,我们已经掌握了微调的要点以及它对模型的重大影响。现在,让我们深入了解Ludwig提供的高级配置和微调参数的细节。
一个模型真正的力量不仅来自其架构,还来自微调过程,这个过程可以将其塑造成符合我们需求的样子。如前所述,微调的有效性取决于将模型引向正确的方向。一个方法是提供特定的提示和数据,并将这些提示封装起来。
想象一下,我们向模型提供一个提示,与特定的指令和上下文配对,并让魔法发生。提示作为导向,引导模型对手头任务的理解。这就是Ludwig的高级功能发挥作用的地方。
代码:
qlora_fine_tuning_config = yaml.safe_load(
““”
model_type: llm
base_model: meta-llama/Llama-2-7b-hf
input_features:
– name: instruction
type: text
output_features:
– name: output
type: text
prompt:
template: >-
下面是一个描述任务的指令,与可能提供更多上下文的输入配对。编写合适完成请求的响应。
### 指导:{instruction}
### 输入:{input}
### 响应:
生成:
温度:0.1
最大新标记数:512
适配器:
类型:lora
量化:
位数:4
预处理:
全局最大序列长度:512
分割:
类型:随机
概率:
– 0.9 # 训练
– 0.05 # 验证
– 0.05 # 测试
训练器:
类型:微调
迭代次数:1
批次大小:1
评估批次大小:2
梯度积累步数:16
学习率:0.0004
学习率调度器:
预热比例:0.03
““”
)
model = LudwigModel(config=qlora_fine_tuning_config, logging_level=logging.INFO)
results = model.train(dataset=df)
请点击此处查看完整代码 – Ludwig:Fine-Tune Llama-2-7b
微调后的推理和模型输出
经过精细的微调之后,是时候见证模型的实际行动并观察其推理能力了。在这个阶段,模型根据接受的训练生成输出。
通过设置参数、使用YAML配置并定义适配器、量化和与训练相关的具体要素,Ludwig提供了一个用户友好而又强大的环境,可以将模型塑造成符合个人喜好的样子。
此外,监控微调过程和理解内存占用的重要性不能被低估。例如,使用LoRA适配器以及量化可以显著减少内存使用,使过程更加高效和实用。
微调后,推理成为焦点。现在,模型已经准备好处理分配的任务,根据提供的提示生成输出。然而,难点在于这些模型是自回归的,意味着它们一次生成一个标记。由于标记生成和计算的原因,推理过程虽然较慢,但能够展示模型的能力。
推理输出可能不完美,特别是如果微调的迭代次数有限。然而,通过调整生成配置(温度、最大新标记等),可以改变输出,从而改进模型的响应。
结论
LLM从闭源到开源模型的演进凸显了微调和Ludwig的先进功能在塑造适应性强、高效的语言模型中的作用。尽管数据集有限,但未来为多样化、定制化的LLM提供了希望。随着我们进一步探索语言模型的领域,开源LLM的进展不仅将塑造人工智能的未来,还将在各个行业带来创新应用和量身定制的解决方案。合作和开源贡献将为语言建模提供更全面、更易访问、更高效的方法。
主要要点:
- 闭源模型提供了简单入门但拥有有限拥有权;开源模型允许定制但需要自主托管,平衡可访问性和控制性。
- 通过微调来定制预训练的LLM,增强了特定任务的性能,使通用型和面向任务型模型达到平衡。
- Ludwig的声明性方法简化了模型的定制和微调参数,优化了微调过程,获得更好的模型输出。
常见问题
关于作者:Arnav Garg
Arnav Garg是Predibase公司的高级机器学习工程师,他将是您在本篇文章中的指南。他是应用机器学习和大规模训练方面的专家,专注于微调优化。Arnav的专业知识还涉及分布式训练的扩展和构建成本效益和高效训练的可靠性机制。
DataHour 页面:https://community.analyticsvidhya.com/c/datahour/efficient-fine-tuning-of-llms-on-single-t4-gpu-using-ludwig