LSTM(Long short-term memory) 长短期记忆
是RNN(循环神经网络)的一种
用得非常多,主要解决时间序列问题,是专门为时间序列设计的人工神经网络
LSTM结构
标准RNN中,其内部A是这样的,tanh是激活函数
LSTM
输入门控制输入(新记忆)的输入幅度;
遗忘门控制之前记忆状态的输入幅度;
输出门控制最终记忆的输出幅度
Sigmoid层起到一个gate门的作用(0代表不通过任何信息,1代表全部通过)
换种画法
本时刻输入前的参数 W(i), W(f), W(o)和3个上一时刻隐状态前的参数U(i),U(f),U(o)
LSTM的关键点是细胞状态,就是穿过图中的水平线。
单元状态有点像是个传送带。它贯穿整个链条,只有一些线性相互作用。这很容易让信息以不变的方式向下流动。
分层详解
黄颜色矩形表示人工神经网络层,就是1层、2层、3层那样,只不过用了一个矩形代替
粉色点代表矩阵运算
汇总是两个标量变成向量分开是复制一份,分别传送一份
考虑一个问题,让你来写个paper来解决一个问题,给你看一本书,这本书就是关系你要解决的那个问题的。让你在看这本书之前先写一次报告,然后看完这本书之后再写一次报告。这两次不同的报告中间发生了什么变化呢?你是因为读了这本书之后,会有一部分东西依赖于这本书,无形当中对你造成了影响。但是你又不可能100%完全依赖于这本书,你也有先验知识,所以你第二份报告一定是我的认识+书的知识,衡量一下它俩的权重,然后做一个权重加和,得出报告。
确定有多少信息来自于我们的内部经验,来自于历史
套sigma函数是为了得到一个百分比
tanh是到-1~1
sigma是0~1负的表明这本书可能会对你带来误导,比如你读一本书,这本书完全不对,也会对带来影响
Ct~告诉我们这本书读完之后,能给我们提供多少新的信息
it告诉我们新的信息里面有多少信息是可靠的,是我们可以拿来用的这两个激活函数不一样,所以他们的意义就完全不一样
ft,我有多相信之前的历史
it,我有多相信当前学的这本书
我们基于这个新的Ct来帮我们做预测
Ct是你对整个问题最新的认识
把你所有的想法,整体的思想,有选择的输出一些,而不是全部输出(人的想法就是这样。会有所保留,不会全部说出来,或者只选择一些和当前主题有关的说,或者是当某些情况满足的时候才会说)
所以说Ct并不是全部都要输出,要经过tanh激活之后才会输出这些信息你想输出但是最后真正输出了多少呢 ot
ht-1是上一时刻的ht
xt是你新观测到的数据ht-1到ht这也是为什么叫循环神经网络,他在不断作循环
这个图还是上面框架,但是换了一种画图方式
state t-1就是Ct-1,state t 就是Ct
ft决定了C,t-1是有多少信息流入Ct
at是新的信息能给我们提供多少有效的信息,就是Ct~
it是新的信息有多少是我们想保留的黄色的Ct直接网上传,变成下一时刻的Ct-1
通过tanh激活,激活之后乘上ot,就变成输出了
用真实值来套模型
x,y是告诉的,是一致的(y就是x的标签)
W,U,b是不知道的,一开始随便猜一个值,不断去优化用这些值来套刚才的公式
最开始输入的是x和ht-1的汇总(concatenate)
[1 2 0]流进来之后,0会在U0的位置,[1 2]会放在out-1的位置,[1 2]会放在x-的位置,就得出了a0,就是当前时刻我学到的
.......
然后用输出的结果0.536和真值 0.5之间的差异,作为代价函数来去优化我们的参数W、U、b
t=1时刻的输出0.77197和1.25差的就挺多了
优化
一般加了帽子的是值的输出,没加的就是真值
E就是代价函数,损失函数。我们优化就是要不断调试参数,使E最小通过求斜率,导数
用优化好的参数再套回到模型的那些参数W、U、b,再求出来的结果就和y值很接近了。
也就是说模型已经可以很好得模拟过去了,那我们接下来就要让他模拟未来
衡量它的好坏也是用confusion matrix来衡量,把它预测是和实际观测的来比较
看它的 TP+TN(即判断对了多少次)/总次数
LSTM变体
GRU则是LSTM的一个变体,GRU只有两个门了,分别为更新门和重置门。更新门用于控制前一时刻的状态信息被带入到当前状态中的程度,更新门的值越大说明前一时刻的状态信息带入越多。重置门用于控制忽略前一时刻的状态信息的程度,重置门的值越小说明忽略得越多。
RNN(LSTM)进行图像分类实战
RNN 特别适合做序列类型的数据,那么 RNN 能不能想 CNN 一样用来做图像分类呢?下面我们用 mnist 手写字体的例子来展示一下如何用 RNN 做图像分类,但是这种方法并不是主流,这里我们只是作为举例。
对于一张手写字体的图片,其大小是 28 * 28,我们可以将其看做是一个长为 28 的序列,每个序列的特征都是 28,也就是
这样我们解决了输入序列的问题,对于输出序列怎么办呢?其实非常简单,虽然我们的输出是一个序列,但是我们只需要保留其中一个作为输出结果就可以了,这样的话肯定保留最后一个结果是最好的,因为最后一个结果有前面所有序列的信息,就像下面这样
# RNN循环神经网络 分类 (时间顺序,图片从上往下读取) import torch import torch.nn as nn import torchvision.datasets import torchvision.transforms as transforms import matplotlib.pyplot as plt import torch.utils.data as Data class RNN(nn.Module): def __init__(self): super(RNN, self).__init__() self.rnn = nn.LSTM( input_size=28, # 每行的像素点 hidden_size=64, num_layers=1, # the num of RNN layers batch_first=True, # 表示在输入数据的时候,数据的维度为(batch,time_step,input) # 如果自己定义的维度为(time_step,batch,input),则为False ) self.out = nn.Linear(64, 10) def forward(self, x): # x :(batch,time_step,input_size) # 每步对输入的x进行计算,每次计算完之后产生一个自己生成的理解;下一次传入神经网络的不只是这次的input,还有上一次生成的(h_n,h_c) # 然后生成输出结果r_out和自己的理解(h_n,h_c); (h_n,h_c)为分线和主线层的hidden state # None:第一次输入的时候没有hidden state # r_out 中有从第一次到最后一次一共28个output r_out, (h_n, h_c) = self.rnn(x, None) # r_out: (batch,time_step,output_size) # h_n,h_c: (n_layers,batch,hidden_size) # 选取最后一次的r_out进行输出 # r_out[:,-1,:]的值也是h_n的值 out = self.out(r_out[:, -1, :]) return out torch.manual_seed(1) # Hyper parameters # 图片大小28*28,一共读取28次,每次读取一行28个数据点 EPOCH = 1 BATCH_SIZE = 64 TIME_STEP = 28 # RNN time step/image_height INPUT_SIZE = 28 # 每个时间点包含多少个数据点, image_width LR = 0.01 DOWNLOAD_MNIST = False train_data = torchvision.datasets.MNIST( root='dataset/', train=True, transform=torchvision.transforms.ToTensor(), download=DOWNLOAD_MNIST, ) # plt.imshow(train_data.data[0].numpy(),cmap='gray') # plt.title(train_data.targets[0].numpy()) # plt.show() test_data = torchvision.datasets.MNIST(root='dataset/', train=False) # 批训练 50samples,1 channel,28*28 (50,1,28,28) train_loader = Data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True) # 去前2000个测试数据进行测试 test_x = torch.unsqueeze(test_data.data, dim=1).type(torch.FloatTensor)[:2000] / 255 # shape from(2000,28,28)to(2000,1,28,28) test_y = test_data.targets[:2000] 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() # 分类标签不是one-hot的形式 for epoch in range(EPOCH): for step, (x, b_y) in enumerate(train_loader): b_x = x.view(-1, 28, 28) # reshape x to (batch,time_step,input_size) output = rnn(b_x) loss = loss_func(output, b_y) optimizer.zero_grad() loss.backward() optimizer.step() if step % 50 == 0: x = test_x.view(-1, 28, 28) test_out = rnn(x) pred_y = torch.max(test_out, 1)[1].data.squeeze() # torch.max(input,dim)返回每一行中的最大值的标签 accuracy = (pred_y == test_y).numpy().sum() / test_y.size(0) print('step: {} | train loss: {} | test accuracy: {} '.format(step, loss.data, accuracy)) # 取前十个数据看一下结果 test_out = rnn(test_x[:10].view(-1, 28, 28)) pred_y = torch.max(test_out, 1)[1].data.squeeze() print('prediction:', pred_y) print('real value:', test_y[:10])