向量one-hot编码和embedding

参考资料:12.循环神经网络(基础篇)_哔哩哔哩_bilibili

1. 什么是one-hot编码?

One-hot编码是一种将分类变量转换为二进制向量的方法,其中只有一个元素是1,其他都是0。

2. one-hot编码的缺点:

  • 维度太高。比如字母级就26个,词级的就更高了。有可能映射完毕后是几万维的,这是灾难性的。
  • 向量过于稀疏。
  • 硬编码。

 所以希望能找到一个 低维、稠密、能从数据中学习的编码方式。embedding应运而生。

 

3. 什么是embedding?

Embedding是一种将分类变量映射到低维空间的方法,常用于自然语言处理中的词嵌入。

针对上一篇文章中的Seq2Seq示例,将one-hot编码修改为embedding的形式:

import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim
import matplotlib.pyplot as plt
#加了embedding版本的
batch_size = 1  #我们现在是1个字符1个字符训练的
input_size = 4  #输入维度 这边ehlo四个字母就是4
hidden_size = 8 #假设从hidden 层输出的维度是8,而我们需要的最终分类数为4,那么需要再经过一个全连接层
seq_len = 5     #字符串长度是5,就相当于是输入的序列是5
num_layers = 2  #设置的rnn层数
embedding_size = 10 #嵌入层的维度
num_class = 4 #最终的分类数

#我们希望一个序列hello-》ohlol

idx2char = ['e','h','l','o']
x_data = [[1,0,2,2,3]] #(batch_size,seq_len)
y_data = [3,1,2,3,2] #(batch_size* seq_len)


inputs = torch.LongTensor(x_data) #shape:[1,5]
labels = torch.LongTensor(y_data) #shape:[5]

class Model(torch.nn.Module):
    def __init__(self):
        super(Model,self).__init__()
        #embedding层
        self.emb = torch.nn.Embedding(input_size,embedding_size)
        self.rnn = torch.nn.RNN(input_size=embedding_size,
                                hidden_size=hidden_size,
                                num_layers=num_layers,
                                batch_first=True)
        self.fc = torch.nn.Linear(hidden_size,num_class)

    def forward(self,x):
        #第一个hidden初始化放到forward里了
        hidden = torch.zeros(num_layers,
                             batch_size,
                             hidden_size)

        x = self.emb(x)
        x,_ = self.rnn(x,hidden)
        x = self.fc(x)
        return x.view(-1,num_class)


net = Model()

criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(net.parameters(),lr=0.05)

for epoch in range(15):
    optimizer.zero_grad()
    outputs = net(inputs)
    loss = criterion(outputs,labels)

    loss.backward()
    optimizer.step()
    _, idx = outputs.max(dim = 1)
    idx = idx.data.numpy()

    print("predicted string: ", "".join([idx2char[x] for x in idx]),end='')
    print(',epoch [%d/15] loss = %.4f' %(epoch+1,loss.item()))

运行结果:

运用了embedding,可以看到得到预期结果的训练轮数减少了,说明效果更好了。

另:

torch.nn.Embedding用法详解:【python函数】torch.nn.Embedding函数用法图解-CSDN博客

torch.nn.Embedding(num_embeddings, embedding_dim, padding_idx=None, max_norm=None, norm_type=2.0, scale_grad_by_freq=False, sparse=False, _weight=None, _freeze=False, device=None, dtype=None)

其实最常用的就是前两个参数:

  • num_embeddings: 词的数量
  • embedding_dim: 定义的嵌入的维度

从one-hot变成embedding的一个好处就是,编码的长度和总的词数没有关系了。所以针对比较长的序列,会大大节省编码空间。

  • one-hot: 编码需要的张量大小 [length,word_total_count]
  • embedding:编码需要的张量大小 [length,embedding_size]
import torch
import torch.nn as nn

embedding = nn.Embedding(3, 4)
x = torch.LongTensor([[1, 2, 1], [0, 1, 1]])

y = embedding(x)

print('权重:\n', embedding.weight)
print('输出:')
print(y.shape)
  • 输入的x的shape为[2,3]
  • 输出的y的shape为[2,3,4]

embedding相当于一个词典,用来查找映射关系

相当于是把输入的每一项扩展了一个维度,映射到了embedding size大小的维度上。这里的embedding size是4。

一个注意的点是:这里的输入x里的每一项不能超过embedding里面的最大容量,即词索引是不能超出词典的最大容量,本例子中是nn.Embedding()的第一个参数3,超过了就相当于在词典中找不到对应的映射关系里。

同样的大小,如果我们还是用one-hot编码的话:

import torch
import torch.nn as nn

batch_size = 1  #我们现在是1个字符1个字符训练的
input_size = 10  #针对每个字符,用one-hot编码之后,每个字符的长度是4
hidden_size = 10 #针对每个字符,用one-hot编码之后,每个字符的长度是4
seq_len = 12     #字符串长度是5,就相当于是输入的序列是5
num_layers = 1

#-------------one-hot-------------------


x_data = [1,0,2,2,3,9,8,2,3,5,6,4]

#one-hot编码对照查表
one_hot_lookup = [
    [1,0,0,0,0,0,0,0,0,0], 
    [0,1,0,0,0,0,0,0,0,0], 
    [0,0,1,0,0,0,0,0,0,0],
    [0,0,0,1,0,0,0,0,0,0],
    [0,0,0,0,1,0,0,0,0,0],
    [0,0,0,0,0,1,0,0,0,0],
    [0,0,0,0,0,0,1,0,0,0],
    [0,0,0,0,0,0,0,1,0,0],
    [0,0,0,0,0,0,0,0,1,0],
    [0,0,0,0,0,0,0,0,0,1]
]
#将输入数据转换为onehot编码
x_one_hot = [one_hot_lookup[x] for x in x_data]
inputs = torch.Tensor(x_one_hot).view(batch_size,seq_len,input_size)
print(inputs.shape)

#torch.Size([1, 12, 10])

#-------------embedding-------------------

embedding = nn.Embedding(10, 3)
x = torch.LongTensor([[1,0,2,2,3,9,8,2,3,5,6,4]])

y = embedding(x)


print(y.shape)

#torch.Size([1, 12, 3])

可以看到one-hot输出的shape为:torch.Size([1, 12, 10])

embedding的为 torch.Size([1, 12, 3])

而这个10是由输入的总词数决定的,所以当总词数很多时,张量会很大很大,而embedding的3是我们人为自定义的,当总词数比较多的时候会比较有优势。

  • 43
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值