目录
一、词嵌入层
二、循环网络层
2.1 RNN网络原理
2.2 Pytorch RNN API
自然语言处理(Nature language Processing,NLP)研究的主要是通过计算机算法来理解自然语言。对于自然语言来说,处理的数据主要就是人类的语言,如:汉语、英语、法语等。该类型的数据不像前面接触的过的结构化数据、或者图像数据可以很方便的进行数值化
一、词嵌入层
在进行文本数据处理时,需要将文本进行数据值化,然后进行后续的训练工作。词嵌入层的作用就是将文本转换为向量的
词嵌入层首先会根据输入的词的数量构建一个词向量矩阵。如:有 100 个词,每个词希望转换成 128 维度的向量,那么构建的矩阵形状即为100 * 128,输入的每个词都对应了矩阵中的一个向量
在 PyTorch 中,可以使用 nn.Embedding 词嵌入层来实现输入词的向量化
- 先将语料进行分词,构建词与索引的映射,可以将这个映射称为词表,词表中每个词都对应了一个唯一的索引
- 然后使用 nn.Embedding 构建词嵌入矩阵,词索引对应的向量即为该词对应的数值化后的向量表示
nn.Embedding 对象构建时,最主要有两个参数:
- num_embeddings 表示词的数量
- embedding_dim 表示用多少维的向量来表示每个词
import torch
import torch.nn as nn
import jiebadef main():text = '北京冬奥的进度条已经过半,不少外国运动员在完成自己的比赛后踏上归途。'# 1. 分词words = jieba.lcut(text)print('words:', words)# 2. 构建词表index_to_word = {}word_to_index = {}# 分词去重unique_words = list(set(words))for idx, word in enumerate(unique_words):index_to_word[idx] = wordword_to_index[word] = idx# 3.构建词嵌入层# num_embeddings为词的数量,embedding_dim为词嵌入的维度embedding = nn.Embedding(num_embeddings=len(index_to_word), embedding_dim=4)# 4.文本转为词向量表示for word in words:idx = word_to_index[word]word_vector = embedding(torch.tensor(idx))print('%3s\t' % word, word_vector)if __name__ == "__main__":main()
words: ['北京', '冬奥', '的', '进度条', '已经', '过半', ',', '不少', '外国', '运动员', '在', '完成', '自己', '的', '比赛', '后', '踏上', '归途', '。']北京 tensor([1.1339, 1.1262, 0.6638, 1.5691], grad_fn=<EmbeddingBackward0>)冬奥 tensor([0.2753, 0.3262, 1.7691, 1.2225], grad_fn=<EmbeddingBackward0>)的 tensor([-0.7507, 1.8850, 1.4340, -0.8074], grad_fn=<EmbeddingBackward0>)
进度条 tensor([ 0.5693, -0.0951, -0.4607, 0.0555], grad_fn=<EmbeddingBackward0>)已经 tensor([-0.5726, 0.0812, 1.1051, -0.0020], grad_fn=<EmbeddingBackward0>)过半 tensor([0.0691, 0.1430, 1.9346, 1.4653], grad_fn=<EmbeddingBackward0>), tensor([-1.1009, 0.7833, -0.9021, 1.8811], grad_fn=<EmbeddingBackward0>)不少 tensor([ 0.7913, 0.0890, -0.7459, 1.1473], grad_fn=<EmbeddingBackward0>)外国 tensor([-0.6079, 0.2563, 0.8344, -0.5977], grad_fn=<EmbeddingBackward0>)
运动员 tensor([-1.9587, 0.0995, -1.0728, -0.2779], grad_fn=<EmbeddingBackward0>)在 tensor([-1.6571, -1.2508, -0.8138, -2.6821], grad_fn=<EmbeddingBackward0>)完成 tensor([-1.4124, -0.5624, 1.2548, 0.1708], grad_fn=<EmbeddingBackward0>)自己 tensor([-2.5485, -0.1839, -0.0079, 0.1326], grad_fn=<EmbeddingBackward0>)的 tensor([-0.7507, 1.8850, 1.4340, -0.8074], grad_fn=<EmbeddingBackward0>)比赛 tensor([ 0.3826, -1.8883, 0.5677, 0.1951], grad_fn=<EmbeddingBackward0>)后 tensor([ 0.2585, -1.2181, 1.0165, -0.4775], grad_fn=<EmbeddingBackward0>)踏上 tensor([-1.6933, 1.2826, 0.9993, -0.2306], grad_fn=<EmbeddingBackward0>)归途 tensor([ 3.8709, 0.5133, 0.1683, -0.9699], grad_fn=<EmbeddingBackward0>)。 tensor([-0.9352, -1.2663, -1.5860, -0.2301], grad_fn=<EmbeddingBackward0>)
词嵌入层默认使用的是均值为 0,标准差为 1 的正态分布进行初始化,也可以理解为是随机初始化。这个用来表示词的文本真的能够表达出词的含义吗?
nn.Embedding 中对每个词的向量表示都是随机生成的,当一个词输入进来后,会使用随机产生的向量来表示该词。该词向量参与到下游任务的计算,下游任务计算后,会和目标结果进行对比产生损失。接下来,通过反向传播更新所有的网络参数,就包括了 nn.Embedding 中的词向量表示。这样通过反复的前向计算、反向传播、参数更新,最终每个词的向量表示就会变得更合理
二、循环网络层
文本数据是具有序列特性的。如:"我爱你",这串文本就是具有序列关系的,"爱" 需要在 "我" 之后,"你" 需要在 "爱" 之后,若颠倒了顺序,那么可能就会表达不同的意思
为了能够表示出数据的序列关系需要使用循环神经网络(Recurrent Nearal Networks,RNN) 来对数据进行建模,RNN 是一个具有记忆功能的网络,作用于处理带有序列特点的样本数据
2.1 RNN网络原理
上图中 h 表示隐藏状态,每一次的输入都会有包含两个值:上一个时间步的隐藏状态、当前状态的输入值,输出当前时间步的隐藏状态
上图中,为了更加容易理解,画了 3 个神经元, 但是实际上只有一个神经元,"我爱你" 三个字是重复输入到同一个神经元中的
举例
假设要实现文本生成,也就是输入"我爱" 这两个字,来预测出"你",其如下图所示:
将上图展开成不同时间步的形式,如下图所示:
首先初始化出第一个隐藏状态,一般都是全0的一个向量,然后将 "我" 进行词嵌入,转换为向量的表示形式,送入到第一个时间步,然后输出隐藏状态 h1,然后将 h1 和 "爱" 输入到第二个时间步,得到隐藏状态 h2,将 h2 送入到全连接网络,得到 "你" 的预测概率
循环网络网络可以有多个神经元
依次将 "你爱我" 三个字分别送入到每个神经元进行计算,假设词嵌入时 "你爱我" 的维度为 128,经过循环网络 "你爱我" 三个字的词向量维度就会变成 4。循环神经网络的的神经元个数会影响到输出的数据维度
神经元内部如何计算?
表示输入数据的权重
表示输入数据的偏置
表示输入隐藏状态的权重
表示输入隐藏状态的偏置
最后对输出的结果使用 tanh 激活函数进行计算,得到该神经元的输出
2.2 Pytorch RNN API
RNN 层输入的数据为三个维度:(seq_len,batch_size,input_size)
import torch
import torch.nn as nn# 输入单个数据
def test01():# seq_len, batch_size, input_sizeinputs = torch.randn(1, 1, 128)# 隐藏层 num_layers, batch_size, hidden_sizehn = torch.zeros(1, 1, 256)# input_size 输入词向量的维度# hidden_size 隐藏层的大小, 隐藏层的神经元个数, 影响最终输出结果的维度rnn = nn.RNN(input_size=128, hidden_size=256)outputs, hn = rnn(inputs, hn)print("outputs shape", outputs.shape)# outputs shape torch.Size([1, 1, 256])print("hidden shape", hn.shape)# hidden shape torch.Size([1, 1, 256])# 输入句子
def test02():# seq_len, batch_size, input_sizeinputs = torch.randn(8, 1, 128)hn = torch.zeros(1, 1, 256)rnn = nn.RNN(input_size=128, hidden_size=256)outputs, hn = rnn(inputs, hn)print("outputs shape", outputs.shape)# outputs shape torch.Size([8, 1, 256])print("hidden shape", hn.shape)# hidden shape torch.Size([1, 1, 256])# 输入批量数据
def test03():# seq_len, batch_size, input_sizeinputs = torch.randn(8, 32, 128)hn = torch.zeros(1, 32, 256)rnn = nn.RNN(input_size=128, hidden_size=256)outputs, hn = rnn(inputs, hn)print("outputs shape", outputs.shape)# outputs shape torch.Size([8, 32, 256])print("hidden shape", hn.shape)# hidden shape torch.Size([1, 32, 256])if __name__ == "__main__":# test01()# test02()test03()