第2章 语言模型基础

Transformer模型是深度学习,尤其是自然语言处理(NLP)领域的一次重大突破。

从概念上看,Transformer模型可以被视为一个"黑盒子",以机器翻译任务为例,它能够接收某种语言的输入文本,并输出对应语言的翻译版本。

从内部结构来看,Transformer由编码器(encoder)和解码器

(decoder)两大部分构成。

Transformer的核心结构由词嵌入、编码器、解码器、输出生成四个部分组成

词嵌入

Transformer中输入单词的词嵌入包含单词编码和位置编码。单词编码用于编码单词的语义,位置编码用于编码单词的位置

单词编码

单词编码(word embedding)是一种以数字方式表示句子中单词的方法,该方法用来表示单词语义特征。

(1)神经网络编码

基于神经网络语言模型

它用于解决统计语言模型(如N-Gram模型)中常见的维度灾难问题。该方法是训练一个神经网络,在训练中,每个参与训练的句子告诉模型有哪些单词在语义上相近,最终模型为每个单词生成一种分布式表示,这种表示能够捕捉并保留单词的语义和句法关系。

神经网络主要由三个部分组成:一个词嵌入层,用于生成词嵌入表示,且单词之间参数共享;一个或多个隐藏层,用于生成词嵌入的非线性关系;一个激活层,用于生成整个词汇表中每个单词的概率分布。

该网络使用损失函数在反向传播过程中更新参数,并尝试找到单词之间相对较好的依赖关系,同时保留语义和句法属性。

1)单词索引。对单词建立索引,句子中的每个单词都会被分配一个数字。

word_list = " ".join(raw_sentence) .split()

word_list = list(set(word_list))

word2id = {w: i for i, w in enumerate(word_list)}

id2word = {i: w for i, w in enumerate(word_list)}

n_class = len(word2id)

2)构建模型。

import torchimport torch.nn as nn

#假设已定义的超参数(需与你之前的词汇表逻辑对应)

n_class = 4 # 词汇表大小(对应你之前的n_class)

m = 10 # 词嵌入维度(每个单词映射为10维向量)

n_step = 2 # 上下文窗口大小(用前2个单词预测下一个)

n_hidden = 20 # 隐藏层神经元数量

class NNLM(nn.Module):

def init (self): # 修正:__init__双下划线,补全方法定义

super(NNLM, self).init() # 修正:super调用完整,初始化父类

复制代码
    # 1. 词嵌入层:将单词索引映射为低维稠密向量
    self.embeddings = nn.Embedding(n_class, m)  # (词汇表大小, 嵌入维度)
    
    # 2. 第一个全连接层:对展平后的嵌入向量做线性变换
    self.hidden1 = nn.Linear(n_step * m, n_hidden, bias=False)  # 输入维度=窗口数×嵌入维
    
    # 3. 隐藏层的偏置项(用Parameter注册为可训练参数)
    self.ones = nn.Parameter(torch.ones(n_hidden))  # 修正:原代码可能笔误,实际是隐藏层偏置
    
    # 4. 第二个全连接层:将隐藏层输出映射到词汇表维度
    self.hidden2 = nn.Linear(n_hidden, n_class, bias=False)
    
    # 5. 直接连接层:输入嵌入向量跳过隐藏层,直接映射到词汇表(NNLM的经典结构)
    self.hidden3 = nn.Linear(n_step * m, n_class, bias=False)
    
    # 6. 输出层的偏置项(可训练)
    self.bias = nn.Parameter(torch.ones(n_class))

def forward(self, X): # 前向传播:输入→计算→输出

X: 输入张量,形状为 (batch_size, n_step) → 每个样本是"前n_step个单词的索引序列"

X_emb = self.embeddings(X) # 1. 词嵌入:(batch_size, n_step) → (batch_size, n_step, m)

复制代码
    # 2. 展平:将(n_step, m)的二维向量转为一维(便于全连接层处理)
    X_flat = X_emb.view(-1, n_step * m)  # (batch_size, n_step*m) → -1表示自动计算batch_size
    
    # 3. 隐藏层计算:线性变换 + tanh激活(加上偏置self.ones)
    tanh = torch.tanh(self.hidden1(X_flat) + self.ones)  # (batch_size, n_hidden)
    
    # 4. 输出层计算:两路信号叠加 + 输出偏置
    # 路1:隐藏层→输出层(self.hidden2);路2:输入直接→输出层(self.hidden3)
    output = self.hidden3(X_flat) + self.hidden2(tanh) + self.bias  # (batch_size, n_class)
    
    return output  # 输出形状:(batch_size, 词汇表大小) → 每个位置是对应单词的得分(未归一化)

NNLM 的核心思路:通过词嵌入将单词映射到低维空间,再用神经网络学习上下文到下一个单词的映射。

在该过程中,首先初始化词嵌入层。词嵌入层相当于一个查找表,被索引表示的单词通过词嵌入层,然后再通过第一个隐藏层并与偏置量求和,求和结果传递给tanh函数。最后计算输出

3)损失函数。这里使用交叉熵损失函数,并将模型输出传递给

softmax函数获得单词的概率分布。

Criterions = nn.CrossEntropyLoss()

4)进行训练。经过训练最终得到单词的编码结果。

词向量编码

词向量模型包含两种模式:词袋(Bag-ofWords)模型和跳跃(Skip-gram)模型。

比神经网络语言模型的复杂性更小。词向量模型可以在更大的

数据集中训练,但缺点是如果数据较少,就无法像神经网络语言模型那样精确地表征数据。

词袋模型又称为CBOW模型,它基于目标词前后的n个单词来预测目标单词。假设句子为:"她正在踢毽子",我们以"踢"作为目标词,取n=2,那么[正,在,毽,子]等前后单词及目标单词踢将

被一起输入给模型。CBOW模型通过计算log2V来降低计算词表中单词分布概率的复杂性,V代表词汇表大小。该模型速度更快,效率更高。

训练过程:

1)定义一个窗口函数,该函数提取目标单词的左、右各n个单词。

上述CBOW函数包含两个输入参数:数据和窗口大小。窗口大小定义了应该从单词左侧和右侧提取多少个单词。for循环首先定义了句子中迭代的开始索引和结束索引,即从句子中的第3个单词开始到倒数第3个单词结束。在循环内部将窗口提取到的单词和目标单词存储在列表中。

例如,给定句子为:"她正在踢毽子",CBOW模型的窗口大小为

2,此时窗口提取的单词为"正""在""毽"和"子",目标单词是"踢",当i=2(窗口大小)时,代码如下。

2)构造模型。CBOW模型只包含一个词嵌入层、一个经过ReLU层的隐藏层和一个输出层。代码如下:

class CBOW_Model(torch.nn.Module):

def init_(self, vocab_size, embedding_dim):

super (CBOW_Model, self) ._ init_()

self.embeddings = nn.Embedding(vocab_size, embedding_dim)

self.linear1 = nn.Linear(embedding_dim, 128)

self.activation_function1 = nn.ReLU()

self.linear2 = nn.Linear(128, vocab_size)

def forward(self, inputs):

embeds = sum(self.embeddings(inputs)).view(1,-1)

out = self.linear1(embeds)

out = self.activation_function1(out)

out = self.linear2(out)

return out

该模型非常简单,单词索引输入词嵌入层,之后经过隐藏层,隐藏层输出经过一个非线性层ReLU之后经过输出层得到最终结果。

3)损失函数。与神经网络语言模型一样,我们采用交叉熵损失函数。优化器选择随机梯度下降

criterion = nn.CrossEntropyLoss ()

optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

4)训练模型。训练代码与神经网络语言模型的代码一致

for epoch in range(50):

total_loss = 0

for context, target in data:

context_vector = make_context_vector(context, word_to_ix)

output = model(context_vector)

target = torch.tensor([word_to_ix[target] ])

total_loss += loss_function(output, target)

optimizer.zero_grad()

total_loss.backward()

optimizer.step()

最终将语句单词转化为数字序列。

对比词向量模型,跳跃模型是基于目标单词来预测目标单词的上下邻近单词。假设目标单词n=2,同样以句子"她正在踢毽子"为例,单词"踢"将被输入模型用于预测目标单词("正","在","毽","子")。

跳跃模型与词袋模型类似,不同点在于创建上下文和目标单词。其训练过程如下:

1)设置目标单词和上下文变量。由于跳跃模型只需要一个上下文单词和n个目标单词变量,因此只需要反转CBOW模型的代码,

def skipgram(sentences, window_size=1):

skip_grams = [ ]

for i in range(window_size, len(word_sequence) - window_size):

target = word_sequence[i]

context = [word_sequence[i - window_size], word sequence[i + window

size]]

for w in context:

skip_grams.append([target, w])

return skip_grams

2)构建模型

class skipgramModel(nn.Module):

def_init_ (self):

super (skipgramModel, self) ._ init_()

self.embedding = nn.Embedding(voc_size, embedding_size)

self.W = nn.Linear(embedding_size, embedding_size, bias=False)

self.WT = nn.Linear(embedding_size, voc_size, bias=False)

def forward(self, X):

embeddings = self.embedding(X)

hidden_layer = nn.functional.relu(self.W(embeddings))

output_layer = self.WT(hidden_layer)

return output_layer

3)损失函数和优化器。

criterion = nn.CrossEntropyLoss ()

optimizer = optim.Adam(model.parameters(), lr=0.001)

4)训练模型。

for epoch in range(5000):

input_batch, target_batch = random_batch()

input_batch = torch.Tensor(input_batch)

target_batch = torch.LongTensor(target_batch)

optimizer.zero_grad()

output = model (input_batch)

loss = criterion(output, target_batch)

if (epoch + 1) % 1000 == 0:

print ('Epoch:', '%04d' % (epoch + 1), 'cost =', '{ : . 6f} '.format(loss))

loss.backward()

optimizer.step()

相比词袋模型,跳跃模型增加了计算的复杂性,因为它必须根据一定数量的相邻单词来预测邻近单词。在实际的语句中,距离较远的单词相关性往往比较差。

全局词向量表示

(Global Vectors for Word Representation, GloVe)是一种

基于全局信息来获得词向量的方法。该方法使用了语料库的全局统计特征,也使用了局部的上下文特征,GloVe通过引入共现矩阵(Cooccurrence Probabilities Matrix)来表征。

单词词嵌入编码方法的特点如下:

❍ 神经网络语言模型的性能优于早期的统计模型,例如n-gram模型。

❍ 神经网络语言模型解决了维度灾难问题,并通过其分布式表示保留了上下文语义和句法属性,但计算成本很高。

❍ 词向量模型降低了计算复杂性,比神经网络语言模型效率更高,它可以在大量数据上进行训练,用高维向量表示。

❍ 词向量模型有两种:词袋模型和跳跃模型。前者比后者运算更快。

❍ GloVe纳入了全局信息但无法解决一词多义和陌生词问题。

位置编码

位置编码(Position Embedding)用来表示句子中单词的位置,且每个位置被赋予唯一的表示。位置编码要满足以下特点:编码值可以表示单词在句子中的绝对位置,且是有界的;句子长度不一致时单词间的相对位置距离也要保持一致;可以表示从未见过的句子长度。

Transformer词嵌入过程如上图

编码器

编码器由多头注意力、加法和归一化、前馈层组成

自注意力是机器学习使用的一种学习机制,它属于仿生学的一种应

用,即人类会把注意力放在重点关注的信息上。在自然语言处理任务中,自注意力用于捕获输入序列内的依赖性和关系,它让模型通过关注自身来识别和权衡输入序列不同部分的重要性。在编码器中,自注意力的输入参数有三个:查询(query)、键(key)、值(value)

编码器结构


这三个参数在结构上很相似,都是被参数化的向量。每个单词的词嵌入向量都由这三个矩阵向量来表征,帮助计算机理解和处理句子中单词之间的关系。这三个矩阵向量的作用各不相同。

1)查询:该矩阵表示正在评估其上下文的目标单词。系统通过使用查询矩阵转换该目标单词的表示形式,生成一个查询向量。此查询向量用于衡量其与句子中其他单词的关联度。

2)键:该矩阵用于生成句子中每个单词对应的键向量。通过对每个键向量和目标单词的查询向量进行比较,计算得出目标单词与句子中其他词之间的相关性。查询向量和关键向量之间的相似度分数越高,表示相应单词之间的关系越紧密。

3)值:该矩阵用于生成句子中所有单词的值向量。这些向量保存每个单词的上下文信息。使用查询向量和键向量计算相似度分数后,系统计算值向量的加权和。每个值向量的权重由相似度分数确定,确保最终的上下文表示更多地受到相关单词的影响。

Transformer中的自注意力使用的是多头注意力,即通过组合多个类似的注意力计算给予了Transformer更大的辨别能力。

缩放点积注意力的结构,这是多头注意力的核心组成部分。

加法和归一化

加法过程是一种残差机制,主要为了解决深层神经网络训练过程的不稳定性,即深度神经网络随着层数的增加,损失逐渐减小然后趋于稳定,继续增加层数损失反而增大的现象。简单来说就是用来防止梯度消失。

归一化用来归一化参数,加快训练速度,提高训练的稳定性。

前馈层

前馈层作为注意力层后面的子层,由两个线性层或致密层构成。第一层的大小为(d_model,d_ffn),第二层的大小为(d_ffn,d_model)。

解码器

解码器也由多头注意力、加法和归一化、前馈层组成,每个解码器包含两层多头注意力,这两个多头注意力与编码器中的多头注意力作用不同。

解码头

解码器将输出传递给解码头,解码头将接收到的解码器输出向量映射为单词分数。词库中的每个单词在句子中的每个位置都会有一个分数。假设我们的最终输出的句子有4个词,词库中有1000个词,那么在这4个词的每个位置都会生成1000个分数,这些分数代表词库中每个词出现在句子中每个位置的可能性。之后这些分数将被送入softmax层,softmax将这些分数转换为概率(加起来为1.0)。在每个位置上,我们找到概率最高的单词的索引,然后将该索引映射到词汇表中相应的单词。最后,这些单词形成Transformer的输出序列。

自动编码器

自动编码器(Autoencoder)是一类神经网络,它通过无监督学习重建其输入数据,同时在这个过程中实现数据的有效压缩。

自动编码器的结构主要包括两个部分:编码器网络和解码器网络。编码器网络负责将高维的输入数据转换为更低维的表示,而解码器网络则从这些低维表示中恢复出原始数据。这种结构不仅能够捕捉数据中的关键特征,而且有助于在解码过程中有效地重建数据。

自动编码器的核心是学习输入数据的有效表示。这一过程涉及编码器将数据编码为低维表示,解码器再基于这些表示重建原始数据。网络通过学习一个接近恒等的映射来实现这一点,其参数优化过程可以通过计算输入与重建输出之间的均方差来量化。

ELMo(来自语言模型的嵌入)采用动态方法生成词嵌入,它基于训练好的语言模型,并能根据上下文实时调整词嵌入,有效地缓解了词义歧义的问题。 该模型的原理是首先训练好一个静态的语言模型来表征词向量,该模型存在上述提到的缺点,即无法理解歧义,当执行实际任务时,可实时根据上下文单词的语义去调整之前表征好的词向量,这样经过调整后的词向量更能表达具体语境中的含义,从而解决多义词问题。该方法是一种结合上下文语境的动态调整思路。ELMo模型是采用双向LSTM构建,在一个大型文本语料库中训练而成的。

左侧的双向LSTM代表正向编码器,负责从前往后提取信息,输入为句子序列中从左到右(除目标词外)的上文,右侧的双向LSTM代表逆向编码器,负责从后往前提取信息,输入为句子序列中从右到左的逆序下文。

ELMo的预训练过程完成后,单词经过ELMo模型可以得到三层编码信息,Ei代表最底层的单词特征信息编码,从下往上第一层LSTM包含了句法特征的单词位置编码信息,第二层LSTM包含了语义特征的单词位置编码信息。

在预训练好的网络结构完成后,我们如何将其应用于特定的下游任

务,比如问答(QA)问题?假设有一个问句X,首先,我们将这个句子X输入已经预训练好的ELMo网络中。通过这个过程,句子X中的每个单词在ELMo网络中都会生成对应的三层编码信息。接下来,我们分别为这三层编码信息分配一个权重a,这些权重是可以通过学习得到的。然后,我们根据各自的权重进行加权求和,将这三层编码融合成一个词向量,这样句子中的每个单词都有一个对应的词向量。在执行问答任务时,问句X中的每个词对应的词向量作为任务的输入,这样可以利用预训练ELM。网络中的语境信息,从而增强下游任务的性能。

BERT

旨在通过联合左侧和右侧的上下文,从未标记文本中预训练出一个深度双向表示模型。BERT的结构中只包含Transformer的编码部分,与按顺序(从左到右或从右到左)读取文本输入的方向模型

不同,BERT的编码器会读取整个单词序列,这被认为是双向读取,准确来说是非定向的。BERT的这种特性允许模型根据单词的周围环境(包含左侧和右侧)来学习单词的上下文,它的训练结果表明双向训练的语言模型比单向的语言模型能够更深入地了解语言上下文和流程。

BERT的预训练方式包含两种:MLM(Masked Language Model掩码语言模型)和NSP(Next Sentence Prediction,下句预测)。

MLM

MLM任务的目标是使模型能够根据词的上下文推断被掩盖的词。训练时,输入序列中约15%的词会被选中,并作为标签去预测,这15%中:

❍ 80%的词被替换为[mask],这主要是为了让模型在没看到原始词的条件下通过上下文来正确预测该词。

❍ 10%的词用随机词来替换,这迫使模型在每个词上学习全局语境特征准确预测该位置的原始词,从而使BERT获得更好的语境词向量,有助于解决一词多义问题。

❍ 10%的词保持不变,即BERT模型有1.5%比例的词是已知的,这是为了防止模型总是预测被掩盖的词。

基于以上的处理方式,BERT模型会尝试根据序列中掩盖单词周围的上下文来预测掩盖单词的原始值。

NSP

在进行NSP训练时,模型接受一对句子作为输入,其目标是判断这两个

句子是否在原文中是连续的。为了帮助模型在训练中区分两个句子,

输入句子对按以下方式进行处理:

❍ 50%的句子对(句子A和句子B)是相邻的,即在原始语料中,句子B是跟在句子A后面的。

❍ 剩余50%的句子对,句子B不是句子A的后续句子,而是从语料库中随机选取的一个不相关的句子。

❍ 对句子对添加特殊的标记,即句子A开头插入[CLS]标记,句子B末尾插入[SEP]标记,句子A和句子B中间插入[SEP]标记。

❍ [CLS]标记用来区分句子A和句子B是否连续。

经过以上处理后,输入句子对用于训练一个理解句子关系的BERT模型。

微调

除了输出层,BERT的预训练和微调都使用了相同的架构

且使用相同的预训练参数来初始化不同下游任务的模型。在微调

过程中,所有的参数都会被微调。[CLS]是在每个输入示例前面添加的特殊符号,[SEP]是特殊的分隔符标记,如分割问题或答案。

BERT可以通过微调被灵活应用在多种不同的语言处理任务上,且只需要在模型结构上做很小的改动:

❍ 对于文本分类任务(如情感分析),BERT的微调非常直接。可以在[CLS]标记的输出向量上(该向量旨在捕捉输入序列的整体意义)添加一个简单的分类层。这个分类层将针对特定的分类任务进行训练,比如区分文本是正面情感还是负面情感。

❍ 对于问答任务(如SQuAD v1.1),BERT需预测文本序列中答案的位置。这可以通过在预训练模型的基础上,在答案的开始和结束位置各加入一个预测层来实现。这些层会学习并识别出每个单词是否为答案的开始或结束,且在训练过程中优化这些预测。

❍ 对于命名实体识别(NER)任务,BERT模型需要识别出文本中的命名实体,如人名、组织名、地点或时间等。为了完成这个任务,BERT对每个词的向量表示都会输出给一个特别设计的分类层,该层能够预测每个词所对应的实体类别(或无实体类别)。

总之,通过在BERT的输出层上添加定制任务的特定层,并在有监督的任务数据上进行微调,BERT能够灵活地适应一系列不同的NLP任务且表现出色。这种方法的优势在于,微调过程利用了BERT深层次预训练模型中的丰富语言知识,提高了特定模型的性能。

基于BERT还有很多衍生模型,如WWM、NEZHA、MacBERT和ERNIE等,它们分别有不同的特点。

1)WWM。WWM是谷歌在2019年发布的BERT升级版本,该方法引入了全词掩码。简单来说,原有基于WordPiece的分词方式会把一个完整的词切分为若干个子词,在生成预训练样本时,这些子词会被随机掩盖,在WWM中,如果一个词的部分WordPiece字词被掩盖,则该词会被完整掩盖掉。该方法能更好地处理语义信息,尤其是对那些由多个词符组成的词。

2)NEZHA。NEZHA是一个专门为中文优化的BERT变种,它由华为诺亚方舟实验室开发,对BERT模型的预训练和微调过程进行了改进,即增加相对位置编码函数、全词掩码、混合精度训练和改进优化器,使之更适合中文的语言特点,提升了中文文本的处理效果。

3)MacBERT。MacBERT来自哈工大SCIR实验室,该论文提出在预训练阶段用相似的单词来掩盖,缩小了预训练和微调阶段的差异。

4)ERNIE。ERNIE由百度开发,整合了丰富的知识图谱信息。ERNIE在预训练阶段使用了实体级的掩盖策略,能够理解实体之间的关系。通过对训练数据中的词法结构、语法结构、语义信息进行统一建模,极大地增强了通用语义表示能力,ERNIE能更好地理解和处理复杂语言结构和知识信息。

自回归模型(AutoRegressive Model, AR模型)是基于过去数据来预测当前数据的线性模型。

在机器学习领域,有判别式模型(Discriminative Model)和生成式模型(Generative Model)两种

生成式模型更适合大规模数据学习,判别式模型更适合小样本

标注数据。GPT中使用了生成式的预训练和判别式的微调。以GPT-1为例,GPT-1语言模型是使用BooksCorpus数据集进行训练的。

BooksCorpus包含约7000本未出版的书籍,这些书籍有助于在未见过的数据上训练语言模型。该语料库还包含长段的连续文本,这有助于模型处理远程依赖关系。

本质上,监督微调是通过在Transformer模型中添加线性层和Softmax 层来获取下游任务的任务标签来实现的。

特定任务的输入转换。对于一些文本分类任务,可以直接按以上所

述对预训练模型进行微调。但对于一些其他任务,如文本蕴涵或问

答,它们特点是结构化的输入,如有序句子对或文档、问题和答案的三元组等。由于预训练模型是在连续的文本序列上训练的,因此对于

此类任务需要对输入做一些修改以适应预训练模型。整体的逻辑是需要将结构化输入转换为一个有序的序列,以使预训练模型可以进行处理。对于不同的任务,处理方法也不同

GPT的模型设计遵循了原始Transformer的工作原理,不同之处在于GPT只保留了解码层,从而使得模型更为简单。整体上,GPT模型由12个解码器堆叠成,每个解码器包含掩码多头注意力层、正则层、前馈层等。

相关推荐
熬夜敲代码的小N2 小时前
自然语言处理与Transformer模型
人工智能·自然语言处理·transformer
KlipC2 小时前
字节跳动加码AI布局
人工智能·klipc
十铭忘2 小时前
SAM2跟踪的理解14——mask decoder
人工智能·计算机视觉
HyperAI超神经2 小时前
入选NeurIPS 2025,智源/北大/北邮提出多流控制视频生成框架,基于音频解混实现精确音画同步
人工智能·ai·音视频·视频生成·neurlps 2025
草莓熊Lotso2 小时前
C++ 异常完全指南:从语法到实战,优雅处理程序错误
android·java·开发语言·c++·人工智能·经验分享·后端
yi个名字2 小时前
智能编码新时代:Vibe Coding与MCP驱动的工作流IDE革命
ide·人工智能
IT_陈寒2 小时前
Python性能优化实战:7个让代码提速300%的冷门技巧(附基准测试)
前端·人工智能·后端
熊猫钓鱼>_>2 小时前
多智能体协作:构建下一代高智能应用的技术范式
人工智能·ai·去中心化·wpf·agent·多智能体·multiagent
likeshop 好像科技2 小时前
AI知识库架构深度解析:智能体记忆与学习的智慧核心
人工智能·学习·架构