Transformer模型详解

news2024/11/15 7:58:35

1. 前言

transformer结构是google在2017年的Attention Is All You Need论文中提出,在NLP的多个任务上取得了非常好的效果,可以说目前NLP发展都离不开transformer。最大特点是抛弃了传统的CNN和RNN,整个网络结构完全是由Attention机制组成。 由于其出色性能以及对下游任务的友好性或者说下游任务仅仅微调即可得到不错效果,在计算机视觉领域不断有人尝试将transformer引入,近期也出现了一些效果不错的尝试,典型的如目标检测领域的detr和可变形detr,分类领域的vision transformer等等。 本文从transformer结构出发,结合视觉中的transformer成果(具体是vision transformer和detr)进行分析。

1.1. Transformer 概览

首先,让我们先将 Transformer 模型视为一个黑盒,如下图所示。在机器翻译任务中,将一种语言的一个句子作为输入,然后将其翻译成另一种语言的一个句子作为输出。

Transformer 模型(黑盒模式)

Transformer 本质上是一个 Encoder-Decoder 架构。因此中间部分的 Transformer 可以分为两个部分:编码组件和解码组件。如下图所示: 

Transformer 模型(Encoder-Decoder 架构模式)

其中,编码组件由多层编码器(Encoder)组成(在论文中作者使用了 6 层编码器,在实际使用过程中你可以尝试其他层数)。解码组件也是由相同层数的解码器(Decoder)组成(在论文也使用了 6 层)。如下图所示:

每个编码器由两个子层组成:Self-Attention 层(自注意力层)和 Position-wise Feed Forward Network(前馈网络,缩写为 FFN)如下图所示。每个编码器的结构都是相同的,但是它们使用不同的权重参数。 

编码器的输入会先流入 Self-Attention 层。它可以让编码器在对特定词进行编码时使用输入句子中的其他词的信息(可以理解为:当我们翻译一个词时,不仅只关注当前的词,而且还会关注其他词的信息)。后面我们将会详细介绍 Self-Attention 的内部结构。然后,Self-Attention 层的输出会流入前馈网络。

解码器也有编码器中这两层,但是它们之间还有一个注意力层(即 Encoder-Decoder Attention),其用来帮忙解码器关注输入句子的相关部分(类似于 seq2seq 模型中的注意力)。

1.2. 引入张量

现在我们已经了解了模型的主要组成部分,让我们开始研究各种向量/张量,以及他们在这些组成部分之间是如何流动的,从而将输入经过已训练的模型转换为输出。

和通常的 NLP 任务一样,首先,我们使用词嵌入算法(Embedding)将每个词转换为一个词向量。在 Transformer 论文中,词嵌入向量的维度是 512。
 

每个词被嵌入到大小为 512 的向量中。我们将用这些简单的框代表这些向量。

嵌入仅发生在最底层的编码器中。所有编码器都会接收到一个大小为 512 的向量列表——底部编码器接收的是词嵌入向量,其他编码器接收的是上一个编码器的输出。这个列表大小是我们可以设置的超参数——基本上这个参数就是训练数据集中最长句子的长度。

对输入序列完成嵌入操作后,每个词都会流经编码器的两层。

接下来,我们将换一个更短的句子作为示例,来说明在编码器的每个子层中发生了什么。

上面我们提到,编码器会接收一个向量作为输入。编码器首先将这些向量传递到 Self-Attention 层,然后传递到前馈网络,最后将输出传递到下一个编码器。

1.3. Transformer 整体结构

Transformer 的整体结构,左图Encoder和右图Decoder

可以看到 Transformer 由 Encoder 和 Decoder 两个部分组成,Encoder 和 Decoder 都包含 6 个 block。Transformer 的工作流程大体如下:

第一步:获取输入句子的每一个单词的表示向量 XX由单词的 Embedding(Embedding就是从原始数据提取出来的Feature) 和单词位置的 Embedding 相加得到。

Transformer 的输入表示

第二步:将得到的单词表示向量矩阵 (如上图所示,每一行是一个单词的表示 x) 传入 Encoder 中,经过 6 个 Encoder block 后可以得到句子所有单词的编码信息矩阵 C,如下图。单词向量矩阵用X_{n \times d}表示, n 是句子中单词个数,d 是表示向量的维度 (论文中 d=512)。每一个 Encoder block 输出的矩阵维度与输入完全一致。

Transformer Encoder 编码句子信息

第三步:将 Encoder 输出的编码信息矩阵 C传递到 Decoder 中,Decoder 依次会根据当前翻译过的单词 1~ i 翻译下一个单词 i+1,如下图所示。在使用的过程中,翻译到单词 i+1 的时候需要通过 Mask (掩盖) 操作遮盖住 i+1 之后的单词。

Transofrmer Decoder 预测

上图 Decoder 接收了 Encoder 的编码矩阵 C,然后首先输入一个翻译开始符 "<Begin>",预测第一个单词 "I";然后输入翻译开始符 "<Begin>" 和单词 "I",预测单词 "have",以此类推。这是 Transformer 使用时候的大致流程,接下来是里面各个部分的细节。

2. Transformer 的输入

Transformer 中单词的输入表示 x单词 Embedding位置 Embedding (Positional Encoding)相加得到。

Transformer 的输入表示

2.1. 单词 Embedding

单词的 Embedding 有很多种方式可以获取,例如可以采用 Word2Vec、Glove 等算法预训练得到,也可以在 Transformer 中训练得到。

2.2. 位置 Embedding

Transformer 中除了单词的 Embedding,还需要使用位置 Embedding 表示单词出现在句子中的位置。因为 Transformer 不采用 RNN 的结构,而是使用全局信息,不能利用单词的顺序信息,而这部分信息对于 NLP 来说非常重要。所以 Transformer 中使用位置 Embedding 保存单词在序列中的相对或绝对位置。

位置 Embedding 用 PE表示,PE 的维度与单词 Embedding 是一样的。PE 可以通过训练得到,也可以使用某种公式计算得到。在 Transformer 中采用了后者,计算公式如下:

其中,pos 表示单词在句子中的位置,d 表示 PE的维度 (与词 Embedding 一样),2i 表示偶数的维度,2i+1 表示奇数维度 (即 2i≤d, 2i+1≤d)。使用这种公式计算 PE 有以下的好处:

  • 使 PE 能够适应比训练集里面所有句子更长的句子,假设训练集里面最长的句子是有 20 个单词,突然来了一个长度为 21 的句子,则使用公式计算的方法可以计算出第 21 位的 Embedding。
  • 可以让模型容易地计算出相对位置,对于固定长度的间距 k,PE(pos+k) 可以用 PE(pos) 计算得到。因为 Sin(A+B) = Sin(A)Cos(B) + Cos(A)Sin(B), Cos(A+B) = Cos(A)Cos(B) - Sin(A)Sin(B)。

将单词的词 Embedding 和位置 Embedding 相加,就可以得到单词的表示向量 xx 就是 Transformer 的输入。

3. Self-Attention(自注意力机制)

Transformer Encoder 和 Decoder

上图是论文中 Transformer 的内部结构图,左侧为 Encoder block,右侧为 Decoder block。红色圈中的部分为 Multi-Head Attention,是由多个 Self-Attention组成的,可以看到 Encoder block 包含一个 Multi-Head Attention,而 Decoder block 包含两个 Multi-Head Attention (其中有一个用到 Masked)。Multi-Head Attention 上方还包括一个 Add & Norm 层,Add 表示残差连接 (Residual Connection) 用于防止网络退化,Norm 表示 Layer Normalization,用于对每一层的激活值进行归一化。

因为 Self-Attention是 Transformer 的重点,所以我们重点关注 Multi-Head Attention 以及 Self-Attention,首先详细了解一下 Self-Attention 的内部逻辑。

3.1. Self-Attention 结构

Self-Attention 结构

上图是Self-Attention的结构,在计算的时候需要用到矩阵Q(查询), K(键值), V(值)。在实际中,Self-Attention接收的是输入(单词的表示向量x组成的矩阵X) 或者上一个 Encoder block 的输出。而Q, K, V正是通过 Self-Attention 的输入进行线性变换得到的。

3.2. Q, K, V 的计算

Self-Attention 的输入用矩阵X进行表示,则可以使用线性变阵矩阵WQ, WK, WV计算得到Q, K, V。计算如下图所示,注意 X, Q, K, V 的每一行都表示一个单词。

Q, K, V 的计算

3.3. Self-Attention 的输出

得到矩阵 Q, K, V之后就可以计算出 Self-Attention 的输出了,计算的公式如下:

Self-Attention 的输出

公式中计算矩阵QK每一行向量的内积,为了防止内积过大,因此除以d_k的平方根。Q乘以K的转置后,得到的矩阵行列数都为 n,n 为句子单词数,这个矩阵可以表示单词之间的 attention 强度。下图为Q乘以K^T,1234 表示的是句子中的单词。

Q乘以K的转置的计算

得到QK^T之后,使用 Softmax 计算每一个单词对于其他单词的 attention 系数,公式中的 Softmax 是对矩阵的每一行进行 Softmax,即每一行的和都变为 1.

对矩阵的每一行进行 Softmax

得到 Softmax 矩阵之后可以和V相乘,得到最终的输出Z

Self-Attention 输出

上图中 Softmax 矩阵的第 1 行表示单词 1 与其他所有单词的 attention 系数,最终单词 1 的输出Z_1

等于所有单词 i 的值V_i根据 attention 系数的比例加在一起得到,如下图所示:

Zi 的计算方法

3.4. Multi-Head Attention

在上一步,我们已经知道怎么通过 Self-Attention 计算得到输出矩阵 Z,而 Multi-Head Attention 是由多个 Self-Attention 组合形成的,下图是论文中 Multi-Head Attention 的结构图。

Multi-Head Attention

从上图可以看到 Multi-Head Attention 包含多个 Self-Attention 层,首先将输入X分别传递到 h 个不同的 Self-Attention 中,计算得到 h 个输出矩阵Z。下图是 h=8 时候的情况,此时会得到 8 个输出矩阵Z

多个 Self-Attention

得到 8 个输出矩阵Z_1Z_8之后,Multi-Head Attention 将它们拼接在一起 (Concat),然后传入一个Linear层,得到 Multi-Head Attention 最终的输出Z

Multi-Head Attention 的输出

可以看到 Multi-Head Attention 输出的矩阵Z与其输入的矩阵X的维度是一样的。

4. Encoder 结构

Transformer Encoder block

上图红色部分是 Transformer 的 Encoder block 结构,可以看到是由 Multi-Head Attention, Add & Norm, Feed Forward, Add & Norm 组成的。刚刚已经了解了 Multi-Head Attention 的计算过程,现在了解一下 Add & Norm 和 Feed Forward 部分。

4.1. Add & Norm

Add & Norm 层由 Add 和 Norm 两部分组成,其计算公式如下:

Add &amp;amp;amp;amp;amp; Norm 公式

其中 X表示 Multi-Head Attention 或者 Feed Forward 的输入,MultiHeadAttention(X) 和 FeedForward(X) 表示输出 (输出与输入 X 维度是一样的,所以可以相加)。

AddX+MultiHeadAttention(X),是一种残差连接,通常用于解决多层网络训练的问题,可以让网络只关注当前差异的部分,在 ResNet 中经常用到:

残差连接

Norm指 Layer Normalization,通常用于 RNN 结构,Layer Normalization 会将每一层神经元的输入都转成均值方差都一样的,这样可以加快收敛。

4.2. Feed Forward

Feed Forward 层比较简单,是一个两层的全连接层,第一层的激活函数为 Relu,第二层不使用激活函数,对应的公式如下。

Feed Forward

X是输入,Feed Forward 最终得到的输出矩阵的维度与X一致。

4.3. 组成 Encoder

通过上面描述的 Multi-Head Attention, Feed Forward, Add & Norm 就可以构造出一个 Encoder block,Encoder block 接收输入矩阵X_{n \times d},并输出一个矩阵O_{n \times d}。通过多个 Encoder block 叠加就可以组成 Encoder。

第一个 Encoder block 的输入为句子单词的表示向量矩阵,后续 Encoder block 的输入是前一个 Encoder block 的输出,最后一个 Encoder block 输出的矩阵就是编码信息矩阵 C,这一矩阵后续会用到 Decoder 中。

Encoder 编码句子信息

5. Decoder 结构

Transformer Decoder block

上图红色部分为 Transformer 的 Decoder block 结构,与 Encoder block 相似,但是存在一些区别:

  • 包含两个 Multi-Head Attention 层。
  • 第一个 Multi-Head Attention 层采用了 Masked 操作。
  • 第二个 Multi-Head Attention 层的K, V矩阵使用 Encoder 的编码信息矩阵C进行计算,而Q使用上一个 Decoder block 的输出计算。
  • 最后有一个 Softmax 层计算下一个翻译单词的概率。

5.1. 第一个 Multi-Head Attention

Decoder block 的第一个 Multi-Head Attention 采用了 Masked 操作,因为在翻译的过程中是顺序翻译的,即翻译完第 i 个单词,才可以翻译第 i+1 个单词。通过 Masked 操作可以防止第 i 个单词知道 i+1 个单词之后的信息。下面以 "我有一只猫" 翻译成 "I have a cat" 为例,了解一下 Masked 操作。

下面的描述中使用了类似 Teacher Forcing 的概念,不熟悉 Teacher Forcing 的童鞋可以参考以下上一篇文章Seq2Seq 模型详解。在 Decoder 的时候,是需要根据之前的翻译,求解当前最有可能的翻译,如下图所示。首先根据输入 "<Begin>" 预测出第一个单词为 "I",然后根据输入 "<Begin> I" 预测下一个单词 "have"。

Decoder 预测

Decoder 可以在训练的过程中使用 Teacher Forcing 并且并行化训练,即将正确的单词序列 (<Begin> I have a cat) 和对应输出 (I have a cat <end>) 传递到 Decoder。那么在预测第 i 个输出时,就要将第 i+1 之后的单词掩盖住,注意 Mask 操作是在 Self-Attention 的 Softmax 之前使用的,下面用 0 1 2 3 4 5 分别表示 "<Begin> I have a cat <end>"。

第一步:是 Decoder 的输入矩阵和 Mask 矩阵,输入矩阵包含 "<Begin> I have a cat" (0, 1, 2, 3, 4) 五个单词的表示向量,Mask 是一个 5×5 的矩阵。在 Mask 可以发现单词 0 只能使用单词 0 的信息,而单词 1 可以使用单词 0, 1 的信息,即只能使用之前的信息。

输入矩阵与 Mask 矩阵

第二步:接下来的操作和之前的 Self-Attention 一样,通过输入矩阵X计算得到Q,K,V矩阵。然后计算QK^T的乘积QK^T

Q乘以K的转置

第三步:在得到

之后需要进行 Softmax,计算 attention score,我们在 Softmax 之前需要使用Mask矩阵遮挡住每一个单词之后的信息,遮挡操作如下:

Softmax 之前 Mask

得到 Mask QK^T之后在 Mask QK^T上进行 Softmax,每一行的和都为 1。但是单词 0 在单词 1, 2, 3, 4 上的 attention score 都为 0。

第四步:使用 Mask QK^T 与矩阵 V相乘,得到输出 Z,则单词 1 的输出向量Z_1是只包含单词 1 信息的。

第五步:通过上述步骤就可以得到一个 Mask Self-Attention 的输出矩阵Z_i,然后和 Encoder 类似,通过 Multi-Head Attention 拼接多个输出Z_i 然后计算得到第一个 Multi-Head Attention 的输出ZZ与输入X维度一样。

5.2. 第二个 Multi-Head Attention

Decoder block 第二个 Multi-Head Attention 变化不大, 主要的区别在于其中 Self-Attention 的 K, V矩阵不是使用 上一个 Decoder block 的输出计算的,而是使用 Encoder 的编码信息矩阵 C 计算的。

根据 Encoder 的输出 C计算得到 K, V,根据上一个 Decoder block 的输出 Z 计算 Q (如果是第一个 Decoder block 则使用输入矩阵 X 进行计算),后续的计算方法与之前描述的一致。

这样做的好处是在 Decoder 的时候,每一位单词都可以利用到 Encoder 所有单词的信息 (这些信息无需 Mask)。

5.3. Softmax 预测输出单词

Decoder block 最后的部分是利用 Softmax 预测下一个单词,在之前的网络层我们可以得到一个最终的输出 Z,因为 Mask 的存在,使得单词 0 的输出 Z0 只包含单词 0 的信息,如下:

Decoder Softmax 之前的 Z

Softmax 根据输出矩阵的每一行预测下一个单词:

Decoder Softmax 预测

这就是 Decoder block 的定义,与 Encoder 一样,Decoder 是由多个 Decoder block 组合而成。

6. Transformer 总结

  • Transformer 与 RNN 不同,可以比较好地并行训练。
  • Transformer 本身是不能利用单词的顺序信息的,因此需要在输入中添加位置 Embedding,否则 Transformer 就是一个词袋模型了。
  • Transformer 的重点是 Self-Attention 结构,其中用到的 Q, K, V矩阵通过输出进行线性变换得到。
  • Transformer 中 Multi-Head Attention 中有多个 Self-Attention,可以捕获单词之间多种维度上的相关系数 attention score。

参考文献

Attention Is All You Need

Transformer 模型详解_空杯的境界的博客-CSDN博客_transformer

Transformer模型详解(图解最完整版) - 知乎

3W字长文带你轻松入门视觉transformer - 知乎

台大李宏毅21年机器学习课程 self-attention和transformer_哔哩哔哩_bilibili

The Illustrated Transformer – Jay Alammar – Visualizing machine learning one concept at a time.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/171720.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VESC操作入门——控制霍尔电机、无感电机和AS5047P

目录一、设备说明二、VESC4驱动霍尔电机2.1、硬件准备2.2、硬件连接2.3、打开软件2.4、连接2.5、校准电机2.6、主界面操作三、VESC4驱动无感电机3.1、硬件准备3.2、硬件连接3.3、打开软件3.4、校准电机四、VESC4驱动AS5047P4.1、软硬件修改4.2、硬件准备4.3、硬件连接4.4、校准…

Win32解决透明字体改变时重叠的问题,GetClientRect与GetWindowRect的使用

透明字体,改变时发生文本重叠,解决办法是刷新窗体局部区域,该区域是文本或者按钮等控件的区域 Win32 API中使用InvalidateRect函数使指定区域失效,意味着要刷新该区域,再用UpdateWindow函数强迫窗体立即刷新 RECT rc; ... InvalidateRect(hWnd,&rc,true); UpdateWind…

Python操作文件及其内容的常用方式

Python操作文件及其内容的常用方式 文章目录Python操作文件及其内容的常用方式1&#xff1a;修改文件名1.1&#xff1a;修改指定文件名1.2&#xff1a;修改目录下的所有文件的文件名2&#xff1a;读取文件2.1&#xff1a;读取文件内容2.1.1&#xff1a;按行读取2.1.2&#xff1…

[Arduino]环境安装与配置

最近着迷与Arduio&#xff0c;可以连接控制各种器件帮助人类降低负担&#xff0c;如室内外温度动态采集、声控灯、自动给绿植浇水等各种应用&#xff0c;感觉挺有意思&#xff1b;随着最近两年物联网的推广及“万物互联”的普及&#xff0c;个人觉得物联网还是有点花样的&#…

认证授权功能分析

1 模块需求分析 1.1 什么是认证授权 截至目前&#xff0c;项目已经完成了课程发布功能&#xff0c;课程发布后用户通过在线学习页面点播视频进行学习。如何去记录学生的学习过程呢&#xff1f;要想掌握学生的学习情况就需要知道用户的身份信息&#xff0c;记录哪个用户在什么…

1949-2020年各省全要素生产率(年度)

1949-2020年各省全要素生产率(年度) 1、时间&#xff1a;1949-2020年 2、计算说明&#xff1a;产出为实际GDP&#xff0c;投入要素为从业人员数、固定资产&#xff08;永续盘存法&#xff09; 3、范围&#xff1a;包括31省 4、指标说明&#xff1a; 全要素生产率&#xf…

TiCDC 源码阅读(三)TiCDC 集群工作过程解析

内容概要 TiCDC 是一款 TiDB 增量数据同步工具&#xff0c;通过拉取上游 TiKV 的数据变更日志&#xff0c;TiCDC 可以将数据解析为有序的行级变更数据输出到下游。 本文是 TiCDC 源码解读的第三篇&#xff0c;主要内容是讲述 TiCDC 集群的启动及基本工作过程&#xff0c;将从…

MSR 5660设备进行流量整形和带宽保证的实现案例

组网及说明现场客户使用MSR5660设备替换客户处原有华为的设备&#xff0c;之前华为的设备做了GTS流量整形和WFQ的带宽保证&#xff0c;需求见下面图片描述配置步骤一、针对G1/0/1口入方向进行本地优先级的映射&#xff1a;&#xff08;1&#xff09;对进入设备G1/0/1口的流量进…

机器学习(二):人工智能发展历程

文章目录 人工智能发展历程 一、人工智能的起源 1、图灵测试 2、达特茅斯会议 二、发展历程 人工智能发展历程 一、人工智能的起源 1、图灵测试 测试者与被测试者&#xff08;一个人和一台机器&#xff09;隔开的情况下&#xff0c;通过一些装置&#xff08;如键盘&…

synchronized 原理(锁升级、锁消除和锁粗化)

目录&#xff1a;一、前言二、锁升级三、锁消除四、锁粗化一、前言根据之前的锁策略&#xff0c;可以总结出, synchronized 具有以下特性( JDK 1.8):1. 开始时是乐观锁, 如果锁冲突频繁, 就转换为悲观锁。2. 开始是轻量级锁实现, 如果锁被持有的时间较长, 就转换成重量级锁。3.…

Javascript 职责链模式

现实中的职责链模式 职责链模式的例子在现实中并不难找到&#xff0c;以下就是两个常见的跟职责链模式有关的场景。 ❏ 如果早高峰能顺利挤上公交车的话&#xff0c;那么估计这一天都会过得很开心。因为公交车上人实在太多了&#xff0c;经常上车后却找不到售票员在哪&#x…

年前最后一次分享5款小工具

马上要回家过年了&#xff0c;今年最后一次分享&#xff0c;希望大家喜欢。 1.图片管理器——Imagine 在管理器支持直接预览压缩包图片。支持图片编辑、图片批量转换、批量重命名、支持 GIF 动态图片编辑。如插入帧、修改帧的速度、循环播放、尺寸。同时还支持让系统右键菜单…

熟悉 NestJS (文末附视频)

前言 经过了需求分析以及技术选型之后&#xff0c;我们正式步入了第三个环节&#xff1a;脚手架搭建。 工欲善其事&#xff0c;必先利其器&#xff0c;NestJS 为开发者提供了很多开箱即用的功能&#xff0c;我们可以根据团队的需求搭建一套适配所有业务开发的基础脚手架。所以…

帮助有一定计算机基础的人 快速复习并重新拾起C语言基础

这里写目录标题1.C语言程序举例2.详解C语言程序结构1&#xff09;#include2&#xff09;main 函数&#xff1a;3&#xff09;{} 括号&#xff0c;程序体和代码块4&#xff09;注释5&#xff09;print 函数6&#xff09;return 语句3 C程序的编译步骤是怎样&#xff1f;4.数据的…

【Go基础】面向对象和反射机制

文章目录一、面向对象1. 面向对象的概念2. 构造函数3. 继承与重写4. 泛型二、反射1. 反射介绍2. 反射的基础数据类型3. 反射API3.1 reflect.Type①如何得到Type②指针Type转为非指针Type③获取struct成员变量的信息④获取struct成员方法的信息⑤获取函数的信息⑥判断类型是否实…

『 MySQL篇 』:MySQL表的CURD操作

&#x1f4e2; MySQL 系列专栏持续更新中 … MySQL专栏 ​ 目录 目录一、SQL语句- SQL通用语法- 注释- SQL语句分类二、 基础表操作- 创建表- 查看库中的表- 查看表结构- 删除表- 重命名表三、MySQL 中的增删查改操作- 增加&#xff08;insert语句&#xff09;- 查询(select语…

CSS 使用 @font-face 引入外部字体

CSS 使用 font-face 引入外部字体下载所需字体到本地把下载字体文件放入font文件夹里定义字体引用字体结果&#x1f62c;没有退路时&#xff0c;潜能就发挥出来了 CSS 中使用开源字体 得意黑 得意黑的字体是真的好看 ✨推荐使用 下载所需字体到本地 这里介绍一款不错的中文字…

Shiro:核心组件、配置类、多Realm场景、自定义拦截器、实战场景

目录Shiro 的核心组件Shiro 认证流程Shiro 授权流程单 RealmShiro 登陆认证 SimpleAuthenticationInfo 对象多 RealmShiroConfigShiro过滤器配置 ShiroFilterFactoryBeanShiro自定义过滤器Shiro 过滤器执行链路梳理代码自取层级结构Login.javaBearerTokenRealm.javaShiroRealm.…

桶排序详细说明及实现-python

前言&#xff1a; 说到桶排序&#xff0c;那必定要有桶&#xff0c;那么桶的作用是什么呢&#xff1f;桶的作用就是将序列分为若干份放到桶中&#xff0c;每个桶中能装入的数量范围是一定的&#xff0c;只有最后一个桶可以设置装入很多。这是因为当分的桶一定时&#xff0c;前面…

SpringMVC-基础入门

文章目录SpringMVC1&#xff0c;SpringMVC概述2&#xff0c;SpringMVC入门案例2.1 需求分析2.2 案例制作步骤1:创建Maven项目步骤2:补全目录结构步骤3:导入jar包步骤4:创建配置类步骤5:创建Controller类步骤6:使用配置类替换web.xml步骤7:配置Tomcat环境步骤8:启动运行项目步骤…