模型预训练
一句话:使用尽可能多的训练数据,从中提取出尽可能多的共性特征,从而能让模型对特定任务的学习负担变轻。
背景
“预训练“方法的诞生是出于这样的现实:
- 标注资源稀缺而无标注资源丰富: 某种特殊的任务只存在非常少量的相关训练数据,以至于模型不能从中学习总结到有用的规律。
比如说,如果我想对一批法律领域的文件进行关系抽取,我就需要投入大量的精力(意味着时间和金钱的大量投入)在法律领域的文件中进行关系抽取的标注,然后将标注好的数据“喂”给模型进行训练。但是即使是我标注了几百万条这样的数据(实际情况中,在一个领域内标注几百万条几乎不可能,因为成本非常高),和动辄上亿的无标注语料比起来,还是显得过于单薄。“预训练”这时便可以派上用场。
预训练 的本质
如果用一句话来概括“预训练”的思想,那么这句话可以是
- 模型参数不再是随机初始化,而是通过一些任务(如语言模型)进行预训练
- 将训练任务拆解成共性学习和特性学习两个步骤
上面的两句分别从两个不同的角度来解释了预训练思想的本质。第一句话从模型的角度,第二句话从数据的角度。下面展开讲讲第二种解释。
【学习任务的分解】
“预训练“的做法一般是将大量低成本收集的训练数据放在一起,经过某种预训方法去学习其中的共性,然后将其中的共性“移植”到特定任务的模型中,再使用相关特定领域的少量标注数据进行“微调”,这样的话,模型只需要从”共性“出发,去“学习”该特定任务的“特殊”部分即可。
其实举一个最简单的身边的例子大家就懂了,让一个完全不懂英文的人(我们称ta为)去做英文法律文书的关键词提取的工作会完全无法进行,或者说ta需要非常多的时间去学习,因为ta现在根本看不懂英文。但是如果让一个英语为母语但是没接触过此类工作的人(我们称ta为B)去做这项任务,ta可能只需要相对比较短的时间学习就可以上手这项任务。在这里,英文知识就属于“共性”的知识,这类知识不必要只通过英文法律文书的相关语料进行学习,而是可以通过大量英文语料,不管是小说、书籍,还是自媒体,都可以是学习资料的来源。
因此,可以将预训练类比成学习任务分解:在上面这个例子中,如果我们直接让A去学习这样的任务,这就对应了传统的直接训练方法。如果我们先让A变成B,再让ta去学习同样的任务,那么就对应了“预训练+微调”的思路。
在上面的例子中,B的学习路径是先学习英文,再学习法律文书关键词提取。而图中的C是将“法律文书关键词提取”任务进一步分解成为“法律文书”+“关键词提取”,先学习英文的法律文书领域的知识,再去学习如何在英文的法律文书领域做关键词提取。
很显然,从“英文法律文书”出发的学习速度 > 从“英文”出发的学习速度 > 从0出发的学习速度。其实采用“预训练”思路的B和C,不仅仅是学习速度高于A,更重要的是,他们的学习效果往往好于A。
这就和机器学习领域的“预训练”不谋而合。
预训练 的发展历程
NLP进入神经网络时代之后。NLP领域中的预训练思路可以一直追溯到word2vec的提出([1])。
第一代预训练模型专注于word embedding的学习(word2vec),神经网络本身关于特定任务的部分参数并不是重点。其特点是context-free,也即word embedding,每个token的表示与上下文无关,比如“苹果”这个词在分别表示水果和公司时,对应的word embedding是同样的。
第二代预训练模型以context-aware为核心特征,也就是说“苹果”这个词在分别表示水果和公司时,对应output是不一样的,其中具有代表性的有ELMo([2]), GPT([3]), BERT([4])等。
需要提一点的是,早期的PTMs研究者们在模型结构上做的尝试比较多,比如ELMo使用了双向LSTM。然而在Transformer出现后,研究者们研究的重点就从模型结构转移到了训练策略上。比如GPT和BERT都是基于Transformer结构的: GPT基于Transformer decoder,而BERT基于Transformer encoder。因此,本篇文章也是侧重于解释不同的训练策略。
NLU 任务的 预训练 和 NLG 任务的预训练
NLP领域主要分为自然文本理解(NLU)和自然语言生成(NLG)两种任务。何为理解?我看到一段文字,我懂了它的意思,但是只需要放在心里----懂了,但不需要说出来。何为生成?我看到一段文字,我懂了它的意思,并且能够用语言组织出我理解的内容----懂了,还需要说出来。
文本理解( NLU )和文本生成( NLG )任务
常见的NLU benchmark有GLUE([5]): 包含九项NLU任务,语言均为英语。涉及到自然语言推断、文本蕴含、情感分析、语义相似等多个任务。
常见的NLG任务则有: 机器翻译,摘要生成,对话系统,等等。
- Machine Translation
- (Abstractive) Summarization
- Dialogue (chit-chat and task-based)
- Creative writing: storytelling, poetry-generation
- Freeform Question Answering (i.e. answer is generated, not extracted from text or knowledge base)
- Image captioning
预训练 方法
两种基本 范式 :自回归( AR ) 预训练 和自 编码 ( AE )预训练
在预训练语言模型的学习过程中,我们往往最早会接触到GPT和BERT。实际上,GPT和BERT代表了两种最基本的预训练范式,它们分别被称作“自回归预训练“(如GPT)和“自编码预训练”(如BERT),各自适用于不同类型的下游任务,其中GPT往往更适合文本生成任务,BERT往往更适合文本理解任务。两者都是基于Transformer结构的部分参数。
- GPT对应了decoder的预训练,而BERT对应了encoder的预训练。
- GPT --> AR/LM,适用于NLG任务
GPT这一缩写来自于Generative Pre-Training,也就是生成式预训练,这个名称已经预示着GPT擅长文本生成任务。
GPT的优化目标是单向(从左到右或者从右到左)建模序列的联合概率,是传统意义上的语言模型,后预测的词以先预测的词为条件,比较适合文本生成任务,但是缺陷是只使用了单向的语言表征信息,无法获取双向上下文信息表征,而文本理解任务中经常需要用到双向的上下文信息(比如,完形填空),因此,这就带来了pre-train阶段和下游NLU任务的不一致。
- BERT --> AE/MLM,适用于NLU任务
BERT的全称为Bidirectional Encoder Representations from Transformers,名称中强调了“双向表示”,预示着BERT是双向建模的。BERT双向建模的方式就是将一些位置的token替换成特殊的[MASK]字符,并且在目标端去预测这些被替换的字符。BERT的特点在于它在预训练阶段已经使用了双向上下文信息,因此特别适合NLU任务。
- 从GPT和BERT出发: XLNet, MPNet
由GPT和BERT这两个基本的预训练范式出发,后面一些工作致力于将两者的优点结合,做出一个既适用于NLG又适用于NLU的“大一统”预训练模型。其中的代表有:
- XLNet, 提出Permuted Language Model (PLM),将GPT的从左向右建模扩展成乱序建模,来弥补GPT无法获取双向上下文信息的缺陷。
- MPNet, 在XLNet基础上进一步弥合pre-train阶段和下游任务fine-tune阶段的预训练目标,试图统一PLM和MLM,
模型训练
随着GPT系列问世开启大模型时代,大规模预训练模型取得惊人效果的同时也同时遇到了以下两个痛点:
- 大模型所需的计算操作数量可能会导致训练时间过长
- GPU 显存大小有限,无法容纳大型模型
那么如何在多个GPU上更快地训练大模型?答案是采用分布式训练,目前常见的训练并行技术主要包括数据并行、模型并行、流水线并行和混合并行等。
数据并行 – 模型并行 – 流水并行 – 混合并行
数据并行(Data Parallelism)
DP (Data Parallel)
本质上是单进程多线程的实现方式,只能实现单机 训练不能算是严格意义上的分布式训练。步骤如下:
- 首先将模型加载到主GPU上,再复制到各个指定从GPU;
- 将输入数据按照Batch维度进行拆分,各个GPU独立进行forward计算;
- 将结果同步给主GPU完成梯度计算和参数更新,将更新后的参数复制到各个从GPU。
主要存在的问题:
- 单进程多线程模式,由于锁的机制导致线程间同步存在瓶颈。
- 使用普通的All-Reduce机制,所有的卡需要将梯度同步给0号GPU节点,并由0号GPU节点平均梯度后反向传播,再分发给所有其他节点,意味着0号GPU节点负载很重。
- 由于2的原因,导致0号GPU通讯成本是随着GPU数量的上升而线性上升的。
- 不支持多机多卡。
同步梯度更新
异步梯度更新
GPU0 作为参数服务器 参数服务器分布在所有的GPU
DDP (Distribution Data Parallel)
DDP是一种多进程的基于Ring-All-Reduce通讯算法的数据并行策略,采用 AllReduce 架构,在单机和多机上都可以使用。负载分散在每个GPU节点上,通信成本是恒定的,与 GPU 数量无关。Pytorch DDP 引入bucket在backward的时候进行梯度更新,当一个bucket内部的梯度计算完成后直接开始进行AllReduce操作,而不需要等到backward计算结束,由此提升吞吐量和训练效率,总的来说有以下几个特点:
- 负载分散在每个GPU节点上,所以每个节点的通讯时间基本是一致的。并且不需要通过0号GPU分发全模型的参数到每个GPU上。
- 使用ring-all-reduce的方式进行通讯,随着 GPU 数量 N 增加,总传输量恒定。也就是理论上,随着GPU数量的增加,ring all-reduce有线性加速能力。
- 多进程的方式,突破锁的束缚,相比多线程在容灾以及拓展上更有优势。
- 每个进程并不是同步所有的参数,而是只同步梯度的入参,减少通讯数据
FSDP(Fully Sharded Data Parallel)
FSDP是pytorch1.11的新特性。其新特性目的主要是训练大模型。我们都知道pytorch DDP用起来简单方便,但是要求整个模型能加载一个GPU上,这使得大模型的训练需要使用额外复杂的设置进行模型拆分。pytorch的FSDP从DeepSpeed ZeRO以及FairScale的FSDP中获取灵感,打破模型分片的障碍(包括模型参数,梯度,优化器状态),同时仍然保持了数据并行的简单性。Pytorch 的FSDP是一种数据并行的训练方法,它实际上就是ZeRO-3,传统的数据并行会在每个GPU上维护一份模型参数,梯度,优化器状态的副本,但是FSDP将这些状态分片到所有的数据并行worker中,并且可以选择将分片的模型参数卸载到CPU上。
模型并行
按模型的layer层切分到不同设备,即层间并行,我们称之为流水线并行,将计算图中的层内的参数切分到不同设备,即层内并行,称之为张量模型并行。
张量并行
数学原理
张量并行最常见的是matmul矩阵算子并行,通过矩阵相乘算子并行可以拓展到Emedding、MLP、Transformer等算子的并行。
对于transformer self-attention:
流水线并行
其核心思想是,模型按层分割成若干块,每块都交给一个设备。在前向传递过程中,每个设备将中间的激活传递给下一个阶段。在后向传递过程中,每个设备将输入张量的梯度传回给前一个流水线阶段。
朴素流水线并行:同一时刻只有一个设备进行计算,其余设备处于空闲状态,计算设备利用率较低。
小批次流水线并行:将朴素流水线并行的batch进行再拆分成mini-batch,减小设备空闲状态时间,可以显著提高流水线并行效率。
Gpipe
在小批次流水线里面比较经典的一篇论文是GPipe。 在GPipe中提出了三个点:
- 将模型划分为不同的阶段
- 在pipline中引入老人mini-batch,即将micro-batch继续划分为更小的mini-batch,充分利用流水线并行的效率。
- 重计算技术,在反向计算的时候,重新计算网络模型前向结果的输出。
流水线并行的两种模式
F-then-B: 前向反馈计算完之后才开始后向反馈的计算。
1F1B:前向计算和反向计算交叉进行,可以及时释放不必要的中间变量,减少机器的空载时间。
PipeDream核心在于解决两个问题:
- 对于一个给定的模型与分布式系统,如何划分任务(即哪个节点负责哪些layer,某些layer是数据并行还是模型并行)
- 对于流水线模型,如何避免流水线本身带来的训练的问题。
多维混合并行
也称为3D并行,指的是数据并行、模型并行、流水线并行混合在一起
优化器相关的并行
目前随着模型越搞越大,单个GPU的显存目前通常无法装下那么大的模型了。那么就要想办法对占显存的地方进行优化。通常来说模型训练的过程中,GPU上需要进行存储的参数包括了模型本身的参数,优化器的状态,激活函数的输出值,梯度,以及一些零时的buffer,占比如下图所示:
可以看到模型参数仅占一部分的,当进行混合精度运算时,其中Model State (Optimizer States + Gradient + 模型参数)占到了一大半以上。
ZeRO
零冗余优化器,减少了训练时在数据并行和模型并行中的冗余,可能能够达到千亿级别的参数(Megatron是百亿)。
- ZeRO可以在多于100亿个参数上训练模型,并且在400个GPU上达到超线性加速的效果。
- ZeRO可以训练多达13B个参数的模型(Megatron GPT:8.3B,T5:11B),并且不需要模型并行。
- ZeRO-17B在精度上取得很好的效果。
开源库链接:github.com/microsoft/d…
当前并行训练的现状:
DP(Data Parallelism):现有的DP技术不会减少每个设备的内存,当前的带有32GB内存的GPU,训练参数超过1.4B的模型就会内存耗尽。
MP(Model Parallelism):MP垂直分割模型,将每一层的计算和参数划分到多个设备上,需要在每一层之间进行通信。因此,它们在GPU通信带宽高的单机中效果不错,但是在多机中,效率会迅速下降。
根据分析,在现有的模型训练中,主要有两处容易消耗内存:
- 模型状态,包括优化器状态(如Adam中的momentum和variances、梯度、参数)
- 剩余状态,包括激活(Gpipe用重算解决)、临时的缓冲区和不可用的碎片内存
ZeRO主要针对以上两个问题进行优化,具体优化的思路可以阅读:ZeRO(零冗余优化器)
ZeRO有三个不同级别,对Model States进行不同程度的分割:
- ZeRO-1 : Optimizer States Sharding
ZeRO-1即没有将模型本身进行分,也没有将Gradient进行分片,而是只将优化器进行分片。
- ZeRO-2 : Optimizer States & Gradients Sharding
为了减少梯度Gradient冗余以此进一步节省内存,ZeRO-2提出gradient sharding,在FairScale里称之为Sharded Data Parallel(SDP)。
- ZeRO-3 : Optimizer States & Gradients & Parameters Sharding
为了进一步节省更多的内存,ZeRO-3提出进行模型参数的分片。类似以上两种分片方式,ranks仅负责模型参数的切片。可以进行参数切片的原因主要有以下两点:
- AllReduce操作可以被拆分为Reduce与allgather操作的结合。
- 模型的每一层拥有该层的完整参数,并且整个层能够直接被一个GPU装下。所以计算前向的时候,除了当前rank需要的层之外,其余的层的参数可以抛弃。
详情可以参照:数据并行Deep-dive: 从DP 到 Fully Sharded Data Parallel (FSDP)完全分片数据并行
分布式训练框架
模型微调
什么是微调
Finetune是指在一个已经预先训练好的机器学习模型的基础上,通过在特定领域或任务的数据集上重新训练模型,以进一步提高其性能。这个过程通常涉及到在已经预训练好的模型上微调参数,以适应新的数据集和任务。
为什么要微调
A mouse riding on the head of an elephant, using reins to steer the giant creature.
微调技术
fine-tuning技术
Fine-tuning的基本思想是采用已经在大量文本上进行训练的预训练语言模型,然后在小规模的任务特定文本上继续训练它。经典的fine-tuning方法将预训练模型与少量特定任务数据一起继续训练。在这个过程中,预训练模型的权重被更新,以更好地适应任务。
微调可以简单概括以下四个步骤。
- 在源数据集(例如ImageNet数据集)上预训练神经网络模型,即源模型。
- 创建一个新的神经网络模型,即目标模型。这将复制源模型上的所有模型设计及其参数(输出层除外)。我们假定这些模型参数包含从源数据集中学到的知识,这些知识也将适用于目标数据集。我们还假设源模型的输出层与源数据集的标签密切相关;因此不在目标模型中使用该层。
- 向目标模型添加输出层,其输出数是目标数据集中的类别数。然后随机初始化该层的模型参数。
- 在目标数据集上训练目标模型。输出层将从头开始进行训练,而所有其他层的参数将根据源模型的参数进行微调。
适配器训练(adapter training)
随着计算机硬件性能的提高,预训练模型参数量越来越多,在训练下游任务时进行全模型微调变得昂贵且耗时,Adapter 的出现缓解了这个问题。Adapter在预训练模型每层中插入用于下游任务的参数,在微调时将模型主体冻结,仅训练特定于任务的参数,减少训练时算力开销。
Adapter模块设计方法
2019年,Houlsby N等人将Adapter引入NLP领域,作为全模型微调的一种替代方案。Adapter主体架构下图所示。
在预训练模型每一层(或某些层)中添加Adapter模块(如上图左侧结构所示),微调时冻结预训练模型主体,由Adapter模块学习特定下游任务的知识。通过添加Adapter模块来产生一个易于扩展的下游模型。Adapter方法不需要微调预训练模型的全部参数,只微调adapter中的少量参数,然后使用原模型+Adapter 中的参数组成一个微调后的模型。来存储有关该任务的知识,降低对模型微调的算力要求。
prompt-tuning
prompt-tuning是一种更近期的精调预训练语言模型的方法,重点是调整输入提示(input prompt)而非修改模型参数。这意味着预训练模型保持不变,只有输入提示被修改以适应下游的任务。通过设计和优化一组提示,可以使预训练模型执行特定任务。只需要考虑如何设计模板或指令,而模型和训练目标则都是复用预训练阶段的,即在整个训练过程中,无须添加任何参数(或只需要添加非常少量的与模板有关的参数),而其他参数都是训练好的。
- 参数有效性学习的背景:在一般的计算资源条件下,大规模的模型(例如GPT-3)很难再进行微调,因为所有的参数都需要计算梯度并进行更新,消耗时间和空间资源。为了解决这个问题,参数有效性学习被提出,其旨在确保模型效果不受太大影响的条件下尽可能地提高训练的时间和空间效率。
- 参数有效性 训练:在参数有效性学习过程中,大模型中只需要指定或额外添加少量的可训练参数,而其余的参数全部冻结,这样可以大大提高模型的训练效率的同时,确保指标不会受到太大影响。
一些值得注意的prompt-tuning技术包括:
-
Prefix tuning:
由Li和Liang在论文“Prefix-Tuning: Optimizing Continuous Prompts for Generation”(2021)中提出。前缀调整涉及学习特定任务的连续提示,在推理过程中将其添加到输入之前。通过优化这个连续提示,模型可以适应特定任务而不修改底层模型参数,这节省了计算资源并实现了高效的精调。与传统的基于微调的方法相比,基于提示的学习最显著的特点是,它倾向于修改任务,让模型使用自己的知识来完成任务,而不需要专门设计额外的结构来使模型适应任务。 在训练中,保持模型的参数不变,只训练前缀中的参数,我们获得一组前缀,允许模型执行特定任务。具体来说,由于GPT-2的结构是一个多层Transformer解码器,它对下一个单词的预测是由所有前面的单词共同决定的,所以只要我们能够对左侧上下文进行有针对性的更改,那么我们就可以控制模型的生成。
“past_key_values”最初用于存储模型的先前计算结果以加快计算速度,但前缀调整巧妙地利用了这一点,通过全连接神经网络(MLP)将一组参数映射到“past_key_values”所需的大小,然后将它们传递给模型的“past_key_values“参数,以实现在X之前添加前缀的目的。传递给模型后,该参数将与模型的现有键和值在序列长度维度上串联,从而控制摘要的生成。
fine-tuning与Prefix tuning的差异示意图:
-
P-tuning:
P-tuning重新审视了关于模版的定义,放弃了“模版由自然语言构成”这一常规要求,从而将模版的构建转化为连续参数优化问题, 并不关心模版长什么样,我们只需要知道模版由哪些token组成,该插入到哪里,插入后能不能完成我们的下游任务,输出的候选空间是什么。
- 左图的做法是传统的做法,我们事先定义好一个prompt,如这里的"The capital of 原始样本 is 【MASK】";
- P-tuning做法是用一些伪Prompt代替这些显式的prompt,具体的做法是可以用预训练词表中的【unused】token作为伪Prompt(bert的vocab里有【unused 1】 ~ 【unused99】,就是为了方便增加词汇的),然后通过训练去更新这些token的参数。
也就是,P-tuning的prompt Prompt不是显式的,不是我们可以看得懂的字符,而且一些隐式的、经过训练的、模型认为最好的prompt token。
1、构建 Prompt****
代入前文所述的例子,x = "I love this movie."。首先,设计一个Prompt模板(Prompt Template):
Overall it was a [z] movie
在实际研究中,[z]是需要模型进行填充的空位,[z]的位置和数量决定了Prompt的类型。
例如,根据[z]位置的不同,可以将prompt分为
- cloze prompt([z]在句中)
- prefix prompt([z]在句末)
具体选择哪一种则取决于任务形式和模型类别。
至于Prompt模板的选择,大致可以分为
-
手工设计模板
-
自动学习模板:自动学习类别下又包含
- 离散Prompt:离散Prompt主要包括:Prompt Mining、Prompt Paraphrasing、Gradient-based Search、Prompt Generation、LM Scoring
- 连续Prompt:连续Prompt包括Prefix Tuning和Hybrid Tuning
模型微调实践
微调参数
-
LORA_R:表示LORA模型中的reward。reward是指模型为输入生成的文本的质量,也就是模型生成的文本与期望输出的文本之间的相似度。
-
LORA_ALPHA:表示LORA模型中的alpha值。alpha值是用来调整reward和KL散度之间的权重的。
-
LORA_DROPOUT:表示LORA模型中的dropout值。dropout是一种防止过拟合的技术,它会在训练过程中随机将一些神经元置为0,从而降低神经网络的复杂度。
-
VAL_SET_SIZE:表示用于验证的数据集的大小。训练过程中,通常会将一部分数据集留出来用于验证,以便及时监测模型的训练效果。验证集的大小需要根据具体的数据集和任务进行设置。
-
num_virtual_tokens,token_dim: 通常要求输入的是token(向量)序列,即二维矩阵[num_token,token_dim],可以理解为输入句子的单词个数和词向量维度,
-
num_transformer_submodules : transformer子模块数一般设置1-2个
-
num_attention_heads:multi-head attention 的head数 一般为12
-
num_virtual_tokens:指令文本的长度 ,研究表明这个数量一般设置在10-20之间。
-
num_layers: LSTM 堆叠的层数,默认值是1层,如果设置为2,第二个LSTM接收第一个LSTM的计算结果。
-
encoder_hidden_size:MLP中间层的参数,编码器中的隐藏窗口大小
基于LLM-Adapters框架微调
代码
https://github.com/AGI-Edgerunners/LLM-Adapters
下载模型(可使用个人模型目录)
hdfs dfs -get hdfs://haruna/home/byte_dm_bytelingo/llama/model/7b-hf
hdfs dfs -get hdfs://haruna/home/byte_dm_bytelingo/llama/model/tokenizer.mode
本次微调目录结构
- /mnt/bn/lingo/models/
- 7b-hf/
- xxx.xx
- tokenizer.model
微调指令
cd LLM-Adapters
单GPU运行
CUDA_VISIBLE_DEVICES=0 python finetune.py \
--base_model '/mnt/bn/lingo/models/7b-hf/' \
--data_path 'math_data.json' \
--output_dir '/mnt/bn/lingo/train/output' \
--batch_size 16 \
--micro_batch_size 4 \
--num_epochs 3 \
--learning_rate 3e-4 \
--cutoff_len 256 \
--val_set_size 120 \
--adapter_name lora
多GPU运行
WORLD_SIZE=2 CUDA_VISIBLE_DEVICES=0,1 torchrun --nproc_per_node=2 --master_port=3192 finetune.py \
--base_model 'yahma/llama-7b-hf' \
--data_path 'math_data.json' \
--output_dir './trained_models/llama-lora' \
--batch_size 16 \
--micro_batch_size 4 \
--num_epochs 3 \
--learning_rate 3e-4 \
--cutoff_len 256 \
--val_set_size 120 \
--adapter_name lora
// data_path模型目录可修改,数据可替换为自己的数据
微调
使用其他模型和其他微调方法
代码
https://code.byted.org/bytelingo/lingo_llama/blob/dev_lhy/train/finetune.py
微调指令
cd LLM-Adapters
CUDA_VISIBLE_DEVICES=0 python finetune.py \
--base_model '/mnt/bn/lingo/models/THDUM/' \
--data_path 'math_data.json' \
--output_dir '/mnt/bn/lingo/train/output' \
--batch_size 16 \
--micro_batch_size 4 \
--num_epochs 3 \
--learning_rate 3e-4 \
--cutoff_len 256 \
--val_set_size 120 \
--adapter_name p_tuning
// data_path模型目录可修改,数据可替换为自己的数据
模型评估
- 使用 GPT-4 进行自动评估
- 人工评估
- 指标评估(BLEU-4、ROUGE分数)
实践部分
报错总结
merlin开发机上执行finetune报错:
- 报错详情
(base) mlxlabs7pakrj964412863-20230420115619-4kzypb-kruavs-worker:train# python -m torch.distributed.launch --nproc_per_node=1 finetune.py --base_model '/home/work/llama/model/LLaMA-7B-HL/7b-hf' --data_path './alpaca_data.json' --output_dir '/mnt/bn/lingo-content/train' --batch_size 256 --micro_batch_size 16 --num_epochs 2
/root/miniconda3/lib/python3.10/site-packages/torch/distributed/launch.py:181: FutureWarning: The module torch.distributed.launch is deprecated
and will be removed in future. Use torchrun.
Note that --use-env is set by default in torchrun.
If your script expects `--local-rank` argument to be set, please
change it to read from `os.environ['LOCAL_RANK']` instead. See
https://pytorch.org/docs/stable/distributed.html#launch-utility for
further instructions
warnings.warn(
===================================BUG REPORT===================================
Welcome to bitsandbytes. For bug reports, please run
python -m bitsandbytes
and submit this information together with your error trace to: https://github.com/TimDettmers/bitsandbytes/issues
================================================================================
bin /root/miniconda3/lib/python3.10/site-packages/bitsandbytes/libbitsandbytes_cuda113.so
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: /root/miniconda3 did not contain ['libcudart.so', 'libcudart.so.11.0', 'libcudart.so.12.0'] as expected! Searching further paths...
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: /opt/tiger/yarn_deploy/hadoop/lib/native:/opt/tiger/yarn_deploy/hadoop_current/lib/native:/opt/tiger/yarn_deploy/hadoop_current/lzo/lib: did not contain ['libcudart.so', 'libcudart.so.11.0', 'libcudart.so.12.0'] as expected! Searching further paths...
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('1,10.0.0.0/8,127.0.0.0/8,fd00'), PosixPath('/8,100.64.0.0/10,fe80'), PosixPath('/10,172.16.0.0/12,169.254.0.0/16,192.168.0.0/16'), PosixPath('byted.org,bytedance.net,.byted.org,.bytedance.net,localhost,.ecombdimg.com,.byteimg.com,127.0.0.1,')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('//reckon.bytedance.net'), PosixPath('https')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('/opt/tiger/tez_deploy/tez/lib/*'), PosixPath('/opt/tiger/tez_deploy/conf'), PosixPath('/opt/tiger/tez_deploy/tez/*')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('/opt/tiger/tez_deploy/conf')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('hub.byted.org/reckon/data.reckon.mlx.image_32'), PosixPath('924bf9592393cb0fd6d411a3564a52d3')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('/data00/yarn/pid')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('http'), PosixPath('8118'), PosixPath('//sys-proxy-rd-relay.byted.org')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('//lf6-config.bytetcc.com/obj/tcc-config-web')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('/opt/tiger/tez_deploy/tez')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('https'), PosixPath('//ml.bytedance.net')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('/opt/tiger/studio_loader'), PosixPath('/opt/tiger/lite_sdk'), PosixPath('/tmp/mlx/workspace'), PosixPath('/opt/tiger/mlx_notebook_pysdk/mlx-pysdk')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('https'), PosixPath('//workspace.byted.org')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('http'), PosixPath('//luban-source.byted.org/repository/scm')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('/data00/yarn/logs')}
warn(msg)
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: WARNING: The following directories listed in your path were found to be non-existent: {PosixPath('/tmp/torchelastic_yxgzg04z/none_rmatx3vq/attempt_0/0/error.json')}
warn(msg)
CUDA_SETUP: WARNING! libcudart.so not found in any environmental path. Searching in backup paths...
/root/miniconda3/lib/python3.10/site-packages/bitsandbytes/cuda_setup/main.py:145: UserWarning: Found duplicate ['libcudart.so', 'libcudart.so.11.0', 'libcudart.so.12.0'] files: {PosixPath('/usr/local/cuda/lib64/libcudart.so'), PosixPath('/usr/local/cuda/lib64/libcudart.so.11.0')}.. We'll flip a coin and try one of these, in order to fail forward.
Either way, this might cause trouble in the future:
If you get `CUDA error: invalid device function` errors, the above might be the cause and the solution is to make sure only one ['libcudart.so', 'libcudart.so.11.0', 'libcudart.so.12.0'] in the paths that we search based on your env.
warn(msg)
CUDA SETUP: CUDA runtime path found: /usr/local/cuda/lib64/libcudart.so
CUDA SETUP: Highest compute capability among GPUs detected: 8.6
CUDA SETUP: Detected CUDA version 113
CUDA SETUP: Loading binary /root/miniconda3/lib/python3.10/site-packages/bitsandbytes/libbitsandbytes_cuda113.so...
Training Alpaca-LoRA model with params:
base_model: /home/work/llama/model/LLaMA-7B-HL/7b-hf
data_path: ./alpaca_data.json
output_dir: /mnt/bn/lingo-content/train
batch_size: 256
micro_batch_size: 16
num_epochs: 2
learning_rate: 0.0003
cutoff_len: 256
val_set_size: 2000
lora_r: 8
lora_alpha: 16
lora_dropout: 0.05
lora_target_modules: ['q_proj', 'v_proj']
train_on_inputs: True
add_eos_token: False
group_by_length: False
wandb_project:
wandb_run_name:
wandb_watch:
wandb_log_model:
resume_from_checkpoint: False
prompt template: alpaca
Loading checkpoint shards: 100%|████████████████████████████████████████████████████████████████████████| 33/33 [00:13<00:00, 2.48it/s]
The tokenizer class you load from this checkpoint is not the same type as the class this function is called from. It may result in unexpected tokenization.
The tokenizer class you load from this checkpoint is 'LLaMATokenizer'.
The class this function is called from is 'LlamaTokenizer'.
Found cached dataset json (/root/.cache/huggingface/datasets/json/default-23690eb874ba94e5/0.0.0/0f7e3662623656454fcd2b650f34e886a7db4b9104504885bd462096cc7a9f51)
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 21.65it/s]
trainable params: 4194304 || all params: 6742609920 || trainable%: 0.06220594176090199
Loading cached split indices for dataset at /root/.cache/huggingface/datasets/json/default-23690eb874ba94e5/0.0.0/0f7e3662623656454fcd2b650f34e886a7db4b9104504885bd462096cc7a9f51/cache-748d73bde0c2b58b.arrow and /root/.cache/huggingface/datasets/json/default-23690eb874ba94e5/0.0.0/0f7e3662623656454fcd2b650f34e886a7db4b9104504885bd462096cc7a9f51/cache-bdeef54f833771f5.arrow
Traceback (most recent call last):
File "/home/work/llama/lingo_llama/train/finetune.py", line 284, in <module>
fire.Fire(train)
File "/root/miniconda3/lib/python3.10/site-packages/fire/core.py", line 141, in Fire
component_trace = _Fire(component, args, parsed_flag_args, context, name)
File "/root/miniconda3/lib/python3.10/site-packages/fire/core.py", line 475, in _Fire
component, remaining_args = _CallAndUpdateTrace(
File "/root/miniconda3/lib/python3.10/site-packages/fire/core.py", line 691, in _CallAndUpdateTrace
component = fn(*varargs, **kwargs)
File "/home/work/llama/lingo_llama/train/finetune.py", line 237, in train
args=transformers.TrainingArguments(
File "<string>", line 111, in __init__
File "/root/miniconda3/lib/python3.10/site-packages/transformers/training_args.py", line 1279, in __post_init__
and (self.device.type != "cuda")
File "/root/miniconda3/lib/python3.10/site-packages/transformers/training_args.py", line 1643, in device
return self._setup_devices
File "/root/miniconda3/lib/python3.10/site-packages/transformers/utils/generic.py", line 54, in __get__
cached = self.fget(obj)
File "/root/miniconda3/lib/python3.10/site-packages/transformers/training_args.py", line 1577, in _setup_devices
self.distributed_state = PartialState(backend=self.ddp_backend)
File "/root/miniconda3/lib/python3.10/site-packages/accelerate/state.py", line 129, in __init__
torch.distributed.init_process_group(backend="nccl", **kwargs)
TypeError: torch.distributed.distributed_c10d.init_process_group() got multiple values for keyword argument 'backend'
ERROR:torch.distributed.elastic.multiprocessing.api:failed (exitcode: 1) local_rank: 0 (pid: 119) of binary: /root/miniconda3/bin/python
- 解决办法
找到报错的根本原因:(贴图上标红色的)
finetune参数:
- lora hyperparams
learning_rate: 学习速率
lora_target_modules: LoRA 目标模块,用于指定要对哪些模块的参数进行微调。比如我们可以对 Q, K, V, O 都进行微调;也可以只对 Q、V 进行微调。不同的设定会影响需要微调的参数量,也会影响训练过程中的计算量。比如当我们设定只对 Q、V 进行微调时,需要训练的参数量(trainable parameters)只占整个模型参数总量的 6% 左右。
lora_r:LoRA 的秩,也是影响训练参数量的一个重要因素。客观来说,使用 LoRA 这样的方法训练得到的模型,在效果上必然会和直接在原始大模型基础上进行训练的效果有一定差异。因此,可以结合所拥有的机器配置、可以容忍的最大训练时长等因素,来灵活地配置 LoRA 的使用方法。
lora_alpha:
- llm hyperparams
train_on_inputs: bool = True, # if False, masks out inputs in loss
add_eos_token: bool = False,
group_by_length: bool = False, # faster, but produces an odd training loss curve
wandb params
wandb_project: str = "",
wandb_run_name: str = "",
wandb_watch: str = "", # options: false | gradients | all
wandb_log_model: str = "", # options: false | true
resume_from_checkpoint: str = None, # either training checkpoint or final adapter
prompt_template_name: str = "alpaca", # The prompt template to use, will default to alpaca.
llama模型HF格式解析
config.json
{
"architectures":[
"LLaMAForCausalLM" //LLamMA因果语言模型
],
"bos_token_id":0, //起始符的id
"eos_token_id":1, //结束符的id
"hidden_act":"silu", //编码层和池化层中的非线性激活函数,可取的值为"gelu", "relu", "silu" and "gelu_new"
"hidden_size":4096, //编码层和池化层的大小
"intermediate_size":11008, //Transformer encoder 中feed-forward层的大小
"initializer_range":0.02, //The sttdev of the truncated_normal_initializer for initializing all weight matrices.(所有权重矩阵初始化范围)
"max_sequence_length":2048, //限定每个句子最长由多少个词组成
"model_type":"llama",
"num_attention_heads":32, //Transformer encoder中注意力层attention head的个数
"num_hidden_layers":32, //Transformer encoder 中隐层的个数
"pad_token_id":-1, //对输入进行 padding用的token_id,使得每个输入的 vector 都具有相同的长度
"rms_norm_eps":0.000001, //
"torch_dtype":"float16", //表示torch.Tensor的数据类型的对象,pytorch有八种不同的数据类型
"transformers_version":"4.27.0.dev0", //transformers版本
"use_cache":true, //将保存上一个参数并返回,加速decoding
"vocab_size":32000 //词典中不同字符的数量
}
模型并行训练有两种方式
- 使用原生训练方式
- 借助huggingface的Trainer包的API来实现