Datawhale 组队学习👍 fun-transformer task02 transformer简述
Datawhale 组队学习 fun-transformer
Datawhale项目链接:https://www.datawhale.cn/learn/summary/87
笔记作者:博客园-岁月月宝贝💫
微信名:有你在就不需要🔮给的勇气
Task 02 fun-transformer简述
家人们,这段时间压力真的很大很大(没有不迟于1点睡觉的),刚刚结束了昨晚的Demo Day,好幸运看好我作品的老师也是评委老师😎,但是我不知道也是这个老师前面给我提的改进建议(新版小蛇没有完全按他的想法改喵,他也提到这点了🙃)。最开心的一点是每个选手不是答辩五分钟嘛😊,家人们我严卡到5:00:00了(我自己有计时),当时真的超级紧张,把握好时间真的超级开心!
另外,很荣幸被评为第二期的“优秀学习者”!我会更认真整理笔记的!!!
1月25日会公布第二期的结果(在公众号上),我们期待一把吧!!!
首先,我想先衔接上一个Task 01,为大家引入一下Attention机制!
注意力机制是自然语言处理(NLP)中基于Encoder - Decoder 的神经机器翻译(Neural Machine Translation )系统的一种改进。后面不仅用于神经机器翻译啦,还拓展到了计算机视觉、语音处理等方面。
也就是在 2015 年Bahdanau 等人提出第一个 Attention 模型之前,神经机器翻译是基于Encoder - Decoder 的 RNN / LSTM的。
💡编码器和解码器(Encoder - Decoder)回顾 组成单位 :LSTM/RNN 单元 工作原理 : ①编码器 LSTM 处理整个输入句子并将其编码为上下文向量 ,上下文向量 对应LSTM/RNN 的最后一个隐藏状态(输入句子的一个很好的总结)。即编码器的所有中间状态都会被忽略,最终状态 id 被认为是解码器的初始隐藏状态 。 ②解码器 LSTM 或 RNN 单元 依次生成句子中的单词。 工作原理白话版: 有两个 RNN/LSTM。一个为编码器 ——它会读取输入的句子并尝试理解它,然后再对其进行总结。它将总结(上下文向量)传递给解码器 ,解码器只需查看输入的句子即可对其进行翻译。 |
❓但是Encoder - Decoder 方法有缺点嘛?
当编码器试图理解较长的句子时,它会创建一个糟糕的总结(我们知道,如果编码器的总结不好,翻译也会不好),这官方称为 RNN/LSTM 的长程依赖问题。
⚠那RNN/LSTM 的长程依赖问题的根源在哪里呢?
由于梯度消失/爆炸问题,RNN 无法记住较长的句子和序列。它只能记住刚刚看到的部分。另外,尽管 LSTM 被认为比 RNN 更能捕捉长程依赖关系,但它在特定情况下容易遗忘。
编码器-解码器网络的性能会随着输入句子长度的增加而迅速下降。
——提出编码器-解码器网络的Cho et al (2014)
那我们没有更好的方法嘛?!大家记不记得上一个教程的熊猫聚焦问题?只要我们的记忆有侧重点,总结(哪怕在非常开阔的视野中)图片核心内容出现失误的可能性就会大大降低啦!
古人也想到啦!原来的Encoder - Decoder 方法最大的缺点之一在于无法赋予某些输入词比其他词更重要的重要性!
不仅可以在上下文向量中考虑所有输入词,还可以赋予每个输入词相对的重要性。
——Bahdanau 等人(2015)
那么,当我们赋予模型attention能力之后,我们模型可以在输出最终答案之前,针对(编码器-暂定)生成的每一个句子,它(解码器-暂定)都会在编码器隐藏状态中搜索一组可获得最相关信息的位置。这个想法被称为“注意力”。
(暂定-标注的地方==我的假设,不一定对,读者请继续阅读)
下面,我们正式进入第一节—— Attention 机制!
1.Attention 机制
我们先来了解下注意力机制的官方原理!
机制核心:“加权求和”
工作流程:
1️⃣分解输入:假设有一堆单词(或任何类型的数据)想要计算机理解。首先,计算机会将这些输入分解成更小的部分,例如单个单词。
2️⃣挑选出重要的部分:然后,它会查看这些部分并决定哪些是最重要的。它会通过将每个部分与它心中的问题或“查询”进行比较来实现这一点。
3️⃣分配重要性:根据每个部分与问题的匹配程度,为其分配一个分数。分数越高,该部分就越重要。
4️⃣集中注意力:对每个部分进行评分后,系统会确定对每个部分应给予多少关注。得分较高的部分会获得更多关注,而得分较低的部分则会获得较少关注。
5️⃣加权求和:最后,计算机将所有信息加起来,但会给重要的信息赋予更大的权重。这样,计算机就能更清楚地了解输入中最重要的信息。
💎(下面是罗清泉老师找到的金句,太完美了!真的舍不得删❤)
从上面的建模,我们可以大致感受到 Attention 的思路简单,四个字“带权求和”就可以高度概括,大道至简。做个不太恰当的类比,人类学习一门新语言基本经历四个阶段:死记硬背(通过阅读背诵学习语法练习语感)->提纲挈领(简单对话靠听懂句子中的关键词汇准确理解核心意思)->融汇贯通(复杂对话懂得上下文指代、语言背后的联系,具备了举一反三的学习能力)->登峰造极(沉浸地大量练习)。
这也如同attention的发展脉络,RNN 时代是死记硬背的时期,attention 的模型学会了提纲挈领,进化到 transformer,融汇贯通,具备优秀的表达学习能力,再到 GPT、BERT,通过多任务大规模学习积累实战经验,战斗力爆棚。
要回答为什么 attention 这么优秀?是因为它让模型开窍了,懂得了提纲挈领,学会了融汇贯通。
——阿里技术
(读到这里,star为罗老师奉上!!!)
👨👩👧👦下面我们来了解下“注意力家族”!
很久很久以前,计算机视觉先有了软注意力和硬注意力:
软注意力:所有图像块都被赋予一定的权重。
硬注意力:一次只考虑一个图像块。
软注意力启发了全局注意力的产生,so全局注意力会考虑编码器 LSTM 和解码器 LSTM 的所有隐藏状态(因为他们都被赋予了一定权重)来计算“可变长度上下文向量 ⏹” 。
这样来看,全局注意力缺点确实很明显,因为必须考虑所有隐藏状态,将它们连接成一个矩阵,并与正确维度的权重矩阵相乘,才能得到前馈连接的最后一层,另外,随着输入大小的增加,前馈连接中的节点数量增加,直接导致矩阵大小增加,所以会产生大量计算。
因此,就有了”不考虑所有编码输入,而是只考虑一部分“,来生成上下文向量的局部注意力机制❤,即避免了软注意力的昂贵计算,而且比硬注意力更容易训练!!!
全局注意力考虑所有隐藏状态(蓝色),而局部注意力仅考虑一个子集(aligned:对准的,选好的)
☑局部注意力实现流程:
- 预测对齐位置:模型需要预测源句子中哪个位置\(p_t\)对于当前的目标词嵌入最为重要。
- 定义注意力窗口:在预测的位置\(p_t\)周围定义一个大小为 2D 的窗口,其中 D 是经验选择的一个参数。
- 计算上下文向量:上下文向量是窗口内输入序列的加权平均值。权重是根据预测的位置和窗口内的每个位置之间的相似度来计算的。
☑序列到序列模型的对齐方式:
单调对齐
单调对齐是一种简单的对齐策略,其中对齐位置 \(p_t\) 直接设置为时间步 \(t\)。这意味着在时间 \(t\) 时,模型假设只有 \(t\) 附近的信息是重要的。这种对齐方式适用于源序列和目标序列之间存在严格的时间顺序关系的情况,例如在机器翻译中,源句子和目标句子的单词顺序大致相同。
预测对齐
预测对齐则更为复杂,模型会根据当前的隐藏状态 \(h_t\) 来预测对齐位置 \(p_t\)。具体来说,预测对齐的公式为:$$ p_t = S \cdot \sigma(v_p^\top \tanh(W_p h_t)) $$
- \(S\) 是源句子的长度,用于将预测的对齐位置缩放到合理的范围内。
- \(v_p\) 和 \(W_p\) 是模型在训练过程中学习到的参数,用于计算对齐位置。
- \(\tanh\) 是双曲正切函数,用于将隐藏状态 \(h_t\) 映射到 \((-1, 1)\) 的范围内,增加模型的非线性能力。
- \(\sigma\) 是Sigmoid函数,其定义为:$$sigma(x) = \frac{1}{1 + e^{-x}} $$,Sigmoid函数的输出范围在 \((0, 1)\) 之间,这使得它非常适合用于将预测的对齐位置映射到源句子的长度范围内。
2.Transformer模型
💪基本架构:
编码器-解码器架构
Transformer的核心是其编码器-解码器架构,他们分别负责处理输入序列和生成输出序列。编码器和解码器中的每一层都包含相同的子层,包括自注意力机制和前馈网络。这种架构不仅有助于全面理解输入序列,而且能够生成上下文丰富的输出序列。
如上图,输入序列(inputs)先进行Embedding,经过Encoder之后结合上一次output再输入Decoder,最后用softmax计算序列下一个word的概率。
位置编码
尽管Transformer模型具有强大的功能,但它缺乏对元素顺序的内在理解🚫。通过将输入嵌入与位置信息结合起来,位置编码使模型能够区分序列中元素的相对位置。这种细致的理解对于捕捉语言的时间动态和促进准确理解至关重要。
多头注意力
Transformer模型的一个显著特征是它能够同时关注输入序列的不同部分——这是通过多头注意力实现的。通过将查询、键和值向量分成多个头,并进行独立的自注意力计算,模型获得了对输入序列的细致透视,能够带有多样化的上下文信息。
前馈网络
与人类大脑能够并行处理信息的能力类似,Transformer模型中的每一层都包含一个前馈网络——一种能够捕捉序列中元素之间复杂关系的多功能组件。通过使用线性变换和非线性激活函数,前馈网络促进了模型对复杂语义文本的理解和生成。
🔄工作流程:
【神经网络来了!人工智能启动】 https://www.bilibili.com/video/BV1TTUUYVEKk/?share_source=copy_web&vd_source=129596c9d4d2a4a999fcc393630d4917
除了上面的B站教程,我们还可以看下面两个有趣的猫猫例子!

🐱这是一个大模型的翻译任务,我们给了大模型两个例子:
- The cat drank the milk because it was hungry。
- The cat drank the milk because it was sweet。
容易发现,在第一句中,“it”指的是“猫”,而在第二句中,它指的是“牛奶”。
参照上图,我们可以发现,当模型处理单词“it”时,self-attention 会给模型提供更多关于其含义的信息,让它能够将“it”与正确的单词联系起来。
除此之外,Transformer为每个单词提供了多个注意力分数,帮助模型处理更多关于句子的意图和语义的细微差别:
例如,在处理单词“it”时,第一个分数突出了“cat”,而第二个分数突出了“hungry”。因此,模型在翻译过程中解码单词“it”时,会将“cat”和“hungry”的某些方面结合到翻译的单词中。
😁下面是一个可爱的例子,我看到后真的惊讶于作者的想象力!
假设你要组织一次聚会,需要准备食物、饮料和娱乐活动。可以将这个过程类比为一个Transformer模型的工作方式:
聚会阶段 | 对应结构 | 涉及内容 |
---|---|---|
策划阶段 | 编码器 - Encoder | 💫收集信息:你首先询问朋友们他们喜欢的食物、饮料和娱乐活动,这就像Transformer模型的编码器在接收输入信息。 💫记录喜好:你将每个人的喜好记录下来,比如小明喜欢披萨,小红喜欢果汁,小刚喜欢玩桌游。这些记录就像Transformer模型中的“编码”过程,将原始信息转换成内部表示。 |
统筹安排 | 自注意力机制 - Self-Attention | 💫考虑关联性:在准备聚会时,你注意到小明和小红都是素食者,所以你会准备水果披萨和蔬菜拼盘。这个考虑关联性的过程就像Transformer模型中的自注意力机制,它能够注意到不同信息之间的关联性。 💫优先级排序:你发现大多数人都喜欢披萨,所以你决定披萨应该是主要食物。这个决定过程就像自注意力机制中的权重分配,重要的信息会被赋予更高的权重。 |
执行阶段 | 解码器 - Decoder | 💫按计划准备:根据记录的喜好和考虑到的关联性,你开始准备食物、饮料和娱乐活动。这就像Transformer模型的解码器,它根据编码器的输出和自注意力机制的结果来生成最终的输出。 💫调整细节:在准备过程中,你可能会根据实际情况调整计划,比如发现某人不喜欢某种饮料,你会换成另一种。这就像解码器在生成输出时会根据上下文调整细节。 |
聚会进行时 | - | 💫享受成果:最终,聚会顺利进行,朋友们享受着他们喜欢的食物、饮料和娱乐活动。这就像Transformer模型成功完成了翻译任务或其他序列到序列的任务。 |
通过这个例子,我们可以看到:
- 编码器(策划阶段)接收并处理输入信息(朋友的喜好)。
- 自注意力机制(统筹安排)识别信息之间的关联性并分配权重(考虑素食者和大多数人的喜好)。
- 解码器(执行阶段)根据编码器的输出和自注意力机制的结果生成最终的输出(准备聚会所需的物品)。
这样,Transformer模型就像一个高效的聚会组织者,能够根据不同的需求和关联性来完成任务。
📕发展历程:
下图是一张发展时间线:
- Word Embedding(词嵌入):
- 时间:2013年
- 词嵌入是NLP中的一项基础技术,它将词汇映射到连续的向量空间中,使得语义相似的词在向量空间中距离较近。这为后续的NLP任务提供了一种有效的词表示方法。
- Transformer:
- 时间:2017年
- Transformer模型由Vaswani等人提出,它基于自注意力机制,能够并行处理序列数据,极大地提高了模型的训练效率。Transformer模型在机器翻译等任务中取得了突破性的性能,成为后续许多模型的基础架构。
- 2018年的关键模型和技术:
- ELMo(Embeddings from Language Models):
- ELMo通过预训练语言模型来生成词嵌入,能够捕捉上下文信息,从而生成更丰富的词表示。
- Transformer-decoder:
- 这是Transformer模型的一个变体,主要用于解码任务,如机器翻译。
- GPT-1(Generative Pre-trained Transformer):
- 由OpenAI提出,GPT-1是一个基于Transformer解码器的预训练语言模型,能够在多种文本生成任务中取得良好性能。
- BERT(Bidirectional Encoder Representations from Transformers):
- BERT通过在Transformer编码器的基础上引入掩码语言模型(MLM)和下一句预测(NSP)任务,能够生成双向的词表示,极大地提升了模型在各种NLP任务中的性能。
- ELMo(Embeddings from Language Models):
- 2019年的关键模型和技术:
- Transformer-XL:
- Transformer-XL通过引入相对位置编码和循环机制,解决了Transformer模型在处理长序列时的局限性,能够更好地捕捉长距离依赖关系。
- XLNet:
- XLNet通过引入双向Transformer编码器和自回归预训练策略,进一步提升了模型在各种NLP任务中的性能。
- GPT-2:
- GPT-2是GPT-1的改进版本,具有更大的模型规模和更强的生成能力,能够在多种文本生成任务中取得更好的性能。
- Transformer-XL:
- 2020年的关键模型:
- GPT-3:
- GPT-3是GPT系列的最新版本,具有1750亿参数,是目前最大的预训练语言模型之一。它在多种NLP任务中展现出了惊人的性能,甚至在一些任务中达到了接近人类的水平。
- GPT-3:
这张图展示了NLP领域中一些重要的里程碑模型和技术,反映了该领域的快速发展和不断进步👍。
Transformer架构由编码器(Encoder)和解码器(Decoder)组成,是近年来NLP领域中非常重要的一种模型结构。图片中的模型可以根据它们是否使用编码器、解码器或两者都有来分类。
编码器(Encoder)模型:
这些模型主要关注理解语言,通常用于文本分类、命名实体识别、问答等任务。
- BERT(Bidirectional Encoder Representations from Transformers):双向编码器,通过预训练语言模型来生成词嵌入,能够捕捉上下文信息。
- RoBERTa(Robustly Optimized BERT Pretraining Approach):BERT的优化版本,通过改进训练过程和数据集来提升性能。
- DistilBERT:BERT的简化版本,更小、更快,但保持了BERT的性能。
- ALBERT(A Lite BERT):通过减少参数数量来优化BERT,同时保持性能。
- ELECTRA:与BERT类似,但使用不同的预训练策略,通过生成和判别任务来提升性能。
- DeBERTa(Decoding-enhanced BERT with disentangled attention):通过增强解码能力和解耦注意力机制来提升BERT的性能。
- XLM(Cross-lingual Language Model):多语言版本的BERT,能够处理多种语言。
- XLM-R(XLM-RoBERTa):XLM的优化版本,通过改进训练过程来提升性能。
解码器(Decoder)模型:
这些模型主要关注生成语言,通常用于文本生成、机器翻译等任务。
- GPT(Generative Pre-trained Transformer):生成式预训练Transformer,通过解码器来生成文本。
- GPT-2:GPT的改进版本,具有更大的模型规模和更强的生成能力。
- GPT-3:GPT-2的进一步扩展,具有1750亿参数,是目前最大的预训练语言模型之一。
- CTRL(Conditional Transformer Language Model):条件Transformer语言模型,能够根据给定的条件生成文本。
- GPT-Neo:开源的GPT-2模型,旨在提供类似的性能但更易于访问。
- GPT-J:GPT-Neo的扩展版本,具有更大的模型规模。
编码器-解码器(Encoder-Decoder)模型:
这些模型结合了编码器和解码器,通常用于需要理解并生成语言的任务,如机器翻译、文本摘要等。
- T5(Text-to-Text Transfer Transformer):将所有文本相关的任务转化为文本到文本的格式,使用编码器-解码器结构。
- BART(Bidirectional and Auto-Regressive Transformers):结合了自回归和双向Transformer,适用于文本生成和理解任务。
- M2M-100(Massive Multilingual Model):多语言翻译模型,能够处理100种语言之间的翻译。
- BigBird:通过扩展Transformer的注意力机制来处理更长的序列。
这张图展示了Transformer架构在NLP领域的广泛应用和不同变体,反映了该领域的快速发展和创新👍。
💬提出对seq2seq模型的影响:
【自注意力机制】:自注意力机制为每个元素生成一个加权表示,有效地建模全局依赖关系(&深度堆叠架构,有助于捕捉复杂的语言特征和长距离依赖关系),而不是像RNN那样仅依赖于之前的信息。
【并行化处理】:Transformer通过自注意力机制的并行化处理,显著提高了训练过程中的计算效率,降低了时间复杂度,它在训练时比基于RNN的seq2seq模型更加高效。这也使得扩大模型规模成为可能。另外,随着模型参数量的增加,模型的表示能力也得到提升。
【位置编码】:由于Transformer不包含递归结构,因此它本身无法捕捉序列元素的位置信息。但位置编码为序列中的每个元素提供了一个位置向量,该向量与元素的嵌入向量(元素的特征表示)相加,保留了序列的顺序信息。
【编码器-解码器架构的改进】:Transformer采用堆叠的编码器和解码器层,每层包含自注意力子层和前馈网络子层,提高了模型的表达能力,并使得模型在预训练阶段能够从大量未标注文本中学习丰富的语言表示。
【预训练模型的基石】:极大地推动了自然语言处理(NLP)领域的发展,尤其是它在机器翻译、文本摘要、问答系统等任务中的应用。也为BERT、GPT系列等预训练模型的出现奠定了基础(都是在Transformer的基础上通过增加层数和宽度来扩大模型规模,从而在多项NLP任务上取得突破)。
💡在预训练阶段,Transformer模型通过两种主要任务来学习语言表示: 掩码语言模型(Masked Language Model, MLM)和下一句预测(Next Sentence Prediction, NSP)。MLM任务通过随机掩码覆盖输入序列中的某些词,训练模型预测这些词;NSP任务则训练模型判断两个句子是否在原始文本中是连续的。这些任务帮助模型学习到深层的规律和语义信息。 |
【多语言和多模态的扩展】:Transformer模型的结构也易于扩展到此类任务。例如,多语言版的BERT(mBERT)和XLM等模型通过在多种语言上预训练,能够处理跨语言的NLP任务。而多模态的Transformer模型,如ViT(Vision Transformer)和CLIP(Contrastive Language-Image Pre-training),则能够处理图像和文本的结合任务。
Transformer的提出对seq2seq模型产生了革命性的影响,它不仅在模型结构上带来了创新,而且在实际应用中取得了显著的成功。不仅如此,Transformer模型的提出和成功应用为预训练模型的发展提供了坚实的基础,它在语言表示学习、迁移学习、模型规模扩大、多语言和多模态扩展、社区资源积累以及性能提升等方面都产生了深远的影响。
⏩迁移学习
Transformer模型在预训练后,可以在多种下游任务中进行微调(fine-tuning),而无需从头开始训练。这种迁移学习的方式大大减少了特定任务所需的数据量和训练时间,使得模型能够在资源有限的场景下也能取得良好的性能。
预训练:
从头开始训练模型,所有的模型权重都被随机初始化,然后在没有任何先验知识的情况下开始训练。这个过程不仅需要海量的训练数据,而且时间和经济成本都非常高。
微调参数:
不会从头训练模型,将别人预训练好的模型权重通过迁移学习应用到自己的模型中,即使用自己的任务语料对模型进行“二次训练”,通过微调参数使模型适用于新任务。
3. Transformer vs CNN vs RNN
可以从每层的计算复杂度、并行的操作数量、学习距离长度三个方面比较 Transformer、CNN、RNN 三个特征提取器。
😝每层复杂度:可以假设输入序列长度为n(可以理解为单词数),每个元素的维度为 d(可以理解为单词长度),输出序列长度也为 n,每个元素的维度也是 d(最简单的,可以理解为翻译操作)。
我们细讲下Self-Attention处理序列时的复杂度:
首先,给大家三个点,对应Self-Attention每个输入元素(比如一个词)都被映射为的三个向量:Query(Q)、Key(K)和Value(V),
Query(Q):约等于你想查到某个词的需求。
Key(K):约等于这个词的页数+这页第几个,可以唯一确定到这个词。
Value(V):这个词的解释。
♐\(O(n^2 * d)\)的计算方法(改成了本人的话喵):
整个序列中共有\(n\)个\(Key\)向量,对于输入序列中的每个Query向量 \(Q_i\),我们需要将其与每个\(Key\)向量 \(Key_j\) 进行点乘操作。因为 \(Q_i\) 和 \(Key_j\) 均为\(d*1\)维向量,所以每次点乘计算复杂度为 \(O(d)\)。因此一个Query向量 \(Q_i\)点乘\(n\)个\(Key\)向量的复杂度为 \(O(n*d*1)\) 。由于序列中包含\(n\)个Query向量,所以整个Self-Attention机制的总复杂度是\(O(n^2 * d)\) 。
辅助:【Transformer论文逐段精读【论文精读】】 https://www.bilibili.com/video/BV1pu411o7BE/?share_source=copy_web&vd_source=0c3f112a84e3e961fa46fa4e526e43be (可以从1:05:50开始看)
下面三部分是

的涵盖版:
🍎 计算复杂度
Self-Attention:考虑到 n 个 key 和 n 个 query 两两点乘,每层计算复杂度为 \(O(n^2*d)\)
RNN:考虑到矩阵(理解为正方形)和输入向量相乘,每层计算复杂度为 \(O(n*d^2)\)
CNN:对于k个卷积核经过n次一维卷积,每层计算复杂度为 \(\ O(k \times n \times d^2)\)深度可分离卷积:每层计算复杂度为$\ O(k \cdot n \cdot d + n \cdot d^2) \ $因此:当 \(n\leq d\text{}\) 时,self attention 要比 RNN 和 CNN 快,这也是大多数常见满足的条件。
当 \(n>d\) 时,可以使用受限 self attention,即:计算 attention时仅考虑每个输出位置附近窗口的 r 个输入。这带来两个效果:
-
每层计算复杂度降为为$O(r \times n \times d) $
-
最长学习距离降低为 r,因此需要执行为 $\ O\left(\frac{n}{r}\right) \ $次才能覆盖到所有输入。
🍌并行操作数量
即必须串行的操作数量:
- 对于 self-attention,CNN,其串行操作数量为 \(O(1)\),并行度最大。
- 对于 RNN,其串行操作数量为 \(O(n)\),较难并行化。
🍇最长计算路径
即覆盖所有输入的需要操作的数量(次数):
- 对于self-attention,最长计算路径为 \(O(1)\);对于 self-attention stricted,最长计算路径为 \(O(n/r)\)。
- 对于常规卷积,则需要 \(O(n/k)\) 个卷积才能覆盖所有的输入(即如果要覆盖所有的输入,需要的卷积核数量与输入长度
n
和卷积核大小k
有关);对于空洞卷积(😳通过在标准卷积核的元素之间插入空洞(即间隔)来扩大感受野,而不增加参数数量),则需要 \(O(log_kn)\) 才能覆盖所有的输入(😳随着输入长度n
的增加,所需卷积核数量的增长速度会减慢,因为每个卷积核可以覆盖更大的输入区域)。 - 对于 RNN,最长计算路径为 \(O(n)\)。
作为额外收益,self-attention 可以产生有可解释性的模型:通过检查模型中的注意力分布,可以展示与句子语法和语义结构相关的信息。
4.输入嵌入(Input Embedding)
Encoder 的输入包含词向量(Word Embedding )和位置嵌入(Position Embedding),词向量和正常的网络一样,通过学习获得,维度为 \(d_{model}\);位置嵌入通常是对每个词的嵌入向量进行加和,以保持模型对序列顺序的敏感性。
💡位置编码(Position Embedding)通常不是模型参数的一部分,而是一种固定的或者可学习的嵌入向量,它们在模型训练过程中不会通过反向传播进行更新。 |
词嵌入(Word Embedding)
在PyTorch中,nn.Embedding层用于实现词嵌入(Word Embedding),它本质上是一个查找表(Lookup Table),可以根据输入的索引(通常是单词的序号)来检索对应的嵌入向量。
nn.Embedding层权重矩阵的两种导入选择:
选择 | 固化与否 | 特点 |
---|---|---|
使用预训练的Embeddings | 是 | ①权重矩阵是从预训练的词嵌入模型中加载的,比如Word2Vec、GloVe或FastText等。 ②在加载这些预训练的嵌入向量后,通常会将这些权重设置为不可训练"Freeze"(即不需要梯度),这样在训练过程中这些嵌入向量保持不变。 ③优点是可以利用预训练嵌入中包含的丰富语义信息,尤其是在训练数据不足的情况下,这有助于提高模型的性能。 |
进行随机初始化(当然也可以选择预训练的结果) | 否 | ①在这种情况下,权重矩阵是随机初始化的,通常使用均匀分布或高斯分布进行初始化。 ②这些嵌入向量在训练过程中是可训练的"Trainable",意味着它们会随着模型的训练而更新。 ③优点是嵌入向量可以根据特定任务**进行调整,可能捕捉到预训练嵌入中未包含的特定领域信息。 |
位置嵌入/位置编码(Position Embedding)
在RNN中,对句子的处理是一个个 word 按顺序输入的。但在 Transformer 中,输入句子的所有word是同时处理的,没有考虑词的排序和位置信息。
因此,Transformer 的作者提出了加入 “positional encoding” 的方法来解决这个问题。“positional encoding“”使得 Transformer 可以衡量 word 位置有关的信息。
💻具有位置信息的编码实现
Positional Encoding的公式如下:
这里的意思是将 $pos $的位置映射为一个 \(d_{model}\) 维的位置向量,这个向量的第$ i $个元素的数值就是 \({PE}_{(pos,i)}\)。
其中,\(2i\) 指的是 embedding 词向量的偶数维度,$2i+1 $指的是embedding 词向量的奇数维度。
直接训练出来的位置向量和上面公式计算出来的位置向量两者效果接近。
我们这里选用公式构造的Position Embedding,它不需要训练参数,即使在训练集中没有出现过的句子长度上也能用。
原理讲解:
虽然 \(d_{model}\)是固定的,但“pos”和“i”会变化。让我们试着理解后两者:
由于“sin”曲线以间隔重复,在下图中可以看到,尽管 \(P_0\) 和 \(P_6\) 处于两个截然不同的位置,但它们具有相同的位置嵌入值(可以看紫色深浅度)。这就是等式中的“i”部分发挥作用的地方。
如果改变上述等式中的“i”,将得到一系列具有不同频率的曲线。通过读取不同频率的位置嵌入值,最终会为 \(P_0\)和 \(P_6\) 在不同嵌入维度上提供不同的值(下图)。
🤔为什么选用公式构造的Position Embedding?
Position Embedding本身是一个绝对位置的信息,但在语言中,相对位置也很重要(比如段落的段首和段位,就比较重要)。
我们这张图片展示了一个三角恒等式,即两角和的正弦公式$ \sin(\alpha + \beta) = \sin \alpha \cos \beta + \cos \alpha \sin \beta $ 以及$\cos(\alpha+\beta)=\cos\alpha\cos\beta-\sin\alpha\sin\beta \(,这表明位置\)p+k\(的向量可以表示成位置\) p $的向量的线性变换,这种机制便于模型捕捉序列中不同位置的元素之间的相对位置关系。
——Google选择上述位置向量公式的一个重要原因
相对位置的其他体现:
可以看到同一序列中不同位置的单词(横轴),至少在某一维度上的位置编码数值不一样,但在单个纬度又符合某个正弦或者余弦。
😜总结来说,Positional Encoding 公式通过结合周期性、位置差异、维度特定编码和平滑变化,为序列中的每个单词提供了一个反映了单词的相对位置信息的独特的编码,并允许 Transformer 模型在处理序列数据时考虑到这些位置信息。
”Encoder 的输入包含词向量(Word Embedding )和位置嵌入(Position Embedding)“,了解了位置嵌入,最后我们再来了解下词向量!
5.词向量
定义:将文字转化为的计算机可理解的语言。
生成过程:把语言这种符号信息转化为向量形式的数字信息,即将自然语言问题转换为机器学习的问题。
主流编码方法: 独热编码 (One-hot encoding) 模型和分布式表征 (distributed representation) 模型。
🏡分布式表征:
核心思想:词语的语义是通过上下文信息来确定的,即相同语境出现的词,其语义也相近。
最大的贡献:让相关或者相似的词,在距离上更接近了(一定程度上解决了独热编码忽略词汇上下文的问题)。
向量表示:一般形式如 [0.618, −0.401, −0.401, 0.419, …]。具体来说,不同于独热编码的稀疏表示,分布式表征是一种固定长度的稠密词向量。而且,词典的长度是固定的,也避免了词向量维度过大导致的计算问题。
相关方法:Word2Vec 模型、LSA 矩阵分解模型、PLSA 潜在语义分析概率模型、LDA 文档生成模型等
总的来说,就是分布式表征可以做到算得又快又好,而 Word2Vec 正好可以产生分布式表征的词向量。
基于统计的词向量
词袋模型(Bag - of - Words,BoW)
对于句子“我喜欢自然语言处理”和“自然语言处理很有趣,我很喜欢”,在词袋模型中它们的表示是相同的,都是包含“我”“喜欢”“自然语言处理”“很”“有趣”这些词的集合。
🍎原理:
一种简单的文本表示方法。忽略文本中词语的顺序和语法结构,仅将文本看作是词的集合。
🍌构建过程:
- 首先,需要构建一个词汇表。词汇表是语料库中所有不重复单词的集合。例如,对于一个包含多篇科技文章的语料库,词汇表可能包含“算法”“数据”“模型”等单词。
- 然后,对于每段文本,统计词汇表中每个单词在该文本中出现的次数。假设词汇表有 \(n\) 个单词,那么每段文本可以用一个长度为 \(n\) 的向量来表示,向量中的每个元素对应词汇表中的一个单词,元素的值为该单词在文本中的出现次数。
🍇缺点:
- 词袋模型没有考虑单词的顺序和语义信息。例如,“我喜欢他”和“他喜欢我”在词袋模型中的表示是一样的,但它们的语义完全不同。
- 词汇表的大小会影响向量的维度。如果词汇表很大,那么向量的维度会很高,这会导致计算成本增加,并且可能会出现维度灾难的问题。
TF - IDF(词频 - 逆文档频率)
🍎原理:
在词袋模型的基础上,考虑了单词在文本中的重要性。TF(词频)表示一个单词在一段文本中出现的频率,IDF(逆文档频率)表示一个单词在整个语料库中的稀有程度。通过将TF和IDF相乘,可以得到一个单词在特定文本中的重要性得分。
🍌构建过程:
-
计算TF:
对于文本 \(d\) 中的单词 \(w\),\(TF_{w,d}=\frac{n_{w,d}}{\sum_{k}n_{k,d}}\),其中 \(n_{w,d}\) 是单词 \(w\) 在文本 \(d\) 中的出现次数,\(\sum_{k}n_{k,d}\)是文本 \(d\) 中所有单词的出现次数之和。
-
计算IDF:
\(IDF_{w}=\log\frac{N}{df_{w}}\),其中 \(N\) 是语料库中的文本总数,\(df_{w}\) 是包含单词\(w\) 的文本数量。 -
最后,计算TF - IDF:
\(TF - IDF_{w,d}=TF_{w,d}\times IDF_{w}\)。
🍇优点和缺点:
- 优点:能够一定程度上体现单词在文本中的重要性,对文本分类等任务有一定的帮助。
- 缺点:没有考虑单词的语义信息,并且对于一些常见词(如“的”“是”等),TF - IDF值可能会过高,因为它们在很多文本中都会出现,这可能会掩盖其他更有意义单词的重要性。
基于神经网络的词向量
Word2Vec
🍎原理:
- Word2Vec的两种主要架构:CBOW(Continuous Bag - of - Words)和Skip - Gram。
- CBOW是根据上下文来预测中心词。例如,对于句子“我爱自然语言处理”,如果以“自然语言处理”为中心词,那么“我”和“爱”就是上下文。模型的目标是根据“我”和“爱”来预测“自然语言处理”。
- Skip - Gram则是相反,它是根据中心词来预测上下文。例如,根据“自然语言处理”来预测“我”和“爱”。
🍌训练过程:
- 首先,需要将文本进行预处理,包括分词、构建词汇表等操作。假设词汇表大小为 \(V\),词向量维度为 \(d\)。
- 对于CBOW:
- 输入是上下文单词的one - hot编码向量(如果词汇表大小为 \(V\),那么一个单词的one - hot编码向量是一个长度为 \(V\) 的向量,只有该单词对应的位置为1,其他位置为0)。假设上下文窗口大小为 \(m\),那么输入是 \(2m\) 个one - hot编码向量。
- 这些one - hot编码向量会经过一个输入层到隐藏层的权重矩阵 \(W_{V\times d}\),将它们转换为隐藏层的向量表示。隐藏层的维度为 \(d\)。
- 隐藏层向量经过一个隐藏层到输出层的权重矩阵 \(W'_{d\times V}\),得到输出向量。输出向量的维度为 \(V\),表示每个单词在词汇表中出现的概率。
- 通过反向传播算法,根据预测概率和真实中心词的标签,调整权重矩阵 \(W\) 和 \(W'\),使得预测概率更接近真实标签。
- 对于Skip - Gram,训练过程类似,只是输入和输出的角色互换。
🍇优点:
- 能够学习到单词的语义信息。例如,语义相似的单词在向量空间中会比较接近。“苹果”和“香蕉”的词向量距离会比“苹果”和“汽车”的词向量距离更近。
- 可以通过简单的向量运算来体现单词之间的语义关系。例如,“男厨师”-“男人” + “女人” ≈ “女厨师”。
GloVe(Global Vectors for Word Representation)
例如,在一个语料库中,“苹果”和“水果”共现的次数可能会比较多,而“苹果”和“汽车”共现的次数会比较少。
🍎原理:
GloVe结合了基于统计的方法和神经网络的方法。它基于单词的共现矩阵来学习词向量。共现矩阵记录了单词在语料库中的共现次数(一起出现次数)。
🍌训练过程:
- 首先,构建单词的共现矩阵。设语料库中有 \(N\) 个单词,那么共现矩阵 \(X\) 是一个 \(N\times N\) 的矩阵,\(X_{ij}\) 表示单词 \(i\) 和单词 \(j\) 在语料库中的共现次数。
- 然后,定义一个目标函数,该目标函数基于共现矩阵中的元素与单词词向量之间的关系。具体来说,目标函数旨在最小化预测的共现概率与实际共现概率之间的差异。
- 通过随机梯度下降等优化算法,更新词向量,使得目标函数达到最小值。
🍇优点:
- 能够有效地利用语料库中的统计信息,学习到单词之间的语义和语法关系。
- 训练速度相对较快,并且在一些大规模的语料库上能够取得很好的性能。
词向量的完整生成过程
1️⃣数据收集与预处理阶段
- 收集语料库
- 首先要获取大量的文本数据作为语料库。这些文本可以来自各种渠道,比如新闻文章、学术论文、小说、社交媒体帖子等。
- 语料库要适配模型领域🔁。例如,若要构建一个用于新闻领域的词向量模型,就会收集大量的新闻稿件,像路透社、美联社等新闻机构发布的内容。
- 语料库的规模和质量对词向量的性能有很大影响。一般来说,更大、更多样化的语料库能够让词向量更好地捕捉语言的各种语义和语法特征。
- 文本清洗:
- 对收集到的文本进行清洗,去除噪声信息。这包括删除HTML标签(如果是网页文本)、特殊字符(如过多的标点符号、表情符号等非文本核心内容)、多余的空格等。例如,将“<p>这是一段带有HTML标签的文本。</p>”清理为“这是一段带有HTML标签的文本。”
- 分词:
- 对于像中文、日文等语言,需要进行分词操作。
- 以中文为例,将句子“我爱自然语言处理”分为“我”“爱”“自然语言处理”等单词。
- 对于英文等以空格分隔单词的语言,也可能需要进行一些特殊的处理,如将缩写词(如“don't”拆分为“do”和“not”)等情况。
- 构建词汇表:
- 统计语料库中出现的所有不重复单词,形成词汇表。词汇表记录了模型能够处理的所有单词,并且可以为每个单词分配一个唯一的索引。例如,一个简单的词汇表可能包含“苹果”“香蕉”“汽车”等单词,并且每个单词都有对应的索引,如“苹果”的索引为0,“香蕉”的索引为1等。
2️⃣模型选择与架构设计阶段
1.选好基于统计的模型(早期方法)/基于神经网络的模型(主流方法)
2.模型训练阶段
- 定义目标函数:
- 对于基于统计的模型,目标函数可能是最小化文本表示与实际统计特征之间的差异。例如,在TF - IDF模型中,要使得计算得到的TF - IDF值能够准确反映单词在文本中的重要性。
- 对于基于神经网络的模型,目标函数通常是最小化预测结果与真实标签之间的误差。
- 在Word2Vec的CBOW架构中,目标是最小化预测的中心词概率分布与真实中心词之间的交叉熵损失;
- 在Skip - Gram架构中,是最小化根据中心词预测上下文单词的概率分布与真实上下文单词之间的交叉熵损失;
- 在GloVe模型中,目标函数是最小化预测的共现概率与实际共现概率之间的差异。
- 选择优化算法:
- 常用的优化算法有随机梯度下降(SGD)及其变种,如Adagrad、Adadelta、Adam等。这些算法用于更新模型的参数,以逐步减小目标函数的值。
- 例如,Adam优化算法会根据梯度的一阶矩估计和二阶矩估计来调整学习率,从而更有效地更新参数,加快模型收敛速度。
- 训练过程监督与调整:
- 在训练过程中,需要监控模型的性能指标,如损失值的下降情况、词向量的质量评估指标(如单词之间的相似度准确性)等。
- 如果损失值下降过慢或者出现不下降甚至上升的情况,可能需要调整优化算法的参数(如学习率),或者检查数据是否存在问题(如数据标注错误、数据格式不符合要求等)。
3.词向量生成与评估阶段
- 生成词向量:
- 经过训练后,模型会为词汇表中的每个单词生成一个词向量。这些词向量可以是模型的中间层输出(如Word2Vec隐藏层的输出),也可以是通过模型的参数计算得到的(如GloVe模型通过学习共现矩阵得到的词向量)。
- 例如,对于词汇表中的“苹果”这个单词,模型会生成一个固定维度(如100维、300维等)的向量来表示它。
- 评估词向量质量:
- 可以使用一些评估指标来衡量词向量的质量。常用的有单词相似度评估,通过比较词向量之间的余弦相似度等指标来判断语义相似的单词在向量空间中的距离是否接近。
- 例如,“汽车”和“卡车”这两个语义相似的单词,它们的词向量余弦相似度应该较高。
- 还可以通过词向量在下游任务(如文本分类、命名实体识别等)中的性能来间接评估词向量的质量。如果使用某组词向量的下游任务取得了较好的效果,说明这组词向量能够较好地捕捉语言特征。
(上图为历程中的红楼梦人物词向量相似度!期待有时间加入更多尝试!!!)
参考引用
1、《Attention Is All You Need》https://arxiv.org/abs/1706.03762
2、An annotated implementation of the Transformer paper:https://github.com/harvardnlp/annotated-transformer/tree/master
3、https://transformers.run/c1/transformer/
4、Transformer: PyTorch Implementation of "Attention Is All You Need":https://github.com/hyunwoongko/transformer
5、多头注意力代码实现: https://www.cnblogs.com/xiximayou/p/13978859.html
6、Notes about "Attention is all you need":https://github.com/hkproj/transformer-from-scratch-notes
7、Position Embedding 的实现:https://www.cnblogs.com/wevolf/p/15188846.html
8、External-Attention-pytorch:https://github.com/xmu-xiaoma666/External-Attention-pytorch
9、Transformer输入输出细节以及代码实现https://blog.csdn.net/wl1780852311/article/details/121033915
10、Word2vec 官方文档:https://radimrehurek.com/gensim/models/word2vec.html
11、多模态任务对齐:https://aistudio.baidu.com/blog/detail/16
12、词向量如何来的?https://www.cnblogs.com/ghj1976/p/word-vector.html
最后,再次谢谢DW!下面是代码实践部分:
代码实践
Word2Vec
下面是一个简单的词向量训练流程,从数据准备(分词)到模型训练,再到获取训练结果(词汇表和词向量)。
必下库:
gensim jieba matplotlib
1️⃣数据准备:
import jieba # jieba分词库,用于对中文文本进行分词处理。 # 准备训练数据 sentences = [ list(jieba.cut( '我喜欢吃苹果' )), list(jieba.cut( '苹果很好吃' )), list(jieba.cut( '水果是健康的' )), list(jieba.cut( '梨子也很好吃' )), list(jieba.cut( '我也喜欢吃凤梨' )), list(jieba.cut( '苹果凤梨都是一种水果' )) , list(jieba.cut( '苹果是一种又香又甜的水果' )), list(jieba.cut( '梨子跟凤梨也是一种又香又甜的水果' )), ] print(sentences)
🖥(分词后的句子列表):
[['我', '喜欢', '吃', '苹果'], ['苹果', '很', '好吃'], ['水果', '是', '健康', '的'], ['梨子', '也', '很', '好吃'], ['我', '也', '喜欢', '吃', '凤梨'], ['苹果', '凤梨', '都', '是', '一种', '水果'], ['苹果', '是', '一种', '又', '香', '又', '甜', '的', '水果'], ['梨子', '跟', '凤梨', '也', '是', '一种', '又', '香', '又', '甜', '的', '水果']]
2️⃣Word2Vec模型训练词向量:
# 使用gensim库中的Word2Vec模型来训练词向量,并获取训练后的词向量和词汇表 from gensim. models import Word2Vec # 训练词向量模型 model = Word2Vec(sentences, window=5, min_count=1, workers=4) # 获取所有词 vocab = model.wv.index_to_key # 获取所有词向量 vectors = model.wv[vocab] print(vectors)
window=5
:表示上下文窗口大小为 5,即在训练时,每个单词会考虑其前后 5 个单词的上下文信息(我想共10个单词)来学习词向量。
min_count=1
:表示单词在语料库中出现的最小次数为 1,即所有在训练数据中出现过的单词都会被纳入词汇表并学习其词向量。 如果设置为一个较大的值,如min_count=2,则只有在语料库中出现次数大于等于 2 的单词才会被学习词向量,出现次数小于 2 的单词将被忽略。
workers=4
:指定使用 4 个线程来进行训练,以加快训练速度(每个人电脑配置不同,可以利用多核优势提高训练效率)。
🖥(训练后的词向量):
🎬红楼梦
为了保证代码的可复现性,本案例利用《红楼梦》展示 Word2Vec 的基础应用。
1️⃣导入第三方库&数据预处理:
# 导入第三方库 import jieba import re import numpy as np from sklearn.decomposition import PCA import gensim from gensim.models import Word2Vec import matplotlib.pyplot as plt import matplotlib # 数据预处理 f = open(r"hongloumeng.txt",encoding='utf-8') lines = [] for line in f: temp = jieba.lcut(line) words = [] for i in temp: # 去除不必要字符 i = re.sub("[\s+\.\!\/_.$%^*(++\"\'“”《》]+|[+——!,。?、\ ~·@#¥%……&* ( ) '------------';:‘]+","",i) if len(i) > 0: words.append(i) if len(words) > 0: lines.append(words) print(lines[:3]) # 展示前三段的分词结果
🖥(前三段的分词结果):
[['第', '1', '章', '甄士隐', '梦幻', '识通灵', '贾雨村', '风尘', '怀', '闺秀'], ['此', '开卷', '第一回', '也'], ['作者', '自云', '因曾', '历过', '一番', '梦幻', '之后', '故', '将', '真事', '隐去', '而', '借', '通灵', '说', '撰此', '石头记', '一书', '也', '故曰', '甄士隐', '云云', '但书中', '所记', '何事', '何人', '自己', '又', '云', '今', '风尘碌碌', '一事无成', '忽', '念及', '当日', '所有', '之', '女子', '一一', '细考', '较', '去', '觉其', '行止', '见识', '皆', '出', '我', '之上', '我', '堂堂', '须眉', '诚不若', '彼', '裙钗', '我', '实愧', '则', '有', '馀', '悔', '又', '无益', '大', '无可如何', '之日', '也', '当此', '日', '欲', '将', '已往', '所赖', '天思祖德', '锦衣纨裤', '之', '时', '饫甘餍肥', '之', '日', '背', '父兄', '教育', '之恩', '负', '师友', '规训', '之德', '以致', '今日', '一技无成', '半生', '潦倒', '之罪', '编述', '一集', '以告', '天下', '知', '我', '之', '负罪', '固多', '然', '闺阁', '中', '历历', '有人', '万', '不可', '因', '我', '之', '不肖', '自护己', '短', '一并', '使', '其', '泯灭', '也', '所以', '蓬牖', '茅', '椽', '绳床瓦灶', '并', '不足', '妨', '我', '襟怀', '况', '那', '晨风', '夕', '月', '阶柳庭花', '更', '觉得', '润人', '笔墨', '我', '虽不学', '无文', '又', '何妨', '用', '假语', '村言', '敷', '演出', '来', '亦可', '使', '闺阁', '昭传', '复可破', '一时', '之闷', '醒', '同人', '之目', '不', '亦', '宜乎', '故曰', '贾雨村', '云云', '更于', '篇', '中间', '用', '梦', '幻', '等', '字', '却是', '此书', '本旨', '兼寓', '提醒', '阅者', '之意']]
2️⃣ 调用 Word2Vec 建立模型
# 调用 Word2Vec 建立模型 # 常用参数 # vector_size: 词向量的维度; # window: 上下文窗口长度; # min_count: 少于该字段的次数的单词会被丢弃 model = Word2Vec(lines, vector_size = 20, window=3, min_count=3, \ epochs=7,negative=10) # 这里为了训练速度,对参数值设置较低 # 输入一个路径,保存训练好的模型,其中./data/model目录事先要存在 model.save("data/model/word2vec_gensim") # 查看 “林黛玉” 的词向量 print(model.wv.get_vector("林黛玉")) # 查看与“林黛玉”最相近的前10个词 print(model.wv.most_similar('林黛玉',topn=10)) # 查看“林黛玉”与“贾宝玉”的相似度 print(model.wv.similarity('林黛玉', '贾宝玉'))
以下结果展示了经过训练后“林黛玉”词向量表示,“林黛玉”相似度前 10 的词语,以及“林黛玉”与“贾宝玉”的相似度。
🖥:
[-0.26091352 -0.59285027 -0.13512401 -0.01244547 0.01794916 0.16232903 0.18596677 0.4919579 -0.3022464 0.08286952 0.68066573 0.20418276 0.37712505 0.28194344 0.47464842 0.36523166 -0.54659736 -0.5709575 -0.97067654 -0.9070233 ] [('这般光景', 0.9891781806945801), ('消息', 0.9836994409561157), ('细看', 0.9818145632743835), ('近前', 0.9803917407989502), ('通红', 0.9801235795021057), ('吱', 0.9791161417961121), ('非常', 0.9785283207893372), ('镯子', 0.9783412218093872), ('先来', 0.9783284664154053), ('贾母问', 0.9782575964927673)] 0.9515984
3️⃣画图形式展现 Word2Vec 模型结果:
🐕如果你的系统也提醒:”未找到 SimHei 字体,可能需要安装“,可以采用如下方法:
☝#查看matplotlib支持的字体 >>> from matplotlib.font_manager import FontManager >>> fm = FontManager() >>> mat_fonts = set(f.name for f in fm.ttflist) >>> print(mat_fonts) #该方法macOS用户同样适用。 plt.rcParams['font.sans-serif'] = ['这里选择你可以使用的字体,请手动添加']*
✌plt.rcParams['font.sans-serif'] = ['SimSun'] #matplotlib字体显示,一般修改成宋体可以显示汉字
👩我的方法:font_path = 'C:\Windows\Fonts\simsun.ttc'
zhfont1 = fm.FontProperties(fname=font_path, size=10)
import numpy as np import matplotlib.pyplot as plt from sklearn.decomposition import PCA import matplotlib.font_manager as fm def plot_word_vectors(model, words_to_plot): rawWorVec = [] word2ind = {} for i, w in enumerate(model.wv.index_to_key): rawWorVec.append(model.wv[w]) word2ind[w] = i rawWorVec = np.array(rawWorVec) X_reduced = PCA(n_components=2).fit_transform(rawWorVec)#PCA 降维 fig = plt.figure(figsize=(16, 16)) ax = fig.gca() ax.set_facecolor('white') ax.plot(X_reduced[:, 0], X_reduced[:, 1], '.', markersize=1, alpha=0.3, color='black') # fm = FontManager() # mat_fonts = set(f.name for f in fm.ttflist) # print(mat_fonts) # # 查找系统中可用的中文字体 # font_list = fm.findSystemFonts(fontpaths=None, fontext='ttf') # for font in font_list: # if 'SimSun.ttc' in font: # zhfont1 = fm.FontProperties(fname=font, size=10) # break # else: # print("未找到 SimSun 字体,可能需要安装。") # return # 指定SimSun字体的完整路径 font_path = 'C:\\Windows\\Fonts\\simsun.ttc' zhfont1 = fm.FontProperties(fname=font_path, size=10) for w in words_to_plot: if w in word2ind: ind = word2ind[w] xy = X_reduced[ind] plt.plot(xy[0], xy[1], '.', alpha=1, color='green', markersize=10) plt.text(xy[0], xy[1], w, alpha=1, color='blue', fontproperties=zhfont1) plt.show() # 假设已经加载了词向量模型 # 这里需要根据实际情况加载模型,例如: from gensim.models import Word2Vec model = Word2Vec.load("data/model/word2vec_gensim") words = ['紫鹃', '香菱', '王熙凤', '林黛玉', '贾宝玉'] plot_word_vectors(model, words)
🖥(词向量可视化的结果):
不难发现相似度高的词语距离比较近!贾宝玉、王熙凤、林黛玉都非常近!下图又加入了黛玉的两个”情敌“,薛宝钗和晴雯,效果如下:
😺欢迎红学爱好者来分析一下!
单词位置向量/嵌入的相对关系
Positional Encoding的公式如下:
下面是带有位置信息编码的实现过程:
import torch import torch.nn as nn import numpy as np import matplotlib.pyplot as plt import math # 定义 PositionalEncoding 类 class PositionalEncoding(nn.Module): def __init__(self, d_model, max_len=5000): super(PositionalEncoding, self).__init__() # 创建一个足够长的位置编码张量,初始化为0 # max_len 是位置编码的最大长度,d_model 是编码的维度 self.encoding = torch.zeros(max_len, d_model) # 创建位置张量,形状为 (max_len, 1) position = torch.arange(0, max_len).unsqueeze(1) # 计算正弦和余弦函数中的分母部分,形状为 (d_model // 2,) div_term = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model)) # 计算正弦编码,并填充到偶数索引位置 self.encoding[:, 0::2] = torch.sin(position * div_term) # 计算余弦编码,并填充到奇数索引位置 self.encoding[:, 1::2] = torch.cos(position * div_term) # 增加一个批次维度,以便于广播操作 self.encoding = self.encoding.unsqueeze(0) def forward(self, x): # 将位置编码与输入张量相加 # x 的形状为 (batch_size, seq_len, d_model) # 我们只取位置编码的前 seq_len 个位置 return x + self.encoding[:, :x.size(1)] # 创建 PositionalEncoding 实例,设置编码维度为20,最大长度为100 pe = PositionalEncoding(20, max_len=100) # max_len 应该至少与输入序列长度相等 # 创建一个全零张量来模拟输入序列,形状为 (1, 100, 20) # 这代表一个批次大小为1的序列,序列长度为100,编码维度为20 y = pe.forward(torch.zeros(1, 100, 20)) # 绘制位置编码 plt.figure(figsize=(15, 5)) plt.plot(np.arange(100), y[0, :, 4:8].data.numpy()) plt.legend(["dim %d" % p for p in [4, 5, 6, 7]]) plt.show()
得以验证:可以看到同一序列中不同位置的单词(横轴),至少在某一维度上的位置编码数值不一样,但在单个纬度又符合某个正弦或者余弦。
谢谢大家!★,°:.☆( ̄▽ ̄)/$:.°★ 。请大家持续关注博客园-岁月月宝贝,继续为大家分享高质量内容!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)