PyTorch实现Transformer模型详解

attention 应用于RNN

注意力应用于文本:Bahdanau等2014发布的
Neural Machine Translation by Jointly Learning to Align and Translate
和 Luong等2015年发布的
Effective Approaches to Attention-based Neural Machine Translation
两篇论文。

解码器:

上下文向量:RNN 加上注意力机制之后,编码器把
所有
时间步的 hidden state(隐藏层状态)传递给解码器,而不是只传递最后一个 hidden state(隐藏层状态),解码器对编码器传输的所有隐藏层状态给分数,经过softmax,变成注意力分数,注意力分数对隐藏层状态加权和得到上下文向量。

  1. 注意力模型的解码器 RNN 的输入包括:一个embedding 向量,和一个初始化好的解码器 hidden state(隐藏层状态)。
  2. RNN 处理上述的 2 个输入,产生一个输出和一个新的 hidden state(隐藏层状态
    h4
    向量),其中
    输出会被忽略
  3. 注意力的步骤
    :我们使用编码器的 hidden state(隐藏层状态)和 h4 向量来计算这个时间步的
    上下文向量
    (相似度加权和)(C4)。
  4. 我们把 h4 和 C4
    拼接起来
    ,得到一个向量。
  5. 我们把这个向量输入一个
    前馈神经网络
    (这个网络是和整个模型一起训练的)。
  6. 前馈神经网络的输出的
    输出表示这个时间步输出的单词
  7. 在下一个时间步重复这个步骤。

[基于 TensorFlow 的 神经机器翻译 (seq2seq)
指南
。]

transformer 基于self-attention 结构

自注意力的优点:可以并行计算。

编码器:

编码器(Encoder)接收的输入都是一个向量列表,输出也是大小同样的向量列表,然后接着输入下一个编码器。(第一 个/层 编码器的输入是
词向量

而后面的编码器的输入是
上一个编码器的输出

。)

注意力分数:

自注意力:Query=Key=Value (是词向量分别和 3 个矩阵相乘得到的,而这个矩阵是我们要学习的参数。)

score:计算 “
Thinking
” 对应的 Query 向量和其他位置的每个词的 Key 向量的点积,而得到的。如果我们计算句子中第一个位置单词的 Attention Score(注意力分数),那么第一个分数就是 q1 和 k1 的内积,第二个分数就是 q1 和 k2 的点积,再除以

(

d

k

e

y

)

\sqrt(d_{key})

(

d

k

e

y

)

(除以一个数是为了
在反向传播时,求取梯度更加稳定
:导数不会梯度爆炸)。在经过一个softmax层。将每个分数分别与每个 Value 向量相乘,就是对不同的词施加注意力的过程。

将上面的向量输入前馈神经网络。

**tips:**用矩阵可以加速计算所有词的位置的向量。

多头
注意力:

  1. 扩展了模型
    关注不同位置
    的能力。
  2. 多头注意力机制赋予 attention 层多个“子表示空间”。多组

W

Q

,

W

K

W

V

W^Q, W^K W^V

W

Q

,

W

K

W

V

的权重矩阵(在 Transformer 的论文中,使用了 8 组注意力(attention heads)。

然后将8组矩阵拼接成一个矩阵,和权重矩阵

W

Q

W^Q

W

Q

相乘,再输入前馈神经网络(每行代表一个词)。

编码实现 transformer
torch.nn.MultiheadAttention(embed_dim, num_heads, dropout=0.0, bias=True, add_bias_kv=False, add_zero_attn=False, kdim=None, vdim=None)
# embed_dim:最终输出的 K、Q、V 矩阵的维度,这个维度需要和词向量的维度一样
# num_heads:设置多头注意力的数量。如果设置为 1,那么只使用一组注意力。如果设置为其他数值,那么 - - num_heads 的值需要能够被 embed_dim 整除
# dropout:这个 dropout 加在 attention score 后面


解码器

QUERY是下层输入,K、V是编码器的输出。

编码器的multi-head 函数:

torch.nn.MultiheadAttention(embed_dim, num_heads, dropout=0.0, bias=True, add_bias_kv=False, add_zero_attn=False, kdim=None, vdim=None)
#传入参数
forward(query, key, value, key_padding_mask=None, need_weights=True, attn_mask=None)

####例子

## nn.MultiheadAttention 输入第0维为length
# batch_size 为 64,有 12 个词,每个词的 Query 向量是 300 维
query = torch.rand(12,64,300)
# batch_size 为 64,有 10 个词,每个词的 Key 向量是 300 维
key = torch.rand(10,64,300)
# batch_size 为 64,有 10 个词,每个词的 Value 向量是 300 维
value= torch.rand(10,64,300)

embed_dim = 300
num_heads = 1
# 输出是 (attn_output, attn_output_weights)
multihead_attn = nn.MultiheadAttention(embed_dim, num_heads)
attn_output = multihead_attn(query, key, value)[0]
# output: torch.Size([12, 64, 300])
# batch_size 为 64,有 12 个词,每个词的向量是 300 维
print(attn_output.shape)

torch.nn.linear

y

=

x

A

T

b

y = xA^T + b

y

=

x

A

T

b

import torch

x = torch.randn(128, 20)  # 输入的维度是(128,20)
m = torch.nn.Linear(20, 30)  # 20,30是指维度
output = m(x)
print('m.weight.shape:\n ', m.weight.shape)
print('m.bias.shape:\n', m.bias.shape)
print('output.shape:\n', output.shape)

# ans = torch.mm(input,torch.t(m.weight))+m.bias 等价于下面的
ans = torch.mm(x, m.weight.t()) + m.bias   
print('ans.shape:\n', ans.shape)

print(torch.equal(ans, output))
#输出   
m.weight.shape:
  torch.Size([30, 20])
m.bias.shape:
 torch.Size([30])
output.shape:
 torch.Size([128, 30])
ans.shape:
 torch.Size([128, 30])    
True

位置编码的可视化

左半部分的值是由 sine 函数产生的,而右半部分的值是由 cosine 函数产生的,然后将他们拼接起来,得到每个位置编码向量。由欧拉公式,这可以唯一指定位置。

论文中的方法和上面图中的稍有不同,它不是直接拼接两个向量,而是将两个向量交织在一起

在解码器里,Self Attention 层只允许关注到输出序列中早于当前位置之前的单词。具体做法是:在 Self Attention 分数经过 Softmax 层之前,屏蔽当前位置之后的那些位置。

transformer 前向过程

两层编码器,一层解码器。

编码器一般有多层,第一个编码器的输入是一个序列,最后一个编码器输出是一组注意力向量 K 和 V。这些注意力向量将会输入到每个解码器的Encoder-Decoder Attention层,这有助于解码器把注意力集中中输入序列的合适位置。Encoder-Decoder Attention层使用前一层的输出作为当前解码器的额输入,加上位置编码来构造 Query 矩阵,而 Key 矩阵和 Value 矩阵来自于解码器最终的输出。

和编码器中的 Self Attention 层不太一样:在解码器里,Self Attention 层只允许关注到输出序列中早于当前位置之前的单词。具体做法是:在 Self Attention 分数经过 Softmax 层之前,屏蔽当前位置之后的那些位置。

解码器最终的输出怎么转换为单词:

在softmax后面连接一个线性层,把解码器输出的向量,映射到一个更长的向量,这个向量称为 logits 向量(logits向量的长度是词汇表的长度)。

transformer 训练过程

由于模型每个时间步只产生一个输出,我们可以认为:模型是从概率分布中选择概率最大的词,并且丢弃其他词。这种方法叫做贪婪解码(greedy decoding)。另一种方法是每个时间步保留两个最高概率的输出词,然后在下一个时间步,重复执行这个过程:假设第一个位置概率最高的两个输出的词是”I“和”a“,这两个词都保留,然后根据第一个词计算第二个位置的词的概率分布,再取出 2 个概率最高的词,对于第二个位置和第三个位置,我们也重复这个过程。这种方法称为集束搜索(beam search),在我们的例子中,beam_size 的值是 2(含义是:在所有时间步,我们保留两个最高概率),top_beams 的值也是 2(表示我们最终会返回两个翻译的结果)。beam_size 和 top_beams 都是你可以在实验中尝试的超参数。

transformer 训练

1. 词表,文本切割

如果有100哥句子,那么就有100个切割的列表。指定最大长度 假设=128。

2. 词嵌入处理

在torch里基于
torch.nn.Embedding
实现,实例化时需要设置的参数为词表的大小和被映射的向量的维度比如
embed = nn.Embedding(10,8)
。向量的维度通俗来说就是向量里面有多少个数。注意,第一个参数是词表的大小,如果你目前最多有8个词,通常填写10(多一个位置留给unk和pad),你后面万一进入与这8个词不同的词就映射到unk上,序列padding的部分就映射到pad上。num_features或者embed_dim是向量的维数。

(batch_size, seq_length, embed_dim) 一般深度学习只改变embed_dim

Transformer 代码

# 示例  平均分布的赋值
w = torch.empty(3,5)
print(nn.init.xavier_uniform_(w))

KaTeX parse error: Expected ‘}’, got ‘_’ at position 57: …{6}{\text { fan_̲in }+\text { fa…

fan_in, fan_out = _calculate_fan_in_and_fan_out(tensor)

fan_in = tensor.size(1) * receptive_field_size

fan_out = tensor.size(0) * receptive_field_size

所以上面的是 sqrt*(6/8)

# 位置编码
Tensor = torch.Tensor
def positional_encoding(X, num_features, dropout_p=0.1, max_len=512) -> Tensor:
    r'''
        给输入加入位置编码
    参数:
        - num_features: 输入进来的维度
        - dropout_p: dropout的概率,当其为非零时执行dropout
        - max_len: 句子的最大长度,默认512
    
    形状:输出的是加上PE的E
        - 输入: [batch_size, seq_length, num_features]
        - 输出: [batch_size, seq_length, num_features]

    例子:
        >>> X = torch.randn((2,4,10))
        >>> X = positional_encoding(X, 10)
        >>> print(X.shape)
        >>> torch.Size([2, 4, 10])
    '''

    dropout = nn.Dropout(dropout_p)
    P = torch.zeros((1,max_len,num_features))
    X_ = torch.arange(max_len,dtype=torch.float32).reshape(-1,1) / torch.pow(
        10000,torch.arange(0,num_features,2,dtype=torch.float32) /num_features)
    P[:,:,0::2] = torch.sin(X_)
    P[:,:,1::2] = torch.cos(X_)
    X = X + P[:,:X.shape[1],:].to(X.device)  # X + 位置编码(按照max_length 给定编码)
    return dropout(X)

# 位置编码例子
X = torch.randn((2,4,10))  
X = positional_encoding(X, 10)

  • 格式化写法简化
  • 时刻用 reshape确认维数、数值类型
  • 切片 [:,:,0::2] ,广播:/num_features
  • torch.pow 幂指数
位置编码原理

link

transformer位置编码对token进行,衡量token的位置差异(相对位置编码)。结合方式是每个token输入E与位置编码PE相加,所以PE的维数要与E一致。

位置编码的要求:

  • 它能为每个时间步 t 输出一个独一无二的编码;
  • 不同长度的句子之间,任何两个时间步之间的距离应该保持一致(线性);
  • 模型应该能毫不费力地泛化到更长的句子(根据d给定)。它的值应该是有界的;
  • 它必须是确定性的。

位置编码分类:函数型、表格型。transformer的位置编码:函数型相对位置编码。

位置编码过程

[n,d] .给定一个长度为[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AuwzsVyK-1629276734855)(https://www.zhihu.com/equation?tex=n)]的输入序列,让[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iXTJgAhJ-1629276734856)(https://www.zhihu.com/equation?tex=t)]表示词在序列中的位置,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oiamGuQW-1629276734857)(https://www.zhihu.com/equation?tex=%5Coverrightarrow%7Bp_t%7D+%5Cin+%5Cmathbb%7BR%7D%5Ed)]表示[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-387BbYTv-1629276734858)(https://www.zhihu.com/equation?tex=t)]位置对应的向量,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nw8DKX4D-1629276734859)(https://www.zhihu.com/equation?tex=d)]是token词向量的维度。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AbS2bzXc-1629276734860)(https://www.zhihu.com/equation?tex=f%3A+%5Cmathbb%7BN%7D+%5Crightarrow+%5Cmathbb%7BR%7D%5Ed)]是生成位置向量[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ny95Ght2-1629276734860)(https://www.zhihu.com/equation?tex=%5Coverrightarrow%7Bp_t%7D)]的函数,定义如下:

p

t

(

i

)

=

f

(

t

)

(

i

)

:

=

{

sin

(

ω

k

t

)

,

if

i

=

2

k

)

cos

(

ω

k

t

)

,

if

i

=

2

k

1

\overrightarrow{p_{t}}{(i)}=f(t){(i)}:=\left{\begin{array}{ll} \sin \left(\omega_{k} \cdot t\right), & \text { if } i=2 k) \ \cos \left(\omega_{k} \cdot t\right), & \text { if } i=2 k+1 \end{array}\right.

p

t

(

i

)

=

f

(

t

)

(

i

)

:

=

{

sin

(

ω

k

t

)

,

cos

(

ω

k

t

)

,

if

i

=

2

k

)

if

i

=

2

k

1

ω

k

=

1

1000

0

2

k

/

d

\omega_{k}=\frac{1}{10000^{2 k / d}}

ω

k

=

1

0

0

0

0

2

k

/

d

1

对处在t位置的d维长度的向量添加位置编码为:

p

t

=

[

sin

(

ω

1

t

)

cos

(

ω

1

t

)

sin

(

ω

2

t

)

cos

(

ω

2

t

)

sin

(

ω

d

/

2

t

)

cos

(

ω

d

/

2

t

)

]

\overrightarrow{p_{t}}=\left[\begin{array}{c} \sin \left(\omega_{1} \cdot t\right) \ \cos \left(\omega_{1} \cdot t\right) \ \sin \left(\omega_{2} \cdot t\right) \ \cos \left(\omega_{2} \cdot t\right) \ \vdots \ \sin \left(\omega_{d / 2} \cdot t\right) \ \cos \left(\omega_{d / 2} \cdot t\right) \end{array}\right]

p

t

=

sin

(

ω

1

t

)

cos

(

ω

1

t

)

sin

(

ω

2

t

)

cos

(

ω

2

t

)

sin

(

ω

d

/

2

t

)

cos

(

ω

d

/

2

t

)

举个例子

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hCVf2SU5-1629276734861)(C:\Users\zhu\AppData\Roaming\Typora\typora-user-images\image-20210818144948639.png)]

为什么这种编码可以表示相对距离

相对距离
:位置pos和pos+k的token的距离能够在PE中表示出来。

证明如下:

回顾三角公式:

sin

(

α

β

)

=

sin

α

cos

β

cos

α

sin

β

cos

(

α

β

)

=

cos

α

cos

β

sin

α

sin

β

\begin{array}{l} \sin (\alpha+\beta)=\sin \alpha \cdot \cos \beta+\cos \alpha \cdot \sin \beta \ \cos (\alpha+\beta)=\cos \alpha \cdot \cos \beta-\sin \alpha \cdot \sin \beta \end{array}

sin

(

α

β

)

=

sin

α

cos

β

cos

α

sin

β

cos

(

α

β

)

=

cos

α

cos

β

sin

α

sin

β

计算pos+k位置的PE:

P

E

(

p

o

s

k

,

2

i

)

=

sin

(

w

i

(

p

o

s

k

)

)

=

sin

(

w

i

p

o

s

)

cos

(

w

i

k

)

cos

(

w

i

p

o

s

)

sin

(

w

i

k

)

=

cos

(

w

i

k

)

P

E

(

p

o

s

,

2

i

)

sin

(

w

i

k

)

P

E

(

p

o

s

,

2

i

1

)

P

E

(

p

o

s

k

,

2

i

1

)

=

cos

(

w

i

(

p

o

s

k

)

)

=

cos

(

w

i

p

o

s

)

cos

(

w

i

k

)

sin

(

w

i

p

o

s

)

sin

(

w

i

k

)

=

cos

(

w

i

k

)

P

E

(

p

o

s

,

2

i

1

)

sin

(

w

i

k

)

P

E

(

p

o

s

,

2

i

)

\begin{array}{l} P E_{(p o s+k, 2 i)}=\sin \left(w_{i} \cdot(p o s+k)\right)\=\sin \left(w_{i} p o s\right) \cos \left(w_{i} k\right)+\cos \left(w_{i} p o s\right) \sin \left(w_{i} k\right)\ =\cos \left(w_{i} k\right) P E_{(p o s, 2 i)}+\sin \left(w_{i} k\right) P E_{(p o s, 2 i+1)} \ \ P E_{(p o s+k, 2 i+1)}=\cos \left(w_{i} \cdot(p o s+k)\right)\=\cos \left(w_{i} p o s\right) \cos \left(w_{i} k\right)-\sin \left(w_{i} p o s\right) \sin \left(w_{i} k\right) \=\cos \left(w_{i} k\right) P E_{(p o s, 2 i+1)}-\sin \left(w_{i} k\right) P E_{(p o s, 2 i)} \end{array}

P

E

(

p

o

s

k

,

2

i

)

=

sin

(

w

i

(

p

o

s

k

)

)

=

sin

(

w

i

p

o

s

)

cos

(

w

i

k

)

cos

(

w

i

p

o

s

)

sin

(

w

i

k

)

=

cos

(

w

i

k

)

P

E

(

p

o

s

,

2

i

)

sin

(

w

i

k

)

P

E

(

p

o

s

,

2

i

1

)

P

E

(

p

o

s

k

,

2

i

1

)

=

cos

(

w

i

(

p

o

s

k

)

)

=

cos

(

w

i

p

o

s

)

cos

(

w

i

k

)

sin

(

w

i

p

o

s

)

sin

(

w

i

k

)

=

cos

(

w

i

k

)

P

E

(

p

o

s

,

2

i

1

)

sin

(

w

i

k

)

P

E

(

p

o

s

,

2

i

)

就可以将

P

E

p

o

s

k

PE_{pos+k}

P

E

p

o

s

k

写成一个矩阵的乘法。

[

P

E

(

p

o

s

k

,

2

i

)

P

E

(

p

o

s

k

,

2

i

1

)

]

=

[

u

v

v

u

]

×

[

P

E

(

p

o

s

,

2

i

)

P

E

(

p

o

s

,

2

i

1

)

]

\left[\begin{array}{c} P E_{(p o s+k, 2 i)} \ P E_{(p o s+k, 2 i+1)} \end{array}\right]=\left[\begin{array}{cc} u & v \ -v & u \end{array}\right] \times\left[\begin{array}{c} P E_{(p o s, 2 i)} \ P E_{(p o s, 2 i+1)} \end{array}\right]

[

P

E

(

p

o

s

k

,

2

i

)

P

E

(

p

o

s

k

,

2

i

1

)

]

=

[

u

v

v

u

]

×

[

P

E

(

p

o

s

,

2

i

)

P

E

(

p

o

s

,

2

i

1

)

]

所以

P

E

p

o

s

k

PE_{pos+k}

P

E

p

o

s

k

P

E

p

o

s

PE_{pos}

P

E

p

o

s

是线性关系,两者的倍数差别取决于k(位置差别)。

如何更直接的得到k,将

P

E

p

o

s

k

PE_{pos+k}

P

E

p

o

s

k

P

E

p

o

s

PE_{pos}

P

E

p

o

s

取内积:

P

E

p

o

s

P

E

p

o

s

k

=

i

=

0

d

2

1

sin

(

w

i

p

o

s

)

sin

(

w

i

(

p

o

s

k

)

)

cos

(

w

i

p

o

s

)

cos

(

w

i

(

p

o

s

k

)

)

=

i

=

0

d

2

1

cos

(

w

i

(

p

o

s

(

p

o

s

k

)

)

=

i

=

0

d

2

1

cos

(

w

i

k

)

\begin{aligned} P E_{p o s} \cdot P E_{p o s+k} &=\sum_{i=0}^{\frac{d}{2}-1} \sin \left(w_{i} p o s\right) \cdot \sin \left(w_{i}(p o s+k)\right)+\cos \left(w_{i} p o s\right) \cdot \cos \left(w_{i}(p o s+k)\right) \ &=\sum_{i=0}^{\frac{d}{2}-1} \cos \left(w_{i}(p o s-(p o s+k))\right.\ &=\sum_{i=0}^{\frac{d}{2}-1} \cos \left(w_{i} k\right) \end{aligned}

P

E

p

o

s

P

E

p

o

s

k

=

i

=

0

2

d

1

sin

(

w

i

p

o

s

)

sin

(

w

i

(

p

o

s

k

)

)

cos

(

w

i

p

o

s

)

cos

(

w

i

(

p

o

s

k

)

)

=

i

=

0

2

d

1

cos

(

w

i

(

p

o

s

(

p

o

s

k

)

)

=

i

=

0

2

d

1

cos

(

w

i

k

)

k越大,cos越小,内积越小,可对应得到两个token的相对位置差别。

缺点
:该编码方式只能找到相对位置,不能找到方向关系,即谁在谁的后面。如对于PE无法分辨出

P

E

p

o

s

k

PE_{pos+k}

P

E

p

o

s

k

P

E

p

o

s

k

PE_{pos-k}

P

E

p

o

s

k

的区别。

多头注意力
参数初始化

qkv维数一样,可以放在一起训练,或者q 不等于kv则分开训练。

self.q_proj_weight = Parameter(torch.empty((embed_dim, embed_dim)))
#torch.empty是按照所给的形状形成对应的tensor,特点是填充的值还未初始化,类比torch.randn(标准正态分布),这就是一种初始化的方式。在PyTorch中,变量类型是tensor的话是无法修改值的,而Parameter()函数可以看作为一种类型转变函数,将不可改值的tensor转换为可训练可修改的模型参数,即与model.parameters绑定在一起,register_parameter的意思是是否将这个参数放到model.parameters,None的意思是没有这个参数。

xavier_uniform_(self.in_proj_weight)
#reset_parameters()函数,这个是用来初始化参数数值的。xavier_uniform意思是从连续型均匀分布里面随机取样出值来作为初始化的值,xavier_normal_取样的分布是正态分布。正因为初始化值在训练神经网络的时候很重要,所以才需要这两个函数。

命名:in_proj_bias的意思就是一开始的线性变换的偏置

前向训练网络
  • query, key, value通过_in_projection_packed变换得到q,k,v
q, k, v = _in_projection_packed(query, key, value, in_proj_weight, in_proj_bias)

对于
nn.functional.linear
函数,其实就是一个线性变换,与
nn.Linear
不同的是,前者可以提供权重矩阵和偏置,执行[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EPFyy596-1629276734861)(https://camo.githubusercontent.com/a5016b70476aeff20d93e8632ba67120029d725c/687474703a2f2f6c617465782e636f6465636f67732e636f6d2f7376672e6c617465783f793d78575e542b62)],而后者是可以自由决定输出的维度。

  • 遮挡机制

对于attn_mask来说,若为2D,形状如
(L, S)
,L和S分别代表着目标语言和源语言序列长度,若为3D,形状如
(N * num_heads, L, S)
,N代表着batch_size,num_heads代表注意力头的数目。若为attn_mask的dtype为ByteTensor,非0的位置会被忽略不做注意力;若为BoolTensor,True对应的位置会被忽略;若为数值,则会直接加到attn_weights。

因为在decoder解码的时候,只能看该位置和它之前的,如果看后面就犯规了,所以需要attn_mask遮挡住。【直接用pytorch里面的】


attn_mask
不同的是,
key_padding_mask
是用来遮挡住key里面的值,详细来说应该是
<PAD>
,被忽略的情况与attn_mask一致。

logical_or

#输入两个tensor,并对这两个tensor里的值做逻辑或运算,只有当两个值均为0的时候才为False,其他时候均为True
a = torch.tensor([0,1,10,0],dtype=torch.int8)
b = torch.tensor([4,0,1,0],dtype=torch.int8)
print(torch.logical_or(a,b))
# tensor([ True,  True,  True, False])

#masked_fill,输入是一个mask,和用以填充的值。mask由1,0组成,0的位置值维持不变,1的位置用新值填充。
r = torch.tensor([[0,0,0,0],[0,0,0,0]])
mask = torch.tensor([[1,1,1,1],[0,0,0,0]])
print(r.masked_fill(mask,1))
# tensor([[1, 1, 1, 1],
#         [0, 0, 0, 0]])

  • 点积注意力

torch.bmm 是batch矩阵乘法,If
input
is a

(

b

×

n

×

m

)

(b \times n \times m)

(

b

×

n

×

m

)

tensor,
mat2
is a$ (b \times m \times p)

t

e

n

s

o

r

,

o

u

t

w

i

l

l

b

e

a

tensor, out will be a

t

e

n

s

o

r

,

o

u

t

w

i

l

l

b

e

a

(b \times n \times p)$ tensor.

#transpose 交换函数的维数,contiguous()断开两个变量的依赖,相当于改变之后的不会影响之前的变量了。
q = q.contiguous().view(tgt_len, bsz * num_heads, head_dim).transpose(0, 1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值