Transformer 大语言模型(LLM)基石 - Transformer架构详解 - 位置编码(Positional Encoding)详解与算法实现

锋哥原创的Transformer 大语言模型(LLM)基石视频教程:

https://www.bilibili.com/video/BV1X92pBqEhV

课程介绍

本课程主要讲解Transformer简介,Transformer架构介绍,Transformer架构详解,包括输入层,位置编码,多头注意力机制,前馈神经网络,编码器层,解码器层,输出层,以及Transformer Pytorch2内置实现,Transformer基于PyTorch2手写实现等知识。

Transformer 大语言模型(LLM)基石 - Transformer架构详解 - 位置编码(Positional Encoding)详解与算法实现

由于Transformer不具有像RNN或LSTM那样的时间步长顺序结构,它需要一个方法来注入序列中元素的位置信息。位置编码是将每个词语在序列中的位置表示为一个向量,与输入嵌入相加。

  • 功能:向模型提供词语的位置信息,使得Transformer能够理解词语在序列中的相对位置。

  • 结构:位置编码向量与输入嵌入矩阵逐元素相加,形状保持不变,仍为[batch_size, seq_len, embedding_dim]。

偶数位置用正弦函数,奇数位置用余弦函数。

公式参数:pos 是 词位置索引 dmodel是词嵌入维度

Transformer 中使用正弦和余弦函数进行位置编码,主要基于以下几个原因:

  1. 相对位置信息:正弦和余弦函数具有周期性,能够自然地表示相对位置。对于任意固定的偏移量 k,位置 pos+k 的位置编码可以表示为位置 pos 的位置编码的线性函数。这意味着模型可以很容易地学习到相对位置信息。

  2. 距离感知:正弦和余弦函数的乘积(内积)可以反映位置之间的距离。两个位置编码的内积会随着位置距离的增加而减少,这有助于模型捕捉序列中的顺序和距离。

  3. 唯一性:不同位置的正弦和余弦组合是唯一的,确保了每个位置都有唯一的编码。

  4. 连续性和平滑性:正弦和余弦函数是连续且平滑的,这有助于模型在训练过程中稳定地学习位置信息。

  5. 可扩展性:正弦和余弦函数可以处理任意长度的序列,因为它们的值域在 [-1, 1] 之间,不会随着位置增大而爆炸或消失。

代码实现:

复制代码
import math
​
import torch
from torch import nn
​
​
# 输入嵌入层类
class Embeddings(nn.Module):
    def __init__(self, vocab_size, embedding_dim):  # vocab_size:词表大小 embedding_dim:词嵌入维度的大小
        super().__init__()
        self.embedding_dim = embedding_dim
        self.embedding = nn.Embedding(vocab_size, embedding_dim)
​
    def forward(self, x):
        embed = self.embedding(x)
        # 缩放词嵌入(原始Transformer论文中的关键细节) 防止向量初始值过大,稳定训练
        return embed * math.sqrt(self.embedding_dim)
​
​
# 位置编码器层类
class PositionalEncoding(nn.Module):
    """
    参数:
        d_model: 嵌入维度(必须为偶数)
        max_len: 支持的最大序列长度 也就是样本句子的最大长度
        dropout: Dropout概率 防止模型过拟合,从而提升其泛化能力
    """
​
    def __init__(self, d_model, max_len=100, dropout=0.1):
        super().__init__()
        self.dropout = nn.Dropout(dropout)
        # 创建一个二维张量,用于存储位置编码 [max_len, d_model]->[100, 512]
        self.pe = torch.zeros(max_len, d_model)
        print("pe:", self.pe)
        print("pe.shape:", self.pe.shape)
        # 创建一个二维张量,用于存储位置索引 [max_len, 1] -> [100, 1]
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        print(torch.arange(0, max_len, dtype=torch.float))
        print("position:", position)
        print("position.shape:", position.shape)
        # 创建一个二维张量,用于存储位置编码的 sin 和 cos 值 维度: [d_model/2]
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
        print("div_term:", div_term)
        print("div_term.shape:", div_term.shape)
        # 填充正弦和余弦值 position * div_term [100,256]
        self.pe[:, 0::2] = torch.sin(position * div_term)  # 偶数位置的 sin
        self.pe[:, 1::2] = torch.cos(position * div_term)  # 奇数位置的 cos
        print("pe:", self.pe)
        print("pe.shape:", self.pe.shape)
        # 添加批次维度: [1, max_len, d_model] -> [1,100,512] 目的是为了和输入张量的维度一致
        self.pe = self.pe.unsqueeze(0)
​
    def forward(self, x):
        """
           前向传播
           参数:
              x: 输入张量 [batch_size, seq_len, d_model]
           返回:
              添加位置编码后的张量
        """
        seq_len = x.size(1)
        # x 维度 [3, 5, 512]
        # 将位置编码添加到输入张量中
        print("self.pe[:, :seq_len]:", self.pe[:, :seq_len].shape)  # [1, 5, 512]
        x = x + self.pe[:, :seq_len]
        print("x:", x.shape)  # [3, 5, 512]
        return self.dropout(x)
​
​
if __name__ == '__main__':
    vocab_size = 2000  # 词表大小
    embedding_dim = 512  # 词嵌入维度的大小
    embeddings = Embeddings(vocab_size, embedding_dim)
    embed_result = embeddings(torch.tensor([[1999, 2, 99, 4, 5], [66, 2, 3, 22, 5], [66, 2, 3, 4, 5]]))
    print("embed_result.shape:", embed_result.shape)
    print("embed_result", embed_result)
​
    positional_encoding = PositionalEncoding(embedding_dim)
    result = positional_encoding(embed_result)
    print("result:", result)
    print("result.shape:", result.shape)

vocab_size 和 max_len关系

  • vocab_size:表示模型能够识别的不同词元(token)的数量。例如,如果使用一个包含30000个单词的词汇表,那么vocab_size=30000

  • max_len:表示模型能够处理的单个序列的最大长度(即序列中词元的数量)。例如,如果设定max_len=512,那么每个输入序列最多可以包含512个词元。

torch.exp() 是 PyTorch 中的指数函数,计算输入张量中每个元素的自然指数(以 e 为底)。

其中 e ≈ 2.71828(自然对数的底数)

数学定义:

复制代码
output = e^{input}

函数定义:

复制代码
torch.exp(input, *, out=None) → Tensor

参数:

  • input (Tensor):输入张量

  • out (Tensor, optional):输出张量(可选)

返回值:

  • 与输入张量形状相同的张量,每个元素都是输入对应元素的指数值

torch.log() 是 PyTorch 中的自然对数函数,计算输入张量中每个元素的自然对数(以 e 为底)。

其中 e ≈ 2.71828(自然对数的底数)

数学定义:

复制代码
output = ln(input)

函数定义:

复制代码
torch.log(input, *, out=None) → Tensor

参数:

  • input (Tensor):输入张量(必须为正数)

  • out (Tensor, optional):输出张量(可选)

返回值:

  • 与输入张量形状相同的张量,每个元素都是输入对应元素的自然对数值
相关推荐
阿杰学AI1 分钟前
AI核心知识45——大语言模型之PPO(简洁且通俗易懂版)
人工智能·ai·语言模型·自然语言处理·aigc·ppo·近端策略优化
CoovallyAIHub5 分钟前
南京理工大学联手百度、商汤科技等团队推出Artemis:用结构化视觉推理革新多模态感知
深度学习·算法·计算机视觉
天才少女爱迪生8 分钟前
图像序列预测有什么算法方案
人工智能·python·深度学习·算法
shayudiandian31 分钟前
深度学习可视化:用TensorBoard分析模型训练过程
人工智能·深度学习·neo4j
九年义务漏网鲨鱼34 分钟前
【大模型微调】QLoRA微调原理及实战
深度学习·算法·大模型·智能体
测试人社区-千羽35 分钟前
构建卓越体验:UX测试评估标准的系统框架与实践指南
人工智能·语言模型·自然语言处理·职场和发展·aigc·ux
晞微36 分钟前
基于 Gradio 构建神经网络 GUI 实验平台:感知器 / BP/Hopfield/AlexNet/VGG/ResNet 一站式实现
人工智能·深度学习·神经网络
柳安忆37 分钟前
[GLM-4.6V 多模态能力测评】对论文pipeline图的理解能力 #视觉理解MCP、#GLM我的编码搭子
人工智能·深度学习
拉普拉斯妖10838 分钟前
DAY33 简单的神经网络
人工智能·深度学习·神经网络
andwhataboutit?38 分钟前
神经网络某些概念(持续更新
人工智能·深度学习·神经网络