【李沐论文精读】Transformer精读

论文:Attention is All You Need

参考:李沐视频【Transformer论文逐段精读】、Transformer论文逐段精读【论文精读】、李沐视频精读系列

一、摘要

        主流的序列转换(sequence transduction)模型都是基于复杂的循环或卷积神经网络,这个模型包含一个编码器(encoder)和一个解码器(decoder)。具有最好性能的模型在编码和解码之间通过一个注意力机制链接编解码器。我们提出了一个新的简单网络架构——基于attention 的Transformer。其仅仅是基于注意力机制,而完全不需要之前的循环或卷积。在两个机器翻译任务上的实验BLEU 表明,该模型具有更好的性能,同时并行度更好,并且训练时间更少。(泛化到其它任务效果也不错)

二、结论

        本文介绍了Transformer,这是第一个完全基于注意力的序列转换模型,用多头自注意力(multi-headed self-attention)代替了encoder-decoder架构中最常用的循环层。

三、导言

        序列建模和转换问题(如机器翻译)最新方法是LSTM和GRN等。后面许多研究都围绕循环语言模型和编码器-解码器体系结构进行。
        循环网络模型(RNN)通常是考虑了输入和输出序列的中字符位置的计算。当前时刻隐藏状态h_{t},是由上一时刻隐藏状态h_{t-1}t时刻输入共同决定的。(把之前的信息都放在隐藏状态里,一个个传递下去,是RNN处理时序信息的关键)。这种固有的时序模型难以并行化处理,计算性能就很差。过早的历史信息可能被丢掉,时序信息是一步一步往后传递的,一个大的 h_{t} 存历史信息。每一个 计算步都需要存储,内存开销大。

attention在此之前,已经成功的应用在encoder-decoder架构中,但主要是用在如何把编码器的信息有效的传递给解码器,所以是和RNN一起使用的。
本文提出的Transformer,不再使用循环神经层,而是纯基于注意力机制,来构造输入和输出之间的全局依赖关系。Transformer可以进行更多的并行化,训练时间更短但翻译效果更好。

四、related work

使用卷积神经网络替换循环神经网络,并行计算所有输入和输出位置的隐藏表示,是扩展神经GPU,ByteNet和ConvS2S的基础,因为这样可以减少时序计算。但是CNN对长序列难以建模(因为卷积计算时,卷积核/感受野比较小,如果序列很长,需要使用多层卷积才可以将两个比较远的位置关联起来)。因为卷积计算的时候看一个比较小的窗口,比如, 3 * 3 窗口,如果 2 个像素隔得比较远,需要用很多 3 * 3 的卷积层、一层一层的叠加上去,才能把隔得很远的 2个像素联系起来。但是使用Transformer的注意力机制的话,每次(一层)就能看到序列中所有的位置,就不存在这个问题

但是卷积的好处是,输出可以有多个通道,每个通道可以认为是识别不同的模式,作者也想得到这种多通道输出的效果,所以提出了Multi-Head Attention多头注意力机制。(模拟卷积多通道输出效果)

五、模型

        大部分神经序列转换模型都使用encoder-decoder结构。编码器把一个输入序列(x_{1},x_{2},...,x_{n})映射成(z_{1},z_{2},...,z_{n}),比如一个句子有n个词,x_t是第t个词,z_t是第t个词的向量表示。解码器对z中的每个元素,生成输出序列(y_{1},y_{2},...,y_{m}),其中nm可以一样长、可以不一样长,一个时间步生成一个元素。在每一步中,模型都是自回归的(auto-regressive),在生成下一个结果时,会将先前生成的结果加入输入序列来一起预测。(自回归模型的特点,过去时刻的输出可以作为当前时刻的输入

        Transformer遵循这种整体架构,对编码器和解码器使用堆叠的自注意力(stacked self-attention and point-wise)和逐点全连接层(fully-connected layers),分别如下图的左半部分和右半部分所示。

5.1 Encoder and Decoder Stacks

编码器:编码器由N=6个相同encoder层堆栈(下图标红部分)组成,每层有两个子层。

子层1:multi-head self-attention

子层2:FFNN层(前馈神经网络层,Feed Forward Neural Network),其实就是MLP,为了fancy一点,就把名字起的很长。

  • 两个子层都使用残差连接(residual connection),然后进行层归一化(layer normalization)。
  • 每个子层的输出做残差连接 和 LayerNorm,公式:LayerNorm(x + Sublayer(x)),其中Sublayer(x)是当前子层的输出。(Sublayer(x) 指 self-attention 或者 MLP)
  • residual connections 需要输入输出维度一致,不一致需要做投影。为了简单起见,模型中的所有子层以及嵌入层的向量维度都是d_{model}=512(如果输入输出维度不一样,残差连接就需要做投影,将其映射到统一维度)。(这和之前的CNN或MLP做法是不一样的,之前都会进行一些下采样)

tip:关于LayNorm和BatchNorm的区别以及画图对比

  • BatchNorm简单的2维情况:每一行是一个样本X,每一列是一个feature。每次把一列(1个 feature)放在一个 mini-batch 里,均值变成 0, 方差变成 1 的标准化。

How:(该列向量 - mini-batch 该列向量的均值)/(mini - batch 该列向量的方差)

训练时:mini-batch 计算均值;

测试时:使用 全局 均值、方差。

        BatchNorm 还会学 lambda1 beta,BatchNorm 可以通过学习将向量放缩成任意均值、任意方差 的一个向量。

  • LayerNorm 跟 BatchNorm 在很多时候几乎是一样的,除了实现的方法有点不一样之外。二维输入情况:对每个样本做 Normalization(把每一行变成 均值为 0、方差为 1),不是对每个特征做normalization。

        LayerNorm在操作上和BatchNorm (二维输入) 的关系:LayerNorm整个把数据转置一次,放到 BatchNorm里面出来的结果,再转置回去,基本上可以得到LayerNorm的结果。

        BatchrNorm三维示意图:

        列是seq序列长度n,第3维feature是每个词额外的向量,d = 512 in transformer

        LayerNorm三维示意图(黄色的线): 

为什么LayerNorm用的多?

首先看切割对比:举例分析:4个长度不一样的样本,0 填充到 max_len。

BatchNorm切出来的结果

BatchNorm 计算均值和方差,有效的是阴影部分,其余是0。

        Mini-batch 的均值和方差:如果样本长度变化比较大的时候,每次计算小批量的均值和方差,均值和方差的抖动大。

        全局的均值和方差:测试时遇到一个特别长的全新样本 (最上方蓝色阴影块),训练时未见过,训练时计算的均值和方差可能不好用。

LayerNorm切出来的结果

LayerNorm 每个样本自己算均值和方差,不需要存全局的均值和方差。它更稳定,不管样本长还是短,均值和方差是在每个样本内计算。

Batch Normalization:在特征d/通道维度做归一化,即归一化不同样本的同一特征。缺点是:

  • 计算变长序列时,变长序列后面会pad 0,这些pad部分是没有意义的,这样进行特征维度做归一化缺少实际意义。
  • 序列长度变化大时,计算出来的均值和方差抖动很大。
  • 预测时使用训练时记录下来的全局均值和方差。如果预测时新样本特别长,超过训练时的长度,那么超过部分是没有记录的均值和方差的,预测会出现问题。

Layer Normalization:在样本b维度进行归一化,即归一化一个样本所有特征。

  • NLP任务中一个序列的所有token都是同一语义空间,进行LN归一化有实际意义
  • 因为实是在每个样本内做的,序列变长时相比BN,计算的数值更稳定。
  • 不需要存一个全局的均值和方差,预测样本长度不影响最终结果。

解码器:解码器同样由 N=6个相同的decoder层堆栈组成,每个sub-layer的 residual connections、layer normalization。每个layer里有 2个 encoder 中的 sub-layers, decoder 有第 3 个 sub-layer,对 encoder 的输出做 multi-head attention。decoder是auto-regressive自回归。当前时刻的输入集是之前一些时刻的输出。做预测时,decoder不能看到之后时刻的输出。

  • 子层1:Masked multi-head self-attention:在解码器里,Self Attention 层只允许关注到输出序列中早于当前位置之前的单词。具体做法是:在 Self Attention 分数经过 Softmax 层之前,使用attention mask,屏蔽当前位置之后的那些位置。(目的:在预测第t个时刻的输出的时候,decoder不应该看到t时刻以后的那些输入)所以叫Masked multi-head self Attention。(对应masked位置使用一个很大的负数-inf,使得softmax之后其对应值为0)
  • 子层2:Encoder-Decoder Attention :编码器输出最终向量,将会输入到每个解码器的Encoder-Decoder Attention层,用来帮解码器把注意力集中中输入序列的合适位置。
  • 子层3:FFNN
5.2 注意力机制

        注意力函数是一个将一个query和一些key - value对映射成一个输出的函数,其中所有的query、key、value和output都是一些向量。具体来说,output是value的一个加权和-->输出的维度== value的维度。output 中 value 的权重=查询query和对应key的相似度orcompatibility function权重等价于 query和对应的key的相似度。

5.2.1 缩放的点积注意力(Scaled Dot-Product Attention)

        缩放的点积注意力:

  • 其输入为query、key(维度是d_{k})以及values(维度是d_{v})。
  • 计算query和所有key的点积,得到两个向量的相似度(结果越大相似度越高);然后对每个点积结果除以\sqrt{d_{k}}。(除以\sqrt{d_{k}}目的是防止softmax函数的梯度消失)
  • 点积结果输入softmax函数获得value的权重。
  • 最后对value进行加权求和。

        在实践中,我们同时计算一组query的attention函数,并将它们组合成一个矩阵Q。key和value也一起组成矩阵KV。我们计算的输出矩阵为:

Attention(Q,K,V)=softmax(\frac{QK^{T}}{\sqrt{d_k}})V

        KV矩阵的序列长度是一样的(加权求和),而Q矩阵的序列长度可以和前两者不一样;这种情况发生在:解码器部分的Encoder-Decoder Attention层中,Q 矩阵是来自解码器输出tgt,而KV 矩阵则是来自编码器最后的输出memory。即tgt2 = self.multihead_attn(tgt, memory, memory, attn_mask=memory_mask,key_padding_mask=memory_key_padding_mask)[0]。但是QK的维度必须一样,因为要计算点积。

Scaled Dot-Product Attention和别的注意力机制的区别:

2种常见的注意力机制:

  • 加性的注意力机制(它可以处理你的 query 和 key 不等长的情况)
  • 点积 dot-product 的注意力机制 (本文采用 scaled,除以\sqrt{d_{k}} ),所以你可以看到它的名字它叫做 scale 的。
5.2.2 多头注意力机制(Multi-head attention

        与其做一个单个的注意力函数,不如说把整个query、key、value整个投影到 1个低维,投影h次。然后再做h次的注意力函数,把每一个函数的输出拼接在一起,然后继续投影,会得到最终的输出。

MultiHead(Q,K,V)=Concat(head_{1},...,head_{h})W^{O}

where\, head_{i}=Attention(QW_{i}^{Q},KW_{i}^{K},VW_{i}^{V})

 其中映射由权重矩阵完成:W_{i}^{Q}\in \mathbb{R}^{d_{model}\times d_{k}},W_{i}^{K}\in \mathbb{R}^{d_{model}\times d_{k}},W_{i}^{V}\in \mathbb{R}^{d_{model}\times d_{v}}

\, and\, W^{O}\in \mathbb{R}^{hd_v\times d_{model}}.

        本文采用h=8个平行attention层或者叫head。因为有残差连接的存在使得输入和输出的维度至少是一样的。对于这些head中的每一个,我们使用d_{k}=d_{v}=d_{model}/h=64,总计算成本与具有全部维度的单个head attention相似。

  • 输入X和8组权重矩阵相乘W^{Q},W^{K},W^{V},得到8组Q,K,V矩阵。进行attention计算,得到 8 组 Z矩阵(假设head=8)
  • 把8组矩阵拼接起来,乘以权重矩阵W^{O},将其映射回d维向量(相当于多维特征进行汇聚),得到最终的矩阵Z。这个矩阵包含了所有 attention heads(注意力头) 的信息。
  • 矩阵Z会输入到 FFNN层。(前馈神经网络层接收的也是 1 个矩阵,而不是8个。其中每行的向量表示一个词)

应用例子:翻译Hello World --> 你好世界

        计算 “好” 的时候,“好”作为 query,会跟 “hello” 向量更相近一点,给 “hello” 向量一个比较大的权重。但是 “world”跟后面的词相关, “world”跟当前的query (“好” )相关度没那么高。在算 “好” 的时候,我会给“hello” 向量一个比较大的权重。在算query“世” 的时候,会给第二个 “world”向量,一个比较大的权重。根据解码器的输入的不一样,会根据当前的query 向量,去在编码器的输出里面去挑我(当前 query)感兴趣的东西。

        attention:query 注意到 当前的 query 感兴趣的东西,对当前的 query的不感兴趣的内容,可以忽略掉。attention 作用:在encoder和decoder之间传递信息。

5.2.3 基于位置的前馈神经网络(Position-wise Feed-Forward Networks)

        作用在最后一个维度的 MLP。

  • Position-wise: 把一个 MLP 对每一个词 (position)作用一次,对每个词作用的是同样的 MLP
  • FFN: Linear + ReLU + Linear

        单隐藏层的 MLP,中间 W_1 扩维到4倍 2048,最后 W_2投影回到 512 维度大小,便于残差连接。

        编码器和解码器中的每个层都包含一个全连接的前馈网络,该前馈网络分别且相同地应用于每个位置。该前馈网络包括两个线性变换,并在两个线性变换中间有一个ReLU激活函数。

FNN(x)=max(0,xW_1+b_1)W_2+b_2

        Position就是序列中每个token,Position-wise 就是把MLP对每个token作用一次,且作用的是同一个MLP。说白了就是MLP只作用于最后一个维度d=512。 因为前面的attention层以及抓取了输入序列的相关信息,并做了一次汇聚(拼接后W映射回d维)。所以attention层结果已经有了序列中我感兴趣的信息,所以后面在做MLP投影映射到想要的语义空间时,只需要对每个position(token)单独做MLP就行。从attention抽取序列信息到MLP映射到需要的语义空间(非线性变换),就整个是transformer的处理信息的基础过程。

attention 作用:把整个序列里面的信息抓取出来,做一次汇聚(aggregation)。

对比transformer和RNN,发现两者都是使用MLP来进行语义空间的转换,但区别是二者传递信息的方式不一样:

  • RNN是把上一时刻信息作为输入(和t时刻输入一起),传递给当前时刻,并用MLP做语义转换。
  • Transformer是通过attention层直接关联到全局的序列信息,然后用MLP做语义转换。
5.2.4 Embeddings and Softmax

        将输入的一个词、词语token映射成为一个长为d_{model}的向量。学习到的长为 的向量来表示整个词、词语token。我们还使用普通的线性变换和softmax函数将解码器输出转换为预测的下一个token的概率。在我们的模型中,输入输出两个嵌入层,和pre-softmax线性变换共享相同的权重矩阵(这样训练起来简单一些)。最后我们将这些权重乘以\sqrt{d_{model}}(本文d为512)。编码器、解码器、最后softmax之前的3个embedding 共享权重。这样的好处是训练更简单。

        这是因为一般会把一个向量的L2Norm学到接近1,这样向量维度越大,这样学到的权重值就会很小。但是位置编码是不会这样学成L2Norm(L2范数)接近1的。所以把权重乘上\sqrt{d_{model}}之后,token embedding和位置编码Positional Encoding才接近统一量级。(都在-1到1之间)

5.2.5 位置编码(Positional Encoding)

        Attention计算时本身是不考虑位置信息的,这样序列顺序变化结果也是一样的。所以我们必须在序列中加入关于词符相对或者绝对位置的一些信息。
        为此,我们将“位置编码”添加到token embedding中。二者维度相同,所以可以相加。有多种位置编码可以选择,例如通过学习得到的位置编码和固定的位置编码。
  在这项工作中,使用不同频率的正弦和余弦函数:

PE_{(pos,2i)}=sin(pos/10000^{2i/d_{model}})

PE_{(pos,2i)}=cos(pos/10000^{2i/d_{model}})

        其中pos是位置,i是维度。也就是说,位置编码的每个维度对应于一个正弦曲线。 这些波长形成一个从2\pi10000\cdot 2\pi的集合级数。我们选择这个函数是因为我们假设它会让模型很容易学习对相对位置的关注,因为对任意确定的偏移kPR_{pos+k}可以表示为 PE_{pos}的线性函数。最终编码向量每个元素值都是在-1到1之间。

        此外,我们会将编码器和解码器堆栈中的embedding和位置编码的和再加一个dropout。对于基本模型,我们使用的dropout比例是P_{drop}=0.1

六、结论

为什么要使用自注意力机制?

  • 每层计算的总复杂度,越少越好
  • 顺序计算量,越少代表并行度越高。(顺序计算量就是下一步需要前面多少步计算完成)
  • 网络中长距离依赖之间的路径长度。影响长距离依赖性能力的一个关键因素是前向和后向信号在网络中传播的路径长度。输入和输出序列中任意位置之间的这些路径越短,学习长距离依赖性就越容易。因此,我们还比较了由不同图层类型组成的网络中任意两个输入和输出位置之间的最大路径长度。

上图n是序列长度,d是token维度。

Attention 没有对数据的顺序建模,为什么比RNN好呢?

        RNN 显示的建模了序列信息,理论应该比 attention 效果更好。但是Attention 用了更广泛的 inductive bias(归置偏置),使得Attention没有用空间上的假设,取得和 CNN 一样、 甚至更好的结果。

  • CNN的inductive bias应该是locality和spatial invariance,即空间相近的grid elements有联系而远的没有,和空间不变性(kernel权重共享)
  • RNN的inductive bias是sequentiality和time invariance,即序列顺序上的timesteps有联系,和时间变换的不变性(rnn权重共享)
七、评价

        对Transformer中attention的理解:attention只是起到把整个序列的信息聚合起来的作用,后面的MLP和残差连接是缺一不可的。去掉MLP和残差连接,只有attention,也什么都训练不出来。

        启示:一个新的模型可以在DL上通用。人的感知是多模态的、使得Transformer在文本、语音、视频抽取多维特征。

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

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

相关文章

java实现文件上传到本地

很多时候我们都需要进行文件上传和下载的操作,具体怎么实现网上的代码其实也是挺多的,刚好我的项目中也遇到了文件上传和下载的需求,本篇博文具体讲解上传操作,下篇博文讲解下载操作。 我们具体来想一想要将一个从前端传来的文件…

Servlet快速入门

什么是Servlet? Servlet可以用很多方式来描述,这取决于上下文。 是一种技术,它被用来创建一个Web应用程序。 是一个API,它提供了许多接口和类,包括文档。 是一个接口,创建任何Serwlet都必须实现这个接口。 是一个扩展…

Python影像变化监测-跟踪大盐湖的萎缩

使用 Google Colab 中的 Python 分析 Landsat-8 图像(2014-2023)以创建大盐湖表面区域的时间序列 目录 🌅大盐湖萎缩问题简介💾下载 Landsat-8 图像📈统计文件中的大盐湖地区时间序列⚙️处理 Landsat-8 图像🗺️大盐湖图像的可视化🎥 大盐湖缩小的延时摄影📉大盐…

通过联合部署DDoS高防和WAF提升网站防护能力

如果您的网站遭受的攻击既有流量型攻击,又混杂精巧的Web应用层攻击时(例如SQL注入、跨站脚本攻击、命令注入等)时,推荐您组合使用阿里云DDoS高防和Web 应用防火墙 WAF(Web Application Firewall)&#xff0…

C语言文件操作,linux文件操作,文件描述符,linux下一切皆文件,缓冲区,重定向

目录 C语言文件操作 如何打开文件以及打开文件方式 读写文件 关闭文件 Linux系统下的文件操作 open 宏标志位 write,read,close,lseek接口 什么是当前路径? linux下一切皆文件 文件描述符 文件描述符排序 C语言文件操…

【音视频开发好书推荐1】《RTC程序设计:实时音视频权威指南》

1、WebRTC概述 WebRTC(Web Real-Time Communication)是一个由Google发起的实时音视频通讯C开源库,其提供了音视频采集、编码、网络传输,解码显示等一整套音视频解决方案,我们可以通过该开源库快速地构建出一个音视频通…

牛客练习赛122

D:圆 正着求删除的最小代价不好做,采用逆向思维,求选择一些不相交的线段使得构成一个圆的代价尽量大,最后答案就是所有线段权值之和减去最大代价。 那么如何求这个最大代价呢?显然区间DP 老套路:破环成链&#xff0…

详解Java中集合的List接口实现的ArrayList方法 | Set接口实现的HashSet方法

集合的概念 当我们需要保存一组一样(类型相同)的元素的时候,我们应该使用一个容器来存储,数组就是这样一个容器。 ● 数组的特点: 数组是一组数据类型相同的元素集合; 创建数组时,必须给定…

Discuz IIS上传附件大于28M失败报错Upload Failed.修改maxAllowedContentLength(图文教程)

下图:Discuz X3.5的系统信息,上传许可为1024MB(1GB) 论坛为局域网论坛,仅供内部同事交流使用! 使用官方最新的Discuz! X3.5 Release 20231221 UTF-8 下图:选择上传附件(提示可以最大上传100M)…

【Python】新手入门(2):避免将关键字作为标识符

Python新手入门(2):避免将关键字作为标识符 🌈 个人主页:高斯小哥 🔥 高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1…

保护模式笔记九 中断门和IDT(中断描述符表)

段选择子: 先直观认识一下GDT和段选择子在逻辑地址转换为线性地址中的作用,例如: 给出逻辑地址:21h:12345678h,需要将其转换为线性地址 a. 选择子SEL21h0000000000100 0 01b,他代表的意思是&#xff1a…

Ruoyi框架上传文件

axios资料:axios中文文档|axios中文网 | axios axiosjson 默认情况下,axios将JavaScript对象序列化为JSON。 submit(data) {if (data && this.definitionId) {// 启动流程并将表单数据加入流程变量startProcess(this.definitionId, JSON.string…

数据可视化原理-腾讯-3D热力图

在做数据分析类的产品功能设计时,经常用到可视化方式,挖掘数据价值,表达数据的内在规律与特征展示给客户。 可是作为一个产品经理,(1)如果不能够掌握各类可视化图形的含义,就不知道哪类数据该用…

代码随想录刷题笔记-Day29

1. N皇后 51. N 皇后https://leetcode.cn/problems/n-queens/ 按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上,并且使皇后彼此之间不能相互攻击。 给你一个整数…

【Sql Server】C#通过拼接代码的方式组合添加sql语句,会出现那些情况,参数化的作用

欢迎来到《小5讲堂》,大家好,我是全栈小5。 这是《Sql Server》系列文章,每篇文章将以博主理解的角度展开讲解, 特别是针对知识点的概念进行叙说,大部分文章将会对这些概念进行实际例子验证,以此达到加深对…

JavaWeb JSP

JSP(Java Server Page)是J2EE的功能模块,是Java服务器页面,由Web服务器执行,作用就是降低动态网页开发难度,将Java代码与HTML分离,降低开发难度,本质就是Servlet。 Servlet的缺点&a…

pandas数据分析42——读取和写入stata和spss的数据格式

python就是胶水语言,啥文件基本都能读取,而且pandas作为数据分析最好用的包,其功能自然也很多,可以读取各种数据文件。 本次就来演示一下怎么读取stata文件,和spss文件,他们不仅储存了数据和变量&#xff…

C++--机器人的运动范围

目录 1. 题目 2. 思路 3. C代码测试 4. 测试结果 1. 题目 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格…

如何应对IT服务交付中的问题?

如何应对IT服务交付中的问题? 按需交付服务的挑战IT服务体系的复杂性恶性循环的形成学会洞察的重要性书籍简介参与方式 按需交付服务的挑战 一致性、可靠性、安全性、隐私性和成本效益的平衡:成功的按需交付服务需要满足这些要求,这需要服务…

html样式排版

<template><div class"box"><div class"header">头部</div><div class"main"><div class"left">菜单</div><div class"right"><div class"right-contentr"&g…