用Python代码解释大语言模型的工作原理

ChatGPT 和 GPT-4 等大型语言模型 ( LLM ) 自然语言处理的发展标志着一个重要的里程碑,这些模型在基于文本的任务中展示了接近人类的理解能力。除此之外,OpenAI 引入的大型多模态模型 (LMM) 代表了一个显着的转变,使这些模型能够处理图像和文本数据。

本文将重点关注大模型的核心文本解释技术:标记化和标记嵌入


什么是标记化
这是将文本分割成更小的单元(称为标记)的过程。此步骤对于将复杂的语言结构分解为机器学习模型可以处理和理解的元素至关重要。

  • 字符级标记化是最简单的形式,它将文本划分为单个字符。不过,它的直接性导致标记序列较长,处理效率较低。词级标记化是另一种形式,它将文本分割成单词。这种方法比较直观,但在处理大量词汇时可能会遇到困难,而且在遇到新词或未知词时往往会出现问题。
  • 字节对编码法(BPE)是子字标记化中一种更为平衡的方法。BPE 从由训练语料库中所有独特字符组成的基础词汇开始,确保每个单词都能分解成这些基本单位。BPE 的核心在于其频率分析和词对合并方法。它通过反复扫描语料库来识别出现频率最高的字符对或标记。然后将这些字符对合并成新的标记。这一步至关重要,因为它能让模型识别并合并常见的配对,在后续处理中将其视为单一单元。这一过程会继续进行,每次迭代都会合并下一个最常见的词对。

BPE标记化
随着词汇表每次迭代,都会添加新的标记(合并对)。这个过程不断重复,直到词汇量达到预定的大小或所需的粒度水平。最终的词汇量要在足够详细以表示复杂词汇和足够易于管理以高效处理之间取得平衡。

在对新文本进行标记化处理时,算法会使用这一细化词汇。首先,算法会在词库中搜索最大可能的标记。如果找不到匹配词,算法就会将文本分解成更小的单元,直到找到合适的匹配词为止。这种方法可确保常用词和短语被标记为更少、更大的标记,从而提高处理速度,而不常用的短语则会被进一步分解,以保证准确性。

BPE 在表示常见短语方面的效率、通过子词单位处理罕见词和未知词的能力以及均衡的方法,使其在处理词汇量大或词形复杂的语言时特别有效。这种方法是准备数据供语言学家处理的复杂方法的典范,极大地促进了他们对人类语言的理解。

举例说明 BPE 是如何工作的,并随后给出一些代码,使这一概念更加具体:

让我们以句子 "This is an example "为例。

  • 一开始,BPE 会将句子分解为基本单元。它不是从整个单词开始,而是从字符或一小组字符开始。因此,
  • "This is an example"(这是一个例子)首先会被标记为 "T"、"h"、"i"、"s "等单个字符。
  • 然后,BPE 会分析训练数据中字符对的频率,并开始合并最常见的字符对。例如,如果 "Th"、"is"、"an"、"ex "和 "ambu "是训练数据中最常见的字符对,BPE 就会将它们合并为单个标记。这一过程会反复进行,从而使 "Th"、"is"、"an"、"ex "和 "ambu "等更少、更大的标记词代表句子。(类似压缩,标记化其实是字符串压缩


标记嵌入
一旦完成标记化,文本的下一步就是标记嵌入,这是一个根据上下文捕获单词语义的过程。
该过程包括初始化嵌入、训练模型以调整这些嵌入,以及确保语义相似的标记在嵌入空间中接近。

import numpy as np
import random

#1、词汇和嵌入式随机初始化
vocab = ["cat""dog""animal""pet""feline""canine"]
embedding_dim = 300 # Typical dimensions range from 128 to 4096
embeddings = {word: np.random.randn(embedding_dim) for word in vocab}

#2、模拟训练调整功能
def adjust_embeddings(embeddings, word_pairs, learning_rate=0.01):
for word1, word2 in word_pairs:
if word1 in embeddings and word2 in embeddings:
# 简单调整:使相关词的嵌入更接近
embedding_diff = embeddings[word1] - embeddings[word2]
embeddings[word1] -= learning_rate * embedding_diff
embeddings[word2] += learning_rate * embedding_diff

# 模拟训练数据(与上下文相关的词对)
related_word_pairs = [(
"cat""feline"), ("dog""canine"), ("cat""pet"), ("dog""pet")]

# Simulating training
for _ in range(1000): # Number of iterations
random.shuffle(related_word_pairs)
adjust_embeddings(embeddings, related_word_pairs)

检查调整后的嵌入
print(
"Adjusted Embeddings for 'cat' and 'dog':")
print(
"Cat:", embeddings["cat"][:5]) # Displaying first 5 dimensions
print(
"Dog:", embeddings["dog"][:5])

代码说明

  • 嵌入初始化:模型词汇表中的每个标记(单词或子词)都与一个嵌入相关联,嵌入是一个高维向量。这些嵌入通常是随机初始化的。这种随机性为训练过程提供了调整和细化这些向量的起点。上述代码定义了一个词汇表,并随机为每个单词初始化嵌入,为简单起见,将嵌入维度设置为 300。
  • 模拟训练:在训练过程中,模型会接触大量文本数据。它学习根据每个标记出现的上下文来调整嵌入。此学习过程涉及反向传播和优化算法(例如随机梯度下降或 Adam)来迭代调整嵌入以最小化模型的预测误差。代码使用 adjust_embeddings 函数模拟训练,调整相关词对(如 "cat "和 "feline")的嵌入,使其在向量空间中更接近。这是通过迭代减少每对词的嵌入之间的距离来实现的,模仿了 LLM 根据上下文完善标记嵌入的方式。
  • 调整嵌入与语义关系:  在相似上下文中使用的标记在向量空间中具有彼此接近的嵌入。这种接近度通常使用余弦相似度来衡量。此过程创建一个语义图,其中具有相似含义或用法的单词在高维空间中彼此靠近。
  • 结果:经过多次迭代后,上下文相关词语的嵌入在嵌入空间中被调整到彼此更接近的位置,这代表了 LLM 中上下文学习的一种基本形式。

Transformer增强上下文的解释能力
标记嵌入为大型语言模型 (LLM) 提供了对语言上下文和含义的深入理解。 Transformer 在此基础上并行处理这些嵌入,使用自注意力机制来辨别复杂的单词关系。这种协同作用使大模型能够通过对语言语义和结构的细致理解来解释和生成文本。

Transformers之所以比RNN有“优势”,是因为通过注意力,它能够做3件很酷的事情:

  • 1)基于内容的查找(“我对类似于X的向量感兴趣...”)
  • 2)绝对位置查找(例如从句子开始的第三个单词)
  • 3)相对位置


Transformer 采用分层架构设计,每一层都有助于处理输入数据。每层的核心组件是自注意力机制和前馈神经网络。 Transformer 与 RNN 和 LSTM 等早期模型的区别在于它们能够同时处理输入序列中的所有标记。这种并行处理方法不仅提高了计算效率,而且使模型能够更有效地捕获数据中复杂的依赖关系和关系。

自注意力机制:

  • 每个 Transformer 层的核心是自注意力机制。该机制计算输入序列中每个标记的所谓注意力分数。这些分数衡量模型在处理特定标记时应将多少焦点或“注意力”分配给序列中的其他标记。
  • 自注意力机制允许 Transformer 根据整个序列本身动态调整序列中每个标记的影响力。这对于理解上下文至关重要,因为它使模型能够解释每个标记,而不是孤立的,而是与其他标记相关联。
  • 此外,《Transformer 》采用了所谓的“多头注意力”。这意味着自注意力过程在每一层内并行复制多次。每个复制或​​“头”可能会关注标记关系的不同方面,例如句法或语义连接。通过这样做,模型可以捕获对文本更丰富、更细致的理解。

位置编码:

  • Transformer 设计中的一个独特挑战是它们缺乏固有的序列处理能力——这是 RNN 等序列模型固有的特征。为了解决这个问题,Transformers 使用位置编码。这些被添加到标记嵌入中,以便为模型提供有关序列中每个标记的位置的信息。
  • 位置编码通常使用正弦函数生成。此方法可确保序列中的每个位置接收唯一的编码,从而允许模型根据标记在序列中的顺序来区分标记。
  • 位置信息对于模型理解语言的流程和结构至关重要。

逐层处理:

  • 在 Transformer 模型中,每一层都会处理输入序列,逐步转换和细化每个标记的表示。这种转换是由自注意力机制提供的上下文以及层内前馈网络的后续操作通知的。
  • 当输入数据通过 Transformer 的连续层时,标记的表示变得越来越细化并丰富了上下文信息。这种逐层处理可以实现对语言的复杂理解和生成,使模型能够以显着的效率和效果处理复杂的语言任务。

代码示例及说明
下面是一个简化的 Python 代码示例,用于说明 Transformer 的自注意力机制的基本版本。 

import numpy as np
import math

def softmax(x):
return np.exp(x) / np.sum(np.exp(x), axis=0)

def scaled_dot_product_attention(Q, K, V):
# 计算 Q 和 K 的点积,并按 K 维度的平方根进行缩放
matmul_qk = np.dot(Q, K.T) / math.sqrt(K.shape[1])

# 应用 softmax 获取注意力权重
attention_weights = softmax(matmul_qk)

# 乘以 V 得出输出
output = np.dot(attention_weights, V)
return output, attention_weights

标记嵌入示例(随机初始化)
token_embeddings = np.random.rand(3, 64) # 3 tokens, 64-dimensional embeddings

# 模拟查询 (Q)、键 (K) 和值 (V) 矩阵
Q = token_embeddings
K = token_embeddings
V = token_embeddings

# 应用自我注意
attention_output, attention_weights = scaled_dot_product_attention(Q, K, V)

print("Attention Output:", attention_output)
print(
"Attention Weights:", attention_weights)

代码说明
Softmax 函数:用于对注意力分数进行归一化处理,确保其总和为 1。

标度点积注意力:

  • 该函数是自我注意力机制的核心,用于计算注意力分数。
  • 它将查询(Q)和关键字(K)矩阵的点积按关键字维度的平方根进行缩放,这有助于在训练过程中稳定梯度。
  • Softmax 函数适用于这些分数,由此产生的权重用于创建一个加权和值(V),代表注意力机制的输出。

标记嵌入:

  • 为便于说明,我们使用随机初始化的嵌入。实际上,这些都是通过训练学习的。
  • 为简单起见,我们将同一组嵌入词视为查询、键和值。

输出:

  • attention_output(注意输出)表示应用自我注意后的转换嵌入式。
  • attention_weights(关注权重)可以让我们了解每个标记对序列中其他标记的关注程度。

通过这段代码,我们可以基本了解Transformer 中的自我关注是如何运行的。真实世界中的 Transformer 模型要复杂得多,每一层都有额外的组件,如层归一化、残差连接和前馈网络,所有这些对其高级性能都至关重要。