AI开发者的大语言模型原理课

在学习大语言模型的基本原理的时候,发现遇到了很多问题,比如一个个单词是怎么转换为向量的,是提前得到的还是训练过程中得到?Transformer的输出向量又是怎么转化为自然语言的?Finetune 的时候不是只需要输入和输出吗,为什么FineTune的脚本做了那么多的处理?找了很多资料发现基本都是在讲Transformer 的原理,缺乏顶层视角的原理 ,最后不得已阅读了一下HuggingFace 上关于LLaMA 的代码才得到答案。
就像程序员也需要知道计算机硬件的基本原理一样,一个AI开发者也需要知道大语言模型的基本原理。本文主要是从AI开发者需要知道的角度来写,所以本文不会去涉及太底层的数学逻辑。下面一起来探讨一下。

整体架构

整体框架分为两部分

  1. 分词器(Tokenizer) :将自然语言转化为TokenID的形式,大体可以理解为每个单词对应一个数字。(实际中稍有差异不过不影响理解,后面会详细说明)
  2. 模型 :输入是TokenID ,输出也是TokenID。大名鼎鼎的Transformer是这里面的一部分,当然了是最核心的部分。

为了便于理解,我们看一下用huggingface的transformers库来写代码是什么样的,做一下对比理解。

python 复制代码
from transformers import AutoTokenizer, LlamaForCausalLM

# 加载模型
model = LlamaForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")

#加载分词器
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")

prompt = "Who is LogicBit?"

#对输入的自然语言进行分词,转化为list[TokenID]
inputs = tokenizer(prompt, return_tensors="pt")

# 模型推理,得到list[TokenID]
generate_ids = model.generate(inputs.input_ids, max_length=30)

# 将模型的输出list[TokenID]转化为自然语言
tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]

从上面可以看出基本是和上面画的整体架构是一致的。使用接口上也是分为了分词器和模型。下面依次看一下具体的细节。

Tokenizer

目的 :在计算的时候只能使用数字来进行表示,所以需要将自然语言转化为数字。
编码 : 输入一个句子(str),输出:对应的TokenID的列表(List[TokenID]
解码 :编码的逆向操作。
基本原理 :基于训练样本,通过某种算法得到一个词汇表,然后针对每个词汇进行编号就得到单词与TokenID(也就是编号)的对应关系。

举个例子

假如我的训练样本只有who is LogicBit这句话,那么我的算法就可以直接是每个单词对应一个ID。由于在英文里面空格就是区分两个单词的,所以空格我们就可以不要了。去掉空格相当于只有三个单词。

python 复制代码
#注意一下以下的代码是一个逻辑上的意思,无法运行。
#按单词出现的顺序进行编号
tokenID['who'] = 1
tokenID['is'] = 2
tokenID['LogicBit'] = 3

#编码
tokenizer.encode('who is LogicBit') = [1, 2, 3]
#解码
tokenizer.decode([1, 2, 3]) = 'who is LogicBit'

#Too Simple, right?

扩展一下

实际使用中,其实没有上面这么简单,训练的算法是比较复杂的。因为这里面有很多的考量。比如为了提高表示效率 ,一个单词可能对应多个TokenID。

举个例子,look这个单词就有很多状态的词汇,现在进行时looking,过去分词looked,而watch也同样有watchedwatching。如果按照一个词汇对应一个ID,我们的词汇表就有6个['looking', 'looked', 'looking', 'watch', 'watched', 'watching'],但是如果按照词根来的话就可以表示为[look, watch, ed, ing],只需要4个即可表示。这个时候比如说looking就会对应两个TokenID , ['look', 'ing']。下面具体来看个实际的例子。

python 复制代码
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")

tokenizer.encode("who is LogicBit?")
#output:[1, 1058, 338, 4522, 293, 21591, 29973]
# 1 是llama词汇表中一个句子的起始字符,对应'<s>',每个句子都会有,自动加上去的。
# who      -> 1058
# is       -> 338
# LogicBit -> [4522, 293, 21591] ,这里就出现了一个单词对应多个tokenid的情况。
# ?        -> 29973

模型

模型整体的输入输出 :输入也就是上面分词器得到的List[TokenID],输出也是List[TokenID]Embedding模块 : 将每个tokenID转化为一个向量,向量的维度是模型定义的(看模型大小)。 Transformer :输入一排向量,输出另一排向量(向量的个数是一样的,向量的维度可能不一样) Output Translation:将Transformer的输出转为TokenID。

Embedding模块

  1. TokenID转化为向量

由于一个数字无法有效的衡量一个单词的含义,比如['who', 'error']对应[1058, 1059],但是你不能说whoerror是很像的东西吧,只能说毫无关系。由于一开始不知道哪个关系更近,所以就用一个向量来表示。先假设各个单词之间没有关系,然后在训练中学习。

python 复制代码
## 还是前面的例子
tokenID['who'] = 1
tokenID['is'] = 2
tokenID['LogicBit'] = 3

#假设三个单词没有关系,由于词汇表容量是3,就可以用一个三维的向量来表示。
Vector['who'] = [1, 0, 0]
Vector['is'] = [0, 1, 0]
Vector['LogicBit'] = [0, 0, 1]

为什么要用这种方式? 因为这种表示方法在数学上是正交的,模型在计算相关度的时候一般用 <math xmlns="http://www.w3.org/1998/Math/MathML"> c o s i n cosin </math>cosin的方式来做,这样子各个单词相关度都为0.

  1. Embedding

前面假设各个单词是没有关系,现实里面却是有关系的,所以我们还是需要学习这个关系。
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> E m b e d d i n g = W ∗ V t o k e n Embedding = W*V_{token} </math>Embedding=W∗Vtoken

<math xmlns="http://www.w3.org/1998/Math/MathML"> V t o k e n V_{token} </math>Vtoken即为上面的tokenID转化后的向量。W为需要学习的参数矩阵, <math xmlns="http://www.w3.org/1998/Math/MathML"> [ D o u t D i m , D v o c S i z e ] [D_{outDim},D_{vocSize}] </math>[DoutDim,DvocSize], <math xmlns="http://www.w3.org/1998/Math/MathML"> D o u t D i m D_{outDim} </math>DoutDim是由Transformer定义的向量维度,像LLaMA就是768维, <math xmlns="http://www.w3.org/1998/Math/MathML"> D v o c S i z e D_{vocSize} </math>DvocSize就是词汇表的大小。

Transformer

由于我们主要是讲LLM的顶层原理,所以Transformer的原理不是我们的重点,我们这里主要讲一下Transformer给我们提供的能力。(感兴趣的可以自己可以看下李宏毅老师的视频,讲得非常清楚)

我们这里只画了Decoder,是因为现在大部分生成式的模型都是用的Decoder。

输入输出:都是一排向量。

Output Translation

由于transformer的输出是每个token对应一个向量,但是我们需要的是tokenID,才能转化为自然语言。

输出是一个tokenID,其实是一个分类问题,只不过我们这里的类别比较多,和词汇表的大小一致。如何将一个向量转换为一个类别,机器学习里面有一个通用的做法,就是把这个向量的每个维度看成是一个类别,值代表概率。

  1. 线性转换:由于Transformer的输出的向量维度一般和词汇表的大小不一致,所以需要先转化为词汇表的大小维度,这个有点像是前面embedding的反向操作。经过转换后得到维度为词汇表大小的向量。

<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> o u t p u t = w ∗ z output = w*z </math>output=w∗z

其中w的维度为 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( D i m v o c , D i m T ) (Dim_{voc},Dim_{T}) </math>(Dimvoc,DimT),z为transformer的输出,维度为 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( D i m T , 1 ) (Dim_T,1) </math>(DimT,1).

  1. Softmax:将各个维度进行一次计算得到每个维度的概率。其实也可以不做这个操作,只是原来的数值加起来可能大于1,或者小于1,不方便后续算法的处理而已。

<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> 假设输出向量 x = [ x 1 x 2 . . . x n ] 假设输出向量 x= \begin{bmatrix} x_1\\x_2\\...\\x_n \end{bmatrix} </math>假设输出向量x= x1x2...xn
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> 那么第 i 个类别的概率 p ( i ) = e x i ∑ k = 1 n e x k 那么第i个类别的概率 p(i) = \frac{e^{x_i}}{\sum_{k=1}^ne^{x_k}} </math>那么第i个类别的概率p(i)=∑k=1nexkexi

  1. 选择 :选择概率最高的作为输出的值即可。(注意一下,实际上大语言模型在生成答案的时候会加入随机性,并不一定选择概率最高的,只不过概率最高的被选中的几率更大)

预训练

给定上文,预测下文。简单来说就是预测下一个词是什么?举个例子,假设样本里面有一句话['I', 'am', 'happy'],针对这句话的任务如下:

输入I,预期输出am

输入I am,预期输出 happy
:上面只是为了方便理解,实际上并不是真的将一句话拆分成多次预测任务,而是并行计算,也就是输入I am happy, 输出 I am happy,然后算法在计算某个位置的输出的时候不会考虑后面的结果是什么,也就达到了基于上文去预测的效果。

收集大量的句子按照如上进行训练。

Finetune

给定一个输入输出对的数据集,基于这个数据集在预训练的模型上面做微调。举个例子来说明一下:

输入:who are you? 输出:I am a llm.

之前一直认为就是直接按照这个输入输出进行微调的。

其实并不是,这个看上面的预训练就可以理解了,在预训练的时候llm的输入输出是一样,所以在微调的时候输入输出应该也是一样的(不然transformer的attention计算会出问题,这个留给各位思考一下为什么? )。所以需要将上面两句话合并,也就是输入输出都是who are you? I am a llm.一般Finetune的脚本都会做这个处理。

这里可能又会产生一个疑问?如果是这样子的话,那大语言模型的输出不是会把我们的问题带上吗,但是使用像chatgpt的时候,输出并没有包括输入的问题。这个是因为在对话的时候会做特殊处理,输出的内容会将问题过滤掉。但是原始的输出是会包括输入的内容的。

python 复制代码
#使用huggingface的generate没有做处理的时候,就可以看到输出是带了输入的。
model.generate()
相关推荐
黑白企鹅鹅3 分钟前
自动回复机器人:源码搭建与智能化客户服务
人工智能·机器人·自动化·rpa
pyniu21 分钟前
深度学习1
人工智能·深度学习
前端基地22 分钟前
昇思25天学习打卡营第6天|关于函数与神经网络梯度相关技术探讨
人工智能·python·深度学习·神经网络·学习·机器学习·ai编程
起个别名25 分钟前
详解yolov5的网络结构
人工智能·python·深度学习·yolo
ShowMeAI37 分钟前
我们严重低估了MiniMax;扎克伯格站在了奥特曼的对面;欧洲最强大模型的天才创始人;Notion AI在LLM来临时快速转身奔跑 | ShowMeAI
人工智能·llm·aigc
Mr_Dwj41 分钟前
机器学习与模式识别_清华大学出版社
人工智能·机器学习
是Dream呀43 分钟前
Swin Transformer:深度解析其架构与代码实现
人工智能·深度学习·机器学习
迅狐源码工厂1 小时前
如何选择快手矩阵系统:打造高效短视频营销的指南
大数据·人工智能·矩阵
理论最高的吻1 小时前
智能技术【机器学习】总结
人工智能·神经网络·机器学习·生成模型·模型优化·数据表示·模糊逻辑系统
内容营销专家刘鑫炜2 小时前
内容营销专家刘鑫炜:第一次写学术论文无从下手怎么办?
人工智能·深度学习·数据挖掘