回顾
What is RNNs?
RNN(Recurrent Neural Network)是一类用于处理序列数据的神经网络。首先我们要明确什么是序列数据,摘取百度百科词条:时间序列数据是指在不同时间点上收集到的数据,这类数据反映了某一事物、现象等随时间的变化状态或程度。这是时间序列数据的定义,当然这里也可以不是时间,比如文字序列,但总归序列数据有一个特点——后面的数据跟前面的数据有关系.
我们先来看一个NLP很常见的问题,命名实体识别,
举个例子,现在有两句话:
第一句话:I like eating apple!(我喜欢吃苹果!)
第二句话:The Apple is a great company!(苹果真是一家很棒的公司!)
现在的任务是要给apple打Label,我们都知道第一个apple是一种水果,第二个apple是苹果公司,假设我们现在有大量的已经标记好的数据以供训练模型,当我们使用全连接的神经网络时,我们做法是把apple这个单词的特征向量输入到我们的模型中(如下图),在输出结果时,让我们的label里,正确的label概率最大,来训练模型,但我们的语料库中,有的apple的label是水果,有的label是公司,这将导致,模型在训练的过程中,预测的准确程度,取决于训练集中哪个label多一些,这样的模型对于我们来说完全没有作用。问题就出在了我们没有结合上下文去训练模型,而是单独的在训练apple这个单词的label,这也是全连接神经网络模型所不能做到的,于是就有了我们的循环神经网络。
RNNCell将一向量映射的过程,所以RNN Cell的本质就是一个线性层。而且,特别的是,这个线性层是共享的。
Xt:代表序列中在时刻t相应的数据,输入维度为input_size.
ht-1:记忆体上一时刻存储的状态信息,输入维度为hidden_size
Xt和ht-1同时可以看作RNN Cell的输入,经过权重相乘和偏置的相加,得到接下来的结果。
ht:记忆体内当前时刻存储的状态信息,维度为hidden_size
yt:当前时刻循环核的输出特征,这里没有写,下面会有
注意Xt和ht-1对应的权重是不一样的,与Xt相关的权重维度为input_size×hidden_size,ht-1的维度为hidden_size×hidden_size,这样得到的结果才能融合相加。
cell = torch.nn.RNNCell(input_size=input_size, hidden_size=hidden_size)
# input_size=input_size, hidden_size=hidden_size 为上述解释到的维度。
How to use RNNCell
seqLen 为输入序列的长度
import torch
batch_size = 1 # batch_size
seq_len = 3 # 序列长度
input_size = 4 # 输入维度
hidden_size = 2 # hidden维度
cell = torch.nn.RNNCell(input_size=input_size, hidden_size=hidden_size) # 定义RNNCell的实例化,需要输入input_size和hidden_size
# (seq, batch, features)
dataset = torch.randn(seq_len, batch_size, input_size) # 数据的维度为(seq, batch, features)
hidden = torch.zeros(batch_size, hidden_size) # hidden的维度为(batch_size,hidden_size)
for idx, input in enumerate(dataset): # seq_len=3,所以这里循环为三次,input=(batch_size,input_size)
print('=' * 20, idx, '=' * 20)
print('Input size: ', input.shape)
hidden = cell(input, hidden)
print('outputs size: ', hidden.shape)
print(hidden) # 输出的hidden的维度不发生改变,还是(batch_size,hidden_size)
How to use RNN
这里实例化的输入除了input_size和hiddensiz以外,还需要RNN的层数
输出则外上面提过的yt(h1)和hidden。
yt的输出也是hidden_size
import torch
batch_size = 1 # batch_size
seq_len = 3 # 序列长度
input_size = 4 # 输入维度
hidden_size = 2 # hidden维度
num_layers = 1 # RNN 的层数
cell = torch.nn.RNN(input_size=input_size, hidden_size=hidden_size,num_layers=num_layers) # 定义需要三个变量,和RNNCell 不同
# (seqLen, batchSize, inputSize)
inputs = torch.randn(seq_len, batch_size, input_size) # 数据的维度为(seq, batch, features)
hidden = torch.zeros(num_layers, batch_size, hidden_size) # hidden的维度为(batch_size,hidden_size)
out, hidden = cell(inputs, hidden)
print('Output size:', out.shape)
print('Output:', out)
print('Hidden size: ', hidden.shape)
print('Hidden: ', hidden)
Example 12-1: Using RNNCell
import torch
input_size=4
hidden_size=4
batch_size=1
# 准备数据
idx2char=['e','h','l','o']
x_data=[1,0,2,2,3] # hello
y_data=[3,1,2,3,2] # ohlol
one_hot_lookup=[[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[0,0,0,1]] #分别对应0,1,2,3项
x_one_hot=[one_hot_lookup[x] for x in x_data] # 组成序列张量
print('x_one_hot:',x_one_hot)
# 构造输入序列和标签
inputs=torch.Tensor(x_one_hot).view(-1,batch_size,input_size)
labels=torch.LongTensor(y_data).view(-1,1)
# design model
class Model(torch.nn.Module):
def __init__(self,input_size,hidden_size,batch_size):
super(Model, self).__init__()
self.batch_size=batch_size
self.input_size=input_size
self.hidden_size=hidden_size
self.rnncell=torch.nn.RNNCell(input_size=self.input_size,
hidden_size=self.hidden_size)
def forward(self,input,hidden):
hidden=self.rnncell(input,hidden)
return hidden
def init_hidden(self):
return torch.zeros(self.batch_size,self.hidden_size)
net=Model(input_size,hidden_size,batch_size)
# loss and optimizer
criterion=torch.nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(net.parameters(), lr=0.1)
# train cycle
for epoch in range(20):
loss=0
optimizer.zero_grad()
hidden = net.init_hidden()
print('Predicted String:',end='')
for input ,lable in zip(inputs,labels):
hidden = net(input,hidden)
loss+=criterion(hidden,lable)
_, idx=hidden.max(dim=1)
print(idx2char[idx.item()],end='')
loss.backward()
optimizer.step()
print(',Epoch [%d/20] loss=%.4f' % (epoch+1, loss.item()))
Example 12-2 Using RNN Module
import torch
input_size=4
hidden_size=4
num_layers=1
batch_size=1
seq_len=5
# 准备数据
idx2char=['e','h','l','o']
x_data=[1,0,2,2,3] # hello
y_data=[3,1,2,3,2] # ohlol
one_hot_lookup=[[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[0,0,0,1]] #分别对应0,1,2,3项
x_one_hot=[one_hot_lookup[x] for x in x_data] # 组成序列张量
print('x_one_hot:',x_one_hot)
# 构造输入序列和标签
inputs=torch.Tensor(x_one_hot).view(seq_len,batch_size,input_size)
labels=torch.LongTensor(y_data)
# design model
class Model(torch.nn.Module):
def __init__(self,input_size,hidden_size,batch_size,num_layers=1):
super(Model, self).__init__()
self.num_layers=num_layers
self.batch_size=batch_size
self.input_size=input_size
self.hidden_size=hidden_size
self.rnn=torch.nn.RNN(input_size=self.input_size,
hidden_size=self.hidden_size,
num_layers=self.num_layers)
def forward(self,input):
hidden=torch.zeros(self.num_layers,self.batch_size,self.hidden_size)
out, _=self.rnn(input,hidden)
return out.view(-1,self.hidden_size)
net=Model(input_size,hidden_size,batch_size,num_layers)
# loss and optimizer
criterion=torch.nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(net.parameters(), lr=0.05)
# train cycle
for epoch in range(20):
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: ',''.join([idx2char[x] for x in idx]),end='')
print(',Epoch [%d/20] loss=%.3f' % (epoch+1, loss.item()))
EMBEDDING
Embedding能够用低维向量对物体进行编码还能保留其含义的特点非常适合深度学习。在传统机器学习模型构建过程中,我们经常使用one hot encoding对离散特征,特别是id类特征进行编码,但由于one hot encoding的维度等于物体的总数,比如阿里的商品one hot encoding的维度就至少是千万量级的。这样的编码方式对于商品来说是极端稀疏的,甚至用multi hot encoding对用户浏览历史的编码也会是一个非常稀疏的向量。而深度学习的特点以及工程方面的原因使其不利于稀疏特征向量的处理.
输入输出的维度非常重要
import torch
idx2char = ['e', 'h', 'l', 'o']
x_data = [[1, 0, 2, 2, 3]] # (batch, seq_len)
y_data = [3, 1, 2, 3, 2] # (batch * seq_len)
'''
Input should be LongTensor:
𝒃𝒂𝒕𝒄𝒉𝑺𝒊𝒛𝒆, 𝒔𝒆𝒒𝑳𝒆𝒏
Target should be LongTensor:
𝒃𝒂𝒕𝒄𝒉𝑺𝒊𝒛𝒆 × 𝒔𝒆𝒒𝑳𝒆n
'''
inputs = torch.LongTensor(x_data)
labels = torch.LongTensor(y_data)
# parameters
num_class = 4
input_size = 4
hidden_size = 8
embedding_size = 10
num_layers = 2
batch_size = 1
seq_len = 5
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
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 = torch.zeros(num_layers, x.size(0), hidden_size)
x = self.emb(x) # (batch, seqLen, embeddingSize)
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: ', ''.join([idx2char[x] for x in idx]), end='')
print(', Epoch [%d/15] loss = %.3f' % (epoch + 1, loss.item()))
作业
后补~
参考
通俗易懂的RNN_长竹Danko的博客-CSDN博客_rnn
Pytorch深度学习实践第十二讲 循环神经网络(基础篇)cycy_0918的博客-CSDN博客循环神经网络实验