GEOZ

语言模型进化史:从统计计数到自注意力的跃迁

2026/8/29
语言模型进化史:从统计计数到自注意力的跃迁

AIAI Summary (BLUF)

这篇文章从语言模型的根本任务出发,系统讲解了从N-gram统计模型、神经网络语言模型、RNN/LSTM到Transformer架构的完整演进过程。通过具体代码示例和直观比喻,帮助读者理解自注意力机制、多头注意力等关键概念,为掌握现代大语言模型的工作原理打下扎实基础。

核心洞察

这篇文章最有意思的点是,它把语言模型从 N-gram 到 Transformer 的演进拆成了能手动算一遍的例子。尤其那个只有两句话的迷你语料库,一步步算概率,比直接甩公式友好太多。如果你一直没搞懂词嵌入和注意力机制到底在解决什么问题,这篇能帮你把线串起来。


核心结论

  1. 在仅含语料 datawhale agent learns datawhale agent works 的 Bigram 模型中,句子 datawhale agent learns 的概率按最大似然估计计算为 0.167,即 P(datawhale)=2/6、P(agent|datawhale)=2/2、P(learns|agent)=1/2 三者的连乘结果。

  2. N-gram 模型存在数据稀疏和无法泛化语义相似的问题;词嵌入将词映射为连续向量,使语义相近的词在向量空间中靠近,文中简化二维向量演示中 king - man + womanqueen 的余弦相似度为 1.0000。

  3. RNN/LSTM 必须按时间步顺序处理序列,难以大规模并行;Transformer 完全去掉循环结构,仅依赖注意力机制,从而实现了并行计算。

  4. Transformer 的自注意力让每个词元同时承担 Q、K、V 三种角色,通过 (\frac{QK^\top}{\sqrt{d_k}}) 打分、softmax 加权后对 V 加权求和;多头注意力则将 Q/K/V 切分为 h 个子空间并行计算,使模型能同时捕捉多种关注模式。

一、语言模型与 Transformer 架构

1、从 N-gram 到 RNN

语言模型(Language Model, LM) 是自然语言处理的核心,它要做的事情很简单:计算一个词序列出现的概率。一个靠谱的语言模型能判断出哪个句子通顺、哪个句子别扭。在多智能体系统里,语言模型是智能体看懂人话、生成回复的基础。我们从最早的统计方法讲起,一路走到深度学习,这样你才能明白 Transformer 到底解决了哪些麻烦。

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

深度学习火起来之前,统计方法统治了语言模型。核心想法是:一个句子出现的概率,等于它里面每个词出现的条件概率连乘。假设句子 S 由一组词构成,那 P(S) 可以写成链式法则的形式。

但直接算这个公式根本行不通。像“在某个词之后出现某个词”这样的条件概率,没法从语料库里可靠地估出来,因为那个词序列可能压根没出现过。

图 3.1 马尔可夫假设示意图

后来研究者引入了马尔可夫假设(Markov Assumption):不用回头看一个词的全部历史,只需要看它前面有限的 N 个词就够了。基于这个假设建立的模型就是 N-gram 模型。N 代表上下文窗口的大小。看两个最常见的例子:

  • Bigram(N=2):假设一个词只跟它前面的一个词有关。链式法则里的复杂条件概率就简化成了容易计算的形式。
  • Trigram(N=3):类似,只看前面的两个词。

这些概率可以用**最大似然估计(MLE)**在大型语料库中算出来。名字听着吓人,想法很朴素:最可能的情况就是数据里出现次数最多的那种。比如在 Bigram 模型里,要估计在词 A 后面出现词 B 的概率,就是数数:

用 A 后面跟着 B 的次数,除以 A 出现的总次数。

我们来手动算一遍。假设语料库只有两句话:datawhale agent learnsdatawhale agent works。用 Bigram 模型估计句子 datawhale agent learns 的概率。

第一步:计算第一个词的概率
datawhale 出现了 2 次,总词数 6,所以 P(datawhale) = 2/6。

第二步:计算 P(agent | datawhale)
datawhale agent 出现了 2 次,datawhale 出现了 2 次,所以概率是 2/2 = 1。

第三步:计算 P(learns | agent)
agent learns 出现了 1 次,agent 出现了 2 次,所以概率是 1/2 = 0.5。

最后连乘
P(句子) ≈ 0.333 × 1.000 × 0.5 = 0.167。

下面是 Python 代码,跟上面的手算过程一一对应:

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}")

# 准备 bigram 计数
bigrams = zip(tokens, tokens[1:])
bigram_counts = collections.Counter(bigrams)

# 第二步:P(agent|datawhale)
count_datawhale_agent = bigram_counts[('datawhale', 'agent')]
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 简单,但有两个硬伤:

  1. 数据稀疏:某个词序列要是没在语料库里出现过,概率直接算成 0。平滑技术能缓解,但治不了本。
  2. 泛化能力差:模型不懂词和词之间的语义相似。举个例子,就算语料里有一堆 agent learns,模型也想不到 robot learns 可能成立。要是 robot 这个词或者 robot learns 这个组合没见过,概率就是零。模型完全不知道 agentrobot 意思接近。

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

N-gram 的病根在于把词当成孤立的离散符号。研究者就想:能不能用连续向量来表示词?2003 年 Bengio 等人提出了前馈神经网络语言模型,这是这个方向的开山之作。

做法分两步:

  1. 建一个语义空间:搞一个高维连续向量空间,把词汇表里每个词都映射成空间里的一个点。这个点就是词嵌入(Word Embedding)。语义相近的词,向量位置也近。比如 agentrobot 的向量会挨着,agentapple 会离很远。
  2. 学一个从上下文到下一个词的映射:用神经网络拟合一个函数,输入是前 N 个词的向量,输出是词汇表里每个词出现在当前上下文之后的概率。

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

词嵌入是在训练过程中自动学出来的。模型为了把“预测下一个词”这个任务做好,会不停地调整每个词的向量位置,最后这些向量就带上了丰富的语义信息。词变成向量之后,就能用数学工具量关系了。最常用的是余弦相似度(Cosine Similarity),算两个向量夹角的余弦值。

这个公式的判断标准很直接:

  • 方向完全一样,夹角 0°,余弦值 1,完全相关。
  • 方向正交,夹角 90°,余弦值 0,毫无关系。
  • 方向完全相反,夹角 180°,余弦值 -1,完全负相关。

词向量不光能抓“同义词”这种简单关系,还能搞出更复杂的类比关系。有个著名例子:vector('King') - vector('Man') + vector('Woman') 这个运算结果,在向量空间里离 vector('Queen') 非常近。相当于从“国王”出发,去掉“男性”的方向,加上“女性”的方向,就到了“女王”的位置。这说明词嵌入真的学到了“性别”“皇室”这种抽象概念。

代码里用简化过的二维向量演示一下:

import numpy as np

embeddings = {
    "king": np.array([0.9, 0.8]),
    "queen": np.array([0.9, 0.2]),
    "man": np.array([0.7, 0.9]),
    "woman": np.array([0.7, 0.3])
}

def cosine_similarity(vec1, vec2):
    dot_product = np.dot(vec1, vec2)
    norm_product = np.linalg.norm(vec1) * np.linalg.norm(vec2)
    return dot_product / norm_product

result_vec = embeddings["king"] - embeddings["man"] + embeddings["woman"]
sim = cosine_similarity(result_vec, embeddings["queen"])
print(f"king - man + woman 的结果向量: {result_vec}")
print(f"该结果与 'queen' 的相似度: {sim:.4f}")

输出:

king - man + woman 的结果向量: [0.9 0.2]
该结果与 'queen' 的相似度: 1.0000

神经网络语言模型用词嵌入解决了泛化问题,但它还是被固定窗口卡着:只能看前 N-1 个词,更早的信息直接扔掉。这跟人说话的方式不太一样。后面循环神经网络的出现,就是为了打破这个限制。

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

RNN 的思路很直白:给网络加“记忆”。它引入了一个**隐藏状态(hidden state)**向量,相当于短期记忆。每处理一个词,网络会读当前输入,再结合上一刻的记忆,生成新的记忆传给下一刻。信息就这样沿着序列往后传。

图 3.3 RNN 结构示意图

但标准 RNN 有个严重的毛病:长期依赖问题。训练时靠反向传播调整权重,序列多长网络就多深。序列一长,梯度在往回传的时候一路连乘,要么快速变成零(梯度消失),要么变得巨大(梯度爆炸)。梯度消失意味着模型学不到早期信息对后期输出的影响,长距离依赖就抓不住。

长短时记忆网络(LSTM) 就是冲着这个问题去的。LSTM 是种特殊 RNN,核心创新是加了细胞状态(Cell State)和一套门控机制(Gating Mechanism)。细胞状态像一条独立的信息通路,跟隐藏状态分开走,让信息在时间步之间传得更顺畅。门控机制是几个小型神经网络,学着决定让哪些信息通过,从而控制细胞状态里信息的增删。一共三道门:

  • 遗忘门(Forget Gate):决定从上一时刻的细胞状态里扔掉什么。
  • 输入门(Input Gate):决定当前输入里的哪些新信息写进细胞状态。
  • 输出门(Output Gate):决定根据当前细胞状态,输出什么到隐藏状态。

2、Transformer 架构解析

RNN 和 LSTM 用循环结构处理序列,确实能捕捉长距离依赖。但循环计算有个绕不开的瓶颈:必须按顺序处理。第 t 个时间步要等第 t-1 个时间步算完才行。这样没法大规模并行,处理长序列效率极低,模型规模和训练速度都被卡死了。

Transformer 在 2017 年由谷歌团队提出,彻底扔掉了循环结构,完全靠**注意力(Attention)**机制捕捉序列内部的依赖,实现了真正的并行计算。

1)Encoder-Decoder 整体结构

最初的 Transformer 是给机器翻译设计的端到端模型。宏观上它沿用了一个经典结构:编码器-解码器(Encoder-Decoder)

图 3.4 Transformer 整体架构图

把这个结构理解成一个分工明确的团队:

  1. 编码器(Encoder):负责“理解”整个输入句子。它读完所有输入词元,给每个词元生成一个包含上下文信息的向量表示。
  2. 解码器(Decoder):负责“生成”目标句子。它会参考自己已经生成的前文,再“咨询”编码器的理解结果,生成下一个词。

要真正理解 Transformer,最好的办法是自己动手实现。下面我们用“自顶向下”的方法:先把 Transformer 完整的代码框架搭出来,定义好所有需要的类和函数,然后像拼图一样一个个填上具体实现。

import torch
import torch.nn as nn
import math

# 占位符模块,将在后续小节中实现
class PositionalEncoding(nn.Module):
    """位置编码模块"""
    def forward(self, x):
        pass

class MultiHeadAttention(nn.Module):
    """多头注意力机制模块"""
    def forward(self, query, key, value, mask):
        pass

class PositionWiseFeedForward(nn.Module):
    """位置前馈网络模块"""
    def forward(self, x):
        pass

# 编码器核心层
class EncoderLayer(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)

    def forward(self, x, mask):
        # 残差连接与层归一化将在后面的小节中解释
        # 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

# 解码器核心层
class DecoderLayer(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)

    def forward(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

框架摆在这儿,具体模块怎么实现,接下来一节一节补上。

骨架 forward 里最后两步是前馈网络和残差连接,这两个模块后面再展开,注意力机制才是这个骨架的重头戏。

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

读 "The agent learns because it is intelligent." 这句话时,你看到加粗的 it,会下意识去前面找指代对象。agent 这个词会被你多看两眼。自注意力就是给这种现象做数学建模。

处理序列里某个词的时候,自注意力会同时看句子里所有其他词,给每个词分配一个权重。权重越大,那个词跟当前词的关系越近,它的信息在当前词的新表示里占的份量就越大。

为了做到这一点,每个词元向量要同时承担三种角色:

  • 查询(Query,Q):代表当前词,主动去跟别的词比对。
  • 键(Key,K):代表可被查询的词的标签。
  • 值(Value,V):词本身真正携带的内容。

这三个角色由原始词嵌入乘三个不同的可学习权重矩阵得到。整个计算过程像一场开卷考试。

给句子里的每个词都配好 Q、K、V 三份资料。要更新某个词的表示,就拿着它的 Q 去和所有词的 K 做点积。点积结果就是相关性得分。得分除以 (\sqrt{d_k}) 做一次缩放,防止数值太大把 softmax 推到梯度极小的区域。接着过 softmax,把分数变成加起来等于 1 的权重。最后拿这些权重去乘对应的 V,再求和。得到的向量就是这个词融合全局上下文之后的新表示。

公式写出来是这样:

[
\text{Attention}(Q,K,V)=\text{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)V
]

只做一次注意力计算,模型往往只学到一种关注模式。比如处理 it 时只盯主语,忽略了时态或从属关系。语言里的关系是叠加的,所以有了多头注意力。

多头注意力的做法不复杂。把 Q、K、V 在维度上切成 h 份,每一份独立做一次缩放点积注意力。相当于请了 h 个专家,每个专家从不同角度审视句子。最后把 h 份输出拼回去,再过一个线性层整合。

图 3.5 画的就是这个流程。多头意味着模型能同时抓住不同位置、不同子空间里的关系,表达能力一下丰富了很多。

下面这个类实现了完整的多头注意力:

class MultiHeadAttention(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)

    def scaled_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 is not None:
            # 将掩码中为 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

    def split_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(1, 2)

    def combine_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(1, 2).contiguous().view(batch_size, seq_length, self.d_model)

    def forward(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 层里,多头注意力子层后面都跟着一个逐位置前馈网络。自注意力负责从整个序列里聚合信息,前馈网络负责在这些聚合结果上做更高阶的特征提取。

逐位置的意思是,同一个前馈网络依次处理序列里的每个词元。seq_len 有多长,它就跑多少趟,但所有位置共享同一组权重。每个位置被独立加工,参数量又不会随序列长度增长。

结构很简单。两个线性层夹一个 ReLU 激活。第一层把 d_model 维扩到 d_ff,通常 d_ff 是 d_model 的四倍。第二层再映射回 d_model。这种先扩大再压缩的瓶颈结构,让模型有更大空间去组合特征。

PyTorch 实现:

class PositionWiseFeedForward(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()

    def forward(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。

Add 是残差连接,把子模块的输入 x 直接加到输出上。反向传播时梯度能绕开子模块往前传,层数做得很深也不容易梯度消失。

Norm 是层归一化。它把单个样本的所有特征拉回均值 0、方差 1 的分布。每层输入分布稳定了,训练收敛会快很多。

公式合并起来就是:

[
\text{LayerNorm}(x + \text{Sublayer}(x))
]

5)位置编码

自注意力有一个天生的盲区:它看不到词的顺序。

"agent learns" 和 "learns agent" 这两串词,对注意力机制来说完全一样。它只关心词和词之间的关系,不关心位置。可语言里词序变了意思就全变了。

Transformer 的做法是,每个位置的词嵌入后面额外加一个位置向量。这个向量用一个固定公式直接算出,不参与训练。

原论文用的是正弦和余弦函数:

[
PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right)
]

[
PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right)
]

pos 是词在序列里的位置,从 0 开始数。i 是位置向量的维度下标,从 0 到 d_model/2。d_model 是词嵌入的维度。

偶数维度填 sin,奇数维度填 cos。每个位置拿到的位置向量都不一样,词嵌入就算相同,加完位置编码之后也会被区分开。

代码:

class PositionalEncoding(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) 的大小为 (1, max_len, d_model)
        pe = torch.zeros(1, max_len, d_model)

        # 偶数维度使用 sin, 奇数维度使用 cos
        pe[:, 0, 0::2] = torch.sin(position * div_term)
        pe[:, 0, 1::2] = torch.cos(position * div_term)

        # 将 pe 注册为 buffer,这样它就不会被视为模型参数,但会随模型移动(例如 to(device))
        self.register_buffer('pe', pe)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # x.size(0) 是当前输入的序列长度
        # 将位置编码加到输入向量上
        x = x + self.pe[:x.size(0)]
        return self.dropout(x)

到这里,Transformer 的骨架和每个关键模块就完整了。我们不再继续往下实现,因为要理解的是大模型怎么从这套东西里长出来。下一节看 Decoder-Only 架构。

3、Decoder-Only 架构

前面搭起来的那套完整 Transformer 在机器翻译这类端到端任务上表现很好。但要做对话、创作、智能体大脑,其实未必需要这么复杂的结构。

Transformer 的设计是先理解再生成。编码器读完整个句子,记下全局信息,交给解码器慢慢生成。OpenAI 做 GPT 的时候换了个角度:语言的核心任务,就是预测下一个最可能出现的词。

回答问题、写故事、写代码,说到底都是在一个已有的文本序列后面不断补最合理的下一个词。顺着这个思路,GPT 直接把编码器拿掉了,只留解码器。这就是 Decoder-Only 架构。

Decoder-Only 的工作方式叫自回归,听起来专业,其实就是文字接龙。

  1. 给模型一段起始文本,比如 "Datawhale Agent is"。
  2. 模型算出下一个最可能的词,比如 "a"。
  3. 把这个词粘回输入末尾,新输入变成 "Datawhale Agent is a"。
  4. 模型继续预测下一个词,比如 "powerful"。
  5. 循环这个过程,直到生成完整句子或碰到停止条件。

模型一遍遍看着自己写下的内容,再决定下一笔落在哪。

那它靠什么保证预测第 t 个词的时候,不去偷看后面的答案?

答案在掩码自注意力。注意力分数矩阵算完之后,softmax 之前,把当前位置之后的位置全部替换成极大的负数。softmax 算到这些位置时概率归零。模型在位置 t 只能访问到位置 t 及之前的信息,后面的内容在数学上被拦住了。

就是这一步,保证了自回归的公平性。

Decoder-Only 能统治今天的大模型时代,理由有三。

训练目标统一。预测下一个词这个目标,让模型可以在海量无标注文本上直接预训练。

结构简单。组件少了,规模化扩展容易。GPT-4、Llama 这些动辄千亿万亿参数的大模型,都长在一套简洁的 Decoder-Only 骨架里。

天然适合生成。自回归过程和文本生成、对话、代码补全都是同一套模式,这也是它成为智能体基础架构的原因。

二、与大语言模型交互

1、提示工程

大语言模型像一个能力很强的脑子,提示就是跟它说话的语言。提示工程研究的是怎么把提示设计到位,让模型输出你想要的东西。做智能体时尤其重要,提示写得好,智能体之间协作分工会顺很多。

(1)模型采样参数

调大模型的时候经常碰到 Temperature 这类参数。它们改的是模型对概率分布的采样策略。模型原本的分布由 softmax 算出来,采样参数会重新调整或截断这个分布,进而改变下一个 token 是什么。

Temperature 是控制随机性和确定性的关键参数。它给 softmax 引入一个温度系数 T:

[
\text{softmax}\left(\frac{z_i}{T}\right)
]

T 变小,分布变陡,高概率词被放大,输出更保守,重复率会高一些。T 变大,分布变平,低概率词也有机会出场,输出更多样,但可能不连贯。

  • 温度低于 0.3:输出偏精准、确定。适合事实问答、数据计算、代码生成、法律条文、技术文档这类容不得乱来的场景。
  • 温度在 0.3 到 0.7 之间:输出平衡、自然。适合客服对话、邮件撰写、产品文案、日常聊天。
  • 温度高于 0.7:输出更发散。适合诗歌创作、科幻故事构思、广告语头脑风暴这些要创意的活。

Top-k 做的是截断候选集。把所有 token 按概率从高到低排序,只保留前 k 个,再对这 k 个的概率重新归一化,从里面采样。

Top-k 只管数量,不管分布长什么样。k=1 时等于每次选概率最高的 token,就是贪心采样。

Top-p 不太一样。它从概率最高的 token 开始累加,直到累积概率超过阈值 p。这些被累加进来的 token 组成核集合,再归一化采样。

Top-p 的候选集大小是动态的。分布集中时核集合小,分布散的时候核集合自动变大,对长尾分布更稳。

三个参数同时设置时是分层生效。温度先调整原始分布的陡峭程度,Top-k 再截出前 k 个候选,Top-p 接着从中选累积概率达标的最小集合。但平时 Top-k 和 Top-p 二选一就够,两个都设,最终候选集取的是两者的交集。

温度设成 0,Top-k 和 Top-p 就没意义了。模型每次只会挑概率最高的那个 Token 当输出,其他选项全被跳过。反过来,Top-k 设成 1,温度和 Top-p 也成了摆设。因为能通过筛选的 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)就是用大量“指令-回答”对去微调预训练模型,让它学会理解并执行人的要求。现在大家用的 ChatGPT、DeepSeek、Qwen,通通是各自模型家族里做过指令调优的版本。

给一个文本补全模型下翻译任务,你得先把示例格式拉满:

这是一段将英文翻译成中文的程序。
英文: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)的做法,是在提示里加一句引导语,比如“请逐步思考”,英文里常叫“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)为何需要分词

早期做法很直接:按空格和标点把句子切成单词。问题在于词表会爆炸。语言的词汇量太大了,每个词都占一个条目,词表根本收不住。更麻烦的是,没见过的词,比如 DatawhaleAgent,模型只能傻眼。

改成按字符切,词表小多了,英文字母、数字、标点加起来没多少,也不会碰上生词问题。但单个字符基本不携带语义,模型要花更多力气把字符拼装成意思,学习效率太差。

子词分词(Subword Tokenization)是现在的主流,在词表大小和语义表达之间取了平衡。常见词,像 agent,整个保留为词元。生僻词,比如 Tokenization,拆成 Token 和 ization 这种有含义的片段。词表不会膨胀得太离谱,模型也能靠组合子词理解新词。

2)字节对编码算法解析

字节对编码(Byte-Pair Encoding,BPE)是子词分词里最常用的算法,GPT 系列用的就是它。核心思路是一个贪心的合并过程:

  1. 初始化。词表先放语料中出现过的所有基本字符。
  2. 迭代合并。统计语料里相邻词元对的出现频率,把最高频的那对合并成一个新词元,加入词表。
  3. 重复。一直合并到词表大小达到预设阈值。

假设迷你语料库是 {"hug": 1, "pug": 1, "pun": 1, "bun": 1},目标词表大小是 10。BPE 的训练过程见表 3.1。

表 3.1 BPE 算法合并过程示例

BPE 合并过程

训练结束,词表到了 10,分词规则定形。这时遇到一个没见过的词,bug。分词器先查整个 bug,不在词表里;再查 bu,也没有;最后查到 b 和 ug 都在,于是切成 ['b', 'ug']

下面这段代码模拟了上面的过程:

import re, collections

def get_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

def merge_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

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)
    if not 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 之后的优化算法基本都在它上面改。Google 的 WordPiece 和 SentencePiece 是影响力最大的两个分支。

WordPiece 被 BERT 使用。它和 BPE 很像,但合并标准不是最高频,而是“让语料库的语言模型概率提升最大”。优先合并那些读起来最顺的词元对。

SentencePiece 是 Google 开源的预处理工具,Llama 系列用的就是它。它的特点是直接拿空格当普通字符,通常用下划线 _ 表示。分词和解码完全可逆,还不绑定特定语言。中文这种不用空格分隔的文本,它也能照常处理。

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

分词的细节不需要背下来,但作为智能体的开发者,有些影响绕不开。它直接关系到性能、成本和稳定性。

模型的上下文窗口(8K、128K 之类的数字)按 Token 数量算,不是按字符数或单词数。同一段话,换成中文或英文,Token 数能差出一截;不同分词器处理同一段话,结果也不一样。精确管理输入长度,别让智能体的记忆超出上下文限制,是长时记忆设计的基础。

计费也一样。市面上的模型 API 基本按 Token 计费,你的文本会被切成多少 Token,直接决定运行成本。

还有些奇怪的行为,根源就在分词。模型算 2 + 2 很轻松,换成 2+2(没有空格)就出错,因为它可能被分词器当成一个独立但不常见的词元。首字母大小写也能改变切分结果,同一个词可能因为写法不同变成两套 Token。写提示词、解析模型输出的时候,这些细节不注意,智能体就容易出幺蛾子。

原理讲完,动手跑个真实的模型。

3、调用开源大语言模型

前面我们用 API 接上了大模型,跑起来很快,也省事。但有些场景得走另一条路:数据敏感、要离线运行、或者想把成本控制得更细,模型得部署在本地。

Hugging Face Transformers 正好干这个。它提供一套标准接口,能加载数万个预训练模型,下面就用它实践一把。

选择模型要考虑多数人的电脑配置。这次用的是 Qwen/Qwen1.5-0.5B-Chat,阿里巴巴达摩院开源的对话模型,大概 5 亿参数。体积小,性能不差,入门和本地部署都合适。

装好依赖先:

pip install transformers torch

transformers 里常用两个类:AutoModelForCausalLM 负责自动加载模型权重,AutoTokenizer 负责加载匹配的分词器。第一次运行下面的代码会从 Hugging Face Hub 下载模型文件,耗时看网速。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

model_id = "Qwen/Qwen1.5-0.5B-Chat"

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)

输出是一串 Tensor,包含 input_idsattention_mask

{'input_ids': tensor([[151644, 8948, 198, 2610, 525, 264,  10950, 17847, 13,151645, 198, 151644, 872, 198, 108386, 37945, 100157, 107828,1773, 151645, 198, 151644, 77091, 198]], device='cuda:0'), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]],       device='cuda:0')}

generate() 就能生成回答了。模型输出的照样是一串 Token ID,最后用 decode() 把它翻译回文字:

generated_ids = model.generate(
    model_inputs.input_ids,
    max_new_tokens=512
)

generated_ids = [
    output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]

response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print("\n模型的回答:")
print(response)

运行结果:

我叫通义千问,是由阿里云研发的预训练语言模型,可以回答问题、创作文字,还能表达观点、撰写代码。我主要的功能是在多个领域提供帮助,包括但不限于:语言理解、文本生成、机器翻译、问答系统等。有什么我可以帮到你的吗?

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

给模型几个参考示例再提问,输出质量往往比直接抛一个问题好不少。零样本就是不给示例,靠模型自己的预训练知识硬答。单样本给一个示范,少样本再多给几个。

别小看这几个示例的差别。某些任务上,三五个精心挑选的例子带来的提升,甚至比换个大一号的模型还明显。所以调提示词的时候,别只盯着措辞,先找几个高质量示例放进去,见效更快。

当你把上一节的代码完整跑完,本地电脑上会出现一段模型自己生成的Qwen介绍。恭喜,一个开源大语言模型已经在你自己的机器上跑起来了。

4、模型的选择

上一节跑通的是个小型开源模型,下一步自然要面对那个很实际的问题:市面上的模型那么多,做智能体到底该选谁?

选模型不能只看参数大小。真正要做的是在性能、成本、速度和部署方式之间找到适合自己的平衡。下面先讲选型时要考虑的几个因素,再盘点当前主流的闭源和开源模型。

大模型领域迭代速度很快,今天的主流几个月后可能就被替代。文中的具体型号和性能数据只代表当前阶段,重点看选型思路,别当成长期答案。

1)模型选型的关键考量

性能永远是第一项。不同模型擅长的方向差别很大。逻辑推理和代码生成强的模型,创意写作不一定行。公开榜单像LMSys Chatbot Arena可以帮你快速了解综合水平,但跑分和真实场景之间还是有差距。

成本要区分两类。闭源模型按token计费,单价低但量大。开源模型没有接口费,但GPU、内存和运维都是实打实的开销。

延迟直接关系到体验。客服、游戏NPC这些实时场景,慢一拍的感受很明显。轻量级模型和针对性优化过的模型,速度表现更好。

上下文窗口决定模型一次能处理多少内容。长文档分析、代码库阅读、多轮对话记忆,这些任务没有128K以上的窗口很难撑起来。

部署方式影响数据隐私。用接口最省事,但数据要过第三方服务器。本地部署折腾,换来的是自主性和对数据的掌控。

生态和工具链是隐形效率项。热门模型的教程、社区方案、LangChain这类框架支持都更完善,遇到问题能找到答案。冷门模型资源稀少,踩坑全靠自己。

微调能力在业务数据特殊的场景里是硬指标。能不能用自有数据做定制化训练,直接决定模型在你手上的实用性。开源模型通常更灵活。

安全性和伦理问题越来越实际。偏见、有害输出、幻觉,对面向公众的应用来说都是选型阶段该纳入评估的风险。

这些维度经常互相冲突。性能和成本,隐私和便利,很少能同时拉满。最终哪个优先,取决于智能体具体的应用场景。

2)闭源模型概览

闭源模型通常站在技术最前沿,也提供稳定的接口服务,想快速上手的话是最省事的选择。

OpenAI GPT系列:GPT-3把大模型带进了公共视野,ChatGPT通过RLHF让模型输出更贴近人的预期,GPT-4打开多模态,GPT-5开始把文本、音频、图像的输入输出混在一起做。实时语音对话的响应速度和自然度,这代提升很明显。

Google Gemini系列:Gemini从出生就是原生多模态,文本、代码、图像、视频统一处理,上下文窗口也一直压着别人一头。Ultra、Pro、Nano分别对应最复杂任务、常规任务和边缘设备。2.5系列里Pro的推理继续增强,Flash把速度和成本控制得很好。

Anthropic Claude系列:安全是这个品牌最深的标签。Claude在长文档、减少有害输出、遵循指令这几块尤其稳,企业客户认可度高。Claude 3的Opus、Sonnet、Haiku分别对应性能天花板、均衡款和快跑款。Claude 4 Opus在复杂推理和代码生成上又往前走了一步。

国内主流闭源模型:文心一言、腾讯混元、华为盘古、讯飞星火、月之暗面,这些模型的中文能力是这个阵营里最突出的优势。

3)开源模型概览

开源模型的价值在透明和可控:本地部署、自由微调、完整掌控,这些是闭源接口给不了的权利。代价是技术门槛和运维成本得自己承担。

Meta Llama系列:Llama基本是开源模型里绕不开的地基。Llama 4是Meta第一次换用MoE架构,这种混合专家设计每次只激活任务相关的部分,计算效率高不少。Scout支持一千万token上下文,长文档和移动端是它的目标场景。Maverick主打多模态,编码、复杂推理和多语言都不弱。Behemoth是旗舰型号,STEM基准测试超过了多个竞品。

Mistral AI系列:法国团队,擅长用更小的模型达到接近大模型的水平。Mistral Medium 3.1在代码、STEM推理、跨领域问答上提升明显,基准跑赢过Claude Sonnet 3.7和Llama 4 Maverick。它也支持图文混合输入,还内置了语调适配层,公司拿来做品牌化输出很方便。

国内开源力量:阿里的通义千问和清华智谱的ChatGLM是领头羊,中文能力强,社区也很活跃。

闭源和开源的边界在实际项目里没那么非此即彼。用API起步,后期把敏感业务迁到本地开源模型,这种混合节奏很常见。选型的关键还是想清楚自己的约束条件。

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

大模型这几年的进步,背后有一条可以被量化的规律在支撑,就是缩放法则(Scaling Laws)。它解释了为什么模型越大越聪明,同时也提醒我们数据和算力要跟着一起涨。理解它的另一面,就是看清模型有哪些根治不掉的毛病。

1、缩放法则

缩放法则的核心发现是:模型性能和参数量、训练数据量、计算量之间存在稳定的幂律关系。在对数坐标下,模型loss随这三个因素的增加而稳定下降,画出来几乎是一条直线。意味着只要按比例增加三者的规模,性能会平滑提升,不会遇到明显的瓶颈。

早期研究更强调加参数量。DeepMind 2022年的Chinchilla研究把方向修正了一下:给定计算预算,参数量和训练数据之间有一个最优配比。模型不必搞得太大,但数据要喂得足够多。Chinchilla是700亿参数,用了四倍于GPT-3的数据量,最后性能反超。那个“参数越大越好”的说法,基本到此为止。后来的Llama系列也明显延续了这套思路。

缩放法则最吸引人的副产品是能力涌现。模型规模跨过某个阈值后,一些此前完全不行的能力会突然冒出来。链式思考、指令遵循、多步推理、代码生成,基本都是在百亿千亿参数这个量级附近才出现的。

对智能体开发者的启示很直接:复杂决策和规划是吃模型规模的任务。模型太小,这些能力真的不具备,提示词再精细也换不出来。

2、模型幻觉

模型幻觉说的是模型一本正经地编造信息。生成的内容可能和事实矛盾,可能和输入冲突,也可能只是无中生有。常见的有三类:事实性幻觉,生成内容与真实世界对不上;忠实性幻觉,摘要和翻译时偏离了原文;内在幻觉,生成内容直接否定输入信息。

幻觉出现的原因有几个。训练数据本身就有错误,模型无从分辨。自回归生成的每个token都是概率预测,模型内部没有事实核查这一步。推理链条一断,后续就会顺着错误继续补全。旅游规划智能体推荐一个不存在的景点,或者订一张航班号不存在的机票,就是这么来的。

除了幻觉,知识时效和偏见也挡不住。训练数据截止之后的事,模型就是不知道。数据里的偏见和刻板印象,模型学得很快,输出时还会放大。

缓解幻觉的手段已经有三条成熟路线。

数据上用高质量清洗、事实性知识注入、RLHF来减少源头错误。模型层探索新架构和不确定性表达,让模型在没把握的时候认怂,别硬编。推理侧目前最有效的是检索增强生成(RAG):生成之前先从外部知识库检索材料,把检索结果放回上下文中再作答。多步推理加自检、调用外部工具做实时查询或精确计算,都能把幻觉压到一个更低的频率。

幻觉没法彻底根除,但RAG和工具调用这一套组合拳,已经在实际产品里证明了效果。对大多数应用场景来说,可控即可用。

四、本章小结

这一章从Transformer讲到大模型生态,再到幻觉治理,信息量不小。不过这些内容不是孤立的知识点,它们都是后续构建智能体时的地基。

模型演进与核心架构:从N-gram到RNN、LSTM,再到Transformer,架构更新主要解决过去模型的长距离依赖和并行效率问题。自注意力机制是Transformer的灵魂,也是当前几乎所有大模型的共同基础。

与模型的交互方式:提示工程是引导模型行为的主要手段,零样本、单样本、少样本是基础。分词决定了模型眼里的输入长什么样。上一节本地运行开源模型的练习,则是把这些概念真正落到了一次实操里。

模型生态与选型:闭源模型开箱即用,前沿能力跟上;开源模型透明可定制,代价是要自己维护。选型就是在各种约束条件里做权衡,排行榜第一未必是你的最佳答案。

法则与局限:缩放法则说明性能提升有规律,参数和数据要按比例增长,能力涌现则提示了规模门槛。幻觉、知识过时、偏见是共性问题,RAG、外部工具和推理验证是目前主要的应对手段。

这些基础也直接连着智能体设计。怎么用提示词引导规划决策,怎么选底座模型,怎么在工作流里加防幻觉的验证机制,答案都在这里。

理论部分到这儿收尾。下一章开始进入智能体经典范式的构建,真正动起手来。

顶尖师资,深耕AI大模型前沿技术

课是谁讲的,这点比课程大纲重要得多。这门课请的是真正在一线做项目的实战派,不是念PPT的理论党。他们踩过的坑,总结成经验直接给你,省掉你自己撞墙的时间。

一对一学习规划,职业生涯指导

报名之后会有老师单独找你聊,根据你现在的技术底子、想去的方向,帮你规划学习路线。不是扔给你一套录播课就完事,是有人管你的。

真实商业项目实训

练手用的项目不是玩具,是真实商业项目。学习标准就是商业交付标准,你做的东西要能上线跑。做完的项目可以写进简历,背调也支持。这一条对转行的人特别友好,简历上有真东西可写,面试官问起来也不虚。

大厂绿色直通车,冲击行业高薪岗位

表现好的学员,有直接进大厂面试的通道。身边不少朋友就是靠这个跳槽成功的,薪资涨幅比自己海投简历强多了。


文中涉及到的完整版大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

常见问题(FAQ)

N-gram模型有哪些缺点?

N-gram模型有两个硬伤:一是数据稀疏,未出现过的词序列概率会被算作0;二是泛化能力差,无法利用词语间的语义相似性,例如不知道'agent'和'robot'意思接近。

词嵌入在语言模型中有什么作用?

词嵌入将词语映射为高维连续向量,使语义相近的词在向量空间中距离更近,能通过余弦相似度量化关系,甚至支持向量运算,如'国王-男人+女人≈女王',从而让模型理解词语的语义联系。

语言模型的核心任务是什么?

语言模型的核心任务是计算一个词序列出现的概率,以此判断句子是否通顺。它是自然语言处理的基础,为文本生成、机器翻译等任务提供依据。

晓婷深圳
本文由 晓婷 审核,最后更新于 2026年8月29日
联系编辑 →
← 返回文章列表
分享到:微博

版权与免责声明:本文仅用于信息分享与交流,不构成任何形式的法律、投资、医疗或其他专业建议,也不构成对任何结果的承诺或保证。

文中提及的商标、品牌、Logo、产品名称及相关图片/素材,其权利归各自合法权利人所有。本站内容可能基于公开资料整理,亦可能使用 AI 辅助生成或润色;我们尽力确保准确与合规,但不保证完整性、时效性与适用性,请读者自行甄别并以官方信息为准。

若本文内容或素材涉嫌侵权、隐私不当或存在错误,请相关权利人/当事人联系本站,我们将及时核实并采取删除、修正或下架等处理措施。也请勿在评论或联系信息中提交身份证号、手机号、住址等个人敏感信息。