用例子和代码了解词嵌入和位置编码

1.嵌入(Input Embedding)

让我用一个更具体的例子来解释输入嵌入(Input Embedding)。

背景

假设我们有一个非常小的词汇表,其中包含以下 5 个词:

  • "I"
  • "love"
  • "machine"
  • "learning"
  • "!"

假设我们想把这句话 "I love machine learning !" 作为输入。

步骤 1:创建词汇表(Vocabulary)

我们给每个词分配一个唯一的索引号:

  • "I" -> 0
  • "love" -> 1
  • "machine" -> 2
  • "learning" -> 3
  • "!" -> 4
步骤 2:创建嵌入矩阵(Embedding Matrix)

假设我们选择每个词的向量维度为 3(实际应用中维度会更高)。我们初始化一个大小为 5x3 的嵌入矩阵,如下所示:

嵌入矩阵(Embedding Matrix):
[
  [0.1, 0.2, 0.3],  // "I" 的向量表示
  [0.4, 0.5, 0.6],  // "love" 的向量表示
  [0.7, 0.8, 0.9],  // "machine" 的向量表示
  [1.0, 1.1, 1.2],  // "learning" 的向量表示
  [1.3, 1.4, 1.5]   // "!" 的向量表示
]
步骤 3:查找表操作(Lookup Table Operation)

当我们输入句子 "I love machine learning !" 时,我们首先将每个词转换为其对应的索引:

  • "I" -> 0
  • "love" -> 1
  • "machine" -> 2
  • "learning" -> 3
  • "!" -> 4

然后,我们使用这些索引在嵌入矩阵中查找相应的向量表示:

输入句子嵌入表示:
[
  [0.1, 0.2, 0.3],  // "I" 的向量表示
  [0.4, 0.5, 0.6],  // "love" 的向量表示
  [0.7, 0.8, 0.9],  // "machine" 的向量表示
  [1.0, 1.1, 1.2],  // "learning" 的向量表示
  [1.3, 1.4, 1.5]   // "!" 的向量表示
]
步骤 4:输入嵌入过程

        通过查找表操作,我们把原本的句子 "I love machine learning !" 转换成了一个二维数组,每一行是一个词的嵌入向量。

码示例代

让我们用 Python 和 PyTorch 来实现这个过程:

import torch
import torch.nn as nn

# 假设词汇表大小为 5,嵌入维度为 3
vocab_size = 5
embedding_dim = 3

# 创建一个嵌入层
embedding_layer = nn.Embedding(num_embeddings=vocab_size, embedding_dim=embedding_dim)

# 初始化嵌入矩阵(为了便于理解,这里手动设置嵌入矩阵的值)
embedding_layer.weight = nn.Parameter(torch.tensor([
    [0.1, 0.2, 0.3],  # "I"
    [0.4, 0.5, 0.6],  # "love"
    [0.7, 0.8, 0.9],  # "machine"
    [1.0, 1.1, 1.2],  # "learning"
    [1.3, 1.4, 1.5]   # "!"
]))

# 输入句子对应的索引
input_indices = torch.tensor([0, 1, 2, 3, 4])

# 获取输入词的嵌入表示
embedded = embedding_layer(input_indices)

print(embedded)

输出: 

tensor([[0.1000, 0.2000, 0.3000],
        [0.4000, 0.5000, 0.6000],
        [0.7000, 0.8000, 0.9000],
        [1.0000, 1.1000, 1.2000],
        [1.3000, 1.4000, 1.5000]], grad_fn=<EmbeddingBackward>)

        这样我们就完成了输入嵌入的过程,把离散的词转换为了连续的向量表示。

        当你完成了词嵌入,将离散的词转换为连续的向量表示后,位置编码步骤如下:

2. 理解位置编码

        位置编码(Positional Encoding)通过生成一组特殊的向量,表示词在序列中的位置,并将这些向量添加到词嵌入上,使模型能够识别词序。

2.1 位置编码公式

        位置编码使用正弦和余弦函数生成。具体公式如下:

其中:

  •  是词在序列中的位置。
  •  i是词嵌入向量的维度索引。
  •  d是词嵌入向量的总维度。

2.2 生成位置编码向量

        以下是 Python 代码示例,展示如何生成位置编码向量,并将其添加到词嵌入上:

        生成位置编码向量

import numpy as np
import torch

def get_positional_encoding(max_len, d_model):
    """
    生成位置编码向量
    :param max_len: 序列的最大长度
    :param d_model: 词嵌入向量的维度
    :return: 形状为 (max_len, d_model) 的位置编码矩阵
    """
    pos = np.arange(max_len)[:, np.newaxis]
    i = np.arange(d_model)[np.newaxis, :]
    angle_rates = 1 / np.power(10000, (2 * (i // 2)) / np.float32(d_model))
    angle_rads = pos * angle_rates

    # 采用正弦函数应用于偶数索引 (2i)
    angle_rads[:, 0::2] = np.sin(angle_rads[:, 0::2])

    # 采用余弦函数应用于奇数索引 (2i+1)
    angle_rads[:, 1::2] = np.cos(angle_rads[:, 1::2])

    return torch.tensor(angle_rads, dtype=torch.float32)

# 示例参数
max_len = 100  # 假设最大序列长度为 100
d_model = 512  # 假设词嵌入维度为 512

# 生成位置编码矩阵
positional_encoding = get_positional_encoding(max_len, d_model)
print(positional_encoding.shape)  # 输出: torch.Size([100, 512])

2.3 添加位置编码到词嵌入

        假设你已经有一个词嵌入张量 embedded,它的形状为 (batch_size, seq_len, d_model),可以将位置编码添加到词嵌入中:

class TransformerEmbedding(nn.Module):
    def __init__(self, vocab_size, d_model, max_len):
        super(TransformerEmbedding, self).__init__()
        self.token_embedding = nn.Embedding(vocab_size, d_model)
        self.positional_encoding = get_positional_encoding(max_len, d_model)
        self.dropout = nn.Dropout(p=0.1)

    def forward(self, x):
        # 获取词嵌入
        token_embeddings = self.token_embedding(x)

        # 添加位置编码
        seq_len = x.size(1)
        position_embeddings = self.positional_encoding[:seq_len, :]

        # 词嵌入和位置编码相加
        embeddings = token_embeddings + position_embeddings.unsqueeze(0)
        return self.dropout(embeddings)

# 示例参数
vocab_size = 10000  # 假设词汇表大小为 10000
d_model = 512       # 词嵌入维度
max_len = 100       # 最大序列长度

# 实例化嵌入层
embedding_layer = TransformerEmbedding(vocab_size, d_model, max_len)

# 假设输入序列为一批大小为 2,序列长度为 10 的张量
input_tensor = torch.tensor([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
                             [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]], dtype=torch.long)

# 获取嵌入表示
output_embeddings = embedding_layer(input_tensor)
print(output_embeddings.shape)  # 输出: torch.Size([2, 10, 512])

2.4. 继续进行 Transformer 模型的前向传播

        有了词嵌入和位置编码之后,接下来的步骤就是将这些嵌入输入到 Transformer 模型的编码器和解码器中,进行进一步处理。Transformer 模型的编码器和解码器由多层注意力机制和前馈神经网络组成。

        位置编码步骤通过生成一组正弦和余弦函数的向量,并将这些向量添加到词嵌入上,使 Transformer 模型能够捕捉序列中的位置信息。

import torch
import torch.nn as nn

class MultiHeadSelfAttention(nn.Module):
    def __init__(self, d_model, nhead):
        super(MultiHeadSelfAttention, self).__init__()
        assert d_model % nhead == 0, "d_model 必须能被 nhead 整除"
        self.d_model = d_model
        self.d_k = d_model // nhead
        self.nhead = nhead

        self.W_q = nn.Linear(d_model, d_model)
        self.W_k = nn.Linear(d_model, d_model)
        self.W_v = nn.Linear(d_model, d_model)
        self.fc = nn.Linear(d_model, d_model)
        self.dropout = nn.Dropout(0.1)
        self.scale = torch.sqrt(torch.FloatTensor([self.d_k]))

    def forward(self, x):
        batch_size = x.size(0)
        seq_len = x.size(1)

        # 线性变换得到 Q, K, V
        Q = self.W_q(x)
        K = self.W_k(x)
        V = self.W_v(x)

        # 分成多头
        Q = Q.view(batch_size, seq_len, self.nhead, self.d_k).transpose(1, 2)
        K = K.view(batch_size, seq_len, self.nhead, self.d_k).transpose(1, 2)
        V = V.view(batch_size, seq_len, self.nhead, self.d_k).transpose(1, 2)

        # 计算注意力权重
        attn_weights = torch.matmul(Q, K.transpose(-2, -1)) / self.scale
        attn_weights = torch.nn.functional.softmax(attn_weights, dim=-1)
        attn_weights = self.dropout(attn_weights)

        # 加权求和
        attn_output = torch.matmul(attn_weights, V)

        # 拼接多头输出
        attn_output = attn_output.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model)

        # 最后的线性变换
        output = self.fc(attn_output)
        return output

# 示例参数
d_model = 8
nhead = 2

# 输入张量
x = torch.rand(2, 5, d_model)

# 实例化多头自注意力层
multi_head_attn = MultiHeadSelfAttention(d_model, nhead)

# 前向传播
output = multi_head_attn(x)
print("多头自注意力输出:\n", output)

解释

  • 线性变换:使用 nn.Linear 实现线性变换,将输入张量  通过三个不同的线性层得到查询、键和值向量。
  • 分成多头:使用 view 和 transpose 方法将查询、键和值向量分成多头,形状变为 。
  • 计算注意力权重:通过点积计算查询和键的相似度,并通过 softmax 归一化得到注意力权重。
  • 加权求和:使用注意力权重对值向量进行加权求和,得到每个头的输出。
  • 拼接多头输出:将多头的输出拼接起来,并通过一个线性层进行变换,得到最终的输出。

        查询、键和值向量的生成是多头自注意力机制的关键步骤,通过线性变换将输入向量转换为查询、键和值向量,然后使用这些向量计算注意力权重,捕捉输入序列中不同位置的相关性。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【后端面试题】【中间件】【NoSQL】MongoDB提高可用性的方案(主从结构、仲裁节点、分片、写入语义)

主从结构 MongoDB的高可用和别的中间件的高可用方案基本类似。比如在MySQL里&#xff0c;接触了分库分表和主从同步&#xff1b;在Redis里&#xff0c;Redis也有主从结构&#xff1b;在Kafka里&#xff0c;分区也是有主从结构的。 所以先介绍启用了主从同步 我们的系统有一个关…

使用ChatGPT自动生成测试用例思维导图

使用ChatGPT自动生成测试用例思维导图 引言ChatGPT在测试用例编写中的应用全面覆盖测试场景边界测试避免测试用例重复 借助ChatGPT生成测试用例思维导图准备工作步骤一&#xff1a;与ChatGPT对话步骤二&#xff1a;生成思维导图代码 结语 引言 在编写测试用例时&#xff0c;测…

物联网应用Fast ingest

一、原文路径 Tuning the System Global Area 二、翻译 1、原理 Fast ingest 优化是针对高并发&#xff0c;单行数据的插入这种场景的。比如IOT应用采集&#xff08;很符合国网的用采数据场景&#xff09;。 Fast ingest 使用MEMOPTIMIZE_WRITE 提示来插入数据到 MEMOPTIM…

WordPress付费进群V2主题,多种引流方法,引私域二次变现

全新前端UI界面&#xff0c;多种前端交互特效让页面不再单调&#xff0c;进群页面群成员数&#xff0c;群成员头像名称&#xff0c;每次刷新页面随机更新不重复&#xff0c;最下面评论和点赞也是如此随机刷新不重复 进群页面简介&#xff0c;群聊名称&#xff0c;群内展示&…

thinkphp6/8 验证码

html和后台验证代码按官方来操作 ThinkPHP官方手册 注意&#xff1a; 如果验证一直失败&#xff0c;看看Session是否开启&#xff0c; 打印dump(session_status());结果2为正确的&#xff0c; PHP_SESSION_DISABLED: Session功能被禁用&#xff08;返回值为0&#xff09;。…

WAF的新选择,雷池 SafeLine-安装动态防护使用指南

什么是 WAF WAF 是 Web Application Firewall 的缩写&#xff0c;也被称为 Web 应用防火墙。 区别于传统防火墙&#xff0c;WAF 工作在应用层&#xff0c;对基于 HTTP/HTTPS 协议的 Web 系统有着更好的防护效果&#xff0c;使其免于受到黑客的攻击&#xff1b; 通俗来讲&#…

vue3+ts项目中.env配置环境变量与情景配置

一、环境变量配置 官网https://cn.vitejs.dev/guide/env-and-mode.html#intellisense 1. 新建.env开头的文件在根目录 为了防止意外地将一些环境变量泄漏到客户端&#xff0c;只有以 VITE_ 为前缀的变量才会暴露给经过 vite 处理的代码 .env 所有环境默认加载 .env.developm…

C++ 语法

一、头文件与源文件 头文件用于声明函数,类似于java中service层的接口; 源文件用于实现头文件函数,相当于java中serviceImpl层的实现类; 定义接口 实现接口 使用接口 二、指针概述 定义与使用 定义一个指针p用于存a变量的内存地址,即指针就是地址; 解引用可以获取或修改…

并发编程面试题1

一、原子性高频问题: 1.1 Java中如何实现线程安全? 多线程操作共享数据会出现问题。可以使用锁来解决: 悲观锁: 使用 synchronized 和 Lock乐观锁: 使用 CAS(Compare-And-Swap)可以根据业务情况选择 ThreadLocal,让每个线程处理自己的数据。 1.2 CAS底层实现 回答思…

Prometheus 监控服务器

Prometheus概述 组件化设置&#xff1a;nginx ,ceph , Prometheus 部署Prometheus服务器 配置时间 安装Prometheus服务器 访问web页面&#xff1a;http://192.168.88.5:9090/ 添加被监控端 监控方式&#xff1a; 拉取&#xff1a;pull。监控端联系被监控端&#xff0c;采集数…

116-基于5VLX110T FPGA FMC接口功能验证6U CPCI平台

一、板卡概述 本板卡是Xilinx公司芯片V5系列芯片设计信号处理板卡。由一片Xilinx公司的XC5VLX110T-1FF1136 / XC5VSX95T-1FF1136 / XC5VFX70T-1FF1136芯片组成。FPGA接1片DDR2内存条 2GB&#xff0c;32MB Nor flash存储器&#xff0c;用于存储程序。外扩 SATA、PCI、PCI expres…

STM32远程烧录程序

目录 简介 不同的程序下载方式 ICP&#xff1a;In-Circuit Programming ISP&#xff1a;In-System Programing IAP&#xff1a;In-Application Programming BootLoader Bootloader 是什么&#xff1f; STM32的启动方式 存储器组织 存储器映像 嵌入式SRAM 嵌入式FL…

【JVM系列】内存泄漏

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Python数据分析-股票数据分析(GARCH模型)

一、研究背景 随着金融市场的不断发展和全球经济的日益复杂&#xff0c;市场波动性和风险管理成为投资者和金融机构关注的焦点。波动率是衡量市场风险的重要指标&#xff0c;准确预测和评估波动率对于资产定价、风险控制和投资决策具有重要意义。在金融时间序列分析中&#xf…

唐山养老院哪家好---老了怎么过?到这里,享受生活的每一刻!

随着时间的流逝&#xff0c;我们每个人都将迎来老年时光&#xff0c;而"老了&#xff0c;怎么过&#xff1f;"这个问题&#xff0c;虽然简单&#xff0c;却深深触动了无数人的心。 面对老年生活&#xff0c;每个人都有不同的选择和追求。有的人选择顺其自然&#xf…

单目相机减速带检测以及测距

单目相机减速带检测以及测距项目是一个计算机视觉领域的应用&#xff0c;旨在使用一个摄像头&#xff08;单目相机&#xff09;来识别道路上的减速带&#xff0c;并进一步估计车辆与减速带之间的距离。这样的系统对于智能驾驶辅助系统&#xff08;ADAS&#xff09;特别有用&…

新章节:全设备通用调度算法-通讯重构

新章节&#xff1a;全设备通用调度算法-通讯重构 文章目录 新章节&#xff1a;全设备通用调度算法-通讯重构前言一、重构了TCP和UDP通讯二、优化了OPC和OPCUA三、升级了监控客户端四、升级了通讯的图形化其他升级 前言 现在真的很懒也很少写代码了&#xff0c;写代码和更新进度…

Android 15 应用适配默认全屏的行为变更(Android V的新特性)

简介 Android V 上默认会使用全面屏兼容方式&#xff0c;影响应用显示&#xff0c;导致应用内跟导航标题重合&#xff0c;无法点击上移的内容。 默认情况下&#xff0c;如果应用以 Android 15&#xff08;API 级别 35&#xff09;为目标平台&#xff0c;在搭载 Android 15 的设…

【网络安全的神秘世界】SQL注入(下)

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 3.7 二次注入 不好挖这个漏洞&#xff0c;需要搞懂业务逻辑关系 二次注入通常是指在存入数据库时做了过滤&#xff0c;但是取…

vue中自定义设置多语言,并且运行js脚本自动生成多语言文件

在项目中需要进行多个国家语言的切换时&#xff0c;可以用到下面方法其中一个 一、自定义设置多语言 方法一: 可以自己编写一个设置多语言文件 在项目新建js文件&#xff0c;命名为&#xff1a;language.js&#xff0c;代码如下 // language.js 文档 let languagePage {CN…