PyTorch教程(3)RNN

如何使用MNIST数据集建立递归神经网络?
递归神经网络(RNN)被认为是一种记忆网络。我们使用epoch为1,每次使用64个样品的批量大小来建立输入和输出之间的联系。利用RNN模型,我们可以预测图像中存在的数字。

让我们看看下面的例子。递归神经网络在输入层取一个向量序列,在输出层产生一个向量序列。信息序列在递归层中通过内部状态转换进行处理。有时输出值长期依赖于过去的历史值。这是RNN模型的另一种变体:长短期记忆(LSTM)模型。这适用于以顺序方式使用信息的任何类型的领域 。例如,在一个时间序列中,当前股票价格由历史股票价格决定,依赖关系可以是短的或长的。类似地,使用文本输入向量的长范围和短范围的上下文预测。还有其他行业用例,如噪声分类,其中噪声也是一个信息序列。

分类问题

下面的代码片段解释了使用PyTorch模块执行RNN模型。

权值有三组:U、V、W。权值向量集合,用W表示,表示网络中存储单元之间的信息传递,显示隐藏状态之间的通信。RNN使用Word2vec表示的嵌入层。例如,如果你有20,000个单词和1000个隐藏单位,矩阵的嵌入层大小为20,000×1000。新的表示被传递给LSTM,LSTM输出经过sigmoid函数输出。

import torch
import torchvision.datasets as dsets
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import torch.nn as nn
torch.manual_seed(1)
# 超参数
EPOCH=1
BATCH_SIZE=64
TIME_STEP=28
INPUT_SIZE=28
LR=0.01
DOWNLOAD=True

RNN模型具有超参数,如迭代次数(EPOCH);批大小取决于单个机器中可用的内存;记忆信息序列的时间步长;输入大小和学习率。这些值的选择是指示性的;我们不能在其他用例中依赖它们。超参数的取值选择是一个迭代过程;您可以选择多个参数并决定哪个参数有效,或者对模型进行并行训练并决定哪个参数有效。

train_data = dsets.MNIST(root="./mnist", train=True,
 			transform=transforms.ToTensor(),download=DOWNLOAD)
print(train_data.train_data.size())
print(train_data.train_labels.size())
plt.imshow(train_data.train_data[0].numpy(), cmap="gray")
plt.title("%i"%train_data.train_labels[0])
plt.show()

在这里插入图片描述
前面的脚本显示了示例图像数据集的样子。为了训练深度学习模型,我们需要将整个训练数据集转换成小批量,这有助于我们平均模型的最终精度。通过数据加载器,我们可以加载训练数据,并准备小批量数据。在小批量中进行shuffle选择的目的是确保模型捕获实际数据集中的所有变化。

# 数据加载器返回训练数据的mini-batch
train_loader = torch.utils.data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
# 选择测试集中的前2000个元素
test_data = dsets.MNIST(root="./mnist", train=False,
 			transform=transforms.ToTensor())
# shape(2000, 28, 28)
test_x = test_data.test_data.type(torch.FloatTensor)[:2000]/255.
# convert to numpy
test_y = test_data.test_labels.numpy().squeeze()[:2000]

上述脚本准备训练数据集。使用标志train=False捕获测试数据。使用测试数据中的2000个随机样本进行模型测试。测试标签向量以NumPy数组格式表示。

class RNN(nn.Module):
	def __init__(self):
		super(RNN, self).__init__()
		self.rnn = nn.LSTM(  # 如果使用nn.RNN(),很难学习
				input_size=INPUT_SIZE,  # 
				hidden_size=64, # 隐藏神经元
				num_layers=1, # rnn 层的数目
				batch_first=True, # input & output中batc size作为第一维
				)
		self.out = nn.Linear(64, 10)
	def forward(self, x):
		# x形状为(batch, time_step, input_size)
		# r_out形状为(batch, time_step, output_size)
		# h_n形状(n_layers, batch, hidden_size)
		# h_c形状(n_layers, batch, hidden_size)
		# None表示0初始化隐藏层
		r_out, (h_n, h_c) = self.rnn(x, None)
		# choose r_out at the last time step
		out = self.out(r_out[:,-1,:])
		return out

在之前的RNN课程中,我们训练了一个LSTM网络,它被证明对长时间保持记忆有效,从而有助于学习。如果我们使用nn.RNN()模型,它很难学习参数,因为RNN的普通实现不能长时间保持或记住信息。在LSTM网络中,图像的宽度被认为是输入的大小,隐藏的大小被决定为隐藏层神经元的数量,num_layers表示网络中RNN层的数量。

RNN模块,在LSTM模块中,产生的输出为64×10的向量大小,因为输出层的数字被分类为0到9。最后一个前向函数说明了如何在RNN网络中进行前向传播。

下面的脚本显示了如何在RNN类下处理LSTM模型。在LSTM函数中,输入长度为28,隐层神经元数为64,从隐层的64个神经元到输出的10个神经元。

rnn = RNN()
print(rnn)
# RNN(
#   (rnn): LSTM(28, 64, batch_first=True)
#   (out): Linear(in_features=64, out_features=10, bias=True)
# )
optimizer = torch.optim.Adam(rnn.parameters(),lr=LR)
loss_func = nn.CrossEntropyLoss()  # 标签你不是独热编码

为了优化所有RNN参数,我们使用Adam优化器。在函数内部,我们也使用了学习率。本例中使用的损失函数是交叉熵损失函数。我们需要提供多个epoch来获得最好的参数。

在下面的脚本中,我们打印了训练损失和测试准确性。经过一个epoch后,测试准确率提高到95%,训练损耗降低到0.19。

# 训练和测试
for epoch in range(EPOCH):
	for step, (x,y) in enumerate(train_loader):
		b_x = x.view(-1, 28, 28)
		b_y = y
		output = rnn(b_x)
		loss = loss_func(output, b_y)
		optimizer.zero_grad()
		loss.backward()
		optimizer.step()
		if step % 50 == 0:
			test_output=rnn(test_x)
			pred_y = torch.max(test_output,1)[1].data.numpy().squeeze()
			accuracy = sum(pred_y==test_y)/float(test_y.size)
			print("Epoch:", epoch, "| train loss: %.4f" % loss.item(),
									"|test accuracy: %.4f"% accuracy)
# Epoch: 0 | train loss: 2.3178 |test accuracy: 0.0995
# Epoch: 0 | train loss: 1.1102 |test accuracy: 0.5300
# Epoch: 0 | train loss: 0.7031 |test accuracy: 0.7100
# Epoch: 0 | train loss: 0.6014 |test accuracy: 0.7930
# Epoch: 0 | train loss: 0.7127 |test accuracy: 0.8575
# Epoch: 0 | train loss: 0.3254 |test accuracy: 0.8860
# Epoch: 0 | train loss: 0.3258 |test accuracy: 0.8835
# Epoch: 0 | train loss: 0.3760 |test accuracy: 0.9280
# Epoch: 0 | train loss: 0.2738 |test accuracy: 0.9330
# Epoch: 0 | train loss: 0.1623 |test accuracy: 0.9240
# Epoch: 0 | train loss: 0.1836 |test accuracy: 0.9240
# Epoch: 0 | train loss: 0.3307 |test accuracy: 0.9220
# Epoch: 0 | train loss: 0.1129 |test accuracy: 0.9205
# Epoch: 0 | train loss: 0.0855 |test accuracy: 0.9440
# Epoch: 0 | train loss: 0.1522 |test accuracy: 0.9450
# Epoch: 0 | train loss: 0.2400 |test accuracy: 0.9430
# Epoch: 0 | train loss: 0.2307 |test accuracy: 0.9365
# Epoch: 0 | train loss: 0.1162 |test accuracy: 0.9435
# Epoch: 0 | train loss: 0.1968 |test accuracy: 0.9515

一旦模型训练完毕,下一步就是使用RNN模型进行预测。然后我们比较实际输出和实际输出,以评估模型的执行情况。

test_output = rnn(test_x[:10].view(-1, 28, 28))
pred_y = torch.max(test_output,1)[1].data.numpy().squeeze()
print(pred_y, "prediction number")
print(test_y[:10], "real number")
# [7 2 1 0 4 1 4 9 5 9] prediction number
# [7 2 1 0 4 1 4 9 5 9] real number

回归问题

我们如何为基于回归的问题建立一个递归神经网络?
回归模型需要一个目标函数和一个特征集,然后再用一个函数来建立输入和输出之间的关系。在本例中,我们将使用递归神经网络(RNN)进行回归任务。回归问题似乎很简单;它们确实效果最好,但仅限于显示明确线性关系的数据。当预测输入和输出之间的非线性关系时,它们是相当复杂的。

让我们看一下下面的示例,它显示了输入和输出数据之间的非线性循环模式。在前面的教程中,我们研究了RNN用于分类相关问题的一般示例,预测输入图像的类别。然而,在回归中,RNN的结构会改变,因为目标是预测真实值输出。输出层将有一个神经元处于与回归相关的问题中。

import torch
from torch import nn
import numpy as np
import matplotlib.pyplot as plt
torch.manual_seed(1)
# 超参数
TIME_STEP=10
INPUT_SIZE=1
LR=0.02

下面的脚本显示了一些样本序列,其中目标cos函数近似于sin函数.

# 显示数据
steps = np.linspace(0, np.pi*2, 100, dtype=np.float32)
x_np = np.sin(steps)
y_np = np.cos(steps)
plt.plot(steps, y_np, "r-", label="target (cos)")
plt.plot(steps, x_np, "b-", label="input (sin)")
plt.legend(loc="best")
plt.show()

在这里插入图片描述
让我们看看下面的例子。PyTorch库中的神经网络模块包含RNN函数。在下面的脚本中,我们使用了输入矩阵的大小,隐含层神经元的数量,以及网络中隐藏层的数量。

class RNN(nn.Module):
	def __init__(self):
		super(RNN, self).__init__()
		self.rnn = nn.LSTM(  # 如果使用nn.RNN(),很难学习
				input_size=INPUT_SIZE,  # 
				hidden_size=32, # 隐藏神经元
				num_layers=1, # rnn 层的数目
				batch_first=True, # input & output中batc size作为第一维
				# (batch, time_step, input_size)
				)
		self.out = nn.Linear(32, 1)
	def forward(self, x, h_state):
		# x形状为(batch, time_step, input_size)
		# h_state形状(n_layers, batch, hidden_size)
		# r_out形状为(batch, time_step, hidden_size)
		r_out, h_state = self.rnn(x, h_state)
		
		outs = []  # save all predictions
		for time_step in range(r_out.size(1)):
			# 为每一个time step计算output
			outs.append(self.out(r_out[:,time_step, :]))
		return torch.stack(outs, dim=1), h_state

在创建RNN类函数之后,我们需要提供优化函数,即Adam,这次的损失函数是均方损失函数。由于目标是对一个连续变量进行预测,所以我们在优化层使用MSELoss函数。

rnn = RNN()
print(rnn)
# RNN(
#   (rnn): LSTM(28, 32, batch_first=True)
#   (out): Linear(in_features=32, out_features=1, bias=True)
# )
optimizer = torch.optim.Adam(rnn.parameters(), lr=LR)
loss_func = nn.MSELoss()
h_state = None
plt.figure(1, figsize=(12,5))
plt.ion()

for step in range(100):
	start, end = step * np.pi, (step+1)*np.pi
	# 使用sin预测cos
	steps = np.linspace(start, end, TIME_STEP, dtype=np.float32)
	x_np = np.sin(steps)
	y_np = np.cos(steps)
	x = torch.from_numpy(x_np[np.newaxis,:,np.newaxis])
	# shape(batch, time_step, input_size)
	y = torch.from_numpy(y_np[np.newaxis,:,np.newaxis])
	optimizer.zero_grad()  # 清除梯度值
	prediction,_ = rnn(x, h_state)
	loss = loss_func(prediction, y)  # cross entropy loss
	loss.backward()  # 反向传播,计算梯度
	optimizer.step()  # 更新参数
	# 绘图
	plt.plot(steps, y_np.flatten(), "r-")
	plt.plot(steps, prediction.detach().numpy().flatten(), "b-")
	plt.draw()
	plt.pause(0.05)

在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值