Post

Learning PyTorch

torch

torch.arrange

torch.arange 作用类似于 Python 的 range(),用于生成一个连续数值的一维向量start是起始值(默认 0),end是结束值(不包含端点),step是步长(默认为1)

1
2
tensor = torch.arange(start=0, end=10, step=1, dtype=None, device=None)
print(tensor) # tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

torch.expand

expand()方法用于扩展张量的维度,但不会复制数据,而是通过广播机制让多个维度共享相同的数据,使用如下:

1
2
sizes = x.shape
y = x.expand(*sizes) # (-1 代表保持原维度)

正余弦位置编码实现

对于位置 $pos$和维度索引 $i$,位置编码的计算公式为:

\begin{aligned}
PE_{(pos, 2i)} &= \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) \\
PE_{(pos, 2i+1)} &= \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)
\end{aligned}

Torch的代码实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import torch
import torch.nn as nn
import math

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super().__init__()
        
        # 生成位置编码矩阵 [max_len, d_model]
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)  # [max_len, 1]
        
        # 计算div_term:10000^(2i/d_model)的倒数(对数空间计算避免溢出)
        div_term = torch.exp(
            torch.arange(0, d_model, 2).float() * 
            (-math.log(10000.0) / d_model
        ))  # [d_model/2]
        
        # 填充偶数和奇数维度
        pe[:, 0::2] = torch.sin(position * div_term)  # 偶数维度:sin(pos/10000^(2i/d))
        pe[:, 1::2] = torch.cos(position * div_term)  # 奇数维度:cos(pos/10000^(2i/d))
        
        # 注册为缓冲区(不参与训练)
        self.register_buffer('pe', pe.unsqueeze(0))  # [1, max_len, d_model]
        
    def forward(self, x):
        """
        x: 输入张量 [batch_size, seq_len, d_model]
        返回: [batch_size, seq_len, d_model]
        """
        return x + self.pe[:, :x.size(1)]  # 自动广播相加

正余弦编码的特点:固定不变(没有可学习参数);低维编码变化快,高维编码变化慢,这是因为维度索引 $i$ 越大,则 $\frac{2i}{d_{\text{model}}}$ 越大,对应地三角函数的频率 $\omega$ 也就越小,因此三角函数的变化周期 $T=\frac{2 \pi}{\omega}$ 也就越慢

This post is licensed under CC BY 4.0 by the author.

Trending Tags