0%

lstm | pytorch 写 lstm

使用 pytorch 来实现 lstm


相关博客



参考资料



参数含义


  • input_size:输入的维度,就是你输入x的向量大小(x向量里有多少个元素)
  • hidden_sizeh的维度,LSTM在运行时里面的维度。隐藏层状态的维数,即隐藏层节点的个数,这个和单层感知器的结构是类似的。
  • num_layers:堆叠LSTM的层数,默认值为1LSTM 堆叠的层数,默认值是1层,如果设置为2,第二个LSTM接收第一个LSTM的计算结果。也就是第一层输入 [X0 X1 X2 ... Xt],计算出 [h0 h1 h2 ... ht],第二层将 [h0 h1 h2 ... ht] 作为 [X0 X1 X2 ... Xt] 输入再次计算,输出最后的 [h0 h1 h2 ... ht]
  • bias:偏置 ,默认值:True
  • batch_first: 如果是True,则input为(batch,seq,input_size)。默认值为:False(seq_len, batch, input_size)torch.LSTMbatch_size维度默认是放在第二维度,故此参数设置可以将 batch_size 放在第一维度。如:input 默认是(4,1,5),中间的 1batch_size,指定batch_first=True后就是(1,4,5)。所以,如果你的输入数据是二维数据的话,就应该将 batch_first 设置为True
  • dropout:默认值0。是否在除最后一个 RNN 层外的其他 RNN 层后面加 dropout 层。输入值是 0-1 之间的小数,表示概率。0表示0概率dripout,即不dropout
  • bidirectional:是否双向传播,默认值为False

参数详解

hidden_size

这个参数是指 lstm 中的隐藏层的节点个数。

对于 DNN 又或者是 CNN 来讲,我们都是把整个变量一起输进去,然后得到输出,但是,对于 RNN 结构,我们有一个循环的概念。假如说,一张图片为 28 * 28 ,那么,我们会先输入一个 28 ,得到输出,并且,更新内部参数后,再输入一个 28 ,接着更新内部参数,得到输出。这样循环输入,直到最后全部输进去。

这里要注意的是,虽然输入是循环的,但是,这些输入共享的是一套参数「内部参数」。

从上面这张图就可以清晰看出,整个训练过程。

  • 绿色的代表的是输入
  • 黄色的代表的是隐藏层
  • 蓝色的代表的是输出,可以看出,蓝色的节点数目和黄色的节点数目是一样的

这里有一点需要注意的是,假设我们的数据被我们分成 10 * 20,则,我们只需要把 hidden_size 设置为 20 ,不用去管前面的 10lstm 会根据数据,自己进行循环。

output

那,RNN 对应的输出到底是什么?

先看一张图

可以看出来,output 是每一次循环得到的输出集合。

我们的输入是 (20,10,30) 也就是一个数据的维度信息是 10 * 30 ,一共有 20 个这样的信息,即,batch_size = 20timestep = 10 ,hidden_size = 30

我们经历第一个 timestep 得到的输出是 (20,1,30),再经历完所有的循环后,得到的输出尺寸是 (20,10,30)

但是,我们只需要最后那个时刻的输出就好了。

一个简单的小例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class RNN(nn.Module):
def __init__(self):
super(RNN,self).__init__()

self.rnn = nn.LSTM(
input_size=50,
hidden_size=64,
num_layers=1,
batch_first=True,
)

self.out = nn.Linear(64,2) #最后时刻的hidden映射

def forward(self,x):
r_out, (h_n, h_c) = self.rnn(x, None)
print('lstm out size:')
print(r_out.shape) #这里输出output的size
out = self.out(r_out[:,-1,:]) #取最后一个时刻的hidden作输出
return out

输出为

1
2
lstm out size:
torch.Size([20, 11, 64])

LSTM 实现 MNIST


直接使用 pytorch 定义好的 LSTM ,虽然简单,但是丧失了灵活性。

这个暂时还没运行过。。。网上的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import torch
from torch import nn, optim
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision import datasets

# 定义超参数
batch_size = 100
learning_rate = 1e-3
num_epoches = 20

# 准备(下载)训练集 MNIST 手写数字训练集,(我提前下载了:download=False)
train_dataset = datasets.MNIST(
root='./mnist', train=True, transform=transforms.ToTensor(), download=False)

test_dataset = datasets.MNIST(
root='./mnist', train=False, transform=transforms.ToTensor())

train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)


# 定义 Recurrent Network 模型
class Rnn(nn.Module):
def __init__(self, in_dim, hidden_dim, n_layer, n_class):
super(Rnn, self).__init__()
self.n_layer = n_layer
self.hidden_dim = hidden_dim
self.lstm = nn.LSTM(in_dim, hidden_dim, n_layer, batch_first=True)
self.classifier = nn.Linear(hidden_dim, n_class)

def forward(self, x):
# h0 = Variable(torch.zeros(self.n_layer, x.size(1),
# self.hidden_dim)).cuda()
# c0 = Variable(torch.zeros(self.n_layer, x.size(1),
# self.hidden_dim)).cuda()
out, _ = self.lstm(x)
out = out[:, -1, :]
out = self.classifier(out)
return out


model = Rnn(28, 128, 2, 10) # 图片大小是28x28
use_gpu = torch.cuda.is_available() # 判断是否有GPU加速
if use_gpu:
model = model.cuda()

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)

# 开始训练
for epoch in range(num_epoches):
model.train()
print('epoch {}'.format(epoch + 1))
print('**************************************')
running_loss = 0.0
running_acc = 0.0
for i, data in enumerate(train_loader, 1):
img, label = data
b, c, h, w = img.size()
assert c == 1, 'channel must be 1'
img = img.squeeze(1)
if use_gpu:
img = Variable(img).cuda()
label = Variable(label).cuda()
else:
img = Variable(img)
label = Variable(label)
# 向前传播
out = model(img)
loss = criterion(out, label)
running_loss += loss.data.item() * label.size(0)
_, pred = torch.max(out, 1)
num_correct = (pred == label).sum()
running_acc += num_correct.data.item()
# 向后传播
optimizer.zero_grad()
loss.backward()
optimizer.step()

if i % 300 == 0:
print('[{}/{}] Loss: {:.6f}, Acc: {:.6f}'.format(
epoch + 1, num_epoches, running_loss / (batch_size * i),
running_acc / (batch_size * i)))
print('Finish {} epoch, Loss: {:.6f}, Acc: {:.6f}'.format(
epoch + 1, running_loss / (len(train_dataset)), running_acc / (len(
train_dataset))))
model.eval()
eval_loss = 0.
eval_acc = 0.
for data in test_loader:
img, label = data
b, c, h, w = img.size()
assert c == 1, 'channel must be 1'
img = img.squeeze(1)
# img = img.view(b*h, w)
# img = torch.transpose(img, 1, 0)
# img = img.contiguous().view(w, b, h)
if use_gpu:
img = Variable(img, volatile=True).cuda()
label = Variable(label, volatile=True).cuda()
else:
img = Variable(img, volatile=True)
label = Variable(label, volatile=True)
out = model(img)
loss = criterion(out, label)
eval_loss += loss.data.item() * label.size(0)
_, pred = torch.max(out, 1)
num_correct = (pred == label).sum()
eval_acc += num_correct.data.item()
print('Test Loss: {:.6f}, Acc: {:.6f}'.format(eval_loss / (len(
test_dataset)), eval_acc / (len(test_dataset))))
print()

# 保存模型
torch.save(model.state_dict(), './rnn.pth')
请我喝杯咖啡吧~