论文笔记——Attention Is All You Need(1)

阅读: 评论:0

论文笔记——Attention Is All You Need(1)

论文笔记——Attention Is All You Need(1)

目录

一、Bleu评价(Bilingual Evaluation Understudy)

1、N-gram

2、召回率

3.公式总结

4.BLEU算法代码实现

sentence_bleu语句级的bleu值

corpus_bleu语句级的bleu值

二、WMT数据集

1. 多模态NMT的数据集:

2. IWSLT(国际口语研讨会)数据集:

三、seq2seq

Encoder部分

Decoder部分

seq2seq模型举例

 四、Attention机制

Attention整体流程

Step1 计算Encoder的隐藏状态和Decoder的隐藏状态

Step2 获取每个编码器隐藏状态对应的分数

Step3 通过softmax归一化分数

Step4 用每个编码器的隐藏状态乘以其softmax得分

Step5 把所有对齐的向量加起来

Step6 将上下文向量输入到Decoder中

Step7 反向传播


一、Bleu评价(Bilingual Evaluation Understudy)

机器翻译的结果越接近人工参考译文就认定它的质量越高。

1、N-gram

N-gram 准确率(N-gram Precision)
n-gram是指n 个连续单词组成的单元,称为n 元语法单元。n 越大表示评价时考虑的匹配片段越大BLEU的计算首先考虑待评价译文中n-gram在参考答案中的匹配率,称为n-gram 准确率(n-gram Precision)。其计算方法如下: 


 例如:

原文:今天天气不错

机器译文:It is a nice day today

人工译文:Today is a nice day

如果用1-gram匹配的话:


可以看到机器译文一共6个词,有5个词语都命中的了参考译文,那么它1-gram的匹配度为 5/6

我们再以3-gram举例:

可以看到机器译文一共可以分为四个3-gram的词组,其中有两个可以命中参考译文,那么它3-gram的匹配度为 2/4

依次类推,我们可以很容易实现一个程序来遍历计算N-gram的一个匹配度。一般来说1-gram的结果代表了文中有多少个词被单独翻译出来了,因此它反映的是这篇译文的忠实度;而当我们计算2-gram以上时,更多时候结果反映的是译文的流畅度,值越高文章的可读性就越好

2、召回率


上面所说的方法比较好理解,也比较好实现,但是没有考虑到召回率,举一个非常简单的例子说明:

原文:猫站在地上

机器译文:the the the the

人工译文:The cat is standing on the ground

在计算1-gram的时候,the 都出现在译文中,因此匹配度为4/4 ,但是很明显 the 在人工译文中最多出现的次数只有2次,因此BLEU算法修正了这个值的算法,首先会计算该n-gram在译文中可能出现的最大次数:

  Count是N-gram在机器翻译译文中的出现次数,Max_Ref_Count是该N-gram在一个参考译文中最大的出现次数,最终统计结果取两者中的较小值。然后在把这个匹配结果除以机器翻译译文的N-gram个数。因此对于上面的例子来说,修正后的1-gram的统计结果就是2/4。
  译文整体的准确率等于各n-gram 的加权平均:
  
这里解释一下这个公式,N代表的总共的单词元组,例如,n=1时就是1-gram,一元组。wn是指当前单词元组下所占有的权重比,所有的权重比相加应当为1。从公式可以看出BLEU的取值范围是(0,1],0最差,1最好。这里值得注意的是,当所有的权重值是均分的时候,这里所有元组的平均值就是算术平均值。

短句惩罚因子(Brevity Penalty, BP)
上面的算法已经足够可以有效的翻译评估了,然而N-gram的匹配度可能会随着句子长度的变短而变好,因此会存在这样一个问题:一个翻译引擎只翻译出了句子中部分句子且翻译的比较准确,那么它的匹配度依然会很高。为了避免这种评分的偏向性,BLEU在最后的评分结果中引入了长度惩罚因子(Brevity Penalty)

c: 表示译文的句子长度,
r :表示参考译文的句子长度

这个公式表示当译文小于参考译文(也就是翻译的句子过短是短句的时候)会得到一个BP值,并且最后的结果会乘上这个BP值来惩罚我们的短句翻译。这里放在指数上是为了扩大这种”惩罚“。

最终BLEU 的计算公式为:

总结一下:BLEU值其实也就是“改进版的n-gram”加上“过短惩罚因子”。

3.公式总结

 

  

4.BLEU算法代码实现


通过上面BLEU算法理论的学习,我们知道BLEU算法的原理。
接着要运用到一个nltk工具包里面定义好的BLEU算法。


sentence_bleu语句级的bleu值

anslate.bleu_score import sentence_bleu
reference = ['this', 'is', 'a', 'test']
candidate = ['this', 'is', 'a', 'test']
score = sentence_bleu(reference, candidate)
print(score)

corpus_bleu语句级的bleu值

anslate.bleu_score import corpus_bleu
references = [['this', 'is', 'a', 'test']]
candidates = [['this', 'is', 'a', 'test']]
score = corpus_bleu(references, candidates)
print(score)

score = corpus_bleu(references, candidates,weights,SmoothingFunction)

这里说明一下两个方法的一些参数介绍:

属性名    说明
references    参考语句也就是人工翻译的目标语句
candidates    候选语句也就是机器翻译得到的语句
weights    分配每个元组的权重值,这个权重指的是1-gram,2-gram, 3-gram等等的权重

累积N-Gram得分指的是为各个gram对应的权重加权, 来计算得到一个加权几何平均(weighted geometric mean). 默认情况下, sentence_bleu()corpus_bleu()都是计算累积的4-gram BLEU分数的, 也称之为BLEU-4.
BLEU-4的加权策略如下: 1/4 (25%) 或者 0.25 对 1-gram, 2-gram, 3-gram and 4-gram 的得分.

# 4-gram cumulative BLEU
>>> anslate.bleu_score import sentence_bleu
>>> reference = [['this', 'is', 'small', 'test']]
>>> candidate = ['this', 'is', 'a', 'test']
>>> score = sentence_bleu(reference, candidate)
>>> score
1.0547686614863434e-154
>>> score = sentence_bleu(reference, candidate, weights=(0.25, 0.25, 0.25, 0.25))
>>> score
1.0547686614863434e-154


SmoothingFunction    SmoothingFunction是用来平滑log函数的结果的,防止fn=0(也就是某个元组匹配数为0)时,取对数为负无穷

二、WMT数据集

1. 多模态NMT的数据集:

主要来源于WMT16,WMT17,WMT18的共享任务(Multi30k EN-DE,EN-Fr,EN-CS):

.html

.html

.html

2. IWSLT(国际口语研讨会)数据集:

IWSLT2011~IWSLT2020:;

如IWSLT2015:  

           train和dev:

            :

            test.de:

           总数据集下载: .tgz

2. WMT(国际机器翻译研讨会)数据集:

3. OPUS:/

3. 中文机器翻译数据集:

4. 大规模中文自然语言处理语料:

5. 中文自然语言处理机器翻译语料库:.md

三、seq2seq

seq2seq模型虽然简单,但是特别经典,它的出现可以说给整个NLP带来个翻天覆地的变化。网上已经有很多人做了相关的总结,但是翻看起来还是感觉有点乱,于是想自己总结一个版本,方便自己回忆,也希望所写的内容能给大家带来帮助。由于平时都是直接拿来用,不需要在原理方面做推敲,所以公式部分就不详细介绍了,感兴趣的可以自己去阅读论文,下面只介绍相关原理及应用。

Seq2Seq,全称Sequence to Sequence。它是一种通用的Encoder——Decoder框架,可用于机器翻译、文本摘要、会话建模、图像字幕等场景中。其中Decoder部分的输出通常是不定长的,以机器翻译为例:将一句中文翻译成英文,那么这句英文的长度有可能会比中文短,也有可能会比中文长,所以输出的长度就不确定了。

如下图所示,输入的中文长度为4,输出的英文长度为2。下面的所有内容都以机器翻译为例。

Encoder部分


Encoder负责将输入序列压缩成指定长度的向量,这个向量就可以看成是这个序列的语义,这个过程称为编码。

如下图,获取语义向量最简单的方式就是直接将最后一个输出的隐状态作为语义向量c。也可以对最后一个隐含状态做一个变换得到语义向量,还可以将输入序列的所有隐含状态做一个变换得到语义变量。

下图c代表生成的语义向量,q函数代表相应的变换(线性变换等)

Decoder部分


Decoder部分就是根据Encoder部分输出的语义向量c来做解码工作。以翻译为例,就是生成相应的译文。

注意生成的序列是不定长的。而且上一时刻的输出通常要作为下一时刻的输入,如下图所示,预测y2时y1要作为输入。


得到语义向量c后,通常的做法是将c当做初始状态h0输入到Decoder中:

 
还有一种做法是将c当做每一步的输入:

一般而言在Decoder阶段上一步的输出会作为下一步的输入,如下所示:

seq2seq模型举例

邮箱对话

下面是一个收发邮件的例子,根据发来的消息生成相应的回复。

左右两边各有四个LSTM单元(黄色蓝色方块,每个方块代表一个LSTM单元),得到的语义向量c当做初始状态h0输入到Decoder中:

 四、Attention机制

Attention机制是为了解决当翻译文本过长时,比如:

这里的用are而不用is是因为很久之前有一个pigs,当文本过长的时候,如果用一个和上边seq2seq一样的语义向量,关联性就被减弱了。所以提出了Attention机制

通过这种方法,模型能够有选择地关注输入序列的有用部分,从而了解它们之间的对齐关系,有助于模型更好地处理输入较长的句子。

模型结构如下:

此模型中语义编码c1、c2、c3各不相同,y1、y2、y3的生成方法为:

Attention整体流程


下面的内容会详细介绍Attention的整个流程:

通过为每个单词分配一个权重,注意力机制能够保证当前翻译的单词对原文各个单词的关注点不同(就是对照着原文翻译)。由于这个权重可能大于1,为了方便我们使用softmax进行归一化,得到归一化权重,然后计算Encoder隐藏状态和其对应归一化权重的加权和,得上下文向量c(语义编码)。

注意力层的实现可以分为6个步骤。

Step1 计算Encoder的隐藏状态和Decoder的隐藏状态


首先计算第一个解码器隐藏状态(红色)和所有可用的编码器隐藏状态(绿色)。下图中有4个编码器隐藏状态和当前解码器的隐藏状态。要想输出Decoder的第一个隐藏的状态,需要给Decoder一个初始状态和一个输入,例如采用Encoder的最后一个状态作为Decoder的初始状态,输入为0。

Step2 获取每个编码器隐藏状态对应的分数

计算Decoder的第一个隐藏状态和Encoder所有的隐藏状态的相关性,这里采用点积的方式(默认两个向量长度一样)。后面是一个计算示例。

 decoder_hidden = [10, 5, 10]
 encoder_hidden          score
 ---------------------
      [0, 1, 1]           15 (= 10×0 + 5×1 + 10×1, the dot product)
      [5, 0, 1]           60
      [1, 1, 0]           15
      [0, 5, 1]           35

在上面的例子中,对于编码器的隐藏状态[5, 0, 1],我们获得了较高的注意分值60。这意味着下一个要翻译的单词与此编码器的隐藏状态相关性很大。

Step3 通过softmax归一化分数

我们把得到的分数输入到softmax层进行归一化,归一化之后的分数(标量)加起来等于1,归一化后的分数代表注意力分配的权重 。

 encoder_hidden   score    score^
 -----------------------------
      [0, 1, 1]     15       0
      [5, 0, 1]     60       1
      [1, 1, 0]     15       0
      [0, 5, 1]     35       0

注意,经过softmax之后的分数score^,注意力的分配仅按预期放在了[5, 0, 1]上。实际上,这些数字不是二进制的,而是0到1之间的一个浮点数。

Step4 用每个编码器的隐藏状态乘以其softmax得分

通过将每个编码器的隐藏状态与其softmax之后的分数(标量)相乘,我们得到对齐向量 或标注向量。这正是对齐产生的机制。

 encoder_hidden   score  score^  alignment
 -----------------------------------------
 [0, 1, 1]          15     0     [0, 0, 0]
 [5, 0, 1]          60     1     [5, 0, 1]
 [1, 1, 0]          15     0     [0, 0, 0]
 [0, 5, 1]          35     0     [0, 0, 0]

在这里,我们看到除了[5, 0, 1]外,所有编码器隐藏状态的对齐都被降低到0,这是因为注意力得分较低。这意味着我们可以期望第一个被翻译的单词应该与输入单词中使用[5, 0, 1]嵌入所表示的单词匹配。

Step5 把所有对齐的向量加起来

对齐向量进行求和,生成上下文向量(语义编码)。上下文向量是前一步对齐向量的聚合信息。

 encoder_hidden   score   score^   alignment
 -------------------------------------------
   [0, 1, 1]       15       0      [0, 0, 0]
   [5, 0, 1]       60       1      [5, 0, 1]
   [1, 1, 0]       15       0      [0, 0, 0]
   [0, 5, 1]       35       0      [0, 0, 0]
 context = [0+5+0+0, 0+0+0+0, 0+1+0+0] = [5, 0, 1]

Step6 将上下文向量输入到Decoder中

输入的方式和模型有关。我们会在后面的例子中看到不同的模型是如何利用Encoder的上下文向量的。

 Step7 反向传播

引用:Attention原理详解_小小鸟要高飞-CSDN博客_attention原理

五、文章的更深理解(点开连接看原文)

​​​​​​《Attention is All You Need》浅读(简介+代码) - 科学空间|Scientific Spaces2017年中,有两篇类似同时也是笔者非常欣赏的论文,分别是FaceBook的《Convolutional Sequence to Sequence Learning》和Google的《

首页 信息时代 《Attention is All You Need》浅读(简介+代码) 6Jan

《Attention is All You Need》浅读(简介+代码)

By 苏剑林 | 2018-01-06 | 418386位读者 |   

2017年中,有两篇类似同时也是笔者非常欣赏的论文,分别是FaceBook的《Convolutional Sequence to Sequence Learning》和Google的《Attention is All You Need》,它们都算是Seq2Seq上的创新,本质上来说,都是抛弃了RNN结构来做Seq2Seq任务。

这篇博文中,笔者对《Attention is All You Need》做一点简单的分析。当然,这两篇论文本身就比较火,因此网上已经有很多解读了(不过很多解读都是直接翻译论文的,鲜有自己的理解),因此这里尽可能多自己的文字,尽量不重复网上各位大佬已经说过的内容。

序列编码 #

深度学习做NLP的方法,基本上都是先将句子分词,然后每个词转化为对应的词向量序列。这样一来,每个句子都对应的是一个矩阵X=(x1,x2,…,xt)X=(x1,x2,…,xt),其中xixi都代表着第ii个词的词向量(行向量),维度为dd维,故X∈Rn×dX∈Rn×d。这样的话,问题就变成了编码这些序列了。

第一个基本的思路是RNN层,RNN的方案很简单,递归式进行:

yt=f(yt−1,xt)(1)(1)yt=f(yt−1,xt)
不管是已经被广泛使用的LSTM、GRU还是最近的SRU,都并未脱离这个递归框架。RNN结构本身比较简单,也很适合序列建模,但RNN的明显缺点之一就是无法并行,因此速度较慢,这是递归的天然缺陷。另外我个人觉得RNN无法很好地学习到全局的结构信息,因为它本质是一个马尔科夫决策过程。

第二个思路是CNN层,其实CNN的方案也是很自然的,窗口式遍历,比如尺寸为3的卷积,就是

yt=f(xt−1,xt,xt+1)(2)(2)yt=f(xt−1,xt,xt+1)
在FaceBook的论文中,纯粹使用卷积也完成了Seq2Seq的学习,是卷积的一个精致且极致的使用案例,热衷卷积的读者必须得好好读读这篇文论。CNN方便并行,而且容易捕捉到一些全局的结构信息,笔者本身是比较偏爱CNN的,在目前的工作或竞赛模型中,我都已经尽量用CNN来代替已有的RNN模型了,并形成了自己的一套使用经验,这部分我们以后再谈。

Google的大作提供了第三个思路纯Attention!单靠注意力就可以!RNN要逐步递归才能获得全局信息,因此一般要双向RNN才比较好;CNN事实上只能获取局部信息,是通过层叠来增大感受野;Attention的思路最为粗暴,它一步到位获取了全局信息!它的解决方案是:

yt=f(xt,A,B)(3)(3)yt=f(xt,A,B)
其中A,BA,B是另外一个序列(矩阵)。如果都取A=B=XA=B=X,那么就称为Self Attention,它的意思是直接将xtxt与原来的每个词进行比较,最后算出ytyt!

Attention层 #

Attention定义 #

Attention

Google的一般化Attention思路也是一个编码序列的方案,因此我们也可以认为它跟RNN、CNN一样,都是一个序列编码的层。

前面给出的是一般化的框架形式的描述,事实上Google给出的方案是很具体的。首先,它先把Attention的定义给了出来:

Attention(Q,K,V)=softmax(QK⊤dk−−√)V(4)(4)Attention(Q,K,V)=softmax(QK⊤dk)V
这里用的是跟Google的论文一致的符号,其中Q∈Rn×dk,K∈Rm×dk,V∈Rm×dvQ∈Rn×dk,K∈Rm×dk,V∈Rm×dv。如果忽略激活函数softmaxsoftmax的话,那么事实上它就是三个n×dk,dk×m,m×dvn×dk,dk×m,m×dv的矩阵相乘,最后的结果就是一个n×dvn×dv的矩阵。于是我们可以认为:这是一个Attention层,将n×dkn×dk的序列QQ编码成了一个新的n×dvn×dv的序列

那怎么理解这种结构呢?我们不妨逐个向量来看。

Attention(qt,K,V)=∑s=1m1Zexp(〈qt,ks〉dk−−√)vs(5)(5)Attention(qt,K,V)=∑s=1m1Zexp⁡(〈qt,ks〉dk)vs
其中ZZ是归一化因子。事实上q,k,vq,k,v分别是query,key,valuequery,key,value的简写,K,VK,V是一一对应的,它们就像是key-value的关系,那么上式的意思就是通过qtqt这个query,通过与各个ksks内积的并softmax的方式,来得到qtqt与各个vsvs的相似度,然后加权求和,得到一个dvdv维的向量。其中因子dk−−√dk起到调节作用,使得内积不至于太大(太大的话softmax后就非0即1了,不够“soft”了)。

事实上这种Attention的定义并不新鲜,但由于Google的影响力,我们可以认为现在是更加正式地提出了这个定义,并将其视为一个层地看待;此外这个定义只是注意力的一种形式,还有一些其他选择,比如queryquery跟keykey的运算方式不一定是点乘(还可以是拼接后再内积一个参数向量),甚至权重都不一定要归一化,等等。

Multi-Head Attention #

Multi-Head Attention

这个是Google提出的新概念,是Attention机制的完善。不过从形式上看,它其实就再简单不过了,就是把Q,K,VQ,K,V通过参数矩阵映射一下,然后再做Attention,把这个过程重复做hh次,结果拼接起来就行了,可谓“大道至简”了。具体来说

headi=Attention(QWQi,KWKi,VWVi)(6)(6)headi=Attention(QWiQ,KWiK,VWiV)
这里WQi∈Rdk×d~k,WKi∈Rdk×d~k,WVi∈Rdv×d~vWiQ∈Rdk×d~k,WiK∈Rdk×d~k,WiV∈Rdv×d~v,然后 MultiHead(Q,K,V)=Concat(head1,...,headh)(7)(7)MultiHead(Q,K,V)=Concat(head1,...,headh)
最后得到一个n×(hd~v)n×(hd~v)的序列。所谓“多头”(Multi-Head),就是只多做几次同样的事情(参数不共享),然后把结果拼接

Self Attention #

到目前为止,对Attention层的描述都是一般化的,我们可以落实一些应用。比如,如果做阅读理解的话,QQ可以是篇章的向量序列,取K=VK=V为问题的向量序列,那么输出就是所谓的Aligned Question Embedding

而在Google的论文中,大部分的Attention都是Self Attention,即“自注意力”,或者叫内部注意力。

所谓Self Attention,其实就是Attention(X,X,X)Attention(X,X,X),XX就是前面说的输入序列。也就是说,在序列内部做Attention,寻找序列内部的联系。Google论文的主要贡献之一是它表明了内部注意力在机器翻译(甚至是一般的Seq2Seq任务)的序列编码上是相当重要的,而之前关于Seq2Seq的研究基本都只是把注意力机制用在解码端。类似的事情是,目前SQUAD阅读理解的榜首模型R-Net也加入了自注意力机制,这也使得它的模型有所提升。

当然,更准确来说,Google所用的是Self Multi-Head Attention:

Y=MultiHead(X,X,X)(8)(8)Y=MultiHead(X,X,X)

Position Embedding #

然而,只要稍微思考一下就会发现,这样的模型并不能捕捉序列的顺序!换句话说,如果将K,VK,V按行打乱顺序(相当于句子中的词序打乱),那么Attention的结果还是一样的。这就表明了,到目前为止,Attention模型顶多是一个非常精妙的“词袋模型”而已。

这问题就比较严重了,大家知道,对于时间序列来说,尤其是对于NLP中的任务来说,顺序是很重要的信息,它代表着局部甚至是全局的结构,学习不到顺序信息,那么效果将会大打折扣(比如机器翻译中,有可能只把每个词都翻译出来了,但是不能组织成合理的句子)。

于是Google再祭出了一招——Position Embedding,也就是“位置向量”,将每个位置编号,然后每个编号对应一个向量,通过结合位置向量和词向量,就给每个词都引入了一定的位置信息,这样Attention就可以分辨出不同位置的词了。

Position Embedding并不算新鲜的玩意,在FaceBook的《Convolutional Sequence to Sequence Learning》也用到了这个东西。但在Google的这个作品中,它的Position Embedding有几点区别:

1、以前在RNN、CNN模型中其实都出现过Position Embedding,但在那些模型中,Position Embedding是锦上添花的辅助手段,也就是“有它会更好、没它也就差一点点”的情况,因为RNN、CNN本身就能捕捉到位置信息。但是在这个纯Attention模型中,Position Embedding是位置信息的唯一来源,因此它是模型的核心成分之一,并非仅仅是简单的辅助手段。

2、在以往的Position Embedding中,基本都是根据任务训练出来的向量。而Google直接给出了一个构造Position Embedding的公式:

⎧⎩⎨⎪⎪PE2i(p)=sin(p/100002i/dpos)PE2i+1(p)=cos(p/100002i/dpos)(9)(9){PE2i(p)=sin⁡(p/100002i/dpos)PE2i+1(p)=cos⁡(p/100002i/dpos)
这里的意思是将id为pp的位置映射为一个dposdpos维的位置向量,这个向量的第ii个元素的数值就是PEi(p)PEi(p)。Google在论文中说到他们比较过直接训练出来的位置向量和上述公式计算出来的位置向量,效果是接近的。因此显然我们更乐意使用公式构造的Position Embedding了,我们称之为Sinusoidal形式的Position Embedding。

3、Position Embedding本身是一个绝对位置的信息,但在语言中,相对位置也很重要,Google选择前述的位置向量公式的一个重要原因是:由于我们有sin(α+β)=sinαcosβ+cosαsinβsin⁡(α+β)=sin⁡αcos⁡β+cos⁡αsin⁡β以及cos(α+β)=cosαcosβ−sinαsinβcos⁡(α+β)=cos⁡αcos⁡β−sin⁡αsin⁡β,这表明位置p+kp+k的向量可以表示成位置pp的向量的线性变换,这提供了表达相对位置信息的可能性。

结合位置向量和词向量有几个可选方案,可以把它们拼接起来作为一个新向量,也可以把位置向量定义为跟词向量一样大小,然后两者加起来。FaceBook的论文和Google论文中用的都是后者。直觉上相加会导致信息损失,似乎不可取,但Google的成果说明相加也是很好的方案。看来我理解还不够深刻。

还有,尽管论文给出的Position Embedding是sin,cossin,cos交错的形式,但其实这个交错形式没有特别的意义,你可以按照任意的方式重排它(比如前sinsin后coscos地拼接),原因如下:

1、假如你的Position_Embedding是拼接到原来的词向量中,那么将coscos和sinsin前后连接还是交叉连接,都是没区别的,因为你下一步都是接一个变换矩阵而已;

2、如果你的Position_Embedding是加到原来的词向量中,那么两种方式貌似是有点区别的,但是要注意的是,词向量本身没有局部结构,也就是说,50维的词向量,将每一维打乱重新排个序(当然整体要按同样的顺序来重新排序),它还是等价于原来的词向量。既然相加的对象(词向量)都没有局部结构,我们也没必要强调被加的对象(Position_Embedding)的局部结构(也就是交叉连接)了。

本文发布于:2024-02-05 04:34:37,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170724274663081.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:笔记   论文   Attention
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23