Datawhale 10月31日 09:07
理解现代智能体工作原理:语言模型与Transformer架构
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入探讨了现代AI智能体的工作原理,聚焦于大语言模型(LLM)的核心——Transformer架构。文章从N-gram模型和循环神经网络(RNN)的演进讲起,详细阐述了Transformer如何通过自注意力机制和多头注意力机制,以及其Decoder-Only架构,实现了并行计算和强大的序列处理能力。此外,还介绍了与大语言模型交互的关键技术,如提示工程中的采样参数(Temperature, Top-k, Top-p)和不同样本提示(零样本、单样本、少样本),为理解和应用LLM奠定了坚实基础。

💡 **语言模型基础与演进**:文章首先回顾了语言模型(LM)作为自然语言处理核心的发展历程,从经典的统计方法N-gram模型(包括Bigram和Trigram),到神经网络语言模型引入的词嵌入(Word Embedding)概念,再到循环神经网络(RNN)及其改进的长短时记忆网络(LSTM)。N-gram模型通过计算词序列的条件概率来工作,但面临数据稀疏性和泛化能力差的问题。神经网络语言模型通过词嵌入将词表示为向量,解决了泛化问题,但仍受限于固定上下文窗口。RNN通过引入隐藏状态来处理序列信息,LSTM则通过门控机制解决了长期依赖问题。

🚀 **Transformer架构的核心:自注意力和多头注意力**:Transformer模型完全摒弃了循环结构,依赖自注意力(Self-Attention)机制实现并行计算。自注意力通过计算查询(Q)、键(K)、值(V)向量之间的相关性,为序列中的每个词元分配不同的注意力权重,从而捕捉全局上下文信息。多头注意力(Multi-Head Attention)则通过并行计算多个自注意力头,从不同角度捕捉多种语言关系,极大地增强了模型的表达能力。该架构还包含逐位置前馈网络(Position-wise Feed-Forward Network)、残差连接(Add)与层归一化(Norm)以及位置编码(Positional Encoding),共同构建了一个高效且强大的序列处理模型。

🤖 **Decoder-Only架构与交互**:Transformer的Decoder-Only架构,以GPT系列为代表,专注于“预测下一个词”这一核心任务,通过掩码自注意力(Masked Self-Attention)保证了自回归(Autoregressive)生成过程的连贯性。这种架构结构简单、易于扩展,且天然适合对话、写作等生成式任务,是构建通用智能体的基础。与大语言模型交互时,提示工程(Prompt Engineering)至关重要,包括调整采样参数(Temperature, Top-k, Top-p)以控制输出的随机性与确定性,以及根据示例数量选择零样本、单样本或少样本提示,以引导模型产生期望的输出。

🧠 **现代智能体的工作原理**:现代AI智能体,特别是基于大型语言模型(LLM)的智能体,其核心在于通过Transformer(尤其是Decoder-Only架构)强大的文本理解和生成能力。通过精心设计的提示(Prompt)和合理的采样参数配置,智能体能够理解复杂指令,进行推理,并生成连贯、有用的回应。从基础的语言模型演进到复杂的Transformer架构,再到实际的交互策略,这些技术共同构成了当前AI智能体能够执行多样化任务的基石。

原创 陈思州 2025-10-30 22:45 浙江

 Datawhale干货 

作者:陈思州,Datawhale成员

为了便于大家更系统的入门和学习,最近,我们会为大家分享关于AI智能体的系列内容:《Hello-Agents》项目正式发布,一起从零学习智能体

第一篇:关于智能体(AI Agent)入门,一篇超详细的总结,第二篇:AI 智能体简史

本文将完全聚焦于大语言模型本身解答一个关键问题:现代智能体是如何工作的?我们将从语言模型的基本定义出发,通过对这些原理的学习,为理解LLM如何获得强大的知识储备与推理能力打下坚实的基础。

一、语言模型与 Transformer 架构

1、从 N-gram 到 RNN

语言模型 (Language Model, LM) 是自然语言处理的核心,其根本任务是计算一个词序列(即一个句子)出现的概率。一个好的语言模型能够告诉我们什么样的句子是通顺的、自然的。在多智能体系统中,语言模型是智能体理解人类指令、生成回应的基础。本节将回顾从经典的统计方法到现代深度学习模型的演进历程,为理解后续的 Transformer 架构打下坚实的基础。

1)统计语言模型与N-gram的思想

在深度学习兴起之前,统计方法是语言模型的主流。其核心思想是,一个句子出现的概率,等于该句子中每个词出现的条件概率的连乘。对于一个由词  构成的句子 S,其概率 P(S) 可以表示为:

这个公式被称为概率的链式法则。然而,直接计算这个公式几乎是不可能的,因为像  这样的条件概率太难从语料库中估计了,词序列  可能从未在训练数据中出现过。

图 3.1 马尔可夫假设示意图

为了解决这个问题,研究者引入了马尔可夫假设 (Markov Assumption)。其核心思想是:我们不必回溯一个词的全部历史,可以近似地认为,一个词的出现概率只与它前面有限的  个词有关,如图3.1所示。基于这个假设建立的语言模型,我们称之为 N-gram模型。这里的 "N" 代表我们考虑的上下文窗口大小。让我们来看几个最常见的例子来理解这个概念:

这些概率可以通过在大型语料库中进行最大似然估计(Maximum Likelihood Estimation,MLE)来计算。这个术语听起来很复杂,但其思想非常直观:最可能出现的,就是我们在数据中看到次数最多的。例如,对于 Bigram 模型,我们想计算在词  出现后,下一个词是  的概率 。根据最大似然估计,这个概率可以通过简单的计数来估算:

这里的Count()函数就代表“计数”:

公式的含义就是:我们用“词对  出现的次数”除以“词  出现的总次数”,来作为  的一个近似估计。

为了让这个过程更具体,我们来手动进行一次计算。假设我们拥有一个仅包含以下两句话的迷你语料库:datawhale agent learnsdatawhale agent works。我们的目标是:使用 Bigram (N=2) 模型,估算句子 datawhale agent learns出现的概率。根据 Bigram 的假设,我们每次会考察连续的两个词(即一个词对)。

第一步:计算第一个词的概率 这是datawhale出现的次数除以总词数。datawhale出现了 2 次,总词数是 6。

第二步:计算条件概率 这是词对datawhale agent出现的次数除以datawhale 出现的总次数。datawhale agent出现了 2 次,datawhale出现了 2 次。

第三步:计算条件概率 这是词对agent learns出现的次数除以agent出现的总次数。agent learns出现了 1 次,agent出现了 2 次。

最后:将概率连乘所以,整个句子的近似概率为:

import collections
# 示例语料库,与上方案例讲解中的语料库保持一致
corpus = "datawhale agent learns datawhale agent works"
tokens = corpus.split()
total_tokens = len(tokens)
# --- 第一步:计算 P(datawhale) ---
count_datawhale = tokens.count('datawhale')
p_datawhale = count_datawhale / total_tokens
print(f"第一步: P(datawhale) = {count_datawhale}/{total_tokens} = {p_datawhale:.3f}")
# --- 第二步:计算 P(agent|datawhale) ---
# 先计算 bigrams 用于后续步骤
bigrams = zip(tokens, tokens[1:])
bigram_counts = collections.Counter(bigrams)
count_datawhale_agent = bigram_counts[('datawhale''agent')]
# count_datawhale 已在第一步计算
p_agent_given_datawhale = count_datawhale_agent / count_datawhale
print(f"第二步: P(agent|datawhale) = {count_datawhale_agent}/{count_datawhale} = {p_agent_given_datawhale:.3f}")
# --- 第三步:计算 P(learns|agent) ---
count_agent_learns = bigram_counts[('agent''learns')]
count_agent = tokens.count('agent')
p_learns_given_agent = count_agent_learns / count_agent
print(f"第三步: P(learns|agent) = {count_agent_learns}/{count_agent} = {p_learns_given_agent:.3f}")
# --- 最后:将概率连乘 ---
p_sentence = p_datawhale * p_agent_given_datawhale * p_learns_given_agent
print(f"最后: P('datawhale agent learns') ≈ {p_datawhale:.3f} * {p_agent_given_datawhale:.3f} * {p_learns_given_agent:.3f} = {p_sentence:.3f}")
>>>
第一步: P(datawhale) = 2/6 = 0.333
第二步: P(agent|datawhale) = 2/2 = 1.000
第三步: P(learns|agent) = 1/2 = 0.500
最后: P('datawhale agent learns') ≈ 0.333 * 1.000 * 0.500 = 0.167

N-gram 模型虽然简单有效,但有两个致命缺陷:

    数据稀疏性 (Sparsity) :如果一个词序列从未在语料库中出现,其概率估计就为 0,这显然是不合理的。虽然可以通过平滑 (Smoothing) 技术缓解,但无法根除。

    泛化能力差:模型无法理解词与词之间的语义相似性。例如,即使模型在语料库中见过很多次 agent learns,它也无法将这个知识泛化到语义相似的词上。当我们计算 robot learns的概率时,如果robot这个词从未出现过,或者robot learns这个组合从未出现过,模型计算出的概率也会是零。模型无法理解agentrobot在语义上的相似性。

2)神经网络语言模型与词嵌入

N-gram 模型的根本缺陷在于它将词视为孤立、离散的符号。为了克服这个问题,研究者们转向了神经网络,并提出了一种思想:用连续的向量来表示词。2003年,Bengio 等人提出的前馈神经网络语言模型 (Feedforward Neural Network Language Model) 是这一领域的里程碑[1]

其核心思想可以分为两步:

    构建一个语义空间创建一个高维的连续向量空间,然后将词汇表中的每个词都映射为该空间中的一个点。这个点(即向量)就被称为词嵌入 (Word Embedding)或词向量。在这个空间里,语义上相近的词,它们对应的向量在空间中的位置也相近。例如,agentrobot的向量会靠得很近,而 agent 和 apple 的向量会离得很远。

    学习从上下文到下一个词的映射利用神经网络的强大拟合能力,来学习一个函数。这个函数的输入是前  个词的词向量,输出是词汇表中每个词在当前上下文后出现的概率分布。

图 3.2 神经网络语言模型架构示意图

如图3.2所示,在这个架构中,词嵌入是在模型训练过程中自动学习得到的。模型为了完成“预测下一个词”这个任务,会不断调整每个词的向量位置,最终使这些向量能够蕴含丰富的语义信息。一旦我们将词转换成了向量,我们就可以用数学工具来度量它们之间的关系。最常用的方法是余弦相似度 (Cosine Similarity),它通过计算两个向量夹角的余弦值来衡量它们的相似性。

这个公式的含义是:

通过这种方式,词向量不仅能捕捉到“同义词”这类简单的关系,还能捕捉到更复杂的类比关系。

一个著名的例子展示了词向量捕捉到的语义关系: vector('King') - vector('Man') + vector('Woman')这个向量运算的结果,在向量空间中与vector('Queen')的位置惊人地接近。这好比在进行语义的平移:我们从“国王”这个点出发,减去“男性”的向量,再加上“女性”的向量,最终就抵达了“女王”的位置。这证明了词嵌入能够学习到“性别”、“皇室”这类抽象概念。

import numpy as np
# 假设我们已经学习到了简化的二维词向量
embeddings = {
"king": np.array([0.90.8]),
"queen": np.array([0.90.2]),
"man": np.array([0.70.9]),
"woman": np.array([0.70.3])
}
defcosine_similarity(vec1, vec2):
    dot_product = np.dot(vec1, vec2)
    norm_product = np.linalg.norm(vec1) * np.linalg.norm(vec2)
return dot_product / norm_product
# king - man + woman
result_vec = embeddings["king"] - embeddings["man"] + embeddings["woman"]
# 计算结果向量与 "queen" 的相似度
sim = cosine_similarity(result_vec, embeddings["queen"])
print(f"king - man + woman 的结果向量: {result_vec}")
print(f"该结果与 'queen' 的相似度: {sim:.4f}")
>>>
king - man + woman 的结果向量: [0.90.2]
该结果与 'queen' 的相似度: 1.0000

神经网络语言模型通过词嵌入,成功解决了 N-gram 模型的泛化能力差的问题。然而,它仍然有一个类似 N-gram 的限制:上下文窗口是固定的。它只能考虑固定数量的前文,这为能处理任意长序列的循环神经网络埋下了伏笔。

3)循环神经网络 (RNN) 与长短时记忆网络 (LSTM)

前一节的神经网络语言模型虽然引入了词嵌入解决了泛化问题,但它和 N-gram 模型一样,上下文窗口是固定大小的。为了预测下一个词,它只能看到前 n−1 个词,再早的历史信息就被丢弃了。这显然不符合我们人类理解语言的方式。为了打破固定窗口的限制,循环神经网络 (Recurrent Neural Network, RNN)应运而生,其核心思想非常直观:为网络增加“记忆”能力[2]

如图3.3所示,RNN 的设计引入了一个隐藏状态 (hidden state)向量,我们可以将其理解为网络的短期记忆。在处理序列的每一步,网络都会读取当前的输入词,并结合它上一刻的记忆(即上一个时间步的隐藏状态),然后生成一个新的记忆(即当前时间步的隐藏状态)传递给下一刻。这个循环往复的过程,使得信息可以在序列中不断向后传递。

图 3.3 RNN 结构示意图

然而,标准的 RNN 在实践中存在一个严重的问题:长期依赖问题 (Long-term Dependency Problem) 。在训练过程中,模型需要通过反向传播算法根据输出端的误差来调整网络深处的权重。对于 RNN 而言,序列的长度就是网络的深度。当序列很长时,梯度在从后向前传播的过程中会经过多次连乘,这会导致梯度值快速趋向于零(梯度消失或变得极大(梯度爆炸。梯度消失使得模型无法有效学习到序列早期信息对后期输出的影响,即难以捕捉长距离的依赖关系。

为了解决长期依赖问题,长短时记忆网络 (Long Short-Term Memory, LSTM)被设计出来[3]。LSTM 是一种特殊的 RNN,其核心创新在于引入了细胞状态 (Cell State) 和一套精密的门控机制 (Gating Mechanism) 。细胞状态可以看作是一条独立于隐藏状态的信息通路,允许信息在时间步之间更顺畅地传递。门控机制则是由几个小型神经网络构成,它们可以学习如何有选择地让信息通过,从而控制细胞状态中信息的增加与移除。这些门包括:

2、Transformer 架构解析

在上一节中,我们看到RNN及LSTM通过引入循环结构来处理序列数据,这在一定程度上解决了捕捉长距离依赖的问题。然而,这种循环的计算方式也带来了新的瓶颈:它必须按顺序处理数据。第 t 个时间步的计算,必须等待第 t−1 个时间步完成后才能开始。这意味着 RNN 无法进行大规模的并行计算,在处理长序列时效率低下,这极大地限制了模型规模和训练速度的提升。Transformer在2017 年由谷歌团队提出[4]。它完全抛弃了循环结构,转而完全依赖一种名为注意力 (Attention) 的机制来捕捉序列内的依赖关系,从而实现了真正意义上的并行计算。

1)Encoder-Decoder 整体结构

最初的 Transformer 模型是为端到端任务机器翻译而设计的。如图3.4所示,它在宏观上遵循了一个经典的编码器-解码器 (Encoder-Decoder) 架构。

图 3.4 Transformer 整体架构图

我们可以将这个结构理解为一个分工明确的团队:

    编码器 (Encoder) :任务是“理解输入的整个句子。它会读取所有输入词元(这个概念会在3.2.2节介绍),最终为每个词元生成一个富含上下文信息的向量表示。

    解码器 (Decoder) :任务是“生成目标句子。它会参考自己已经生成的前文,并“咨询”编码器的理解结果,来生成下一个词。

为了真正理解 Transformer 的工作原理,最好的方法莫过于亲手实现它。在本节中,我们将采用一种“自顶向下”的方法:首先,我们搭建出 Transformer 完整的代码框架,定义好所有需要的类和方法。然后,我们将像完成拼图一样,逐一实现这些类的具体功能。

import torch
import torch.nn as nn
import math
# --- 占位符模块,将在后续小节中实现 ---
classPositionalEncoding(nn.Module):
"""
    位置编码模块
    """
defforward(self, x):
pass
classMultiHeadAttention(nn.Module):
"""
    多头注意力机制模块
    """
defforward(self, query, key, value, mask):
pass
classPositionWiseFeedForward(nn.Module):
"""
    位置前馈网络模块
    """
defforward(self, x):
pass
# --- 编码器核心层 ---
classEncoderLayer(nn.Module):
def__init__(self, d_model, num_heads, d_ff, dropout):
        super(EncoderLayer, self).__init__()
        self.self_attn = MultiHeadAttention() # 待实现
        self.feed_forward = PositionWiseFeedForward() # 待实现
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(dropout)
defforward(self, x, mask):
# 残差连接与层归一化将在 3.1.2.4 节中详细解释
# 1. 多头自注意力
        attn_output = self.self_attn(x, x, x, mask)
        x = self.norm1(x + self.dropout(attn_output))
# 2. 前馈网络
        ff_output = self.feed_forward(x)
        x = self.norm2(x + self.dropout(ff_output))
return x
# --- 解码器核心层 ---
classDecoderLayer(nn.Module):
def__init__(self, d_model, num_heads, d_ff, dropout):
        super(DecoderLayer, self).__init__()
        self.self_attn = MultiHeadAttention() # 待实现
        self.cross_attn = MultiHeadAttention() # 待实现
        self.feed_forward = PositionWiseFeedForward() # 待实现
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.norm3 = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(dropout)
defforward(self, x, encoder_output, src_mask, tgt_mask):
# 1. 掩码多头自注意力 (对自己)
        attn_output = self.self_attn(x, x, x, tgt_mask)
        x = self.norm1(x + self.dropout(attn_output))
# 2. 交叉注意力 (对编码器输出)
        cross_attn_output = self.cross_attn(x, encoder_output, encoder_output, src_mask)
        x = self.norm2(x + self.dropout(cross_attn_output))
# 3. 前馈网络
        ff_output = self.feed_forward(x)
        x = self.norm3(x + self.dropout(ff_output))
return x

2)从自注意力到多头注意力

现在,我们来填充骨架中最关键的模块,注意力机制。

想象一下我们阅读这个句子:“The agent learns because it is intelligent.”。当我们读到加粗的 "it" 时,为了理解它的指代,我们的大脑会不自觉地将更多的注意力放在前面的 "agent" 这个词上。自注意力 (Self-Attention)机制就是对这种现象的数学建模。它允许模型在处理序列中的每一个词时,都能兼顾句子中的所有其他词,并为这些词分配不同的“注意力权重”。权重越高的词,代表其与当前词的关联性越强,其信息也应该在当前词的表示中占据更大的比重。

为了实现上述过程,自注意力机制为每个输入的词元向量引入了三个可学习的角色:

这三个向量都是由原始的词嵌入向量乘以三个不同的、可学习的权重矩阵 () 得到的。整个计算过程可以分为以下几步,我们可以把它想象成一次高效的开卷考试:

这个过程可以用一个简洁的公式来概括:

如果只进行一次上述的注意力计算(即单头),模型可能会只学会关注一种类型的关联。比如,在处理 "it" 时,可能只学会了关注主语。但语言中的关系是复杂的,我们希望模型能同时关注多种关系(如指代关系、时态关系、从属关系等)。多头注意力机制应运而生。它的思想很简单:把一次做完变成分成几组,分开做,再合并。

它将原始的 Q, K, V 向量在维度上切分成 h 份(h 就是“头”数),每一份都独立地进行一次单头注意力的计算。这就好比让 h 个不同的“专家”从不同的角度去审视句子,每个专家都能捕捉到一种不同的特征关系。最后,将这 h 个专家的“意见”(即输出向量)拼接起来,再通过一个线性变换进行整合,就得到了最终的输出。

图 3.5 多头注意力机制

如图3.5所示,这种设计让模型能够共同关注来自不同位置、不同表示子空间的信息,极大地增强了模型的表达能力。以下是多头注意力的简单实现可供参考。

classMultiHeadAttention(nn.Module):
"""
    多头注意力机制模块
    """
def__init__(self, d_model, num_heads):
        super(MultiHeadAttention, self).__init__()
assert d_model % num_heads == 0"d_model 必须能被 num_heads 整除"
        self.d_model = d_model
        self.num_heads = num_heads
        self.d_k = d_model // num_heads
# 定义 Q, K, V 和输出的线性变换层
        self.W_q = nn.Linear(d_model, d_model)
        self.W_k = nn.Linear(d_model, d_model)
        self.W_v = nn.Linear(d_model, d_model)
        self.W_o = nn.Linear(d_model, d_model)
defscaled_dot_product_attention(self, Q, K, V, mask=None):
# 1. 计算注意力得分 (QK^T)
        attn_scores = torch.matmul(Q, K.transpose(-2-1)) / math.sqrt(self.d_k)
# 2. 应用掩码 (如果提供)
if mask isnotNone:
# 将掩码中为 0 的位置设置为一个非常小的负数,这样 softmax 后会接近 0
            attn_scores = attn_scores.masked_fill(mask == 0-1e9)
# 3. 计算注意力权重 (Softmax)
        attn_probs = torch.softmax(attn_scores, dim=-1)
# 4. 加权求和 (权重 * V)
        output = torch.matmul(attn_probs, V)
return output
defsplit_heads(self, x):
# 将输入 x 的形状从 (batch_size, seq_length, d_model)
# 变换为 (batch_size, num_heads, seq_length, d_k)
        batch_size, seq_length, d_model = x.size()
return x.view(batch_size, seq_length, self.num_heads, self.d_k).transpose(12)
defcombine_heads(self, x):
# 将输入 x 的形状从 (batch_size, num_heads, seq_length, d_k)
# 变回 (batch_size, seq_length, d_model)
        batch_size, num_heads, seq_length, d_k = x.size()
return x.transpose(12).contiguous().view(batch_size, seq_length, self.d_model)
defforward(self, Q, K, V, mask=None):
# 1. 对 Q, K, V 进行线性变换
        Q = self.split_heads(self.W_q(Q))
        K = self.split_heads(self.W_k(K))
        V = self.split_heads(self.W_v(V))
# 2. 计算缩放点积注意力
        attn_output = self.scaled_dot_product_attention(Q, K, V, mask)
# 3. 合并多头输出并进行最终的线性变换
        output = self.W_o(self.combine_heads(attn_output))
return output

3)前馈神经网络

在每个 Encoder 和 Decoder 层中,多头注意力子层之后都跟着一个逐位置前馈网络(Position-wise Feed-Forward Network, FFN)。如果说注意力层的作用是从整个序列中“动态地聚合”相关信息,那么前馈网络的作用从这些聚合后的信息中提取更高阶的特征。

这个名字的关键在于“逐位置”。它意味着这个前馈网络会独立地作用于序列中的每一个词元向量。换句话说,对于一个长度为seq_len的序列,这个 FFN 实际上会被调用 seq_len次,每次处理一个词元。重要的是,所有位置共享的是同一组网络权重。这种设计既保持了对每个位置进行独立加工的能力,又大大减少了模型的参数量。这个网络的结构非常简单,由两个线性变换和一个 ReLU 激活函数组成:

其中,是注意力子层的输出。 是可学习的参数。通常,第一个线性层的输出维度d_ff会远大于输入的维度d_model(例如d_ff = 4 * d_model),经过 ReLU 激活后再通过第二个线性层映射回d_model维度。这种“先扩大再缩小”的模式,也被称为瓶颈结构,被认为有助于模型学习更丰富的特征表示。

在我们的 PyTorch 骨架中,我们可以用以下代码来实现这个模块:

classPositionWiseFeedForward(nn.Module):
"""
    位置前馈网络模块
    """
def__init__(self, d_model, d_ff, dropout=0.1):
        super(PositionWiseFeedForward, self).__init__()
        self.linear1 = nn.Linear(d_model, d_ff)
        self.dropout = nn.Dropout(dropout)
        self.linear2 = nn.Linear(d_ff, d_model)
        self.relu = nn.ReLU()
defforward(self, x):
# x 形状: (batch_size, seq_len, d_model)
        x = self.linear1(x)
        x = self.relu(x)
        x = self.dropout(x)
        x = self.linear2(x)
# 最终输出形状: (batch_size, seq_len, d_model)
return x

4)残差连接与层归一化

在 Transformer 的每个编码器和解码器层中,所有子模块(如多头注意力和前馈网络)都被一个 Add & Norm操作包裹。这个组合是为了保证 Transformer 能够稳定训练。

这个操作由两个部分组成:

5)位置编码

我们已经了解,Transformer 的核心是自注意力机制,它通过计算序列中任意两个词元之间的关系来捕捉依赖。然而,这种计算方式有一个固有的问题:它本身不包含任何关于词元顺序或位置的信息。对于自注意力来说,“agent learns” 和 “learns agent” 这两个序列是完全等价的,因为它只关心词元之间的关系,而忽略了它们的排列。为了解决这个问题,Transformer 引入了位置编码 (Positional Encoding)

位置编码的核心思想是,为输入序列中的每一个词元嵌入向量,都额外加上一个能代表其绝对位置和相对位置信息的“位置向量”。这个位置向量不是通过学习得到的,而是通过一个固定的数学公式直接计算得出。这样一来,即使两个词元(例如,两个都叫 agent的词元)自身的嵌入是相同的,但由于它们在句子中的位置不同,它们最终输入到 Transformer 模型中的向量就会因为加上了不同的位置编码而变得独一无二。原论文中提出的位置编码使用正弦和余弦函数来生成,其公式如下:

其中:

现在,我们来实现 PositionalEncoding模块,并完成我们 Transformer 骨架代码的最后一部分。

classPositionalEncoding(nn.Module):
"""
    为输入序列的词嵌入向量添加位置编码。
    """
def__init__(self, d_model: int, dropout: float = 0.1, max_len: int = 5000):
        super().__init__()
        self.dropout = nn.Dropout(p=dropout)
# 创建一个足够长的位置编码矩阵
        position = torch.arange(max_len).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))
# pe (positional encoding) 的大小为 (max_len, d_model)
        pe = torch.zeros(1, max_len, d_model)
# 偶数维度使用 sin, 奇数维度使用 cos
        pe[:, 00::2] = torch.sin(position * div_term)
        pe[:, 01::2] = torch.cos(position * div_term)
# 将 pe 注册为 buffer,这样它就不会被视为模型参数,但会随模型移动(例如 to(device))
        self.register_buffer('pe', pe)
defforward(self, x: torch.Tensor) -> torch.Tensor:
# x.size(0) 是当前输入的序列长度
# 将位置编码加到输入向量上
        x = x + self.pe[:x.size(0)]
return self.dropout(x)

本小节主要是为了帮助理解 Transformer 的宏观结构和内部每个模块的运作细节。由于是为了补充智能体学习中大模型的知识体系,也就不再继续往下深入实现。至此,我们已经为理解现代大语言模型打下了坚实的架构基础。在下一节中,我们将探讨 Decoder-Only 架构,看看它是如何基于 Transformer 的思想演变而来。

3、Decoder-Only 架构

前面一节中,我们动手构建了一个完整的Transformer 模型,它能在很多端到端的场景表现出色。但是当任务转换为构建一个与人对话、创作、作为智能体大脑的通用模型时,或许我们并不需要那么复杂的结构。

Transformer的设计哲学是“先理解,再生成”。编码器负责深入理解输入的整个句子,形成一个包含全局信息的上下文记忆,然后解码器基于这份记忆来生成翻译。但 OpenAI 在开发 GPT (Generative Pre-trained Transformer) 时,提出了一个更简单的思想[5]语言的核心任务,不就是预测下一个最有可能出现的词吗?

无论是回答问题、写故事还是生成代码,本质上都是在一个已有的文本序列后面,一个词一个词地添加最合理的内容。基于这个思想,GPT 做了一个大胆的简化:它完全抛弃了编码器,只保留了解码器部分。这就是 Decoder-Only 架构的由来。

Decoder-Only 架构的工作模式被称为自回归 (Autoregressive) 。这个听起来很专业的术语,其实描述了一个非常简单的过程:

    给模型一个起始文本(例如 “Datawhale Agent is”)。

    模型预测出下一个最有可能的词(例如 “a”)。

    模型将自己刚刚生成的词 “a” 添加到输入文本的末尾,形成新的输入(“Datawhale Agent is a”)。

    模型基于这个新输入,再次预测下一个词(例如 “powerful”)。

    不断重复这个过程,直到生成完整的句子或达到停止条件。

模型就像一个在玩“文字接龙”的游戏,它不断地“回顾”自己已经写下的内容,然后思考下一个字该写什么。

你可能会问,解码器是如何保证在预测第t个词时,不去“偷看”第t+1个词的答案呢?

答案就是掩码自注意力 (Masked Self-Attention)。在 Decoder-Only 架构中,这个机制变得至关重要。它的工作原理非常巧妙:

在自注意力机制计算出注意力分数矩阵(即每个词对其他所有词的关注度得分)之后,但在进行 Softmax 归一化之前,模型会应用一个“掩码”。这个掩码会将所有位于当前位置之后(即目前尚未观测到)的词元对应的分数,替换为一个非常大的负数。当这个带有负无穷分数的矩阵经过 Softmax 函数时,这些位置的概率就会变为 0。这样一来,模型在计算任何一个位置的输出时,都从数学上被阻止了去关注它后面的信息。这种机制保证了模型在预测下一个词时,能且仅能依赖它已经见过的、位于当前位置之前的所有信息,从而确保了预测的公平性和逻辑的连贯性。

Decoder-Only 架构的优势

这种看似简单的架构,却带来了巨大的成功,其优势在于:

总而言之,从 Transformer 的解码器演变而来的 Decoder-Only 架构,通过“预测下一个词”这一简单的范式,开启了我们今天所处的大语言模型时代。

二、与大语言模型交互

1、提示工程

如果我们把大语言模型比作一个能力极强的“大脑”,那么提示 (Prompt) 就是我们与这个“大脑”沟通的语言。提示工程,就是研究如何设计出精准的提示,从而引导模型产生我们期望输出的回复。对于构建智能体而言,一个精心设计的提示能让智能体之间协作分工变得高效。

(1)模型采样参数

在使用大模型时,你会经常看到类似Temperature这类的可配置参数,其本质是通过调整模型对 “概率分布” 的采样策略,让输出匹配具体场景需求,配置合适的参数可以提升Agent在特定场景的性能。

传统的概率分布使由 Softmax 公式计算得到的:,采样参数的本质就是在此基础上,根据不同策略“重新调整”或“截断”分布,从而改变大模型输出的下一个token。

Temperature:温度是控制模型输出 “随机性” 与 “确定性” 的关键参数。其原理是引入温度系数,将 Softmax 改写为

当T变小时,分布“更加陡峭”,高概率项权重进一步放大,生成更“保守”且重复率更高的文本。当T变大时,分布“更加平坦”,低概率项权重提升,生成更“多样”但可能出现不连贯的内容。

Top-k :其原理是将所有 token 按概率从高到低排序,取排名前 k 个的 token 组成 “候选集”,随后对筛选出的 k 个 token 的概率进行 “归一化”: $ \hat{p}i = \frac{p_i}{\sum{j \in \text{候选集}} p_j}$

Top-p :其原理是将所有 token 按概率从高到低排序,从排序后的第一个 token 开始,逐步累加概率,直到累积和首次达到或超过阈值 p: ,此时累加过程中包含的所有 token 组成 “核集合”,最后对核集合进行归一化。

在文本生成中,当同时设置 Top-p、Top-k 和温度系数时,这些参数会按照分层过滤的方式协同工作,其优先级顺序为:温度调整→Top-k→Top-p。温度调整整体分布的陡峭程度,Top-k 会先保留概率最高的 k 个候选,然后 Top-p 会从 Top-k 的结果中选取累积概率≥p 的最小集合作为最终的候选集。不过,通常 Top-k 和 Top-p 二选一即可,若同时设置,实际候选集为两者的交集。 需要注意的是,如果将温度设置为 0,则 Top-k 和 Top-p 将变得无关紧要,因为最有可能的 Token 将成为下一个预测的 Token;如果将 Top-k 设置为 1,温度和 Top-p 也将变得无关紧要,因为只有一个 Token 通过 Top-k 标准,它将是下一个预测的 Token。

2)零样本、单样本与少样本提示

根据我们给模型提供示例(Exemplar)的数量,提示可以分为三种类型。为了更好地理解它们,让我们以一个情感分类任务为例,目标是让模型判断一段文本的情感色彩(如正面、负面或中性)。

零样本提示 (Zero-shot Prompting)这指的是我们不给模型任何示例,直接让它根据指令完成任务。这得益于模型在海量数据上预训练后获得的强大泛化能力。

案例: 我们直接向模型下达指令,要求它完成情感分类任务。

文本:Datawhale的AI Agent课程非常棒!
情感:正面

单样本提示 (One-shot Prompting)我们给模型提供一个完整的示例,向它展示任务的格式和期望的输出风格。

我们给模型提供一个完整的示例,向它展示任务的格式和期望的输出风格。

案例: 我们先给模型一个完整的“问题-答案”对作为示范,然后提出我们的新问题。

文本:这家餐厅的服务太慢了。
情感:负面
文本:Datawhale的AI Agent课程非常棒!
情感:

模型会模仿给出的示例格式,为第二段文本补全“正面”。

少样本提示 (Few-shot Prompting)我们提供多个示例,这能让模型更准确地理解任务的细节、边界和细微差别,从而获得更好的性能。

案例: 我们提供涵盖了不同情况的多个示例,让模型对任务有更全面的理解。

文本:这家餐厅的服务太慢了。
情感:负面
文本:这部电影的情节很平淡。
情感:中性
文本:Datawhale的AI Agent课程非常棒!
情感:

模型会综合所有示例,更准确地将最后一句的情感分类为“正面”。

3)指令调优的影响

早期的 GPT 模型(如 GPT-3)主要是“文本补全”模型,它们擅长根据前面的文本续写,但不一定能很好地理解并执行人类的指令。

指令调优 (Instruction Tuning) 是一种微调技术,它使用大量“指令-回答”格式的数据对预训练模型进行进一步的训练。经过指令调优后,模型能更好地理解并遵循用户的指令。我们今天日常工作学习中使用的所有模型(如ChatGPTDeepSeekQwen)都是其模型家族中经过指令调优过的模型。

这是一段将英文翻译成中文的程序。
英文:Hello
中文:你好
英文:How are you?
中文:
请将下面的英文翻译成中文:
How are you?

指令调优的出现,极大地简化了我们与模型交互的方式,使得直接、清晰的自然语言指令成为可能。

4)基础提示技巧

角色扮演 (Role-playing)通过赋予模型一个特定的角色,我们可以引导它的回答风格、语气和知识范围,使其输出更符合特定场景的需求。

# 案例
你现在是一位资深的Python编程专家。请解释一下Python中的GIL(全局解释器锁)是什么,要让一个初学者也能听懂。

上下文示例 (In-context Example)这与少样本提示的思想一致,通过在提示中提供清晰的输入输出示例,来“教会”模型如何处理我们的请求,尤其是在处理复杂格式或特定风格的任务时非常有效。

# 案例
我需要你从产品评论中提取产品名称和用户情感。请严格按照下面的JSON格式输出。
评论:这款“星尘”笔记本电脑的屏幕显示效果惊人,但我不太喜欢它的键盘手感。
输出:{"product_name": "星尘笔记本电脑", "sentiment": "混合"}
评论:我刚买的“声动”耳机音质很棒,续航也超出了我的预期!
输出:

5)思维链

对于需要逻辑推理、计算或多步骤思考的复杂问题,直接让模型给出答案往往容易出错。思维链 (Chain-of-Thought, CoT) 是一种强大的提示技巧,它通过引导模型“一步一步地思考”,提升了模型在复杂任务上的推理能力。

实现 CoT 的关键,是在提示中加入一句简单的引导语,如“请逐步思考”或“Let's think step by step”。

# 思维链提示
一个篮球队在一个赛季的80场比赛中赢了60%。在接下来的赛季中,他们打了15场比赛,赢了12场。两个赛季的总胜率是多少?
请一步一步地思考并解答。
>>>
(模型可能会输出)
好的,我们来一步步计算。
第一步:计算第一个赛季赢得的比赛数。
80场 * 60% = 48场。
第二步:计算两个赛季的总比赛数和总胜利数。
总比赛数 = 80 + 15 = 95场。
总胜利数 = 48 + 12 = 60场。
第三步:计算总胜率。
总胜率 = (总胜利数 / 总比赛数) * 100% = (60 / 95) * 100% ≈ 63.16%。
所以,两个赛季的总胜率约为63.16%。

通过显式地展示其推理过程,模型不仅更容易得出正确的答案,也让它的回答变得更可信、更易于我们检查和纠正。

2、文本分词

我们知道,计算机本质上只能理解数字。因此,在将自然语言文本喂给大语言模型之前,必须先将其转换成模型能够处理的数字格式。这个将文本序列转换为数字序列的过程,就叫做分词 (Tokenization) 。分词器 (Tokenizer) 的作用,就是定义一套规则,将原始文本切分成一个个最小的单元,我们称之为词元 (Token) 。

1)为何需要分词

早期的自然语言处理任务可能会采用简单的分词策略:

为了兼顾词表大小和语义表达,现代大语言模型普遍采用子词分词 (Subword Tokenization)算法。它的核心思想是:将常见的词(如 "agent")保留为完整的词元,同时将不常见的词(如 "Tokenization")拆分成多个有意义的子词片段(如 "Token" 和 "ization")。这样既控制了词表的大小,又能让模型通过组合子词来理解和生成新词。

2)字节对编码算法解析

字节对编码 (Byte-Pair Encoding, BPE) 是最主流的子词分词算法之一[6],GPT系列模型就采用了这种算法。其核心思想非常简洁,可以理解为一个“贪心”的合并过程:

    初始化将词表初始化为所有在语料库中出现过的基本字符。

    迭代合并在语料库上,统计所有相邻词元对的出现频率,找到频率最高的一对,将它们合并成一个新的词元,并加入词表。

    重复重复第 2 步,直到词表大小达到预设的阈值。

案例演示:假设我们的迷你语料库是 {"hug": 1, "pug": 1, "pun": 1, "bun": 1},并且我们想构建一个大小为 10 的词表。BPE 的训练过程可以用下表3.1来表示:

表 3.1  BPE 算法合并过程示例

训练结束后,词表大小达到 10,我们就得到了新的分词规则。现在,对于一个未见过的词 "bug",分词器会先查找 "bug" 是否在词表中,发现不在;然后查找 "bu",发现不在;最后查找 "b" 和 "ug",发现都在,于是将其切分为['b', 'ug']

下面我们用一段简单的 Python 代码来模拟上述过程:

import re, collections
defget_stats(vocab):
"""统计词元对频率"""
    pairs = collections.defaultdict(int)
for word, freq in vocab.items():
        symbols = word.split()
for i in range(len(symbols)-1):
            pairs[symbols[i],symbols[i+1]] += freq
return pairs
defmerge_vocab(pair, v_in):
"""合并词元对"""
    v_out = {}
    bigram = re.escape(' '.join(pair))
    p = re.compile(r'(?<!\S)' + bigram + r'(?!\S)')
for word in v_in:
        w_out = p.sub(''.join(pair), word)
        v_out[w_out] = v_in[word]
return v_out
# 准备语料库,每个词末尾加上</w>表示结束,并切分好字符
vocab = {'h u g </w>'1'p u g </w>'1'p u n </w>'1'b u n </w>'1}
num_merges = 4# 设置合并次数
for i in range(num_merges):
    pairs = get_stats(vocab)
ifnot pairs:
break
    best = max(pairs, key=pairs.get)
    vocab = merge_vocab(best, vocab)
    print(f"第{i+1}次合并: {best} -> {''.join(best)}")
    print(f"新词表(部分): {list(vocab.keys())}")
    print("-" * 20)
>>>
1次合并: ('u''g') -> ug
新词表(部分): ['h ug </w>''p ug </w>''p u n </w>''b u n </w>']
--------------------
2次合并: ('ug''</w>') -> ug</w>
新词表(部分): ['h ug</w>''p ug</w>''p u n </w>''b u n </w>']
--------------------
3次合并: ('u''n') -> un
新词表(部分): ['h ug</w>''p ug</w>''p un </w>''b un </w>']
--------------------
4次合并: ('un''</w>') -> un</w>
新词表(部分): ['h ug</w>''p ug</w>''p un</w>''b un</w>']
--------------------

这段代码清晰地展示了 BPE 算法如何通过迭代合并最高频的相邻词元对,来逐步构建和扩充词表的过程。

后续的许多算法都是在BPE的基础上进行优化的。其中,Google 开发的 WordPiece 和 SentencePiece 是影响力最大的两种。

3)分词器对开发者的意义

理解分词算法的细节并非目的,但作为智能体的开发者,理解分词器的实际影响是重要,这直接关系到智能体的性能、成本和稳定性:

3、调用开源大语言模型

在本书的第一章,我们通过 API 来与大语言模型进行交互,以此驱动我们的智能体。这是一种快速、便捷的方式,但并非唯一的方式。对于许多需要处理敏感数据、希望离线运行或想精细控制成本的场景,将大语言模型直接部署在本地就显得至关重要。

Hugging Face Transformers 是一个强大的开源库,它提供了标准化的接口来加载和使用数以万计的预训练模型。我们将使用它来完成本次实践。

配置环境与选择模型:为了让大多数读者都能在个人电脑上顺利运行,我们特意选择了一个小规模但功能强大的模型:Qwen/Qwen1.5-0.5B-Chat。这是一个由阿里巴巴达摩院开源的拥有约 5 亿参数的对话模型,它体积小、性能优异,非常适合入门学习和本地部署。

首先,请确保你已经安装了必要的库:

pip install transformers torch

transformers库中,我们通常使用AutoModelForCausalLMAutoTokenizer这两个类来自动加载与模型匹配的权重和分词器。下面这段代码会自动从 Hugging Face Hub 下载所需的模型文件和分词器配置,这可能需要一些时间,具体取决于你的网络速度。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
# 指定模型ID
model_id = "Qwen/Qwen1.5-0.5B-Chat"
# 设置设备,优先使用GPU
device = "cuda"if torch.cuda.is_available() else"cpu"
print(f"Using device: {device}")
# 加载分词器
tokenizer = AutoTokenizer.from_pretrained(model_id)
# 加载模型,并将其移动到指定设备
model = AutoModelForCausalLM.from_pretrained(model_id).to(device)
print("模型和分词器加载完成!")

我们来创建一个对话提示,Qwen1.5-Chat 模型遵循特定的对话模板。然后,可以将使用上一步加载的tokenizer将文本提示转换为模型能够理解的数字 ID(即 Token ID)。

# 准备对话输入
messages = [
    {"role""system""content""You are a helpful assistant."},
    {"role""user""content""你好,请介绍你自己。"}
]
# 使用分词器的模板格式化输入
text = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)
# 编码输入文本
model_inputs = tokenizer([text], return_tensors="pt").to(device)
print("编码后的输入文本:")
print(model_inputs)
>>>
{'input_ids': tensor([[15164489481982610525264,  109501784713,15164519815164487219810838637945100157107828,177315164519815164477091198]], device='cuda:0'), 'attention_mask': tensor([[111111111111111111111111]],
       device='cuda:0')}

现在可以调用模型的generate()方法来生成回答了。模型会输出一系列 Token ID,这代表了它的回答。

最后,我们需要使用分词器的decode()方法,将这些数字 ID 翻译回人类可以阅读的文本。

# 使用模型生成回答
# max_new_tokens 控制了模型最多能生成多少个新的Token
generated_ids = model.generate(
    model_inputs.input_ids,
    max_new_tokens=512
)
# 将生成的 Token ID 截取掉输入部分
# 这样我们只解码模型新生成的部分
generated_ids = [
    output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]
# 解码生成的 Token ID
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print("\n模型的回答:")
print(response)
>>>
我叫通义千问,是由阿里云研发的预训练语言模型,可以回答问题、创作文字,还能表达观点、撰写代码。我主要的功能是在多个领域提
供帮助,包括但不限于:语言理解、文本生成、机器翻译、问答系统等。有什么我可以帮到你的吗?

当你运行完所有代码后,你将会在本地电脑上看到模型生成的关于Qwen模型的介绍。恭喜你,你已经成功地在本地部署并运行了一个开源大语言模型!

4、模型的选择

在上一节中,我们成功地在本地运行了一个小型的开源语言模型。这自然引出了一个对于智能体开发者而言至关重要的问题:在当前数百个模型百花齐放的背景下,我们应当如何为特定的任务选择最合适的模型?

选择语言模型并非简单地追求“最大、最强”,而是一个在性能、成本、速度和部署方式之间进行权衡的决策过程。本节将首先梳理模型选型的几个关键考量因素,然后对当前主流的闭源与开源模型进行梳理。

由于大语言模型技术正处于高速发展阶段,新模型、新版本层出不穷,迭代速度极快。本节在撰写时力求提供当前主流模型的概览和选型考量,但请读者注意,文中所提及的具体模型版本和性能数据可能随时间推移而发生变化,且只列举了部分工作并不完整。我们更侧重于介绍其核心技术特点、发展趋势以及在智能体开发中的通用选型原则。

1)模型选型的关键考量

在为您的智能体选择大语言模型时,可以从以下几个维度进行综合评估:

2)闭源模型概览

闭源模型通常代表了当前 AI 技术的最前沿,并提供稳定、易用的 API 服务,是构建高性能智能体的首选。

    OpenAI GPT 系列从开启大模型时代的 GPT-3,到引入 RLHF(人类反馈强化学习)、实现与人类意图对齐的 ChatGPT,再到开启多模态时代的 GPT-4,OpenAI 持续引领行业发展。最新的 GPT-5 更是将多模态能力和通用智能水平提升到新的高度,能够无缝处理文本、音频和图像输入,并生成相应的输出,其响应速度和自然度也大幅提升,尤其在实时语音对话方面表现出色。

    Google Gemini 系列Google DeepMind 推出的 Gemini 系列模型是原生多模态的代表,其核心特点是能统一处理文本、代码、音视频和图像等多种模态的数据,并以其超长的上下文窗口在海量信息处理上具备优势。Gemini Ultra 是其最强大的模型,适用于高度复杂的任务;Gemini Pro 适用于广泛的任务,提供高性能和效率;Gemini Nano 则针对设备端部署进行了优化。最新的 Gemini 2.5 系列模型,如 Gemini 2.5 Pro 和 Gemini 2.5 Flash,进一步提升了推理能力和上下文窗口,特别是 Gemini 2.5 Flash 以其更快的推理速度和成本效益,适用于需要快速响应的场景。

    Anthropic Claude 系列Anthropic 是一家专注于 AI 安全和负责任 AI 的公司,其 Claude 系列模型从设计之初就将 AI 安全放在首位,以其在处理长文档、减少有害输出、遵循指令方面的可靠性而闻名,深受企业级应用青睐。Claude 3 系列包括 Claude 3 Opus(最智能、性能最强)、Claude 3 Sonnet(性能与速度兼顾的平衡之选)和 Claude 3 Haiku(最快、最紧凑的模型,适用于近乎实时的交互)。最新的 Claude 4 系列模型,如 Claude 4 Opus,在通用智能、复杂推理和代码生成方面取得了显著进展,进一步提升了处理长上下文和多模态任务的能力。

    国内主流模型中国在大语言模型领域涌现出众多具有竞争力的闭源模型,以百度文心一言(ERNIE Bot)、腾讯混元(Hunyuan)、华为盘古(Pangu-α)、科大讯飞星火(SparkDesk)和月之暗面(Moonshot AI)等为代表的国产模型,在中文处理上具备天然优势,并深度赋能本土产业。

3)开源模型概览

开源模型为开发者提供了最高程度的灵活性、透明度和自主性,催生了繁荣的社区生态。它们允许开发者在本地部署、进行定制化微调,并拥有完整的模型控制权。

对于智能体开发者而言,闭源模型提供了“开箱即用”的便捷,而开源模型则赋予了我们“随心所欲”的定制自由。理解这两大阵营的特点和代表模型,是为我们的智能体项目做出明智技术选型的第一步。

三、大语言模型的缩放法则与局限性

大语言模型(LLMs)在近年来取得了令人瞩目的进展,其能力边界不断拓展,应用场景日益丰富。然而,这些成就的背后,离不开对模型规模、数据量和计算资源之间关系的深刻理解,即缩放法则(Scaling Laws)。同时,作为新兴技术,LLMs也面临着诸多挑战和局限性。本节将深入探讨这些核心概念,旨在帮助读者全面理解LLMs的能力边界,从而在构建智能体时扬长避短。

1、缩放法则

缩放法则(Scaling Laws)是近年来大语言模型领域最重要的发现之一。它揭示了模型性能与模型参数量、训练数据量以及计算资源之间存在着可预测的幂律关系。这一发现为大语言模型的持续发展提供了理论指导,阐明了增加资源投入能够系统性提升模型性能的底层逻辑。

研究发现,在对数-对数坐标系下,模型的性能(通常用损失 Loss 来衡量)与参数量、数据量和计算量这三个因素都呈现出平滑的幂律关系[9]。简单来说,只要我们持续、按比例地增加这三个要素,模型的性能就会可预测地、平滑地提升,而不会出现明显的瓶颈。这一发现为大模型的设计和训练提供了清晰的指导:在资源允许的范围内,尽可能地扩大模型规模和训练数据量。

早期的研究更侧重于增加模型参数量,但 DeepMind 在 2022 年提出的“Chinchilla 定律”对此进行了重要修正[10]。该定律指出,在给定的计算预算下,为了达到最优性能,模型参数量和训练数据量之间存在一个最优配比具体来说,最优的模型应该比之前普遍认为的要小,但需要用多得多的数据进行训练。例如,一个 700 亿参数的 Chinchilla 模型,由于使用了比 GPT-3(1750 亿参数)多 4 倍的数据进行训练,其性能反而超越了后者。这一发现纠正了“越大越好”的片面认知,强调了数据效率的重要性,并指导了后续许多高效大模型(如 Llama 系列)的设计。

缩放法则最令人惊奇的产物是“能力的涌现”。所谓能力涌现,是指当模型规模达到一定阈值后,会突然展现出在小规模模型中完全不存在或表现不佳的全新能力。例如,链式思考 (Chain-of-Thought) 、指令遵循 (Instruction Following) 、多步推理、代码生成等能力,都是在模型参数量达到数百亿甚至千亿级别后才显著出现的。这种现象表明,大语言模型不仅仅是简单地记忆和复述,它们在学习过程中可能形成了某种更深层次的抽象和推理能力。对于智能体开发者而言,能力的涌现意味着选择一个足够大规模的模型,是实现复杂自主决策和规划能力的前提。

2、模型幻觉

模型幻觉(Hallucination)通常指的是大语言模型生成的内容与客观事实、用户输入或上下文信息相矛盾,或者生成了不存在的事实、实体或事件。幻觉的本质是模型在生成过程中,过度自信地“编造”了信息,而非准确地检索或推理。根据其表现形式,幻觉可以被分为多种类型[11],例如:

幻觉的产生是多方面因素共同作用的结果。首先,训练数据中可能包含错误或矛盾的信息。其次,模型的自回归生成机制决定了它只是在预测下一个最可能的词元,而没有内置的事实核查模块。最后,在面对需要复杂推理的任务时,模型可能会在逻辑链条中出错,从而“编造”出错误的结论。例如:一个旅游规划 Agent,可能会为你推荐一个现实中不存在的景点,或者预订一个航班号错误的机票。

此外,大语言模型还面临着知识时效性不足和训练数据中存在的偏见等挑战。大语言模型的能力来源于其训练数据。这意味着模型所掌握的知识是其训练数据收集时的最新材料。对于在此日期之后发生的事件、新出现的概念或最新的事实,模型将无法感知或正确回答。与此同时训练数据往往包含了人类社会的各种偏见和刻板印象。当模型在这些数据上学习时,它不可避免地会吸收并反映出这些偏见[12]

为了提高大语言模型的可靠性,研究人员和开发者正在积极探索多种检测和缓解幻觉的方法:

    数据层面: 通过高质量数据清洗、引入事实性知识以及强化学习与人类反馈 (RLHF) 等方式[13],从源头减少幻觉。

    模型层面: 探索新的模型架构,或让模型能够表达其对生成内容的不确定性。

    推理与生成层面

      检索增强生成 (Retrieval-Augmented Generation, RAG)[14]: 这是目前缓解幻觉的有效方法之一。RAG 系统通过在生成之前从外部知识库(如文档数据库、网页)中检索相关信息,然后将检索到的信息作为上下文,引导模型生成基于事实的回答。

      多步推理与验证: 引导模型进行多步推理,并在每一步进行自我检查或外部验证。

      引入外部工具: 允许模型调用外部工具(如搜索引擎、计算器、代码解释器)来获取实时信息或进行精确计算。

尽管幻觉问题短期内难以完全消除,但通过上述的策略,可以显著降低其发生频率和影响,提高大语言模型在实际应用中的可靠性和实用性。

四、本章小结

本文介绍了构建智能体所需的基础知识,重点围绕作为其核心组件的大语言模型 (LLM) 展开。内容从语言模型的早期发展开始,详细讲解了 Transformer 架构,并介绍了与 LLM 进行交互的方法。最后,本章对当前主流的模型生态、发展规律及其固有局限性进行了梳理。

核心知识点回顾:

从 LLM 基础到构建智能体:

这一章的LLM基础主要是为了帮助大家更好的理解大模型的诞生以及发展过程,其中也蕴含了智能体设计的部分思考。例如,如何设计有效的提示词来引导 Agent 的规划与决策,如何根据任务需求选择合适的模型,以及如何在 Agent 的工作流中加入验证机制以规避模型的幻觉等问题,其解决方案均建立在本章的基础之上。我们现在已经准备好从理论转向实践。在下一章,我们将开始探索智能体经典范式构建,将本章所学的知识应用于实际的智能体设计之中。

参考文献

[1] Bengio, Y., Ducharme, R., Vincent, P., & Jauvin, C. (2003). A neural probabilistic language model. Journal of Machine Learning Research, 3, 1137-1155.

[2] Elman, J. L. (1990). Finding structure in time. Cognitive Science, 14(2), 179-211.

[3] Hochreiter, S., & Schmidhuber, J. (1997). Long short-term memory. Neural Computation, 9(8), 1735-1780.

[4] Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., ... & Polosukhin, I. (2017). Attention is all you need. In Advances in neural information processing systems (pp. 5998-6008).

[5] Radford, A., Narasimhan, K., Salimans, T., & Sutskever, I. (2018). Improving language understanding by generative pre-training. OpenAI.

[6] Gage, P. (1994). A new algorithm for data compression. C Users Journal12(2), 23-38.

[7] Schuster, M., & Nakajima, K. (2012, March). Japanese and korean voice search. In 2012 IEEE international conference on acoustics, speech and signal processing (ICASSP) (pp. 5149-5152). IEEE.

[8] Kudo, T., & Richardson, J. (2018). SentencePiece: A simple and language independent subword tokenizer and detokenizer for neural text processing. arXiv preprint arXiv:1808.06226.

[9] Kaplan, J., McCandlish, S., Henighan, T., Brown, T. B., Chess, B., Child, R., ... & Amodei, D. (2020). Scaling Laws for Neural Language Models. arXiv preprint arXiv:2001.08361.

[10] Hoffmann, J., Borgeaud, E., Mensch, A., Buchatskaya, E., Cai, T., Rutherford, R., ... & Sifre, L. (2022). Training Compute-Optimal Large Language Models. arXiv preprint arXiv:2203.07678.

[11] Ji, Z., Lee, N., Fries, R., Yu, T., & Su, D. (2023). Survey of Hallucination in Large Language Models.

[12] Bender, E. M., Gebru, T., McMillan-Major, A., & Mitchell, M. (2021). On the Dangers of Stochastic Parrots: Can Language Models Be Too Big? .

[13] Christiano, P., Leike, J., Brown, T. B., Martic, M., Legg, S., & Amodei, D. (2017). Deep reinforcement learning from human preferences. arXiv preprint arXiv:1706.03741.

[14] Lewis, P., Perez, E., Piktus, A., Petroni, F., Karpukhin, V., Goswami, N., ... & Kiela, D. (2020). Retrieval-augmented generation for knowledge-intensive NLP tasks. In Advances in neural information processing systems (pp. 9459-9474).

一起“三连

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

AI智能体 大语言模型 Transformer 语言模型 自然语言处理 深度学习 提示工程 AI Agents Large Language Models Language Models Natural Language Processing Deep Learning Prompt Engineering
相关文章