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

news2024/9/22 17:19:20

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.coloradmin.cn/o/1911056.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Greenplum(三)【分布式事务和两阶段提交协议】

1、事务实现原理和 WAL&#xff08;单机&#xff09; 属性含义数据库系统实现Atomic&#xff08;原子性&#xff09;事务中的操作要么全部正确执行&#xff0c;要么完全不执行&#xff08;要么成功、要么失败&#xff09;Write Ahead Logging 预写日志&#xff0c;分布式事务&…

牛客周赛 Round 50 解题报告 | 珂学家

前言 题解 数学场&#xff0c;对数学头痛, T_T. A. 小红的最小最大 题型: 签到 a, b, x list(map(int, input().split()))if min(a, b) x > max(a, b):print ("YES") else:print ("NO")B. 小红的四则运算&#xff08;easy&#xff09; 思路: 贪心…

职场中的3个误区,你踩坑了吗?

1、个人发展比工资待遇更重要 这句话也不能说是完全错的&#xff0c;但是你要明白能给你提供发展空间的公司&#xff0c;待遇也不会差到哪里去&#xff0c;而且随着你个人能力的提升&#xff0c;发展也会越来越好&#xff0c;你的待遇也自然水涨船高&#xff0c;这个道理其实大…

乐鑫ESP-NOW与Wi-Fi SoC方案家居设备无缝连接,启明云端乐鑫代理商

随着科技的不断进步&#xff0c;智能家居逐渐成为现代生活的一部分。ESP-NOW技术以其独特的无线通信能力&#xff0c;为智能家居领域带来了一场革命。 ESP-NOW是一种由乐鑫定义的无线通信协议&#xff0c;它能够在无需路由器的情况下&#xff0c;实现设备间的直接、快速、低功…

如何高效学习(一)

什么是学习&#xff1f;学习的本质是什么&#xff1f;如何学习&#xff1f;如何更加高效的学习 以下内容均为观看B站UP主(硬核学长2077)所做总结和自我分析 一、自我介绍 ​ 我&#xff0c;一个二三线城市小小程序员&#xff0c;在高中学习就很一般&#xff0c;但当时并没有特…

解锁敦煌网成功秘籍:批量注册买家号测评的高效策略

敦煌网&#xff08;DHgate&#xff09;作为一个跨境电商平台&#xff0c;搭建境外本土网络环境并实现批量注册买家号下单&#xff0c;需要遵循一系列严谨的步骤和考虑多个关键因素。以下是一个概括性的指南&#xff1a; 一、环境要求 国外服务器&#xff1a;首先&#xff0c;…

WPF 初识依赖属性

依赖属性的意义和作用 核心模块内存共享&#xff0c;节省空间数据绑定、样式、模板、动画。。。。如果没有依赖属性&#xff0c;这个框架就是一个控件框架 相当于Winform 依赖属性的基本定义 基本过程&#xff1a;声明、注册、包装 在需要写依赖属性的类中&#xff0c;继承…

Axure第12享:Google加载Axure扩展程序

1、需求描述 在双击打开RP文件进行预览时&#xff0c;提示要为Google浏览器加载Extension&#xff08;扩展程序&#xff09;&#xff0c;如下图所示。 2、解决思路 按照系统指导的操作步骤&#xff0c;但要注意1点&#xff0c;加载“扩展程序”时是选择整个文件夹&#xff0c…

3款ui设计师必备的高效软件,一定要收藏!

UI设计小伙伴们&#xff0c;你们是否在寻找那些能够让设计工作事半功倍的插件呢&#xff1f;今天&#xff0c;我要为大家带来3款UI设计软件中的高效软件&#xff0c;它们不仅能够极大提升我们的工作效率&#xff0c;还能让我们的设计更加专业和精致。让我们一起来看看这些不容错…

动手学深度学习54 循环神经网络

动手学深度学习54 循环神经网络 1. 循环神经网络RNN2. QA 1. 循环神经网络RNN h t h_t ht​ 与 h t − 1 h_{t-1} ht−1​ x t − 1 x_{t-1} xt−1​有关 x t x_t xt​ 与 h t h_t ht​ x t − 1 x_{t-1} xt−1​ 有关 怎么把潜变量变成RNN–假设更简单 潜变量和隐变量的区…

透明加密软件核心技术分享|十款好用的透明加密软件分享

透明加密软件的核心技术在于其能够自动、实时地对文件进行加密和解密&#xff0c;而这个过程对最终用户来说是无感的。这种技术在不改变用户操作习惯的前提下&#xff0c;增强了数据的安全性。下面是透明加密软件的一些核心技术要点。 实时加密与解密&#xff1a;软件在文件被打…

虚拟内存【Linux】

虚拟内存 为什么需要虚拟内存Linux虚拟内存的结构32位系统下的虚拟地址空间64位系统下的虚拟地址空间页表多级页表TLB 流程虚拟内存的作用 为什么需要虚拟内存 为了在进行多进程编码进行内存访问的时候保持内存的隔离性&#xff0c;数据安全性&#xff0c;所以出现了虚拟内存。…

C++--智能指针

普通指针创建动态内存的问题: 1.new和new[]的内存需要使用delete和delete []释放。 2.有时忘记释放内存。 3.不知该在何时释放内存。 智能指针的优点: 在不需要对象时自动释放对象&#xff0c;从而避免内存泄漏和其他与内存管理相关的问题。 智能指针有:unique_ptr,share…

可转债之强赎条款

摘要&#xff1a;每天学习一点金融小知识 做可转债投资&#xff0c;强赎风险是特别需要注意的&#xff0c;若投资者没有及时采取措施&#xff0c;就有可能造成很大的损失。本文从可转债的定义、强赎条款的原因及强赎的情况几个方面来介绍下可转债的强赎条款。 什么是可转换债券…

算法——同步算法

在力扣有这样一道题求交集&#xff0c;与此类似的还有求差集&#xff0c;相关的解法有很多。我这里提供一种思路&#xff1a;利用C的容器set对这两个数组去重&#xff0c;遍历数组插入set即可去重。再同时遍历比较set的每个元素。 代码实现很简单&#xff0c;如下所示&#xff…

【第四届会后4个月检索】第五届计算机网络安全与软件工程国际学术会议(CNSSE 2025)

第五届计算机网络安全与软件工程国际学术会议&#xff08;CNSSE 2025&#xff09; 2025 5th International Conference on Computer Network Security and Software Engineering 重要信息 大会官网&#xff1a;www.cnsse.org 大会时间&#xff1a;2025年2月21-23日 会议地点&…

CTF-PWN-kernel-栈溢出(retuser rop pt_regs ret2dir)

文章目录 参考qwb2018 core检查逆向调试打包上传测试脚本retuserkernel ropinit_credcommit_creds( prepare_kernel_cred(0) )开启KPTI利用swapgs_restore_regs_and_return_to_usermode开启KPTI利用SIGSEGVrop设置CR3寄存器再按照没有KPTI返回 kernel rop ret2userpt_regs 构造…

使用命令行修改Ubuntu 24.04的网络设置

Ubuntu里&#xff0c;使用命令行下修改IP地址&#xff0c;网上有很多方案&#xff0c;我最终觉得这个方案&#xff08;使用Netplan&#xff09;最好&#xff0c;最根本&#xff0c;记录下来备查 1.使用命令ip link show 查看Ubuntu上可以使用的网络接口名称 2.查找Netplan的配…

全志T527 适配YT8531 双以太网

一、确认硬件接口 phy1&#xff1a; phy2&#xff1a; PHY 地址设置&#xff1a; YT8531 的地址由上图所示的三个管脚外接 ( 或内部默认 ) 电阻来配置。外部不接上下拉电阻时&#xff0c;内部默认 phy 地址为 000( 十进制 0) &#xff1b;若外接电阻&#xff0c;例如上图所接…

前端面试题33(实时消息传输)

前端实时传输协议主要用于实现实时数据交换&#xff0c;特别是在Web应用中&#xff0c;它们让开发者能够构建具有实时功能的应用&#xff0c;如聊天、在线协作、游戏等。以下是几种常见的前端实时传输协议的讲解&#xff1a; 1. Short Polling (短轮询) 原理&#xff1a;客户…