PyTorch使用embedding对特征向量进行嵌入

词向量嵌入

在NLP中,一个最基本的问题就是如何在计算机中表示一个单词。一般我们用含有N个单词的词汇表来对单词进行编码,例如词表{“hello”: 0, “world”: 1, “nice”:2, “to”:3, “see”:4, “you”:5 }中只有6个单词,那么nice的编码就是2,但是一篇文章有成百上千甚至更多的词汇时,这就需要embedding操作将词向量进行压缩,用更小的维度去表示大量的词汇空间。

根据语言学的单词分布假说,出现在相似语境中的词在语义上是相互关联的。如果仅仅使用一个数字来表示一个单词,孤立的对待每个单词,就无法发现单词之间的内在联系,无法表示向量之间的相似性,即语义相似度。而通过embedding编码则可以挖掘出不同词向量之间的相似性,并且根据已有的词句对新的句子做出预测。

那么embedding是如何对一个词汇进行编码的呢?它从语义属性的角度考虑进行编码,例如有mathematicians 和physicists两个词汇,假设我们从奔跑能力can run、喜好咖啡程度like coffee、物理研究majored in physics等角度对两个单词作出平均,这样就形成了描述两个单词的词向量记作m=[2.3, 9.4, -5.5]、p=[2.5, 9.1, 6.4],那么接下来就可以通过夹角大小来表示两个向量的相似度cosα=m·p / |m||p|,如果两个向量越相似,其cosα值越接近1,反之趋于-1.这样就用三维向量对单词进行了编码并且挖掘出其内在的语义相似性。

mathematician={ "can run" : 2.3, "likes coffee" : 9.4,"majored in Physics" : −5.5,…}
physicist={ "can run" : 2.5 "likes coffee" : 9.1,"majored in Physics" : 6.4,…}

但是我们可以用成千上万种不同的方式对mathematicians 和physicists进行描绘,我们应该选取哪些值来表示不同的属性呢?与人为的定义属性相比,深度学习的神经网络可以很好地进行特征学习,所以我们可以让embedding在训练过程中自动进行训练和更新,指导找到合适的属性来表示词向量。经过训练之后的向量表示是没有实际的语义属性的,就像如果直接看m=[2.3, 9.4, -5.5],它只是由三个数字组成的向量,并没有我们可以理解的意义。

综上,我们可以看到PyTorch的embedding有三个特点:可以将高维向量压缩、发掘向量之间的联系、可以在训练过程中自动学习和更新。

Embedding函数

使用torch.nn库的Embedding()可以构建embed模型,它需要传入两个参数,输入向量的类型数和输出向量的维度,即向量共有多少种不同类型的取值、最后希望用几个数来表示该向量。需要注意的是,embedding的输入向量必须为long类型的tensor。

如下所示,词表word_to_ix中共有6种不同的单词,最后希望用一个2维向量表示每个单词,那么模型的构建就是Embedding(6, 2)。假设我们要对“nice”进行编码,首先通过词表找到其编号为2,得到其原始编码lookup_tensor,经过embedding之后得到编码为nice_embed。我们也可以一次输入10个单词,即tensor(10,),embedding会作用与每个向量并得到tensor(10,2)的结果。

import torch
import torch.nn as nn

word_to_ix = {"hello": 0, "world": 1, "nice": 2, "to": 3, "see": 4, "you": 5}
embeds = nn.Embedding(6, 2)  # 构建模型
lookup_tensor = torch.tensor([word_to_ix["nice"]], dtype=torch.long)
print(lookup_tensor)
nice_embed = embeds(lookup_tensor)     # 进行编码
print(nice_embed)
'''
tensor([2])
tensor([[ 0.7950, -0.3999]], grad_fn=)
'''

一个具体例子

下面是一个使用embedding进行模型训练的例子,首先给出的材料是一段莎士比亚的诗,训练模型根据前两个单词预测第三个单词。模型在将单词进行embedding编码后送入两个全连接层后输出结果。

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

CONTEXT_SIZE = 2
EMBEDDING_DIM = 10  # 编码向量的维度

test_sentence = """When forty winters shall besiege thy brow,
And dig deep trenches in thy beauty's field,
Thy youth's proud livery so gazed on now,
Will be a totter'd weed of small worth held:
Then being asked, where all thy beauty lies,
Where all the treasure of thy lusty days;
To say, within thine own deep sunken eyes,
Were an all-eating shame, and thriftless praise.
How much more praise deserv'd thy beauty's use,
If thou couldst answer 'This fair child of mine
Shall sum my count, and make my old excuse,'
Proving his beauty by succession thine!
This were to be new made when thou art old,
And see thy blood warm when thou feel'st it cold.""".split()
# 构建训练集数据 ([ 第一个单词, 第二个单词 ], 预测目标)
trigrams = [([test_sentence[i], test_sentence[i + 1]], test_sentence[i + 2])
            for i in range(len(test_sentence) - 2)]
# 构建测试集数据
vocab = set(test_sentence)
word_to_ix = {word: i for i, word in enumerate(vocab)}

# 定义模型
class NGramLanguageModeler(nn.Module):

    def __init__(self, vocab_size, embedding_dim, context_size):
        super(NGramLanguageModeler, self).__init__()
        self.embeddings = nn.Embedding(vocab_size, embedding_dim)
        self.linear1 = nn.Linear(context_size * embedding_dim, 128)
        self.linear2 = nn.Linear(128, vocab_size)

    def forward(self, inputs):
        embeds = self.embeddings(inputs).view((1, -1))  # 进行embedding
        out = F.relu(self.linear1(embeds))  # 经过第一个全连接层
        out = self.linear2(out)  # 经过第二个全连接层
        log_probs = F.log_softmax(out, dim=1)
        return log_probs

# 进行训练
losses = []
loss_function = nn.NLLLoss()
model = NGramLanguageModeler(len(vocab), EMBEDDING_DIM, CONTEXT_SIZE)
optimizer = optim.SGD(model.parameters(), lr=0.001)

for epoch in range(10):
    total_loss = 0
    for context, target in trigrams:
        # 准备输入模型的数据
        context_idxs = torch.tensor([word_to_ix[w] for w in context], dtype=torch.long)

        model.zero_grad()  # 清零梯度缓存

        # 进行训练得到预测结果
        log_probs = model(context_idxs)

        # 计算损失值
        loss = loss_function(log_probs, torch.tensor([word_to_ix[target]], dtype=torch.long))

        # 反向传播更新梯度
        loss.backward()
        optimizer.step()

        total_loss += loss.item()  # 累计损失
    losses.append(total_loss)
print(losses)

你可能感兴趣的:(人工智能,embedding,pytorch)