自从Transformer架构问世以来,大型语言模型(Large Language Models, LLMs)以及AIGC技术的发展速度惊人,它们不仅在技术层面取得了重大突破,还在商业应用、社会影响等多个层面展现出巨大潜力。随着ChatGPT的推出,这一技术日益走进大众视野,这也预示着一个由生成式AI塑造的未来正在加速到来。
与此同时,Meta AI Meta AI在2023年推出了LLama(Large Language Model Meta AI)系列大语言模型,这一模型初期是以较为封闭的形式面向特定研究人员开放。之后,又开源LLama系列模型LLama2。
什么是LLama2?
LLama2是Meta AI公司在2023年推出的一款半开源LLM(所谓半开源即为只有Inference没有Train过程),它是Llama的下一代版本,训练数据集2万亿token,上下文长度由llama的2048扩展到4096,可以理解和生成更长的文本,包括7B、13B、70B三个模型,展现出了卓越的性能,使其迅速在基准测试中崭露头角,标志着生成式人工智能领域的一次重要进步。
LLama2模型的任务是在给定前n个单词的基础上预测句子中下一个单词。该模型的核心特点是其预测过程依赖于过去和当前的输入信息,而不考虑未来的信息。
该模型生成文本的过程中,每次迭代不仅需要提供当前待预测位置前n个单词作为输入,还需要将模型在前一次迭代中生成的单词作为新的输入的一部分。
例如,假设我们想要使用LLama2模型生成一句话,设定n=3,即模型每次基于前3个单词预测下一个单词。生成过程如下:
-
初始输入:提供一个初始前缀,“今天天气”;模型接收到“今天天气”作为输入,预测下一个单词为“晴朗”。
-
第二次迭代:将前一次的预测结果加入到输入序列,形成新的输入:“今天天气晴朗”;模型接收到“今天天气晴朗”作为输入,预测下一个单词为“,”。
-
第三次迭代:将上一次预测的逗号“,”加入到输入序列中,形成新的输入:“今天天气晴朗,”;模型接收到“今天天气晴朗,”作为输入,预测下一个单词为“适合”。
-
后续迭代:以此类推,每次模型预测出一个单词后,都将该单词添加到输入序列中,继续预测下一个单词,直到达到预设的终止条件(如生成一定长度的文本、遇到特定结束符等)。
如下图所示。
相比之下,CV模型在进行图像分类、目标检测等任务时,通常只需要一次性接收整个图像作为输入,然后经过一次推理过程就得出最终结果,无需像llama2这样的语言模型这样进行多次迭代和递归预测。
处理流程
在深入理解LLama2模型结构之前,我们先回顾一下LLM的一般处理流程:
输入
LLM的输入数据通常是一段或多段自然语言文本,可以是一个简单的句子或一段话。文本被表示成单词或字符的序列。
❝[岱宗夫如何?齐鲁青未了。造化钟神秀,阴阳割昏晓。]
❞
tokenization
文本被切分为单词或字符,形成token序列。token序列进一步被序列化为列表或数组,并通过语料库进行索引化,将每个token映射到一个唯一的整数索引,便于模型内部计算。
❝序列化->
['BOS','岱','宗','夫','如','何','?','齐','鲁'...'阴','阳','割','昏','晓','EOS']
假设语料库索引化->
['BOS','10','3','67','89','21','45','55','61'...'7869','9','3452','563','56','EOS']
❞
Embedding
tokenization之后的文本信息变为数字形式的token序列,然后通过Embedding层将数字token映射为一个实数向量Embeding Vector。其中,每个token对应的向量通常具有固定的维度d(如50、100、300、768等),向量中的每个元素(实数)表示token在特定语义空间中的某个属性或特征。
具体来说,Embedding Vector可以表示为一个二维数组或矩阵,其形状与token序列长度相同,每个元素是一个固定维度的向量。这里假设使用一个维度为d=10的Embedding向量,则经过Embedding层后得到的向量表示如下:
'BOS'-> [p_{00},p_{01},p_{02},...,p_{09}]
'10' -> [p_{10},p_{11},p_{12},...,p_{09}]
'3' -> [p_{20},p_{21},p_{22},...,p_{09}]
...
'EOS'-> [p_{n0},p_{n1},p_{n2},...,p_{09}]
位置编码
位置编码(Positional Encoding)用于标识每个token在序列中的位置。让模型在处理不同位置的token时,能够区分它们的相对位置,并为模型提供上下文关系信息。
对于每个位置i,预先计算一个固定的位置向量pe_i,其维度与Embedding相同。在输入模型前,将每个token的Embedding与对应位置的PE相加,得到包含位置信息的token表示:
token_i_with_pe=Embedding_i+pe_i
其中,Embedding_i是第i个token的Embedding,pe_i是第i个位置的位置编码向量。二者相加如下:
[p_{00},p_{01},p_{02},...,p_{09}] [pe_{00},pe_{01},pe_{02},...,pe_{09}]
[p_{10},p_{11},p_{12},...,p_{09}] [pe_{10},pe_{11},pe_{12},...,pe_{09}]
[p_{20},p_{21},p_{22},...,p_{09}] + [pe_{20},pe_{21},pe_{22},...,pe_{09}]
... ...
[p_{n0},p_{n1},p_{n2},...,p_{09}] [pe_{n0},pe_{n1},pe_{n2} ,...,pe_{09}]
transformer
目前大语言模型都是基于transformer结构。在生成任务中,如文本生成、对话响应生成、摘要生成等,模型(比如GPT、llama)通常只使用Transformer架构中的Decoder部分,也就是所谓的Decoder-Only结构。
自回归生成
在生成输出序列任务中,使用自回归(Autoregressive)方式,即每次只生成一个token,并且这个token的生成依赖于之前已经生成的所有token。例如下面的代码:
# 定义使用的LLaMA2模型
model = LLaMA2()
# 定义自回归生成函数
def generate(inputs, n_tokens_to_generate):
# 自回归解码循环,迭代次数等于要生成的token数量
for _ in range(n_tokens_to_generate):
# 将当前输入传入模型进行前向传播
output = model(inputs)
# 使用贪婪采样(Greedy Sampling)策略,选取概率最高的token作为下一个预测结果
next = np.argmax(output[-1])
# 将预测的token添加到输入序列中,供下次迭代使用
inputs.append(next)
# 返回最后生成的n_tokens_to_generate个token
return inputs[len(inputs) - n_tokens_to_generate :]
# 给定初始输入,包含特殊token 'BOS' 和两个汉字 '岱' '宗'
input = [p0, p1, p2]
# 请求生成3个新token
output_ids = generate(input, 3) # 假设生成 ['p3','p4','p5']
# 将生成的token ID解码为实际字符
output_ids = decode(output_ids) # 通过tokenization解码
# 将解码后的token ID转换为词汇表中的词汇(此处假设vocab是一个字典)
output_tokens = [vocab[i] for i in output_ids] # 得到 '夫' '如' '何'
输出处理
生成的token序列通过一个输出层,将每个位置的概率分布转换为对应token的概率。根据概率,选择概率最高的token作为模型预测输出。
'''
从给定的概率分布中采样一个token,采用top-p策略
probs: 表示给定的概率分布
p: 表示概率阈值,在采样过程中,只保留累积概率小于p的部分
'''
def sample_top_p(probs, p):
# 1.概率降序排序:对输入的 probs 张量按最后一个维度(即每个概率向量内部)进行降序排序
probs_sort, probs_idx = torch.sort(probs, dim=-1, descending=True) #给定的概率降序排序
# 2. 计算概率的累计和:对排序后的概率向量进行累计求和,得到一个新的张量,表示每个概率向量的累计概率
probs_sum = torch.cumsum(probs_sort, dim=-1)
# 3. 计算累积概率减去当前概率值是否大于p
# 生成一个布尔型张量 mask,其中 True 表示该位置的累积概率减去当前概率值大于p,False则反之
mask = probs_sum - probs_sort > p
# 4. 在 probs_sort 张量中,将 mask 为 True 的位置(累积概率超过p 的部分)的值置为0。
# 这样就实现了仅保留累积概率小于p的部分
probs_sort[mask] = 0.0
# 5. 归一化处理:对经过截断处理后的probs_sort张量进行归一化,使其概率总和为1
# 使用 sum(dim=-1, keepdim=True) 计算每个概率向量的总和,并保持维度不变。然后进行元素级除法操作,使每个概率向量成为一个合法的概率分布。
probs_sort.div_(probs_sort.sum(dim=-1, keepdim=True))
# 6. 随机采样:进行一次随机抽样,得到一个形状为 (batch_size, 1) 的张量,表示每个批次数据采样到的 token 索引
next_token = torch.multinomial(probs_sort, num_samples=1)
# 7. 还原原始索引:根据next_token中的索引,从probs_idx中提取对应的原始索引
next_token = torch.gather(probs_idx, -1, next_token)
return next_token
模型结构
目前主流的LLM模型大多都是基于Tra
标签:...,一文,模型,llama2,生成,token,pe,probs From: https://blog.csdn.net/Qpeterqiufengyi/article/details/139546880