跳转至

2 词嵌入层

学习目标

  • 知道词嵌入概念
  • 掌握PyTorch词嵌入api

我们在进行文本数据处理时,需要将文本进行数据值化,然后进行后续的训练工作。词嵌入层的作用就是将文本转换为向量的。

1. 词嵌入层的使用

词嵌入层首先会根据输入的词的数量构建一个词向量矩阵,例如: 我们有 100 个词,每个词希望转换成 128 维度的向量,那么构建的矩阵形状即为: 100*128,输入的每个词都对应了一个该矩阵中的一个向量。

在 PyTorch 中,我们可以使用 nn.Embedding 词嵌入层来实现输入词的向量化。接下来,我们将会学习如何将词转换为词向量,其步骤如下:

  1. 先将语料进行分词,构建词与索引的映射,我们可以把这个映射叫做词表,词表中每个词都对应了一个唯一的索引;
  2. 然后使用 nn.Embedding 构建词嵌入矩阵,词索引对应的向量即为该词对应的数值化后的向量表示。

例如,我们的文本数据为: "北京冬奥的进度条已经过半,不少外国运动员在完成自己的比赛后踏上归途。",接下来,我们看下如何使用词嵌入层将其进行转换为向量表示,步骤如下:

  1. 首先,将文本进行分词;
  2. 然后,根据词构建词表;
  3. 最后,使用嵌入层将文本转换为向量表示。

nn.Embedding 对象构建时,最主要有两个参数:

  1. num_embeddings 表示词的数量
  2. embedding_dim 表示用多少维的向量来表示每个词
nn.Embedding(num_embeddings=10, embedding_dim=4)

接下来,我们就实现下刚才的需求:

import torch
import torch.nn as nn
import jieba


if __name__ == '__main__':

    text = '北京冬奥的进度条已经过半,不少外国运动员在完成自己的比赛后踏上归途。'

    # 1. 文本分词
    words = jieba.lcut(text)
    print('文本分词:', words)

    # 2. 构建词表
    index_to_word = {}
    word_to_index = {}

    # 分词去重并保留原来的顺序
    unique_words = list(set(words))
    for idx, word in enumerate(unique_words):
        index_to_word[idx] = word
        word_to_index[word] = idx

    # 3. 构建词嵌入层
    # num_embeddings: 表示词表词的总数量
    # embedding_dim: 表示词嵌入的维度
    embed = nn.Embedding(num_embeddings=len(index_to_word), embedding_dim=4)

    # 4. 文本转换为词向量表示
    print('-' * 82)
    for word in words:
        # 获得词对应的索引
        idx = word_to_index[word]
        # 获得词嵌入向量
        word_vec = embed(torch.tensor(idx))
        print('%3s\t' % word, word_vec)
程序输出结果:

文本分词: ['北京', '冬奥', '的', '进度条', '已经', '过半', ',', '不少', '外国', '运动员', '在', '完成', '自己', '的', '比赛', '后', '踏上', '归途', '。']
----------------------------------------------------------------------------------
 北京  tensor([-0.9270, -0.2379, -0.6142, -1.4764], grad_fn=<EmbeddingBackward>)
 冬奥  tensor([ 0.3541, -0.4493,  0.7205,  0.1818], grad_fn=<EmbeddingBackward>)
    tensor([-0.4832, -1.4191,  0.6283,  0.0977], grad_fn=<EmbeddingBackward>)
进度条  tensor([ 1.4518, -0.3859, -0.6866,  1.1921], grad_fn=<EmbeddingBackward>)
 已经  tensor([ 0.3793,  1.6623, -0.2279, -0.2272], grad_fn=<EmbeddingBackward>)
 过半  tensor([ 0.0732,  1.4832, -0.7802,  0.6884], grad_fn=<EmbeddingBackward>)
    tensor([ 0.6126,  1.0175, -0.4427,  0.6719], grad_fn=<EmbeddingBackward>)
 不少  tensor([ 1.0787, -0.2942, -1.0300, -0.6026], grad_fn=<EmbeddingBackward>)
 外国  tensor([-0.0484, -0.1542,  1.0033, -1.2332], grad_fn=<EmbeddingBackward>)
运动员  tensor([-1.3133,  0.3807,  0.3957,  1.1283], grad_fn=<EmbeddingBackward>)
    tensor([ 0.0146, -1.7078, -0.9399,  1.5368], grad_fn=<EmbeddingBackward>)
 完成  tensor([-0.1084, -0.0734, -0.1800, -0.5065], grad_fn=<EmbeddingBackward>)
 自己  tensor([ 0.8480, -0.4750, -0.1357,  0.4134], grad_fn=<EmbeddingBackward>)
    tensor([-0.4832, -1.4191,  0.6283,  0.0977], grad_fn=<EmbeddingBackward>)
 比赛  tensor([0.0928, 0.8925, 1.1197, 2.5525], grad_fn=<EmbeddingBackward>)
    tensor([ 0.8835,  0.7304,  1.3754, -1.7842], grad_fn=<EmbeddingBackward>)
 踏上  tensor([ 1.0809, -0.3135,  0.6346,  0.3923], grad_fn=<EmbeddingBackward>)
 归途  tensor([ 0.1834, -1.2411, -0.9244, -0.0265], grad_fn=<EmbeddingBackward>)
    tensor([ 0.0290,  0.1881, -1.3138,  0.6514], grad_fn=<EmbeddingBackward>)

2. 关于词嵌入层的思考

我们的词嵌入层默认使用的是均值为 0,标准差为 1 的正态分布进行初始化,也可以理解为是随机初始化。有些同学可能就想,这个用来表示词的文本真的能够表达出词的含义吗?

  1. nn.Embedding 中对每个词的向量表示都是随机生成的
  2. 当一个词输入进来之后,会使用随机产生的向量来表示该词
  3. 该词向量参与到下游任务的计算
  4. 下游任务计算之后,会和目标结果进行对比产生损失
  5. 接下来,通过反向传播更新所有的网络参数,这里的参数就包括了 nn.Embedding 中的词向量表示

这样通过反复的前向计算、反向传播、参数更新,最终我们每个词的向量表示就会变得更合理。

3. 小节

本小节主要讲解了在自然语言处理任务中,经常使用的词嵌入层的使用。它的主要作用就是将输入的词映射为词向量,便于在网络模型中进行计算。

这里需要注意的是, 词嵌入层中的向量表示是可学习的,并不是固定不变的。