1 RNN概述
自然语言处理(Nature language Processing, NLP)研究的主要是通过计算机算法来理解自然语言。对于自然语言来说,处理的数据主要就是人类的语言,例如:汉语、英语、法语等,该类型的数据不像我们前面接触的过的结构化数据、或者图像数据可以很方便的进行数值化。
2 词嵌入层
我们在进行文本数据处理时,需要将文本进行数据值化,然后进行后续的训练工作。词嵌入层的作用就是将文本转换为向量的。
2.1 词嵌入层的使用
词嵌入层首先会根据输入的词的数量构建一个词向量矩阵,例如: 我们有 100 个词,每个词希望转换成 128 维度的向量,那么构建的矩阵形状即为: 100*128,输入的每个词都对应了一个该矩阵中的一个向量。
在 PyTorch 中,我们可以使用 nn.Embedding 词嵌入层来实现输入词的向量化。接下来,我们将会学习如何将词转换为词向量,其步骤如下:
- 先将语料进行分词,构建词与索引的映射,我们可以把这个映射叫做词表,词表中每个词都对应了一个唯一的索引;
- 然后使用 nn.Embedding 构建词嵌入矩阵,词索引对应的向量即为该词对应的数值化后的向量表示。
例如,我们的文本数据为: “北京冬奥的进度条已经过半,不少外国运动员在完成自己的比赛后踏上归途。”,接下来,我们看下如何使用词嵌入层将其进行转换为向量表示,步骤如下:
- 首先,将文本进行分词;
- 然后,根据词构建词表;
- 最后,使用嵌入层将文本转换为向量表示。
nn.Embedding 对象构建时,最主要有两个参数:
- num_embeddings 表示词的数量
- 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 - 根据词获取到对应索引
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: 表示词嵌入的维度
# 这里采用四个维度,表示一个词 - tensor([x1,x2,x3,x4]) - 表示一个词
embed = nn.Embedding(num_embeddings=len(index_to_word), embedding_dim=4)
print(len(index_to_word)) # 18 去重之后,词汇有18个
print(embed) # 经过词嵌入层之后,会得到一个含有18个张量的词嵌入层
# 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.2关于词嵌入层的思考
我们的词嵌入层默认使用的是均值为 0,标准差为 1 的正态分布进行初始化,也可以理解为是随机初始化。这个用来表示词的文本真的能够表达出词的含义吗?
- nn.Embedding 中对每个词的向量表示都是随机生成的
- 当一个词输入进来之后,会使用随机产生的向量来表示该词
- 该词向量参与到下游任务的计算
- 下游任务计算之后,会和目标结果进行对比产生损失
- 接下来,通过反向传播更新所有的网络参数,这里的参数就包括了 nn.Embedding 中的词向量表示
这样通过反复的前向计算、反向传播、参数更新,最终我们每个词的向量表示就会变得更合理。