基于transfomer架构的模型[GPT、BERT、VIT、ST、MAE等等]总结

news2024/11/22 17:01:16

Transformer

首先我们来回顾一下Transformer

模型架构图

对于Transformer从宏观角度可以可以理解为6个Encoder+6个Decoder组成

各部分介绍

输入部分

主要就是词嵌入+位置编码

对于词嵌入比较简单,就是对一个句子里的每个词做一个嵌入操作映射到相应的维度。一般来说就是先把句子中的词转为对应的数字索引,然后根据这个索引做embedding操作得到一个相应维度的向量。

对于位置编码,Transformer这里的位置编码是不可学习的(不会更新),直接用公式计算出来的。

其中dmodel是词嵌入得到单个词的向量的维度大小;

pos表示词的位置,比如"我爱学习啊"这句话有5个词,其位置就分别为0,1,2,3,4;

2i2i+1分别表示在维度的偶数位置和奇数位置(i表示的是维度,这个维度的大小是dmodel,假如dmodel的大小为512,那么维度位置取值为0~511,那么i的取值为0~255,2i的取值为0,2,4···510,2i+1的取值为1,3,5···511),也就是说在偶数位置使用sin,奇数位置使用cos;

最后得到的每个词的位置编码维度大小和其词嵌入维度大小一样(均为dmodel),因此将每个词的位置编码和词嵌入加起来得到最终输入。

Encoder

主要包含多头注意力和前馈神经网络。

首先是Muti-Head Attention,其包含多个Scaled Dot-Product Attention,将多个Scaled Dot-Product Attention的输出结果拼接在一起然后传入Linear层将其映射为和输入矩阵维度一样。

其次对于Scaled Dot-Product Attention则是重点了。图中的Q、K、V是由该模块输入X分别乘WQ,WK,WV得到的,而WQ,WK,WV是随机初始化得到并在训练过程和其他参数一样根据梯度下降进行更新。那么Attention的计算公式如下(dk是Q矩阵的列数):

简单来说就是Q乘K的转置然后除以根号dk来防止内积过大并做softmax操作得到概率最后乘V得到单词之间的Attention系数。

然后Add & Norm比较好理解,第一个Add & Norm就是应用了残差连接将Multi-Head Attention的输入和输出相加然后做layerNorm操作(一种将每一层神经元的输入都转成均值方差都一样的归一化操作);第二个Add & Norm同理将FeedForward的输入和输出相加然后做layerNorm操作。

最后是Feed Forward层,比较简单,主要就是两个linear层。

Decoder

基本和Encoder大差不差。主要的几点不同如下:

  1. 第一个Muti-Head Attention多了个Masked,该操作在Scaled Dot-Product Attention中的Attention计算公式的softmax操作之前使用。该操作就是屏蔽单词后面的内容,使得训练的时候与预测时都不能看到之后的单词信息而保持一致。

  1. 第二个Muti-Head Attention的K、V矩阵则是由Encoder的输出提供的,其输入只生成Q矩阵。

输出部分

一般来说就是通过linear层将其映射到词表维度,最后做softmax求各个词的概率。

GPT

GPT的核心思想是把Transformer的decoder拿出来在没有标号的大量文本数据上训练一个语言模型来获得一个预训练模型,然后用其在子任务上做微调得到每一个任务所需的分类器。

模型架构图

其由12个Transformer中的Decoder模块经修改后组成,相比于原始的Decoder其去掉了第二个Muti-Head Attention。由于语言模型是利用上文预测下一个单词的,那么就保留采用Mask Multi-Head Attention,其中的Masked会对单词的下文遮挡。

实现过程与原理

其主要分为两个阶段:无监督Pre-training和有监督Fine-tuning。

无监督Pre-training

对于第一个阶段。假设有一个未标记的文本u,其中每个词表示为ui,从u1一直到un,取一个超参数k,代表上下文窗口大小(或输入序列的长度)。既然要预测第i个出现的概率,第i个词表示为ui,通过下面公式即使用模型通过ui前面的k个词来预测ui的概率,并把所有的i得到的概率加起来,由于做了log操作,所以相当于所有词出现的概率相乘即这个文本出现的联合概率。总的来说就是训练一个模型使其能够最大概率的输出跟我的文本长得一样的文本。

使用的模型则是上面说transformer的decoder变种。假如我们要预测词u的概率,将其前k个词记为U,将其做一个词嵌入的投影并加上位置信息的编码得到h0(We是词的Embedding矩阵,Wp是位置编码的Embedding矩阵),后面n层每一层的输入来自上一层的输出,最后一层输出做一个词嵌入的投影并通过softmax得到概率分布。

有监督Fine-tuning

对于第二个阶段。假设一个集合输入一个C集合,其输出为一个分类y的标号,将该集合输入到训练好的模型中得到其中transformer_block最后一层的输出,然后乘以参数Wy(预测输出时的参数,该参数随着微调过程会调整)做一个softmax操作就可以得到概率了

那么基于上述把所有带标号的序列输入进去后计算其是真实分类标号的联合概率,那么微调的时候应该使结果L2更大,即联合概率更大。

不仅需要调整参数使得L2更大,结合考虑之前预训练的L1可以提高模型泛化性和加速收敛。也就是在微调时考虑两个目标函数,即不仅需要给定一个序列预测序列的下一个词还需要给定一个完整序列预测其对应的标号。通过一个超参数将这两个目标函数联系起来。不过注意这里的L1使用的是和L2一样的集合C,只是不需要使用到标号y

关于GPT-2和GPT-3

GPT-2在模型方面和GPT差不多,主要不同的地方是在子任务的处理时不提供任何相关的训练样本而是直接用预训练的模型去对子任务做预测,精炼点说就是用无监督的预训练模型去做有监督任务。GPT2.0旨在通过一个数量更大更丰富广泛通用性强质量高的无监督训练数据训练更好更大的预训练模型,把GPT第二阶段Fine-tuning有监督地做下游任务换成了无监督地做下游任务。总的来说GPT模型其发展倾向于用更少的有监督数据+更多的无监督数据去训练模型。

GPT-1在子任务上使用训练样本微调更新参数,GPT-2则不使用相关训练样本,而GPT-3则在子任务上使用少量的相关训练样本但不更新参数。为了更好的了解GPT-2和GPT-3的区别,我们要解释两个概念,“zero-shot”表示不提供样本,“few-shot”表示每个子任务提供10-100个训练样本。那么GPT-2就是一个参数量较小的zero-shot,而GPT-3就是一个参数量更大的few-shot。当然更具体的可以去看论文,这里就不多赘述了

BERT

模型架构图

BERT主要使用了Transformer的Encoder架构,将多个Encoder堆叠在一起,其中使用了12个Encoder,使用了24个Encoder。

修改点

输入部分

BERT相对于GPT系列的单向采用双向表示,使得其对于文本生成类任务不太适用了,并且ICLR2020这篇Incorporating BERT into Neural Machine Translation也尝试了简单的用bert做机器翻译也不会得到好的效果。虽然不适合上述任务,但其可以很好的用于比如NSP(Next Sentence Prediction )任务,一般包含问答(QA)和自然语言推理(NLI)等等。而这类任务一般包含一个句子对的输入。那么每个句子对的开头用一个特殊的分类标记([CLS]),然后用一个特殊的标记([SEP])将句子分开。

相对于原始的Transformer的输入部分,主要有两处修改:

①位置编码被修改为了可学习的位置嵌入;

②新增了段嵌入以表明该词属于哪个句子。

也就是说最终输入为词嵌入+段嵌入+可学习的位置嵌入

词嵌入比较简单,不多赘述。

对于段嵌入,比如图中[CLS] my dog is cute [seq]为第一句话,he likes play ##ing{其实##代表ing是play后缀,所以实际为playing,不过因为play比较常见,所以进行了拆分} [seq]为第二句话,也就是说总共有两段,那么这里可以理解为对这两段做embedding操作,所以得到的结果中处于同一段(句)的词的段嵌入一定相同。

对于位置嵌入,可以理解为对每个词在原来句子中的位置做embedding操作,其实这三个操作的实现几乎一样,都是基于Embedding,只不过嵌入字典的大小不同,说这么多可能还是有点晦涩难懂,看一下代码实现保证你就懂了,如下图:

预训练

没有使用传统的从左到右或从右到左的语言模型来预训练BERT,而是使用两个无监督任务预训练BERT,即MLM和NSP。

GPT是典型的自回归模型(AR,autoregressive),只考虑单侧信息,而BERT是自编码模型(AE,autoencoding),可以利用上下文信息从损坏的输入数据中预测重建原始数据。

MLM

那么BERT是如何损坏文本信息,然后让其重建呢?主要是用到了MASK操作。又因为[MASK]在微调期间不会出现,所以会在预训练和微调之间造成了不匹配。为了缓解这一点,我们并不总是用实际的[MASK]替换单词。

mask操作的策略如下:

首先随机选取15%的词进行MASK(但实际上这15%并不是全部MASK,呼应了前面的那句话),对于这15%的词,再进行如下操作权衡:

  1. 80%替换为[MASK];

  1. 10%替换成随机词(ps:有很小概率随机到自己);

  1. 10%不变

所以总得来说MLM任务类似于把文本随机挖空一些词然后做完形填空。比如输入是一句话“新年快乐”。

对于上面提到的GPT模型其优化目标为:

P(新年快乐)=P(新)P(年|新)P(快|新年)P(乐|新年快);

那么此时BERT的MLM任务的优化目标:

假如经过MASK操作后变为“新[MASK]快乐”

P(新年快乐|新[MASK]快乐)=P([MASK]=年|新[MASK]快乐)

NSP

为了训练一个能够理解句子关系的模型,我们对一个二元化的NSP任务进行了预训练,该任务可以从任何单语言语料库中简单地生成。具体来说,在为每个预训练例子选择句子A和B时,50%的情况下B是A之后的实际下一句句子(标记为IsNext,即正样本), 另外50%的情况下B是语料库中的随机句子(标记为NotNext,即负样本)。如下图所示,C用于NSP(就是用得到的[CLS]表示来做二分类任务)。

微调

上图分别是在各种下游任务(句子对分类任务、单个句子分类任务、问答任务、序列标注任务)上微调BERT。

对于每个任务,我们只需将特定于任务的输入和输出插入BERT,并对所有端到端的参数进行微调。在输出端,词表示被送入输出层用于词级别的任务,例如序列标记或问答,而[CLS]表示被送入输出层用于分类任务,例如包含或情感分析。

与预训练相比,微调成本相对较低。

从完全相同的预训练模型开始,本文中的所有结果可以在一个云TPU上花费最多1小时复现,或者在一个GPU上花几个小时。

扩展

文中提到了的参数量,我们不妨借助下面的图计算推导一下:

  1. 首先嵌入层的字典大小就是词表大小,为30k(ps:这里的k不是指参数,就是千的意思),然后映射到的维度是H;

  1. 然后多头自注意力层,对于每个头得到Q、K、V需要进行映射,其大小均为H*64,由于有A个头,且A*64=H,所以把它们在每个头之间合并起来就相当于3个H*H的矩阵,之后还需要对输出做一个Linear层,其参数大小为H*H,所以总共是

  1. 最后是前馈神经网络层,主要就是两个Linear,第一个Linear输入是H,输出是4H,第二个Linear输入是4H,输出是H,那么总共是

所以说总的参数个数为30k*H + *L

那么把的H和L分别代入得到:

30000*768+12*768*768*12=107,974,656≈110M

30000*1024+12*1024*1024*24=332,709,888≈340M

VIT

模型架构图

VIT(Vision Transformer)就是将多个Encoder堆叠起来,不过根据下面的图来看其Encoder和我们认识中的传统的Transformer的Encoder有细微的差别,具体的留到下面小节分析。

修改点

输入部分

之前的Transformer架构相关模型都是用在NLP领域,所以输入都是由多个词组成的句子,而现在的输入变成了由多个像素组成的图片,那么自然第一想法就是把像素类比为词输入进去,但是一张图片的像素个数往往远大于句子的长度,这样会导致输入的序列长度非常长,所以显然是不行的。

VIT的思想是把图片切成一个个patch,一个patch含有多个像素,对应句子中的token。

  1. 假如图像为HxWxC,其中H为高,W为宽,C为通道数,我们把其切为N个大小为PxPxC的patch,其中N=HW/P^2,N就是Transformer输入的实际有效序列长度;

  1. 在Transformer块中使用的隐向量大小为dmodel,那么需要通过一个线性投影将patch展平然后映射到dmodel维度;

  1. 类似于BERT,开头也加上了[class],所以需要得到其词嵌入,此时序列长度为N+1;

  1. 对整个序列使用标准的可学习1D位置嵌入得到各个“词”的位置嵌入(可以理解为nn.Embedding(N+1,d_model)),其中[class]的词嵌入与其位置嵌入相加,其余patch通过步骤2得到的dmodel维度结果与其位置嵌入相加。得到的最终结果就是真正的输入了。

Encoder部分

传统的Encoder

VIT中的Encoder

在该Encoder部分,无论是多头自注意力层还是MLP层,其都把norm操作提前了。

我个人认为是在做NLP时最开始生成的embedding都是归一化过的,现在改成图像了,所以需要先把图像中每个patch对应的输入先做归一化。

Swin Transformer

模型架构图

宏观来看主要是使用到了多个transformer的encoder部分,首先从上面图可以看出这里encoder的归一化LN操作也提前了且对传统的多头自注意力层做了修改,其次相对于前面谈到的模型,encoder块之间不是简单的堆叠,而是中间穿插了Patch Merging操作。

流程剖析

Patch Partition

这一步的思路和VIT相似,就是把图片切成一个个4x4的patch(VIT中是16x16)。由于输入图片高H、宽W以及通道数3,所以切分后,一个patch就是4x4x3,有HW/16个patch,对应图中表示的维度为

Linear Embedding

这层比较简单,就是应用了线性嵌入层,将其投影到任意维度(记为C),经过这一层得到的维度为

Swin Transformer Block

窗口多头自注意力(W-MSA)

相比于VIT中把含有多个patch的图片类比为含有多个token的句子输入到transformer块中,Swin Transfomer主要是把一个图片分为多个window(一个window中有多个patch),也就是说每次输入到transformer块中的东西变为了一个含有多个patch的window,而一张图片中的多个window就类比为了多个句子,更进一步类比为输入的批。

假设一个图片有hxw个patch(那么类比为输入到transformer块中的序列长度为hw),C为transformer块中的隐层维度,我们来计算一下W-MSA和VIT中MSA的复杂度:

首先对于VIT中的MSA:

  1. 输入X分别与参数相乘得到Q、K、V。其中X维度为(hw,C),的维度均为(C,C)(ps:由于有多头,假设有A个头,使得实际单个头里这些W的维度是(C,B),但由于A*B=C,把各个头的W矩阵拼接起来就是维度(C,C)了,那么等价于我们忽略多头来估算处理的情况,下面第2步涉及到C的也是用到了类似思想)。此时复杂度为

  1. 对于attention公式,涉及到以及softmax操作后与V相乘,第一个矩阵乘法的左右乘数的矩阵维度为(hw,C)和(C,hw),那么复杂度为,第二个矩阵乘法的左右乘数的矩阵维度为(hw,hw)和(hw,c),那么复杂度为。这一步总复杂度为

  1. 最后结果(维度是(hw,C))还需要过一个(C,C)的Linear层得到attention层输出,复杂度为

所以VIT中W-MSA总的复杂度为:

其次对于W-MSA:

VIT中一次输入一个图片,patch个数为hw,类比为序列长度为hw,那么W-MSA一次输入一个窗口,patch个数为,一个图片中有个windows,所以可以理解为批量为的"序列长度"为版本的MSA。式(1)中hw替换为,然后整个结果乘,即

,化简之后得到复杂度如下:

根据公式很明显得出,如果M*M<hw,那么W-MSA的复杂度比MSA更小。

移动窗口多头自注意力(SW-MSA)

W-MSA缺乏跨窗口之间的通信,这限制了它的建模能力。为了引入跨窗口间通信,同时保持非重叠窗口的高效计算。因此提出了SW-MSA,该方法在连续Swin Transformer块中的两个分区配置之间交替(如下图,也就是说每两个encoder块中第一个使用W-MSA,第二个使用SW-MSA)。

具体做法就是对原始图片进行循环移位,比如下图中,把原图的A、B、C部分(其中A的高或宽为M/2)进行了移位得到了新的图像矩阵,对新的图像进行窗口划分即可,这样巧妙的引入了跨窗口间的通信,但又带来了新的问题,即一个窗口中可能包含原本不相邻的patch,这些不相邻的patch不应该一起做自注意力的,因为它们之间没有太大联系且这个移位操作是我们人为引入的,模型不应该学习它们的关系。那么如下图所示需要给含有不相邻patch的窗口引入掩码模板以便在计算attention的softmax操作之前把不相邻的部分变成一个很小的负值,使得其在softmax操作中几乎对其他项没有影响。最后需要把循环移位还原回去,因为我们需要保持原来图片的相对位置,保持其整体图片的语义信息,如果没有这一步,那么图片会在操作过程中一直往右下角移,最终导致其语义信息被破坏掉。

掩码模板就是把不相邻的不需要的那部分设置为-100,其他需要的为0,这样和原始softmax操作之前的矩阵结果加起来,使得不需要的部分数值非常小,经过softmax操作后变为0。这些掩码模板官方给出了很好的示例,具体如下:

第一个窗口不存在不相邻patch,所以掩码模板全为0,其他窗口都存在不相邻patch,所以有特定的掩码模板(模板大小就是seq_len*seq_len,其中seq_len是输入到transformer块中的序列长度,可以理解为一个窗口中的patch数量)。

图中例子图片大小是14x14,一个窗口有7x7个patch(可以类比为每次输入序列长度为49),即M=7,所以移动的部分shift为M/2=3。

掩码模板的更具体实现可参考官方issuehttps://github.com/microsoft/Swin-Transformer/issues/38

相对位置编码

上面说到一个窗口中有多个patch,可以类比为一个序列中有多个token,也就是每次输入到块中的是一个含有多个patch的窗口,显然需要为窗口中的patch加上位置编码,而该模型使用的是相对位置编码,并且不是在输入的时候加上位置编码,而是在计算attention的公式中在进行softmax操作之前得到矩阵上加上位置编码,如下图中的B。

假如一个窗口有MxM个patch(可以类比为NLP中一句话有MxM个token,所以相当于序列长度为),显然B矩阵的形状为

那么B如何得到呢?具体举例如下:

为了方便举例画图,假设一个窗口有4个patch,那么可以类比为序列长度为4,那么此时通过得到的结果矩阵维度是4x4,假设其如下:

分别以patch1、patch2、patch3、patch4为(0,0)原点得到的二维相对位置分别如下:

然后分别把得到的四个矩阵展平为一行,拼接成新的4x4的矩阵:

然后设一个window中有MxM个patch,这个例子中我们M=2,那么操作如下,最终得到4x4的索引矩阵

最后根据这个矩阵中的各个索引去做嵌入操作得到值组成的4x4矩阵就是我们公式中的B矩阵。

其中嵌入操作可以理解为nn.Embedding((2M-1)*(2M-1), 1),相当于对这些索引做一个词典大小为(2M-1)*(2M-1),嵌入向量维度大小为1的嵌入操作,当然了,由于这个索引是不可学习的,考虑到自注意力机制都是多头的,假设有num_heads个头,一般为了方便,可以一次生成所有头的B矩阵(此时维度是4x4xnum_heads),因此嵌入操作修改为nn.Embedding((2M-1)*(2M-1), num_heads)。

Patch Merging

顾名思义就是把邻近的小patch合并成一个patch,是一个下采样的过程。假设原始张量维度为HxWxC,这里为了方便假设C为1,本文中主要是下采样两倍,那么就每隔一个点选一个组成patch,使得原始张量变为了4个,维度变为了,然后用2C个1x1的卷积核操作把通道数变为了2C,最后维度为。具体变换过程如下图:

MAE

模型架构与流程

MAE(Masked Autoencoders)用到了Transformer中的encoder和decoder,不同于之前的模型,大部分都只用到了两者中的一个。该模型主要就是抽取图像特征并进行图像重建恢复。

  1. 将图像划分为多个patch(这一步借鉴于VIT),然后随机mask掉75%的patch;

  1. 然后把未mask的剩余patch作为序列输入到encoder中(这里使用的是前面讲的VIT模型,所以对于输入的处理也相同,即线性投影和位置嵌入);

  1. 因为transformer块的输入和输出形状相同,所以将encoder输出与之前mask掉的patch按照原图的顺序拼接起来作为decoder的输入(对于输入处理当然还是需要加入位置嵌入的);

  1. decoder输出后,最后一层是线性投影,其输出通道的数量等于一个patch中的像素值的数量,以便于重建图像。损失函数则是在像素空间中计算重建图像和原始图像之间的均方误差(MSE),这里只计算被mask的patch上的损失。

其中MAE的解码端只在预训练期间用于执行图像重建任务,在下游任务微调不需要使用。

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

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

相关文章

一次非典型的Netty内存泄露案例复盘

背景 作为后端开发相信大家或多或少都接触过Nettty&#xff0c;说起Netty真实又爱又恨&#xff0c;因为基于它可以很简单的开发高性能的Java网络通信服务&#xff0c;但同时要是不小心就会出现各种奇奇怪怪的问题&#xff0c;特别是由于特殊的内存管理机制很容易出现内存泄漏问…

数据大佬的成长经验分享 | ​我的非典型数据分析之路

小飞象交流会哪有什么错过的人&#xff0c;会离开的都是路人。哪有什么命运不公&#xff0c;都是懒惰让你变得无能。内部交流│19期数据大佬的成长经验分享我的非典型数据分析之路data analysis●●●●分享人&#xff1a;夏宇‍在大数据、人工智能热、5G、物联网的时代&#x…

1、Mavan项目管理工具

1.1 什么是 Maven 1.1.1 什么是 Maven Maven 的正确发音是[ˈmevən]&#xff0c;而不是“马瘟”以及其他什么瘟。Maven 在美国是一个口语化的词 语&#xff0c;代表专家、内行的意思。 一个对 Maven 比较正式的定义是这么说的&#xff1a;Maven 是一个项目管理工具&#xff0…

Spring Boot学习篇(十)

Spring Boot学习篇(十) shiro安全框架使用篇(二)——登录实例(密码以密文方式存储,不含记住密码) 1.模拟注册时,生成密文到数据库中 1.1 在zlz包下创建util包,并在下面创建SHAUtil01类(初始里面无方法)和SHAUtil02类,其目录结构如下所示 1.2 两种生成密文的方式 1.2.1 自己…

一篇文章彻底搞懂折半查找法[二分查找法]算法~

算法实现的要求&#xff1a; 折半查找法又称为二分查找法&#xff0c;这种方法对待查找的列表有两个要求&#xff1a; 1&#xff1a;必须采用顺序存储结构 2&#xff1a;必须按关键字大小有序排列算法思想&#xff1a; 将表中间位置记录的关键字与查找关键字进行比较&#x…

性能测试时那些「难以启齿」的问题-CPU相关

NO.1 为什么cpu使用率可以>100%? 小白的我在进行压测的时候&#xff0c;查看服务的cpu总使用率如下&#xff0c;总使用率会超过100%&#xff0c;这个数据是怎么来的呢&#xff0c;为什么会有大于100%的情况呢&#xff1f; 作为小白的我刚开始觉得这个问题应该很基础&#x…

Go语言实现猜数字小游戏

目录 前言 一、设计思路 二、代码编写 2.1 产生随机数 2.2 用户输入数据 2.3 核心代码 三、 全部代码 四、效果图 总结 前言 最近在学习go语言&#xff0c;刚刚学完go语言的基础语法。编写了一个猜数字的小游戏来练习循环、分支语句、变量定义、输入输出等基础的go语…

4、变量与常量

目录 一、标识符和关键字 1.标识符 2.关键字 二、声明变量 三、声明常量 四、变量的有效范围 1. 成员变量 2. 局部变量 一、标识符和关键字 1.标识符 Java语言规定标识符由任意顺序的字母、下画线&#xff08;_&#xff09;、美元符号&#xff08;$&#xff09;和数字…

【数据结构】手撕八大排序算法

作者&#xff1a;一个喜欢猫咪的的程序员 专栏&#xff1a;《数据结构》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 目录 1.排序的概念&#xff1a; 2.八大排序的思路及其细节 2.1直接插入排序 …

适合编程初学者的开源项目:小游戏2048(安卓Compose版)

目标 为编程初学者打造入门学习项目&#xff0c;使用各种主流编程语言来实现。 2048游戏规则 一共16个单元格&#xff0c;初始时由2或者4构成。 1、手指向一个方向滑动&#xff0c;所有格子会向那个方向运动。 2、相同数字的两个格子&#xff0c;相遇时数字会相加。 3、每次…

SpringMVC面试题

概述 什么是Spring MVC&#xff1f;简单介绍下你对Spring MVC的理解&#xff1f; Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架&#xff0c;通过把模型-视图-控制器分离&#xff0c;将web层进行职责解耦&#xff0c;把复杂的web应用分成逻辑清…

如何在Linux上搭建C++开发环境

工欲善其事&#xff0c;必先利其器&#xff01;我们要在Linux上开发C程序&#xff0c;就要先搭建好它的开发环境。 搭建环境步骤安装Linux安装开发工具写一个demo在项目根目录创建一个构建脚本build.sh使用CodeLite IDE打开项目安装Linux Linux的发行版本很多&#xff0c;萝卜…

测试开发——测试分类

目录 一、 有关测试用例的回顾 二、 测试用例的划分 1、 按照测试对象来划分 可靠性测试 容错性测试 内存泄漏测试 弱网测试 2、按照是否查看代码划分 3、按照开发阶段划分 一、 有关测试用例的回顾 万能测试用例设计公式 如何根据需求去设计测试用例&#xff1f; …

计算机视觉OpenCv学习系列:第三部分、滚动条操作

第三部分、滚动条操作第一节、滚动条操作1.事件响应函数&#xff08;1&#xff09;UI组件时间响应过程&#xff08;2&#xff09;事件响应函数&#xff08;3&#xff09;创建窗口函数&#xff08;4&#xff09;调整图像亮度2.滚动条操作3.代码练习与测试学习参考第一节、滚动条…

Python 协程学习有点难度?这篇文字值得你去收藏

Python 协程在基础学习阶段&#xff0c;属于有难度的知识点&#xff0c;建议大家在学习的时候&#xff0c;一定要反复练习。 Python 中的协程是一种用户态的轻量级线程。它与普通的线程不同&#xff0c;普通线程是由操作系统调度的&#xff0c;而协程是由程序自己调度的。因此&…

【ESP 保姆级教程】玩转emqx篇③ ——认证安全之使用内置数据库(Mnesia)的密码认证

忘记过去&#xff0c;超越自己 ❤️ 博客主页 单片机菜鸟哥&#xff0c;一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2023-01-15 ❤️❤️ 本篇更新记录 2022-01-15 ❤️&#x1f389; 欢迎关注 &#x1f50e;点赞 &#x1f44d;收藏 ⭐️留言&#x1f4dd;&#x1f64…

Transformer模型详解相关了解

文章目录Transformer模型详解1.前言1.1 Transformer 整体结构1.2 Transformer 的工作流程2. Transformer 的输入2.1 单词 Embedding2.2 位置 Embedding3. Self-Attention&#xff08;自注意力机制&#xff09;3.1 Self-Attention 结构3.2 Q, K, V 的计算3.3 Self-Attention 的输…

《神经网络与深度学习》 邱希鹏 学习笔记(一)

一、机器学习的基本要素 机器学习的基本要素: 模型 学习准则 优化算法 其中模型分为线性和非线性。学习准则有用损失函数来评价模型的好坏&#xff0c;还有经验风险最小化准则&#xff0c;大概意思就是在平均损失函数中获得最小的损失函数&#xff0c;但是因为样本可能很小&…

Goodbye 2022,Welcome 2023 | 锁定 2023

引言又是一年春来到&#xff0c;新年应比旧年好。旧岁已辞&#xff0c;新年已到&#xff0c;新旧更迭之际&#xff0c;真想剪个头发换身行头&#xff0c;就能重新出发。但终究是要回头看看啊&#xff0c;那一路而来的荆棘与芬芳&#xff0c;才是成长的印记啊。那就回拨记忆&…

和涤生大数据的故事

1自我介绍 大家好&#xff0c;我是泰罗奥特曼&#xff0c;毕业于东北的一所不知名一本大学&#xff0c;学校在一个小城市里面&#xff0c;最热闹的地方是一个四层楼的商城&#xff0c;专业是信息管理与信息系统&#xff0c;由于是调剂的&#xff0c;所以我也不知道这个专业是干…