开篇
上次我们说到了卷积神经网络,CNN是一种在图像领域经常被使用的一种重要的基础网络。我们熟悉的网络例如VGG,ResNet,Inception-v3,GoogLeNet,AlexNet等等众多著名网络中都有CNN身影。
那今天我们就要介绍一种在语音识别和自然语言处理领域和CNN具有同样低位的另一种网络——循环神经网络RNN。如BERT,Skip-Gram,CBOW等模型。它有两个很重要的部分,LSTM和GRU。详细的RNN知识在这里就不做介绍了,大家可以移步RNN基础知识及LSTM,GRU进行自我学习。
废话不多说,我们开始吧。
循环神经网络RNN
引入库
import torchvision
import torch
import torch.nn as nn
import torchvision.transforms as transforms
设备的配置及参数的设置
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
sequence_length = 28# RNN的处理对象都是一段语音序列或者一段次序列,此参数表示序列长度
input_size = 28# 输入层的特证数
hidden_size = 128# 隐藏层的特征数
num_layers = 2# RNN层数
num_classes = 10# 输出类别
batch_size = 100# 批数
num_epochs = 2# 训练轮数
learning_rate = 0.01# 学习率
MNIST数据的引入及加载
train_dataset = torchvision.datasets.MNIST(root='../../data/',
train=True,
transform=transforms.ToTensor(),
download = True)
test_dataset = torchvision.datasets.MNIST(root = '../../data/',
train=False,
transform=transforms.ToTensor())
train_loader = torch.utils.data.DataLoader(dataset = train_dataset,
shuffle = True,
batch_size = batch_size)
test_loader = torch.utils.data.DataLoader(dataset = test_dataset,
shuffle = False,
batch_size = batch_size)~
RNN类的创建
class RNN(nn.Module):
def __init__(self,input_size,hidden_size,num_layers,num_classes):
super(RNN, self).__init__()
self.hidden_size = hidden_size
self.num_layers = num_layers
"""
batch_first: If ``True``, then the input and output tensors are provided as (batch, seq, feature)——batch_first官方源码解释
即如果batch_first等于True,则这个input和output的shape都是batch在第一维的
"""
self.lstm = nn.LSTM(input_size,hidden_size,num_layers,batch_first=True)
self.fc = nn.Linear(hidden_size,num_classes)
def forward(self, x):
# 设置初始的隐藏单元的状态和cell的状态
h0 = torch.zeros(self.num_layers,x.size(0),self.hidden_size).to(device)
c0 = torch.zeros(self.num_layers,x.size(0),self.hidden_size).to(device)
# 前向传播LSTM
out,_ = self.lstm(x,(h0,c0))
# out:tensor of shape (batch_size,seq,feature)
# 我们只解码out最后一个time_step的隐藏单元状态
out = self.fc(out[:,-1,:])
return out
这里希望大家可以理解上文的h到底是什么了。h就是我们在传播时隐藏层的状态,真正循环的其实是它,上一个隐藏层的状态会来计算当前时间步的隐藏层状态,这个位置在计算的时候通常采用的激活函数为tanh。然后i再利用我们当前算得的隐藏层状态来计算标签y,标签y通常采用softmax来计算。
在LSTM中不仅仅是细胞的状态C会在整个网络中流动,隐藏层单元的状态h也会在网络中流动,因为我们在计算候选细胞状态,更新细胞状态以及经过该层后输出的新隐层单元状态都需要h的参与。
具体大家还是看上文分享的链接,讲的很全面了。
模型的构建以及优化器和损失函数的定义
model = RNN(input_size,hidden_size,num_layers,num_classes).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam((model.parameters()),lr = learning_rate)~
分类问题,我们依然使用交叉熵损失函数和Adam优化器。
训练模型
total_step = len(train_loader)
for epoch in range(num_epochs):
for i,(images,labels) in enumerate(train_loader):
images = images.reshape(-1,sequence_length,input_size)
labels = labels.to(device)
# Forward pass
outputs = model(images)
loss = criterion(outputs,labels)
# Backward amd optimize
optimizer.zero_grad()
loss.backward()
optimizer.step()
if ( i + 1 ) % 100 == 0:
print ('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'
.format(epoch+1, num_epochs, i+1, total_step, loss.item()))
测试模型及保存模型
model.eval()
with torch.no_grad():
correct = 0
total = 0
for images,labels in test_loader:
images = images.reshape(-1,sequence_length,input_size)
labels = labels.to(device)
outputs = model(images)
_,predicted = torch.max(outputs.data,1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Test Accuracy of the model on the 10000 test images: {} %'.format(100 * correct / total))
# Save the model checkpoint
torch.save(model.state_dict(), 'model.ckpt')~
这里的model.eval()依然是我们昨天所说的问题,有一些Module它需要我们去设置model,如果是eval的测试状态,内部会自动调整参数,与训练集不同的。
这里面有一个隐含的Module就是LSTM中的Dropout。在训练的时候我们会丢弃一些单元,但是测试的时候dropout我们设置为1,不丢弃任何单元。
循环神经网络RNN也是非常重要的基础模型,它更主要的应用是在NLP领域,例如文本翻译,语音识别,以及情感分类等等。RNN与CNN也有一个结合之处是当下比较火的,就是我们小时候经常做的题目——看图说话。CNN对图像进行分类分析,RNN通过CNN输出的特征进行语言描述。也是一个也别有趣的领域。
下次我们说RNN一个拓展网络——双向循环网络biRNN
‘