BiliBili | IBM RethinkFun | 大模型微调看这个视频就够了 SFT NEFTune

大模型微调核心技术解析

媒体详情

上传日期
2025-06-01 22:09
来源
https://www.bilibili.com/video/BV1gmWDeLEMZ/
处理状态
已完成
转录状态
已完成
Latest LLM Model
gemini-2.5-pro-exp-03-25

转录

下载为TXT
speaker 1: 今天我们来讨论一下大模型的质量微调。 或者叫做监督微调supervised fine training我会讲以下三个方面的内容,一、chat template对话模板2、completions only只针对回答的微调3、ne给embedding增加噪音的微调最后我会讲如何利用tl库里面的sft。 Tr类来对大模型进行微调,指令微调是在大模型预训练之后的微调过程,大模型几乎所有的能力都是在预训练时获得的,但是预训练后的模型还不知道如何表达自己的知识。 他只会根据你提供的上文继续续写下文,比如你问他中国的首都在哪里,他可能接着续写美国的首都在哪里,而不是直接回答你的问题。 指令微调阶段就是在预训练大模型的基础上,让大模型能更好的用自己学到的知识来回答人提出的问题,我们知道对于一个神经网络的训练,最重要的有三点,一网络结构2. Loss函数3. 训练数据指令微调和预训练没有本质上的不同,他们在网络结构上完全相同,loss函数基本相同,最大的不同就是训练数据的不同,开源的大模型都会推出预训练版本和指令微调版本,他们在出场前进行指令微调时都会有自己格式的对话模板,叫做chachat template。 你想增强大模型在某个业务领域的能力,继续进行自己的指令微调时,必须和原厂的对话模板保持一致,才能获得最好的微调效果。 下面我们看这个例子,比如你想问大模型为什么天空是蓝色的,希望大模型回答是,这是由于光的散射造成的,大模型在指令微调时都要加上一些特殊的头肯,来让大模型意识到现在是要回答问题,而且回答完毕时加上结束的token。 比如这里是拉玛3.1的chat temlet,可以看到它会在开始时加上序列开始符的头,把不同的角色放入ID中,每一个content结束时加上序列结束的token。 上面这个list是哈根face所能接受的一个对话的数据结构,它是一个list,其中每个元素都是一个字典。 其中有两项内容,一个是ro 1, 个是content,一般的rule有system,用来对大模型进行系统设定,user表示用户输入,还有就是assistant,用来表示大模型的回答。 下边这是把对话信息转化为输入给大模型的token,每个大模型和自己的token neither ser是唯一对应的,同时也和自己的chat template是唯一对应的,所以把这个chat template也放在了token neither的配置里面,比如这里是拉玛3.1的token。 Config. Json的配置文件,这里要注意最初版本的拉玛3.1的chat template里有bug,你可以按照我这里贴出来的代码修改一下,具体的代码调用就是生成一个对话的列表,然后通过token的apply chat template的方法,把它转化为要输入给大模型的token序列。 通过训练后的大模型,你只要提供问题,并且在生成聊天模板时将add generation prompt设置为。 那么它就会生成如下的token,它会在后面加上标记为蓝色的这一部分,让大模型来生成接下来的答案,最终大模型输出答案和序列终止符。 好的,我们来看一下最简单的指令微调的代码实现,首先加载token和model定义优化器,然后生成一个对话数据,通过token应用chat template设置label等于input,然后模型前向传播获取loss,反向传播优化参数,保存模型。 指令微调的第二个技巧是compleance only,我们输入给大模型的是整个对话,它会对整个对话的文本进行学习。 刚才简单的训练过程,它会对每个输出的token计算loss。 首先对于系统提示部分,每个样本都是一样的,没有必要重复计算它的loss,另外,为了让大模型把注意力都集中在怎么回答问题上,我们可以只对整个序列里面的答案部分计算loss,这就是completion only,这个是怎么做到的呢? 比如这样输入一个分词后的token序列,大模型会根据当前的token和它之前的token序列预测出下一个token,比如这里根据天空和它前面的token预测出为什么这个token,根据为什么和它之前的token预测出是这个token。 我们需要生成一个lost mask,它来表示对哪些生成的token来计算lost,哪些不计算。 需要计算lost的标志为一,不需要计算的标志为零。 这样最后我们在计算loss时,每个未知的loss乘以loss mask,就忽略掉了那些不需要计算loss token。 下列是具体的代码实现,它它根据开始回答部分的特殊token来确定需要计算lost的token的位置。 比如这里对于拉玛3.1,它开始回答部分前面的token序列就是start head ID assistant end head ID两个换行符,根据这个特殊的token序列就可以找到回答的起始位置,稀释位置。 后面呢都是需要计算loss的token loss mask为一。 前面的都是不需要计算loss的token loss mask为零。 最后再计算出每个token的loss后,乘以对应的loss mask,然后再去均值作为最终的loss。 我们看一下具体的代码实现。 这个代码更复杂一些,接近实际运行的代码。 首先我们看一下用来进行指令微调的数据,它一般包含两个部分,一个是query,一个是answer。 这里有十条数据。 首先我们定一个pto里的data site来加载数据。 在get item函数里,我们返回的是经过tokenzer添加chat template后文本。 然后我们用量化加载加载laa配置获取laa模型。 然后我们定义一个call it函数,它负责对一个batch的训练数据进行整理。 它首先进行token ise,然后按照我们刚才讲过的方法生成loss mask,然后进入训练循环。 从data load里加载的数据,它包含输入的input,同时包含输出token的loss mask,这里得到模型输出的loges。 因为对于最后一个位置的输出我们没有label,所以去掉它input左移移位得到label,然后计算lo计, 算出lo后乘以los mask,求loss的均值,得到最终的loss,反向传播,优化参数。 关于指令微调,我们再讲最后一个知识点,那就是nees,也就是noise embedfine training。 在计算机视觉领域,数据增强是一个很重要的过程,因为训练时总觉得数据不够多,可以通过对图片进行旋转、反转、调整亮度等手段生成更多的样本,提高模型的泛化性,增强模型的精度。 指令微调的数据很多情况下都需要人手动构造,采集代价很大,对于大模型庞大的参数量来说就显得更少了,有没有一种对于文本的数据增强的方法呢,在token进入大模型的第一步就是进行引bedding,而我们知道在token embedding构成的向量空间里。 比如漂亮和美丽、妻子和夫人这些意义相近的词,他们在向量空间里面的距离也是非常相近的。 Ns es tw的思想就是给每个token的embedding随机加上一些噪声,而这些增加的噪声可能让原本句子里面的token变成和它相近的token,这样就相当于增加了样本的丰富度,从而最终提高模型的精度和泛化性。 它的实现很简单,首先有一个噪声系数阿尔法,它可以调节增加噪声的强度,然后对音put的ID进行引计算序列的维度就是序列的长度乘以每个token的维度,然后用噪音系数阿尔法除以序列维度的开方。 这里为什么要除以序列维度的开方呢? 你可以理解为最终是计算一个序列和加了噪音的序列。 它们在空间里的欧式距离,而这个空间就是由序列维度构成的。 除以序列维度的开方就是为了让不同序列长度的文本,它们和自己加了噪声的样本的欧式距离都是一样的,不会因为序列的长度不同而距离不同,最后把生成的噪声加到原来的invbed上就可以了。 这里是原始论文里面的效果对比图,作者在拉玛27B的模型上进行了测试,通过增加一寸可以让模型的表现平均增加10%。 它是我们刚才看的针对回答计算loss的代码上进行修改,得到的修改的代码也很少。 就是增加了噪音系数。 然后这里首先获取token的input ID然后对他们进行embedding,接着计算序列维度,用噪音系数除以序列序列维度的开方生成随机数的范围Manow,然后给原始的embedding,每一位都随机加上从正的manom到负的manom之间的一个随机数,后面的代码基本不变。 上边讲了这么多,是为了让你了解大模型sft的原理。 实际上,你并不要写并不需要写这么多的代码。 Alface til库里面集成了sft春的,它集成了这些功能,你只需要通过简单的配置就可以实现,并且能够适配进行分布式训练。 我们来看一下代码,首先我们需要准备好数据。 Sft t穿的默认支持的数据格式是字典格式,其中有两个元素,一个是prompt,一个是completion,我们需要将我们的数据改成这种格式,然后构建一个data set这样的数据。 他在进行训练时,sft串呢会自动利用tokenier的China template对它进行变换,接着就是量化加载模型生成laa模型的部分,然后我们生成一个sft。 Config, 这里我们指定Netw造声系数阿尔法,接着我们生成一个对批量数据进行整理的call it函数,这个函数可以帮助我们实现只对回答部分计算loss,但是我们需要告诉这个函数判断回答开始的token序列是什么,这里我们填入拉玛3.1的chat template里,标志回答前的token序列,最后传入模型训练数据sft。 Config以及data call it就可以开始训练了。 好了,今天我们就讲到这里,如果视频对你有帮助,请记得点赞支持,我们下期见。

最新摘要 (详细摘要)

生成于 2025-06-01 22:11

概览/核心摘要 (Executive Summary)

本内容主要围绕大型语言模型(LLM)的监督微调(Supervised Fine-Tuning, SFT)展开,详细介绍了提升微调效果的三个关键技术方面:对话模板(Chat Template)、仅针对回答进行微调(Completions Only)以及通过为嵌入层增加噪音来进行微调(NEFTune)。讲者强调,指令微调旨在让预训练后的模型能更好地利用其学到的知识回答人类问题,其与预训练在网络结构和损失函数上基本相同,主要区别在于训练数据。正确使用与预训练模型一致的对话模板至关重要,例如Llama 3.1的模板包含特殊token和角色标识。为提高训练效率和效果,可以仅对模型回答部分计算损失(Completions Only),通过损失掩码(loss mask)实现。NEFTune作为一种文本数据增强方法,通过给token的embedding添加随机噪声,增加样本丰富度,从而提升模型精度和泛化性,据称在Llama 2 7B上平均提升10%性能。最后,讲者介绍了如何利用Hugging Face TRL库中的SFTTrainer工具,通过简单配置即可实现上述微调策略,并支持分布式训练,大大简化了SFT的流程。

大模型微调 (SFT) 概述

  • 定义与目的:监督微调(Supervised Fine-Tuning, SFT),也称指令微调,是在大模型预训练之后进行的微调过程。
    • 讲者观点:> “大模型几乎所有的能力都是在预训练时获得的,但是预训练后的模型还不知道如何表达自己的知识。”
    • 目的:让大模型能更好地用其学到的知识来回答人类提出的问题,而不是仅仅根据上文续写。
  • 与预训练的比较
    • 网络结构:完全相同。
    • Loss函数:基本相同。
    • 训练数据:最大不同。
  • 神经网络训练三要素:网络结构、Loss函数、训练数据。
  • 版本发布:开源大模型通常会推出预训练版本和指令微调版本。

核心技术与概念

对话模板 (Chat Template)

  • 重要性:开源大模型在出厂前进行指令微调时,都有自己格式的对话模板。用户在进行后续微调时,必须和原厂的对话模板保持一致,才能获得最佳效果。
  • 结构示例 (Llama 3.1)
    • 包含特殊token,如序列开始符、序列结束符。
    • 将不同角色(如system, user, assistant)放入ID中。
    • 每个content结束时加上序列结束的token。
  • Hugging Face 数据结构:接受一个list,其中每个元素是一个字典,包含rolecontent
    • role类型:system (系统设定), user (用户输入), assistant (大模型回答)。
  • Tokenization 与 Chat Template
    • 每个大模型与其tokenizer是唯一对应的。
    • Chat template也与tokenizer唯一对应,并配置在tokenizer的配置文件中(如Llama 3.1的 tokenizer_config.json)。
    • 注意:讲者提及 > “最初版本的拉玛3.1的chat template里有bug,你可以按照我这里贴出来的代码修改一下”。(具体代码未在转录文本中展示)
  • 代码调用流程
    1. 生成对话列表。
    2. 通过tokenizer的 apply_chat_template 方法,将其转化为输入给大模型的token序列。
  • 推理时应用:提供问题,并在生成聊天模板时将 add_generation_prompt 设置为 True,模型会在问题后加上特定标记,引导其生成答案和序列终止符。

仅针对回答的微调 (Completions Only)

  • 动机
    • 系统提示部分在每个样本中通常是相同的,无需重复计算其loss。
    • 让大模型更专注于学习如何回答问题。
  • 核心思想:只对整个序列中模型回答(答案)部分的token计算loss。
  • 实现机制:Loss Mask
    • 创建一个与token序列等长的loss_mask
    • 需要计算loss的token(答案部分)对应位置标记为1。
    • 不需要计算loss的token(问题、提示部分)对应位置标记为0。
    • 最终loss = 每个token的loss * 对应的loss_mask值,然后求均值。
  • 确定回答起始位置
    • 通过识别回答部分之前的特殊token序列。
    • 示例 (Llama 3.1):起始回答部分前的token序列为 start_header_id, assistant, end_header_id, 两个换行符。
    • 该位置之后的所有token的loss_mask设为1,之前为0。
  • 代码实现关键步骤 (接近实际运行)
    1. 数据准备:包含 queryanswer
    2. Dataset 定义:在 get_item 中返回经过tokenizer添加chat template后的文本。
    3. 模型加载:可使用量化加载,获取LoRA模型(原文为laa,推测为LoRA)。
    4. Collate 函数
      • 对一个batch的数据进行整理。
      • 进行tokenize。
      • 生成loss mask。
    5. 训练循环
      • 从dataloader加载数据(包含input_ids和loss_mask)。
      • 模型前向传播得到logits。
      • Label生成input_ids 左移一位作为label(因为最后一个位置的输出没有label,所以去掉logits的最后一个位置)。
      • 计算loss,乘以loss_mask,求均值。
      • 反向传播,优化参数。

NEFTune (Noisy Embedding Fine-Tuning)

  • 概念:一种针对文本的数据增强方法,通过给token的embedding随机增加噪声。
  • 类比:计算机视觉领域的数据增强(旋转、翻转、调整亮度等)。
  • 动机
    • 指令微调数据多为手动构造,采集代价大,数量相对较少。
    • 增加样本丰富度,提高模型精度和泛化性。
  • 原理
    • Token进入大模型的第一步是进行embedding。
    • 在embedding向量空间中,意义相近的词(如“漂亮”和“美丽”)距离相近。
    • 增加噪声可能使原始token在embedding空间中轻微移动,可能变为与其语义相近的token,从而等效于增加了新的训练样本。
  • 实现步骤
    1. 定义噪声系数 alpha (调节噪声强度)。
    2. input_ids 进行embedding。
    3. 计算序列维度 d_seq = sequence_length * embedding_dimension
    4. 计算噪声范围的缩放因子:scale = alpha / sqrt(d_seq)
      • 讲者解释:> “除以序列维度的开方就是为了让不同序列长度的文本,它们和自己加了噪声的样本的欧式距离都是一样的,不会因为序列的长度不同而距离不同。”
    5. 给原始embedding的每一位随机加上从 -scale+scale 之间的一个随机数(原文提及Manow,推测为噪声范围或分布参数)。
  • 效果
    • 根据原始论文,在Llama 2 7B模型上测试,NEFTune可使模型表现平均增加 10%
  • 代码修改:在原有针对回答计算loss的代码基础上,增加噪声系数,获取token的input_ids后进行embedding,然后计算噪声并添加到embedding上。

使用 TRL 库进行 SFT

  • TRL (Transformer Reinforcement Learning) 库:Hugging Face提供的库,集成了SFTTrainer。
  • SFTTrainer 功能
    • 集成了如“仅针对回答计算loss”、“NEFTune”等功能。
    • 通过简单配置即可实现微调。
    • 能够适配进行分布式训练。
  • 代码实现步骤
    1. 数据准备
      • SFTTrainer默认支持字典格式数据,包含 promptcompletion 两个元素。
      • 构建Dataset。
      • SFTTrainer会自动利用tokenizer的chat template对数据进行变换。
    2. 模型加载:例如量化加载模型,生成LoRA模型。
    3. SFTConfig 配置
      • 指定 neftune_noise_alpha (NEFTune的噪声系数)。
    4. DataCollator 定义
      • 用于对批量数据进行整理。
      • 实现“仅对回答部分计算loss”时,需告知该函数判断回答开始的token序列。
      • 示例:填入Llama 3.1 chat template中标志回答前的token序列。
    5. 开始训练:将模型、训练数据、SFTConfig以及DataCollator传入SFTTrainer并启动。

总结

讲者通过介绍对话模板、仅针对回答的微调(Completions Only)和NEFTune这三种核心技术,详细阐述了如何进行有效的大模型监督微调。核心观点是,遵循原厂对话模板、优化损失计算范围以及利用数据增强手段(如NEFTune)均能显著提升微调效果。最后,推荐使用TRL库的SFTTrainer简化整个微调流程。