1)任务介绍
今天,我们通过PyTorch搭建一个用于处理序列的RNN。
当我们以sin值作为输入,其对应的cos作为输出的时候,你会发现,即使输入值sin相同,其输出结果也可以是不同的,这样的话,以前学过的FC, CNN就难以处理,因为你的输出结果不仅仅依赖于输出,而且还依赖于之前的程序结果。所以说,RNN在这里就派上了用场。
2)代码实现
上代码之前,还是先了解一下RNN的参数吧:
torch.nn.RNN()
参数 | 含义 |
input_size | 输入 x 的特征数量 |
hidden_size | 隐状态 h 中的特征数量 |
num_layers | RNN 的层数 |
nonlinearity | 指定非线性函数使用 [‘tanh’|’relu’]. 默认: ‘tanh’ |
bias | 如果是 False , 那么 RNN 层就不会使用偏置权重 b_ih 和 b_hh, 默认: True |
batch_first | 如果 True, 那么输入 Tensor 的 shape 应该是 (batch, seq, feature),并且输出也是一样 |
dropout | 如果值非零, 那么除了最后一层外, 其它层的输出都会套上一个 dropout 层 |
bidirectional | 如果 True , 将会变成一个双向 RNN, 默认为 False |
首先,我们定义出RNN模型
import torch
from torch import nn
class Rnn(nn.Module):
def __init__(self, INPUT_SIZE):
super(Rnn, self).__init__()
self.rnn = nn.RNN(
input_size=INPUT_SIZE,
hidden_size=32,
num_layers=1,
batch_first=True
)
self.out = nn.Linear(32, 1)
def forward(self, x, h_state):
r_out, h_state = self.rnn(x, h_state)
outs = []
for time in range(r_out.size(1)):
outs.append(self.out(r_out[:, time, :]))
return torch.stack(outs, dim=1), h_state
选择模型进行训练:
import numpy as np
import matplotlib.pyplot as plt
import torch
from torch import nn
import rnn
# 定义一些超参数
TIME_STEP = 10
INPUT_SIZE = 1
LR = 0.02
# # 创造一些数据
# steps = np.linspace(0, np.pi*2, 100, dtype=np.float)
# 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()
# 选择模型
model = rnn.Rnn(INPUT_SIZE)
print(model)
# 定义优化器和损失函数
loss_func = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=LR)
h_state = None # 第一次的时候,暂存为0
for step in range(300):
start, end = step * np.pi, (step+1)*np.pi
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])
y = torch.from_numpy(y_np[np.newaxis, :, np.newaxis])
prediction, h_state = model(x, h_state)
h_state = h_state.data
loss = loss_func(prediction, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
然后,画图看看最后拟合的结果
plt.plot(steps, y_np.flatten(), 'r-')
plt.plot(steps, prediction.data.numpy().flatten(), 'b-')
plt.show()
其拟合效果如下图:
可以看到,真实的结果和拟合的效果已经十分接近了。
3)结语
神经网络学到RNN的时候,才慢慢感觉到神经网络的真正强大之处。因为RNN引入了”时间“这个特征,我们的训练结果不再受限于输入,而是取决于”随着时间的输入序列“。正是由于这样的原因,它可以广泛的应用到自然语言处理,语言识别,甚至是AI作曲中来。
RNN确实相比于其他的神经网络显得更为复杂和抽象,但是学到这里的时候是非常兴奋的,今天的博文只是借鉴了别人的代码,简单地展示了一些RNN在处理序列上的优势,关于RNN的一些扩展,是可以深入研究的,这将是一件非常有趣的事情。