](https://link.juejin.cn/?target=https%3A%2F%2Fwww.turboimagehost.com%2Fp%2F89494772%2Flmg-train.png.html “https://www.turboimagehost.com/p/89494772/lmg-train.png.html”)LLM大模型新手训练指南
基础
用于语言建模的最常见架构是 Transformer 架构,由 Vaswani 等人在著名论文《[Attention Is All You Need》]中提出。我们不会在这里讨论该架构的具体细节,因为我们必须讨论导致并促成其创建的所有旧技术。Transformer 使我们能够训练具有惊人推理能力的大型语言模型 (LLM),同时保持架构足够简单,以便机器学习新手可以开始训练/使用它们。
用于训练和构建 Transformer 模型的最常见语言是 Python,这是一种非常高级(即远离原始机器代码)的语言。这使得外行人很容易熟悉该过程。最流行的库是 HuggingFace [Transformers],它是当今几乎每个 LLM 培训师的支柱。
本质上,LLM 是一种有损文本压缩形式。我们创建具有随机值和参数的张量(多维矩阵),然后向其输入大量文本数据(以 TB 为单位!),以便它们能够学习所有数据之间的关系并识别它们之间的模式。所有这些模式都存储在我们随机初始化为概率的张量中 - 模型会学习一个特定单词后面跟着另一个单词的可能性,依此类推。LLM 的一个非常高级的定义是“将一种语言(例如英语)的概率分布压缩成一组矩阵。”
例如,如果你在 LLM 中输入“How are”,它会计算下一个单词出现的概率。例如,它可能会为“you?”分配 60% 的概率,“things”分配 20% 的概率,等等。
上面讨论的随机初始化在很大程度上不适用于我们,因为它非常昂贵(我们说的是大型模型的数百万美元)。本文将介绍如何微调模型
- 即采用预先训练的模型并为其提供少量数据(通常为几 MB),以使其行为与您所想的任何任务保持一致。例如,如果您想要一个编码助手模型,您可以根据编码示例对模型进行微调,等等。
Transformer 架构
了解您正在处理的内容始终是一种很好的做法,尽管对于微调目的而言这并不是绝对必要的,因为您将运行调用 Transformers 库的类的脚本。
当然,最好的来源是[《Attention Is All You Need》]这篇论文。它介绍了 Transformer 架构,是一篇非常重要的论文。不过,你可能需要先阅读这些,因为作者假设你已经对神经网络有了基本的了解。我建议按顺序阅读这些:
论文太难读?
你并不孤单。学者们往往会故意混淆他们的论文。你可以随时查找每个主题的博客文章或文章,它们往往会提供易于理解的解释。一个很好的资源是 HuggingFace 博客文章。
训练基础知识
训练 LLM 基本上有三 (3) 种方法:预训练、微调和 LoRA/Q-LoRA。
预训练
预训练涉及几个步骤。首先,收集大量文本数据集(通常以 TB 为单位)。接下来,选择或创建专门用于当前任务的模型架构。此外,训练标记器以适当处理数据,确保它能够有效地对文本进行编码和解码。然后使用标记器的词汇表对数据集进行预处理,将原始文本转换为适合训练模型的格式。此步骤涉及将标记映射到其相应的 ID,并合并任何必要的特殊标记或注意掩码。数据集预处理后,即可用于预训练阶段。
在预训练期间,模型会利用大量数据来学习预测句子中的下一个单词或填补缺失的单词。此过程涉及通过迭代训练程序优化模型的参数,以最大限度地提高根据上下文生成正确单词或单词序列的可能性。
为实现此目的,预训练阶段通常采用自监督学习技术
的变体。向模型提供部分掩蔽的输入序列,其中某些标记被故意隐藏,并且模型必须根据周围上下文预测那些缺失的标记。通过以这种方式对大量数据进行训练,模型逐渐对语言模式、语法和语义关系有了丰富的理解。这种特定方法用于[掩蔽语言建模]。然而,当今最常用的方法是[因果语言建模]。与掩蔽语言建模(其中某些标记被掩蔽并且模型预测那些缺失的标记)不同,因果语言建模侧重于根据前面的上下文预测句子中的下一个单词
。
初始预训练阶段旨在捕捉一般语言知识,使模型成为熟练的语言编码器。但毫不奇怪,它缺乏有关特定任务或领域的具体知识。为了弥补这一差距,预训练之后是后续的微调阶段
。
微调
在初始预训练阶段之后,模型会学习一般的语言知识,然后通过微调我们可以专门化模型的功能,并在更窄、特定于任务的数据集上优化其性能
。
微调过程涉及几个关键步骤。首先,收集特定于任务的数据集,其中包含与所需任务相关的标记示例。例如,如果任务是指令调整,则收集指令-响应
对的数据集。微调数据集的大小明显小于通常用于预训练的数据集。
接下来,使用之前学习到的参数初始化预训练模型。然后在特定任务数据集上训练模型,优化其参数以最小化特定任务的损失函数(即模型与期望结果的“偏差”程度)。
低秩自适应(LoRA)
对于普通消费者来说,将内存需求减少 3 倍仍然是不可行的。幸运的是,引入了一种新的 LoRA 训练方法:量化低秩自适应 (QLoRA)。它利用 bitsandbytes 库对语言模型进行即时和近乎无损的量化,并将其应用于 LoRA 训练过程。这大大减少了内存需求 - 使得在 2 个 NVIDIA RTX 3090 上训练多达 700 亿个参数的模型成为可能!相比之下,通常需要超过 16 个 A100-80GB GPU 来微调该大小的模型;相关成本将是巨大的。
本系列的下一部分将重点介绍微调和 LoRA/QLoRA 方法。
微调
如前所述,微调的成本可能很高,具体取决于您选择的模型大小。您通常需要至少 6B/7B 个参数。我们将介绍一些获取训练计算的选项。
收集数据集
毫无疑问,数据集收集是微调过程中最重要的部分。质量和数量都很重要 - 但质量更重要
。
首先,想想你希望经过微调的模型做什么。写故事?角色扮演?帮你写电子邮件?也许你想创建自己的 AI waifubot。为了本实验的目的,我们假设你想训练一个聊天和角色扮演模型,比如[Pygmalion]。你需要收集对话数据集。具体来说,是互联网角色扮演风格的对话。收集部分可能相当具有挑战性;你必须自己弄清楚.
数据集结构
您需要为数据集概述一个结构。本质上,您需要:
- 数据多样性:您不希望您的模型只执行一项非常具体的任务。在我们假设的用例中,我们正在训练聊天模型,但这并不意味着数据只涉及一种特定类型的聊天。您需要多样化您的训练样本,包括各种场景,以便您的模型可以学习如何为各种类型的输入生成输出。
- 数据集大小:与 LoRA 或 Soft Prompts 不同,您需要相对大量的数据。当然,这与预训练数据集不同。根据经验,请确保您至少有10 MiB 的数据用于微调。过度训练模型非常困难,因此堆叠更多数据始终是一个好主意。
- 数据集质量:数据质量非常重要。你希望数据集能够反映模型的结果。如果你输入垃圾数据,它就会输出垃圾数据。
处理原始数据集
您现在可能有一堆文本数据。在继续之前,您需要将它们解析为适合预处理的格式。假设您的数据集处于以下条件之一:
HTML
如果您从网站上抓取数据,则可能会有 HTML 文件。在这种情况下,您的首要任务是从 HTML 元素中提取数据。如果您的头脑不正常,您会尝试使用纯 RegEx 来执行此操作。这非常低效,所以幸运的是,有库可以处理此问题。您可以使用[Beautiful Soup] Python 库来帮助您解决这个问题。您可以阅读它的文档,但它通常是这样使用的:
from bs4 import BeautifulSoup
# HTML content to be parsed
html_content = '''
<html>
<head>
<title>Example HTML Page</title>
</head>
<body>
<h1>Welcome to the Example Page</h1>
<p>This is a paragraph of text.</p>
<div class="content">
<h2>Section 1</h2>
<p>This is the first section.</p>
</div>
<div class="content">
<h2>Section 2</h2>
<p>This is the second section.</p>
</div>
</body>
</html>
'''
# Create a BeautifulSoup object
soup = BeautifulSoup(html_content, 'html.parser')
# Extract text from the HTML
text = soup.get_text()
# Print the extracted text
print(text)
你将得到如下输出:
Example HTML Page
Welcome to the Example Page
This is a paragraph of text.
Section 1
This is the first section.
Section 2
This is the second section.
CSV
如果您从在线开放数据源获取了数据集,则可以获得 CSV 文件。解析它们的最简单方法是使用pandas
Python 库。基本用法如下:
import pandas as pd
# Read the CSV file
df = pd.read_csv('dataset.csv')
# Extract plaintext from a specific column
column_data = df['column_name'].astype(str)
plaintext = column_data.to_string(index=False)
# Print the extracted plaintext data
print(plaintext)
您必须指定列名。
SQL
这个会有点难。你可以采取明智的方法,使用 MariaDB 或 PostgreSQL 等数据库框架将数据集解析为纯文本,但也有用于此目的的 Python 库;一个例子是[sqlparse]。基本用法是:
>>> import sqlparse
>>> # Split a string containing two SQL statements:
>>> raw = 'select * from foo; select * from bar;'
>>> statements = sqlparse.split(raw)
>>> statements
['select * from foo;', 'select * from bar;']
>>> # Format the first statement and print it out:
>>> first = statements[0]
>>> print(sqlparse.format(first, reindent=True, keyword_case='upper'))
SELECT *
FROM foo;
>>> # Parsing a SQL statement:
>>> parsed = sqlparse.parse('select * from foo')[0]
>>> parsed.tokens
[<DML 'select' at 0x7f22c5e15368>, <Whitespace ' ' at 0x7f22c5e153b0>, <Wildcard '*' … ]
>>>
减少噪音
最好的语言模型是随机的,这使得很难预测它们的行为,即使输入提示保持不变。这有时会导致低质量和不理想的输出。您需要确保数据集中没有不需要的元素。如果您的数据源是合成的,即由 GPT-4/3 生成,这一点就更加重要。
开始训练运行
我们将使用[axolotl]训练器进行微调,因为它使用简单且具有我们需要的所有功能。
如果您正在使用云计算服务(例如 RunPod),那么您可能已经满足所有必要的要求。
- 克隆存储库并安装要求:
git clone https://github.com/OpenAccess-AI-Collective/axolotl && cd axolotl
pip3 install packaging
pip3 install -e '.[flash-attn,deepspeed]'
这将安装 axolotl,然后我们就可以开始微调了。
Axolotl 将所有训练选项都放在一个yaml
文件中。目录中已经有一些examples
针对各种不同模型的示例配置。
对于此示例,我们将使用 QLoRA 方法训练 Mistral 模型,这应该可以在单个 3090 GPU 上实现。要开始运行,只需执行以下命令:
accelerate launch -m axolotl.cli.train examples/mistral/config.yml
恭喜!您刚刚训练了 Mistral!示例配置使用非常小的数据集,因此训练所需的时间非常短。
要使用自定义数据集,您需要将其正确格式化为文件JSONL
。Axolotl 接受多种不同的格式,您可以[在此处]找到示例。然后,您可以编辑文件qlora.yml
并将其指向您的数据集。所有配置选项的完整说明都[在此处],请确保单击展开按钮以查看所有选项!
您现在知道如何训练模型了,但让我们在下一节中了解一些非常重要的信息。我们首先解释一下 LoRA 到底是什么,以及它为什么有效。
低秩自适应(LoRA)
LoRA 是一种训练方法,旨在加快大型语言模型的训练过程,同时减少内存消耗。通过将秩分解权重矩阵对(称为更新矩阵)引入现有权重,LoRA 仅专注于训练这些新添加的权重。这种方法有几个优点:
- 保留预训练权重:LoRA 保持先前训练的权重的冻结状态,最大限度地降低灾难性遗忘的风险。这可确保模型在适应新数据的同时保留其现有知识。
- 训练权重的可移植性:LoRA 中使用的秩分解矩阵与原始模型相比具有明显更少的参数。这一特性使得训练后的 LoRA 权重可以轻松转移并用于其他环境,从而使其具有高度的可移植性。
- 与注意层集成:LoRA 矩阵通常被纳入原始模型的注意层。此外,适应尺度参数可以控制模型适应新训练数据的程度。
- 内存效率:LoRA 改进的内存效率使得以不到本机微调所需计算量的 3 倍的量运行微调任务成为可能。
LoRA 超参数
LoRA 排序
这决定了秩分解矩阵的数量。将秩分解应用于权重矩阵是为了减少内存消耗和计算要求。原始[LoRA 论文]建议将秩设为 8 ( r = 8
)。请记住,秩越高,结果越好,计算要求也越高。数据集越复杂,秩就需要越高。
为了匹配完整的微调,您可以将等级设置为等于模型的隐藏大小。但是,不建议这样做,因为这会浪费大量资源。
from transformers import AutoModelForCausalLM
model_name = "huggyllama/llama-7b" # can also be a local directory
model = AutoModelForCausalLM.from_pretrained(model_name)
hidden_size = model.config.hidden_size
print(hidden_size)
LoRA Alpha
这是 LoRA 的缩放因子,它决定了模型适应新训练数据的程度。alpha 值调整训练过程中更新矩阵的贡献。较低的值会给予原始数据更大的权重,并且与较高的值相比,在更大程度上保持模型的现有知识。
LoRA 目标模块
在这里,您可以确定要训练哪些特定的权重和矩阵。最基本的训练是查询向量(例如q_proj
)和值向量(例如v_proj
)投影矩阵。这些矩阵的名称因模型而异。您可以通过运行以下脚本来找出确切的名称:
from transformers import AutoModelForCausalLM
model_name = "huggyllama/llama-7b" # can also be a local directory
model = AutoModelForCausalLM.from_pretrained(model_name)
layer_names = model.state_dict().keys()
for name in layer_names:
print(name)
这将给你如下输出:
model.embed_tokens.weight
model.layers.0.self_attn.q_proj.weight
model.layers.0.self_attn.k_proj.weight
model.layers.0.self_attn.v_proj.weight
model.layers.0.self_attn.o_proj.weight
model.layers.0.self_attn.rotary_emb.inv_freq
model.layers.0.mlp.gate_proj.weight
model.layers.0.mlp.down_proj.weight
model.layers.0.mlp.up_proj.weight
model.layers.0.input_layernorm.weight
model.layers.0.post_attention_layernorm.weight
...
model.norm.weight
lm_head.weight
命名约定本质上是:{identifier}.{layer}.{layer_number}.{component}.{module}.{parameter}
。以下是每个模块的基本解释(请记住,每个模型架构的名称都不同):
up_proj
:向上(解码器到编码器)注意力传递中使用的投影矩阵。它将解码器的隐藏状态投影到与编码器的隐藏状态相同的维度,以便在注意力计算期间实现兼容性。down_proj
:向下(编码器到解码器)注意力传递中使用的投影矩阵。它将编码器的隐藏状态投影到解码器预期的维度,以进行注意力计算。q_proj
:注意力机制中应用于查询向量的投影矩阵。将输入隐藏状态转换为有效查询表示所需的维度。v_proj
:应用于注意力机制中的值向量的投影矩阵。将输入的隐藏状态转换为有效值表示所需的维度。k_proj
:投影矩阵应用于关键向量等等。o_proj
:应用于注意力机制输出的投影矩阵。在进一步处理之前,将组合的注意力输出转换为所需的维度。
但是,有三个(或 4 个,如果你的模型有偏差)异常值。它们不遵循上面指定的命名约定,放弃了层名称和编号。这些是:
- 嵌入标记权重
embed_tokens
:表示与模型的嵌入层相关的参数,通常放置在模型的开头,因为它用于将输入标记或单词映射到其相应的密集向量表示。如果您的数据集具有自定义语法,则目标很重要。 - 归一化权重
norm
:模型中的归一化层。层或批量归一化通常用于提高深度神经网络的稳定性和收敛性。这些通常放置在模型架构中的某些层内或之后,以缓解梯度消失或爆炸等问题,并有助于加快训练速度和提高泛化能力。通常不针对 LoRA。 - 输出层
lm_head
:语言建模 LLM 的输出层。它负责根据从_前面的_层学习到的表示生成下一个标记的预测或分数。位于底部。如果您的数据集具有自定义语法,则这一点很重要。
QLoRA
QLoRA(量化低秩适配器)是一种高效的微调方法,可减少内存使用量,同时保持大型语言模型的高性能。它能够在单个 48GB GPU 上对 65B 参数模型进行微调,同时保留完整的 16 位微调任务性能。
QLoRA 的主要创新包括:
- 通过冻结的 4 位量化预训练语言模型将梯度反向传播到低秩适配器 (LoRA)。
- 使用��种称为 4 位 NormalFloat (NF4) 的新数据类型,可以最佳地处理正态分布的权重。
- 通过量化量化常数进行双重量化,以减少平均内存占用。
- 分页优化器可在微调过程中有效管理内存峰值。
在接下来的部分中,我们将尝试了解所有训练超参数(又名配置)的作用。
训练超参数
训练超参数在塑造模型的行为和性能方面起着至关重要的作用。这些 hparams 是指导训练过程的设置,决定了模型如何从提供的数据中学习。选择合适的 hparams 可以显著影响模型的收敛性、泛化能力和整体效果。
在本节中,我们将尝试解释在训练阶段需要仔细考虑的关键训练 hparams。我们将讨论批大小、时期、学习率、正则化等概念。通过深入了解这些 hparams 及其影响,您将能够有效地微调和优化模型,确保在各种机器学习任务中获得最佳性能。所以让我们深入研究并揭开训练 hparams 背后的奥秘。
Batch Size和 Epoch
随机梯度下降 (SGD) 是一种具有多个超参数的学习算法。新手经常会混淆的两个参数是批次大小和周期数。它们都是整数值,似乎做同样的事情。让我们回顾一下本节的主要内容:
- 随机机梯度下降(SGD): 这是种利用训练数据集逐步更新模型的迭代学习算法。
- 批次大小:批次大小是梯度下降中的一个超参数,决定了在更新模型内部参数之前处理的训练样本数量。换句话说,它指定了每次迭代中使用多少个样本来计算误差并调整模型。
- 周期数:周期数是梯度下降中的另一个超参数,它控制着训练数据集的完整传递次数。每个周期都涉及处理整个数据集一次,并且模型的参数在每个周期后都会更新。
我们必须将此部分分为五(5)个部分。
随机梯度下降
随机梯度下降 (SGD) 是一种优化算法,用于寻找模型的最佳内部参数,旨在最小化对数损失或均方误差等性能指标。
优化可以看作是一个搜索过程,其中算法学习改进模型。所使用的具体优化算法称为“梯度下降”。这里的“梯度”是指计算误差斜率或[梯度],而“下降”表示沿着这个斜率向下移动以接近最小误差水平。
该算法以迭代方式工作,这意味着它要经历多个离散步骤,每个步骤都旨在增强模型参数。在每个步骤中,模型使用当前的内部参数集对样本子集进行预测。然后将这些预测与实际预期结果进行比较,从而计算出误差。然后利用该误差来更新内部模型参数。更新过程因所使用的算法而异,但在人工神经网络的情况下,采用反向传播更新算法。
在深入探讨批次和时期的概念之前,让我们先澄清一下“样本”的含义。
样本
样本或序列是一行数据。它包含输入到算法中的输入以及用于与预测进行比较并计算误差的输出。
训练数据集由多行数据组成,例如许多样本。样本也可以称为实例、观察、输入向量、序列或特征���量。
现在我们知道了什么是样本,让我们定义一个批次。
Batch
批处理大小是一个 hparam,它决定在更新模型的内部参数之前要处理多少个样本。想象一个“for 循环”,它迭代特定数量的样本并进行预测。处理完批处理后,将预测与预期输出进行比较,并计算出误差。然后,该误差用于通过调整模型的参数来改进模型,沿着误差梯度的方向移动。
训练数据集可以分为一个或多个批次。以下是基于批次大小的不同类型的梯度下降算法:
- 批量梯度下降:当批量大小等于训练样本总数时,称为批量梯度下降。在这种情况下,在更新模型之前,整个数据集用于计算预测并计算误差。
- 随机梯度下降:当批量大小设置为 1 时,称为随机梯度下降。在此,每个样本都单独处理,并且模型参数在每个样本之后更新。这种方法为学习过程引入了更多随机性。
- 小批量梯度下降:当批量大小大于 1 且小于训练数据集的总大小时,称为小批量梯度下降。该算法使用小批量样本,进行预测并相应地更新模型参数。小批量梯度下降在批量梯度下降的效率和随机梯度下降的随机性之间取得了平衡。
通过调整批量大小,我们可以控制计算效率和学习过程的随机性之间的权衡,从而找到有效训练模型的最佳平衡点。
- 批量梯度下降:
Batch Size = Size of Training Set
- 随机梯度下降:
Batch Size = 1
- 小批量梯度下降:
1 < Batch Size < Size of Training Set
对于小批量梯度下降,常用的批量大小包括32
、64
和128
样本。您可能会在大多数教程中的模型中看到使用这些值。
如果数据集不能均匀地除以批次大小,该怎么办?
这种情况经常_发生_。这仅仅意味着最终批次的样本数量少于其他批次。您只需从数据集中删除一些样本或更改批次大小,以便数据集中的样本数量能够均匀地除以批次大小。大多数训练脚本都会自动处理此问题。
现在,让我们讨论一个时代。
Epoch
Epoch是一个超参数,它决定了学习算法在整个数据集上迭代的次数。
一个 (1) epoch 表示训练数据集中的每个样本已被使用一次来更新模型的内部参数。它由一个或多个批次组成。例如,如果我们每个 epoch 有一个批次,它对应于前面提到的批量梯度下降算法。
您可以将 epoch 的数量可视化为在训练数据集上迭代的“for 循环”。在此循环中,还有另一个嵌套的“for 循环”,用于遍历每一批样本,其中每一批包含根据批次大小指定的样本数量。
为了评估模型在各个时期的表现,通常会创建线图,也称为学习曲线。这些图在 x 轴上显示时期(时间),在 y 轴上显示模型的错误或技能。学习曲线可用于诊断模型是否过度学习(训练错误率高,验证错误率低)、学习不足(训练和验证错误率低)或与训练数据集的拟合程度合适(训练错误率低,验证错误率合理低)。我们将在下一部分深入探讨学习曲线。
或者你仍然不明白其中的区别? 在这种情况下,让我们看看批次和时期之间的主要区别…
batch vs epoch
[batch size是模型更新]之前处理的样本数量。
epoch是通过训练数据集的完整次数。
批次的大小必须大于或等于一(bsz=>1)且小于或等于训练数据集中的样本数(bsz=< 样本数)。
可以将周期数设置为介于一 (1) 和无穷大之间的整数值。您可以根据需要运行算法,甚至可以使用除固定周期数之外的其他标准(例如模型误差随时间的变化(或无变化))来停止算法。
它们都是整数值,并且都是学习算法的参数,例如学习过程的参数,而不是学习过程发现的内部模型参数。
您必须为学习算法指定批量大小和时期数。
关于如何配置这些 hparams,没有神奇的经验法则。你应该尝试找到适合你特定用例的最佳点。😄
这是一个快速工作的示例:
假设您有一个包含 200 个样本(行或数据序列)的数据集,并且您选择批次大小为 5 和 1,000 个 epoch。这意味着数据集将分为 40 个批次,每个批次有 5 个样本。模型权重将在每批次 5 个样本之后更新。这也意味着一个 epoch 将涉及 40 个批次或 40 次模型更新。
经过 1,000 个 epoch,模型将接触整个数据集 1,000 次。也就是说,整个训练过程中总共有 40,000 个批次。
请记住,较大的批处理大小会导致更高的 GPU 内存使用率。我们将使用梯度累积步骤来克服这个问题!
学习率
正如在“批次”和“时期”部分中讨论的那样,在机器学习中,我们经常使用一种称为随机梯度下降 (SGD) 的优化方法来训练模型。SGD 中的一个重要因素是学习率,它决定了模型在每次更新权重时应根据估计误差做出多少改变。
可以将学习率视为一个旋钮,用于控制改进模型所采取的步骤的大小。如果学习率太小,模型可能需要很长时间才能学习或陷入次优解决方案。另一方面,如果学习率太大,模型可能会学习得太快,最终导致结果不稳定或不太准确。
选择正确的学习率对于成功训练至关重要。这就像找到金发姑娘区一样——不太小,也不太大,但恰到好处。您需要试验并研究不同的学习率如何影响模型的性能。通过这样做,您可以直观地了解学习率如何影响训练期间模型的行为。
因此,在微调训练过程时,请密切关注学习率,因为它在决定模型的学习和执行效果方面起着重要作用。
学习率和梯度下降
随机梯度下降使用训练数据集中的示例估计模型当前状态的误差梯度,然后使用[误差反向传播算法](简称为“反向传播”)更新模型的权重。训练期间更新权重的量称为步长或“学习率”。
具体来说,学习率是一个可配置的超参数,用于训练,具有非常小的正值,通常在 0.0 到 1.0 之间。(注意:是这些值之间,而不是这些值本身。)
学习率控制模型适应问题的速度。学习率越小,需要的训练次数就越多,因为每次更新权重的变化就越小。学习率越大,变化越快,需要的训练次数就越少。
高学习率 = 更少的时期。
低学习率 = 更多的时期。
“学习率可能是最重要的超参数。如果你只有时间调整一个超参数,那就调整学习率。 ”—— [《深度学习》]
现在我们来学习如何配置学习率。
配置学习率
- 从合理的范围开始:首先考虑在类似任务中常用的一系列学习率值。找出您正在微调的预训练模型使用的学习率,并以此为基础。例如,一个常见的起点是 1e-5(0.00001),这通常被发现对 Transformer 模型有效。
- 观察训练进度:以选定的学习率运行训练过程,并在训练期间监控模型的性能。密切关注损失或准确度等指标,以评估模型的学习效果。
- 太慢?如果学习率太小,您可能会注意到模型的训练进度很慢,需要很长时间才能收敛或取得明显的进步。在这种情况下,请考虑增加学习率以加快学习过程。
- 太快?如果学习率太大,模型可能会学习得太快,导致结果不稳定。太高的迹象
lr
包括训练期间损失或准确度的剧烈波动。如果您观察到这种行为,请考虑降低lr
。 - 迭代调整学习率:根据步骤 3 和步骤 4 中的观察结果,迭代调整学习率并重新运行训练过程。逐渐缩小产生最佳性能的学习率范围。
计算学习率的通用公式是:
base_lr * sqrt(supervised_tokens_in_batch / pretrained_bsz)
指base_lr
的是预训练模型的学习率。对于 Mistral 来说,这个数字是 5e-5。supervised_tokens_in_batch
指的是监督标记的总数(一旦开始训练,axolotl 就会报告这个数字),将其除以总步数(axolotl 也会报告)再除以总时期数;即total_supervised_tokens / (num_steps / num_epochs)
。pretrained_bsz
指的是基础模型的原始批次大小。对于 Mistral 和 Llama 来说,这个数字是 4,000,000(4 百万)。例如,假设我们使用包含 2 百万个监督标记的数据集训练 Mistral,并且我们在单个 GPU 上以批次大小 1 进行训练。我们还假设这需要 350 步。最终公式如下所示:
5e-5 * sqrt(2000000/(350/1) / 4000000) = 0.00000189
(1.89e-6)
作为参考,Llama-2 模型的基本学习率为 3e-4。
梯度累积
批次大小越高,内存消耗也就越大。梯度累积旨在解决这个问题。
梯度积累是一种将用于训练模型的样本批次分成几个将按顺序运行的小批次样本的机制。
[
首先,让我们看看反向传播是如何工作的。
反向传播
在模型中,我们有许多层共同处理数据。将这些层视为相互连接的构建块。当我们将数据通过模型时,它会一步一步地向前穿过这些层。在穿过这些层时,模型会对数据进行预测。
在数据经过所有层级且模型做出预测后,我们需要衡量模型预测的准确性或“正确性”。我们通过计算一个称为“损失”的值来做到这一点。损失告诉我们模型与每个数据样本的正确答案的偏差有多大。
现在到了最有趣的部分。我们希望我们的模型能够从错误中吸取教训,并改进其预测。为此,我们需要弄清楚当我们对模型的内部参数(如权重和偏差)进行细微调整时,损失值如何变化。
这就是梯度概念的由来。梯度帮助我们理解损失值如何随每个模型参数而变化。将梯度视为箭头,它向我们展示了调整参数时损失变化的方向和幅度。
一旦我们有了梯度,我们就可以利用它们来更新模型的参数,使它们变得更好。我们选择一个优化器,它就像一个负责指导这些参数更新的特殊算法。优化器会考虑梯度,以及其他因素,如学习率(更新应该有多大)和动量(有助于提高学习的速度和稳定性)。
为了简化,我们来考虑一种流行的优化算法,称为随机梯度下降 (SGD)。它就像一个公式:V = V - (lr * grad)。在这个公式中,V 表示我们想要更新的模型中的任何参数(如权重或偏差),lr 是控制更新大小的学习率,grad 是我们之前计算的梯度。这个公式告诉我们如何根据梯度和学习率调整参数。
总之,反向传播是一个使用损失值计算模型错误程度的过程。然后我们使用梯度来了解调整模型参数的方向。最后,我们应用优化算法(如随机梯度下降)来进行这些调整,并帮助我们的模型学习并改进其预测。
梯度累积解释
梯度累积是一种技术,我们在不更新模型变量的情况下执行多步计算。相反,我们跟踪这些步骤中获得的梯度,并在稍后使用它们来计算变量更新。这其实很简单!
为了理解梯度累积,我们设想将一批样本分成更小的组,称为小批次。在每个步骤中,我们处理其中一个小批次,但不更新模型的变量。这意味着模型对所有小批次使用同一组变量。
通过不在这些步骤中更新变量,我们确保为每个小批量计算的梯度和更新与使用原始完整批量时相同。换句话说,我们保证从所有小批量获得的梯度总和等于从完整批量获得的梯度。
总而言之,梯度累积使我们能够将批次划分为小批次,在每个小批次上执行计算而不更新变量,然后累积所有小批次的梯度。这种累积确保我们获得与使用完整批次相同的整体梯度。
迭代
因此,假设我们在 5 个步骤中累积梯度。在前 4 个步骤中,我们不更新任何变量,但会存储梯度。然后,在第五步中,我们将前几步的累积梯度与当前步骤的梯度相结合,以计算并分配变量更新。
在第一步中,我们处理一小批样本。我们进行前向和后向传递,这使我们能够计算每个可训练模型变量的梯度。但是,我们不会真正更新变量,而是专注于存储梯度。为此,我们为每个可训练模型变量创建额外的变量来保存累积的梯度。
计算第一步的梯度后,我们将它们存储在为累积梯度创建的相应变量中。这样,第一步的梯度将可供后续步骤使用。
我们在接下来的三个步骤中重复此过程,累积梯度而不更新变量。最后,在第五步中,我们得到了前四个步骤的累积梯度和当前步骤的梯度。结合这些梯度,我们可以计算变量更新并相应地分配它们。以下是一个例子:
现在开始第二步,第二个小批量的所有样本再次传播到模型的所有层,计算第二步的梯度。就像之前的步骤一样,我们暂时不想更新变量,因此无需计算变量更新。但与第一步不同的是,我们不会将第二步的梯度存储在变量中,而是将它们添加到存储在变量中的值中,这些值当前保存着第一步的梯度。
步骤 3 和 4 与第二步几乎相同,因为我们尚未更新变量,并且我们通过将梯度添加到变量中来累积梯度。
然后在第 5 步中,我们确实需要更新变量,因为我们打算在 5 步中累积梯度。计算完第五步的梯度后,我们会将它们添加到累积梯度中,从而得到这 5 步所有梯度的总和。然后,我们将这个总和作为参数插入到优化器中,从而得到使用这 5 步的所有梯度计算的更新,这些梯度是在全局批次中的所有样本上计算的。
如果我们以 SGD 为例,让我们看看在第五步结束时更新后的变量,这些变量是使用这 5 个步骤的梯度计算的(在下面的例子中,N=5):
配置梯度累积步数
正如我们广泛讨论的那样,您需要使用梯度累积步骤来实现接近或大于所需批次大小的有效批次大小。
例如,如果您所需的批次大小为 32 个样本,但您的 VRAM 有限,只能处理 8 个批次大小,则可以将梯度累积步骤设置为 4。这意味着您在执行更新之前在 4 个步骤中累积梯度,从而有效地模拟批次大小为 32(8 * 4)。
总体而言,我建议平衡梯度累积步骤和可用资源,以最大限度地提高计算效率。累积步骤太少可能会导致梯度信息不足,而太多则会增加内存需求并减慢训练过程。
此部分目前正在处理中。
解释学习曲线
学习曲线是从训练数据集中逐步学习的算法最常用的工具之一。将使用验证分割来评估模型,并为损失函数创建一个图,测量模型的当前输出与预期输出的差异。让我们尝试回顾一下学习曲线的具体内容,以及如何使用它们来诊断模型的学习和泛化行为。
概述
用更技术性的解释来说,学习曲线可以比作一张图表,展示时间或经验(x 轴)与学习的进度或提高(y 轴)之间的关系。
让我们以学习日语为例。想象一下,你正踏上学习日语的旅程,一年中每周你都会评估自己的语言技能并分配一个数字分数来衡量你的进步。通过在 52 周的时间内绘制这些分数,你可以创建一个学习曲线,以直观的方式说明你对语言的理解是如何随着时间的推移而演变的。
学习(y 轴)与经验(x 轴)的线图。
为了使其更有意义,让我们考虑一个评分系统,其中分数越低代表学习成果越好。例如,如果你最初在基本词汇和语法方面遇到困难,你的分数可能会更高。然而,随着你继续学习并取得进步,你的分数会下降,表明你对语言的掌握更加扎实。最终,如果你的分数为 0.0,这意味着你已经完美掌握了日语,在学习过程中没有犯任何错误。
在模型的训练过程中,我们可以评估其在每个步骤中的表现。可以在训练数据集上进行此评估,以了解模型的_学习_效果。此外,我们可以在未用于训练的单独验证数据集上对其进行评估,以了解模型的泛化能力***。***
以下是两种常用的学习曲线:
- 训练学习曲线:该曲线源自训练数据集,让我们了解模型在训练期间的学习情况。
- 验证学习曲线:此曲线是使用单独的验证数据集创建的。它帮助我们衡量模型对新数据的推广效果。
对于训练数据集和验证数据集来说,拥有双重学习曲线通常是有益的。
有时,我们可能希望跟踪多个指标以进行评估。例如,在分类问题中,我们可能基于交叉熵损失优化模型,并使用分类准确率评估其性能。在这种情况下,我们创建两个图:一个用于每个指标的学习曲线。每个图可以显示两个学习曲线,一个用于训练数据集,一个用于验证数据集。
我们将这些类型的学习曲线称为:
- 优化学习曲线:这些曲线是根据用于优化模型参数的指标(例如损失)计算的。
- 性能学习曲线:这些曲线源自用于评估和选择模型的指标(例如准确性)。
通过分析这些学习曲线,我们可以对模型的进展及其有效学习和概括的能力获得宝贵的见解。
现在您对学习曲线有了更多的了解,让我们看一下学习曲线图中观察到的一些常见形状。
模型行为诊断
学习曲线的形状和动态可用于诊断模型的行为,进而可能建议可以进行哪些类型的配置更改以改善学习和/或性能。
您可能会在学习曲线中观察到三 (3) 种常见动态:
- 不适合。
- 过度拟合。
- 很合身。
我们将仔细研究每个示例。这些示例将假设您正在查看最小化指标,这意味着y 轴上的相对分数越小,表示学习效果****越好。
欠拟合学习曲线
指无法学习训练数据集的模型。仅从训练损失曲线就可以轻松识别出欠拟合模型。
它可能显示一条平坦的线或相对较高的损失噪声值,表明该模型根本无法学习训练数据集。让我们看看下面的例子,当模型没有适合数据集复杂性的容量时,这种情况很常见:
欠拟合图的特点是:
- 无论是否进行训练,训练损失都保持不变。
- 训练损失持续减少,直至训练结束。
过度拟合学习曲线
这指的是模型对训练数据集的学习太好,导致对数据的记忆而不是概括。这包括训练数据集中存在的统计噪声或随机波动。
过度拟合的问题在于,模型对训练数据的专门化程度越高,它对新数据的泛化能力就越差,从而导致泛化误差增加。泛化误差的增加可以通过模型在验证数据集上的表现来衡量。如果模型的容量超过所需问题所需的容量,从而导致灵活性过高,则通常会发生这种情况。如果模型训练时间过长,也会出现这种情况。
如果出现以下情况,学习曲线图会显示过度拟合:
- 训练损失的图随着经验的积累而不断减少。
- 验证损失的图减少到某个点然后再次开始增加。
验证损失的拐点可能是训练可以停止的点,因为该点之后的经验显示了过度拟合的动态。以下是过度拟合模型的示例图:
适合的学习曲线
这将是您在训练期间的目标——过度拟合和欠拟合模型之间的曲线。
良好的拟合通常被定义为训练和验证损失减少到稳定点,并且两个最终损失值之间的差距最小。
模型在训练数据集上的损失总是低于在验证数据集上的损失。这意味着我们应该预期训练和验证损失学习曲线之间存在一些差距。这种差距被称为“泛化差距”。
此示例图将显示一个拟合良好的模型:
[
如何学习AI大模型?
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段: 从大模型系统设计入手,讲解大模型的主要方法;
第二阶段: 在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;
第三阶段: 大模型平台应用开发借助阿里云PAI平台构建电商领域虚拟试衣系统;
第四阶段: 大模型知识库应用开发以LangChain框架为例,构建物流行业咨询智能问答系统;
第五阶段: 大模型微调开发借助以大健康、新零售、新媒体领域构建适合当前领域大模型;
第六阶段: 以SD多模态大模型为主,搭建了文生图小程序案例;
第七阶段: 以大模型平台应用与开发为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。
👉学会后的收获:👈
• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;
• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;
• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;
• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。
1.AI大模型学习路线图
2.100套AI大模型商业化落地方案
3.100集大模型视频教程
4.200本大模型PDF书籍
5.LLM面试题合集
6.AI产品经理资源合集
👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓