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就可以开始训练了。 好了,今天我们就讲到这里,如果视频对你有帮助,请记得点赞支持,我们下期见。