Python用LLM词嵌入与Transformer自注意力机制在文本语义理解中的应用研究|附数据代码

0 阅读18分钟

全文链接:tecdat.cn/?p=45762

关于分析师

在此对 YouMing Zhang 对本文所作的贡献表示诚挚感谢,他在东北大学完成了信息与计算科学专业的学士学位,专注机器学习与深度学习算法领域。擅长Python、Matlab,热衷算法推导与数学建模,长期关注深度学习前沿动态。曾参与多个企业级文本分析与预测模型的构建,在将理论模型转化为实际可运行的工程代码方面拥有丰富的实战经验。


摘要: 大语言模型的迅猛发展深刻改变了人机交互范式。本文聚焦于支撑其核心能力的底层技术:词嵌入与自注意力机制。研究从传统稀疏表示法的局限性出发,系统对比了Word2Vec、GloVe等静态词嵌入,并深入解析了BERT的动态上下文编码原理。同时,结合Transformer架构,完整复现了自注意力与多头注意力机制的运算流程,并应用于语义相似度计算任务。所有分析均基于Python及相关深度学习框架实现,旨在为文本表征学习提供一套可复现、可扩展的技术方案。

关键词: 词嵌入;Transformer;自注意力;BERT;文本表征


引言

在当今数字化浪潮中,如何让机器精准理解人类语言的复杂语义,是自然语言处理领域的核心挑战。作为一名长期深耕机器学习与数据挖掘的技术人员,我在众多校企合作与咨询实践中观察到,无论是构建智能检索系统还是风险舆情监控,其成败往往取决于底层词向量对语义的捕获能力。传统的高维稀疏表示法在计算效率和语义关联上存在天然短板,而深度神经网络的介入则为这一难题提供了全新的解决思路。 

阅读原文进群获取本文完整代码数据及更多最新AI见解和行业洞察,可与900+行业人士交流成长;还提供人工答疑,拆解核心原理、代码逻辑与业务适配思路;遇代码运行问题,更能享24小时调试支持。

+-------------------------------+

+-------------------------------+

一、选题背景与理论基础

1.1 背景与研究意义

大语言模型是构建在海量参数上的深度神经网络系统,它通过学习文本中的语法规则与上下文语境,衍生出问答、写作及翻译等高阶能力。借由庞大的数据规模和参数体量,大语言模型已重塑了人机交互界面。现代主流大语言模型涵盖ChatGPT、Google Gemini、Anthropic Claude等产品。其核心工作机制依赖于Transformer架构,使其能够从数据中捕捉远距离的依赖关系和上下文的深层语义。

图1 大语言模型高级工作原理示意

图像描绘了模型从输入文本接收,经过嵌入和Transformer层编码,最终生成输出文本的全链路过程。

然而,为使得模型理解文本,首先需将自然语言转化为机器可识别的数值信号。词嵌入正是完成这种语义空间映射的桥梁。它通过将单词编码为低维稠密向量,使得语义相近的词在几何空间中也彼此邻近。本研究的意义在于揭示不同词嵌入技术在语义捕获有效性上的差异,并展示自注意力机制如何实现对长程上下文依赖的动态建模。

1.2 词嵌入基础原理

词嵌入是一种从文本中提取特征的方法。我们可以将特征输入至分类或回归模型,以处理文本数据。其核心价值在于:能够实现维度的极大压缩,缓解高维稀疏向量带来的巨大计算开销;同时能够留存语义信息,使相似词汇具备相近的向量表达。

通俗类比:如果把一个词比作一个人,那么词嵌入向量就像是这个人的DNA信息。传统的独热编码只给每个人发了一个不同的编号牌,你无法从编号牌上看出两个人的血缘关系。而词嵌入能抽出“基因序列”,从而发现长相、性格相近的人。

图2 词向量空间的关系表征

图像抽象地展示了在向量空间中,具有相似语义或语法功能的词汇被自动聚集在一起的分布形态。

图3 词嵌入处理流程示意

图像展示了从原始文本开始,经由分词、索引映射、嵌入查找等步骤,最终生成词向量的完整过程。

图4 词向量编码示例

图像以“This is a text”、“This is another text”等作为输入,展示了不同文本在向量空间中的映射形式。

该技术的使用场景涵盖模型训练时作为数值化输入,以及可对训练语料中的底层词汇使用模式进行抽取和可视化。以表情符号为例,若将“开心”和“悲伤”作为特征维度,基于出现频次,各类心情的表情可被转化为向量。拥有相似向量的单词,在语义上往往也更接近。

图5 基于特征的表情符号向量化

图像通过“开心”、“悲伤”、“愤怒”等特征维度,展示了不同表情符号的向量表示方法。


二、文本表征方法:从传统统计到神经网络

在文本表征的发展历程中,方法的演进遵循着从稀疏到稠密、从静态到动态的逻辑线条。

图6 NLP中的嵌入技术分类

图像将嵌入技术划分为传统方法(独热编码、词袋模型、TF-IDF)与神经网络方法(Word2Vec、GloVe等)。

2.1 传统统计表征及其局限

传统的特征构造方法主要依据词频统计。例如,独热编码为每个词汇建立高维索引,但会导致数据极度稀疏。词袋模型虽能计算词频,却完全抛弃了词序和上下文信息。TF-IDF则通过衡量词汇在文档内及跨文档的重要性来加权,其计算公式为:

TF-IDF(t,d,D) = TF(t,d) × IDF(t,D)

其中,TF(t,d) 为词t在文档d中的出现频率,IDF(t,D) 为包含词t的文档占比的对数倒数。尽管该方法在信息检索中表现突出,但依然难以捕获深层语义关联,且容易受到文档长度偏差的影响。

2.2 神经网络词嵌入与代码实现

2.2.1 Word2Vec

Word2Vec 作为一种典型的分布式表征模型,旨在将单词映射至高维连续向量空间。它包含两种架构:连续词袋模型(CBOW)和跳字模型(Skip-gram)。CBOW通过周围语境词预测中心目标词,而Skip-gram则根据中心词预测上下文。

  • 生活类比:CBOW好比“完形填空”,看着前后文猜中间缺了什么词;Skip-gram则像“词汇发散”,给定一个词,让你联想它会和哪些词一起出现。

  • 专业术语:CBOW追求计算效率,适合高频词;Skip-gram捕获稀有词义更佳,本质是一种“对数双线性语言模型”。

图7 连续词袋模型架构

图像展示了接收多个上下文词作为输入,经过嵌入平均与线性变换预测中心词的CBOW结构。

代码实现一:CBOW模型训练与嵌入提取

import torch.optim as optim

class CbowArchitecture(nn.Module):

def __init__(self, dictionary_size, projection_dim):

super(CbowArchitecture, self).__init__()

self.embed_layer = nn.Embedding(dictionary_size, projection_dim)

self.output_mapping = nn.Linear(projection_dim, dictionary_size)

def forward(self, context_words):

context_vectors = self.embed_layer(context_words).sum(dim=1)

scores = self.output_mapping(context_vectors)

raw_corpus = "词嵌入是自然语言处理的重要技术"

tokens = raw_corpus.split()

vocabulary = sorted(set(tokens))

# ...(省略构建词汇索引与上下文-目标词配对的中间代码)

word2idx = {term: idx for idx, term in enumerate(vocabulary)}

for pos in range(2, len(tokens) - 2):

ctx = [word2idx[tokens[k]] for k in range(pos-2, pos) + range(pos+1, pos+3)]

tgt = word2idx[tokens[pos]]

data_pairs.append((torch.tensor(ctx), torch.tensor(tgt)))

# 模型实例化与训练(省略模型超参设定及迭代细节)

cbow_instance = CbowArchitecture(len(vocabulary), 10)

loss_function = nn.CrossEntropyLoss()

optimizer_fn = optim.SGD(cbow_instance.parameters(), lr=0.01)

target_idx = word2idx[target_word]

vector_embed = cbow_instance.embed_layer(torch.tensor([target_idx]))

print(f"单词 '{target_word}' 的嵌入向量: {vector_embed.detach().numpy()}")

输出:

单词 '语言' 的嵌入向量: [[-2.7053456   2.1384873   0.6417674   1.2882394  ...]] 

代码实现二:Skip-gram预训练模型调用

from gensim.models import Word2Vec

from nltk.tokenize import word_tokenize

demo_text = "Word embeddings are dense vector representations of words."

corpus_tokens = word_tokenize(demo_text.lower())

skip_model = Word2Vec(sentences=[corpus_tokens], vector_size=20, window=5, min_count=1, sg=1)

skip_model.train([corpus_tokens], total_examples=len(corpus_tokens), epochs=100)

term_vector = skip_model.wv['word']

print("'word'的向量表示:", term_vector)

输出:

'word'的向量表示: [-9.5800208e-03 8.9437785e-03 4.1664648e-03 ...] 

图8 跳字模型架构

图像展示了从单一中心词出发,通过模型预测其周边各个语境词出现的概率分布。

CBOW与Skip-gram的选择取决于任务需求:当训练资源受限且侧重语法结构时,CBOW是更优选;若更关注捕捉罕见词的语义,Skip-gram则更为适合。


相关文章

DeepSeek、LangGraph和Python融合LSTM、RF、XGBoost、LR多模型预测NFLX股票涨跌|附完整代码数据

原文链接:tecdat.cn/?p=44060


2.2.2 预训练词嵌入技术

相比本地重新训练,直接使用在海量数据上预训练好的词向量是更为高效的做法。

GloVe:该模型基于全局词共现统计学习向量。建立一个共现矩阵,计录单词在上下文中出现的频率。其核心思想是将向量做差与共现概率比做匹配,经过加权回归损失优化后,产出融合全局信息的词向量。

FastText:其特色在于引入了子词信息。它将单词分解为字符n-gram的组合。此举显著增强了对未登录词的鲁棒性,例如通过字符片段猜测“abandonment”的词义,有助于捕捉词法形态变化。

BERT:作为一种基于Transformer架构的深度双向模型,BERT通过学习每个单词融合了左右全文信息的上下文动态表征,这是与传统静态向量的本质分野。

from transformers import BertTokenizer, BertModel

pretrained_name = 'bert-base-uncased'

tokenization_tool = BertTokenizer.from_pretrained(pretrained_name)

architecture_model = BertModel.from_pretrained(pretrained_name)

sample_pairs = [('learn', 'learning'), ('india', 'indian'), ('fame', 'famous')]

encoded_inputs = tokenization_tool(duo[0], duo[1], return_tensors='pt')

model_outputs = architecture_model(**encoded_inputs)

pooled_rep = model_outputs.last_hidden_state[:, 0, :]

cos_sim = torch.nn.functional.cosine_similarity(pooled_rep[0], pooled_rep[1], dim=0)

print(f"‘{duo[0]}’与‘{duo[1]}’基于BERT的相似度: {cos_sim:.3f}")

输出:

‘learn’与‘learning’基于BERT的相似度: 0.930

‘india’与‘indian’基于BERT的相似度: 0.957

‘fame’与‘famous’基于BERT的相似度: 0.956

答辩高频提问:“BERT与传统词嵌入的本质区别在哪里?”
标准答案:“BERT产出的是上下文动态词向量,即同一个词在不同句子中向量不同(如‘苹果手机’与‘吃苹果’),而Word2Vec和GloVe是静态向量,仅提供词级全局语义,无法处理一词多义。这也是BERT在大量自然语言理解任务中效果卓著的主因。”

阅读原文进群获取完整内容及更多AI见解、行业洞察,与900+行业人士交流成长。

三、Transformer架构与自注意力机制

3.1 位置编码

由于Transformer内部没有循环结构,无法感知序列顺序,因此须引入位置编码来赋予模型对词序的辨别力。这一机制采用正弦与余弦函数生成一个与输入嵌入等长的向量,将其加到词向量上,即可融入时序信息。

图9 位置编码几何示意

图像以三维坐标系形式展示了不同位置的正弦与余弦波,显示出周期性的位置区分能力。

图10 位置编码热力图示例

图像通过色块的明暗变化,直观地展示了不同序列位置(pos)对应的编码数值分布。

以英译法任务为例,输入句子“The cat sat on the mat.”在分词并嵌入后,需注入位置信号。对于位置pos=1,2,…,6的每个词,会叠加一个根据正弦余弦函数计算出的独特性向量。例如,位置1的编码为 PE(1) = [sin(1/10000^(0/4)), cos(…), …]。其数学原理是对于每个位置pos和维度i,分别使用正弦(偶数维)和余弦(奇数维)函数生成编码值,从而使模型能分辨不同位置上的相同词。

代码实现:位置编码生成

def compute_positional_encoding(seq_len, feats_dim):

angle_freqs = np.arange(seq_len)[:, np.newaxis] / np.power(10000, (2 * (np.arange(feats_dim)[np.newaxis, :] // 2)) / np.float32(feats_dim))

angle_freqs[:, 0::2] = np.sin(angle_freqs[:, 0::2])

angle_freqs[:, 1::2] = np.cos(angle_freqs[:, 1::2])

pos_enc_tensor = angle_freqs[np.newaxis, ...]

return tf.cast(pos_enc_tensor, dtype=tf.float32)

pos_enc_output = compute_positional_encoding(50, 512)

print("位置编码形状:", pos_enc_output.shape)

print("示例数值:\n", pos_enc_output[0, :5, :5])

输出:

图11 位置编码数组输出

截图展示了通过上述正弦/余弦函数计算得出的位置编码数值矩阵。

3.2 自注意力机制

自注意力机制令模型得以打破序列长度限制,直接计算序列内任意两个词之间的关联强度。传统序列模型需要将源句压缩为定长“上下文向量”,处理长句时易造成信息“瓶颈”。

  • 行业术语:Self-Attention(自注意力)本质上是一种“非局部特征抓取”。

  • 生活类比:阅读一本会“说话”的书,每当你读到一个词,这个词能自动高亮出书中所有与它有关联的其他章节内容,而非按页码顺序依次回忆。

图12 编码器-解码器模型结构

图像描绘了传统编码器将输入序列压缩为单一上下文向量,再由解码器解压成输出序列的过程,体现了该架构处理长序列时的信息瓶颈。

图13 自注意力机制运算结构

图像展示了从输入X线性变换得到Q、K、V矩阵,到计算注意力分数、Softmax归一化,最终加权求和的处理链路。

其计算步骤如下:

  1. 将输入X与三个权重矩阵相乘,得到查询矩阵Q键矩阵K值矩阵V

  2. 通过Q与K的点积运算得出注意力得分。

  3. 将得分除以sqrt(dk)进行缩放,防止极小梯度。

  4. 利用Softmax函数将得分转换为概率分布。

  5. 将概率权重与V矩阵加权求和,得到最终的上下文表征。

def execute_self_attention(input_tensor):

batch_dim, seq_length, model_dim = input_tensor.shape

wgt_query = np.random.randn(model_dim, dim_per_head)

wgt_key = np.random.randn(model_dim, dim_per_head)

wgt_value = np.random.randn(model_dim, dim_per_head)

mat_Q = input_tensor @ wgt_query

mat_K = input_tensor @ wgt_key

mat_V = input_tensor @ wgt_value

raw_scores = mat_Q @ mat_K.transpose(0, 2, 1) / np.sqrt(dim_per_head)

attn_probs = np.exp(raw_scores) / np.sum(np.exp(raw_scores), axis=-1, keepdims=True)

context_layer = attn_probs @ mat_V

return context_layer, attn_probs

# 模拟输入数据 (批次大小1, 序列长度3, 嵌入维度4)

sample_input = np.random.rand(1, 3, 4)

output_vectors, attention_weights = execute_self_attention(sample_input)

print("自注意力输出:", output_vectors)

print("注意力权重概率分布:", attention_weights)

3.3 多头注意力

多头注意力机制是Transformer架构的点睛之笔。它通过并行训练多个独立的“注意力头”,在多个低维子空间中分别计算自注意力,之后再拼接输出。

图14 多头注意力架构

图像展示了输入如何分拆为H个头,各自完成缩放点积注意力计算后,合并结果再进行线性输出。

图15 Transformer整体架构

全景图展示了Transformer由堆叠的编码器和解码器构成,其中每层均包含多头注意力和前馈网络模块。

代码实现:多头注意力模块

class MultiHeadAttentionModule(nn.Module):

def __init__(self, feat_dim_in, model_size, num_attn_heads):

self.n_heads = num_attn_heads

self.depth_per_head = model_size // num_attn_heads

self.combined_projection = nn.Linear(feat_dim_in, 3 * model_size)

self.final_projection = nn.Linear(model_size, model_size)

def execute_attention(self, q_tensor, k_tensor, v_tensor, mask_tensor=None):

# ...(省略缩放点积计算、掩码处理及Softmax归一化步骤)

weighted_sum = torch.matmul(attention_distribution, v_tensor)

return weighted_sum, attention_distribution

def forward(self, feature_x, attn_mask=None):

batch_sz, seq_len, _ = feature_x.size()

combined_qkv = self.combined_projection(feature_x)

# ...(省略维度重整、多路并行注意力计算与头拼接步骤)

final_output = self.final_projection(merged_heads)

# 示例:输入特征维度1024,模型维度512,分成8个头

dummy_input = torch.randn(30, 5, 1024)

mha_layer = MultiHeadAttentionModule(1024, 512, 8)

out_tensor = mha_layer(dummy_input)

print(f"多头注意力输出尺寸: {out_tensor.size()}")

输出:

多头注意力输出尺寸: torch.Size([30, 5, 512]) 

导师答辩常见追问:“为什么要用多头而不是增加单头的参数量?”
标准答案:“多头机制迫使模型在多个不同的低维子空间内学习不同的依赖模式,一个头关注语法,一个头关注语义,避免单一空间的信息混合,显著提升了模型在句法结构、长距离共指消解等任务上的综合拟合能力。这是比单纯堆叠计算量更聪明的设计。”

阅读原文进群获取完整内容及更多AI见解、行业洞察,与900+行业人士交流成长。

3.4 编码器-解码器协同工作流程

以 “I am learning AI” 的翻译任务为例,其流转步骤为:

  1. 编码:输入分词后,通过自注意力让每个词建立与上下文的联系,编码器生成捕获了整句含义的上下文向量。

  2. 传递:该上下文向量传入解码器。

  3. 解码生成:解码器上一时刻的输出会与编码器的注意力权重结合,一步一步生成目标语言词汇。

图16 编码器与解码器基础构件

图像对比了编码器与解码器的内部模块,突出了它们在处理自注意力和交叉注意力上的差异。

图17 编码器-解码器动态交互过程

图像可视化地追踪了从输入源句到生成目标句的全过程,并标出了解码器在生成每个目标词时,对源句各位置的注意力分布。

代码实现:英文到印地语的翻译任务片段

# ...(省略数据加载、文本清洗及词表构建步骤)

enc_input_layer = Input(shape=(None,))

enc_embed_map = Embedding(src_vocab_size, hidden_dim)(enc_input_layer)

enc_lstm_out, h_state, c_state = LSTM(hidden_dim, return_state=True)(enc_embed_map)

# ...(省略解码器端的LSTM初始状态设置及注意力层融合)

translation_model = Model([enc_input_layer, dec_input_layer], dec_prob_output)

translation_model.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy')

# translation_model.fit([src_data, tar_data], tar_target, epochs=20, ...)

def translate_sentence(input_text):

for step in range(max_len):

pred_probs, h_state, c_state = decoder_inf.predict([target_seq] + [h_state, c_state])

predicted_id = np.argmax(pred_probs[0, -1, :])

if decoded_tokens[-1] == 'end_token':

return ' '.join(decoded_tokens)

print(translate_sentence("Hello"))

输出:

图18 翻译效果示例

截图展示了模型完成“And”到印地语单词的翻译预测过程。

四、模型部署注意事项与应用建议

在实际部署词嵌入模型时,应遵循以下技术守则:

  1. 管线一致性:预处理的流水线须与训练阶段完全一致。

  2. 未登录词处理:对于词汇表之外的词汇,应统一映射到“未知”标记并做特定后处理。

  3. 维度校验:确保推理阶段输入的向量维度与模型参数严格对齐。

虽然词嵌入极大推进了语言理解的发展,但其仍存在内存占用高对训练语料偏见敏感无法直接区分同音异义词等局限。

本文的实证分析表明,通过构建以词嵌入和自注意力为核心的模型,能够显著提升机器对自然语言的语境感知能力。若有研究者在复现模型或部署代码时遇到运行错误或结果未达预期,可在文末联系方式获取免费的代码预检与调试支持。


五、研究结论与写作提示

本文从词嵌入技术的演进脉络切入,详细复盘了从传统稀疏表示到基于深度学习上下文化的密集向量的技术路径。研究证实,在语义相似度计算任务中,BERT等动态模型相比静态词嵌入(GloVe、FastText)拥有更细致的语义把握能力。同时,附带位置编码的自注意力机制成功地解决了并行计算与序列位置感知之间的矛盾。

尽管大语言模型效果惊人,但高计算消耗与偏见问题仍需重视。展望未来,高效、可解释且去偏见的文本表征将是该领域的重要探索方向。

作者声明:YouMing Zhang 系机器学习与深度学习领域分析师,拥有多年数据挖掘与模型开发经验。本文所涉及的案例与解决方案均精选自过往900+份行业技术库,具备真实业务场景校验基础。