python - Pytorch 设计选择中的 LSTM 单元实现
问题描述
我一直在寻找可以扩展的 Pytorch 中的 LSTM 单元的实现,我在此处接受的答案中找到了它的实现。我会把它贴在这里,因为我想参考它。有很多实现细节我不明白,我想知道是否有人可以澄清一下。
import math
import torch as th
import torch.nn as nn
class LSTM(nn.Module):
def __init__(self, input_size, hidden_size, bias=True):
super(LSTM, self).__init__()
self.input_size = input_size
self.hidden_size = hidden_size
self.bias = bias
self.i2h = nn.Linear(input_size, 4 * hidden_size, bias=bias)
self.h2h = nn.Linear(hidden_size, 4 * hidden_size, bias=bias)
self.reset_parameters()
def reset_parameters(self):
std = 1.0 / math.sqrt(self.hidden_size)
for w in self.parameters():
w.data.uniform_(-std, std)
def forward(self, x, hidden):
h, c = hidden
h = h.view(h.size(1), -1)
c = c.view(c.size(1), -1)
x = x.view(x.size(1), -1)
# Linear mappings
preact = self.i2h(x) + self.h2h(h)
# activations
gates = preact[:, :3 * self.hidden_size].sigmoid()
g_t = preact[:, 3 * self.hidden_size:].tanh()
i_t = gates[:, :self.hidden_size]
f_t = gates[:, self.hidden_size:2 * self.hidden_size]
o_t = gates[:, -self.hidden_size:]
c_t = th.mul(c, f_t) + th.mul(i_t, g_t)
h_t = th.mul(o_t, c_t.tanh())
h_t = h_t.view(1, h_t.size(0), -1)
c_t = c_t.view(1, c_t.size(0), -1)
return h_t, (h_t, c_t)
1- 为什么将 self.i2h 和 self.h2h 的隐藏大小乘以 4(在init
方法中)
2-我不明白参数的重置方法。特别是为什么我们要这样重置参数?
3- 为什么我们view
在 forward 方法中使用 h、c 和 x?
4-我也activations
对前向方法部分的列边界感到困惑。举个例子,为什么我们用 3 * self.hidden_size 为上限gates
?
解决方案
1- 为什么将 self.i2h 和 self.h2h 的隐藏大小乘以 4(在
init
方法中)
在您包含的方程式中,输入x和隐藏状态h用于四个计算,其中每个计算都是一个带有权重的矩阵乘法。无论您是进行四次矩阵乘法还是连接权重并进行一次更大的矩阵乘法并随后分离结果,都具有相同的结果。
input_size = 5
hidden_size = 10
input = torch.randn((2, input_size))
# Two different weights
w_c = torch.randn((hidden_size, input_size))
w_i = torch.randn((hidden_size, input_size))
# Concatenated weights into one tensor
# with size:[2 * hidden_size, input_size]
w_combined = torch.cat((w_c, w_i), dim=0)
# Output calculated by using separate matrix multiplications
out_c = torch.matmul(w_c, input.transpose(0, 1))
out_i = torch.matmul(w_i, input.transpose(0, 1))
# One bigger matrix multiplication with the combined weights
out_combined = torch.matmul(w_combined, input.transpose(0, 1))
# The first hidden_size number of rows belong to w_c
out_combined_c = out_combined[:hidden_size]
# The second hidden_size number of rows belong to w_i
out_combined_i = out_combined[hidden_size:]
# Using torch.allclose because they are equal besides floating point errors.
torch.allclose(out_c, out_combined_c) # => True
torch.allclose(out_i, out_combined_i) # => True
通过将线性层的输出大小设置为4 * hidden_size有四个大小为hidden_ size 的权重,因此只需要一层而不是四个。这样做并没有真正的优势,除了可能会带来轻微的性能改进,主要是针对较小的输入,如果单独完成,这些输入不会完全耗尽并行化能力。
4-我也
activations
对前向方法部分的列边界感到困惑。举个例子,为什么我们用 3 * self.hidden_size 为上限gates
?
这就是将输出分开以对应于四个单独计算的输出的地方。输出是 [i_t; f_t; o_t; g_t]
(分别不包括 tanh 和 sigmoid)的串联。
您可以通过将输出分成四个块来获得相同的分离torch.chunk
:
i_t, f_t, o_t, g_t = torch.chunk(preact, 4, dim=1)
但在分离之后,您必须torch.sigmoid
申请i_t
,f_t
和o_t
,torch.tanh
和g_t
。
5- LSTM 的所有参数在哪里?我在这里谈论 Us 和 Ws:
参数W是线性层中的权重,self.i2h
U是线性层中的权重self.h2h
,但是是连接在一起的。
W_i, W_f, W_o, W_c = torch.chunk(self.i2h.weight, 4, dim=0)
U_i, U_f, U_o, U_c = torch.chunk(self.h2h.weight, 4, dim=0)
3- 为什么我们
view
在 forward 方法中使用 h、c 和 x?
基于h_t = h_t.view(1, h_t.size(0), -1)
接近尾声,隐藏状态的大小为[1, batch_size, hidden_size]。h = h.view(h.size(1), -1)
这样就摆脱了第一个奇异维度以获得大小[ batch_size, hidden_size]。也可以使用h.squeeze(0)
.
2-我不明白参数的重置方法。特别是为什么我们要这样重置参数?
参数初始化会对模型的学习能力产生很大影响。初始化的一般规则是使值接近零而不会太小。一个常见的初始化是从平均值为 0 和方差为1 / n的正态分布中提取,其中n是神经元的数量,这反过来意味着标准偏差为1 / sqrt(n)。
在这种情况下,它使用均匀分布而不是正态分布,但总体思路是相似的。根据神经元的数量确定最小/最大值,但避免使它们太小。如果最小值/最大值为1 / n值会变得非常小,因此使用1 / sqrt(n)更合适,例如 256 个神经元:1 / 256 = 0.0039而1 / sqrt(256) = 0.0625。
初始化神经网络通过交互式可视化提供了对不同初始化的一些解释。
推荐阅读
- r - 为什么 reactiveTimer() 自 shiny_1.1.0 以来抛出错误
- asp.net-mvc - 如何查看从 HttpResponseMessage (ASP.NET MVC Web Api) 传递的内容
- xcode - 是否可以通过 shell 制作 xib 文件?
- excel - 更改代码以允许选择多个文件
- c# - 如何从当前时间的时间列表中获取最低的未来时间(现在之后)
- html - 将 CSS 应用于名称带有数字的类
- wcf - 在 WCF REST Web 服务上启用 HTTPS
- javascript - 打印机配置3英寸宽的纸张
- typescript - 错误:未捕获(承诺):错误:无法匹配任何路由。Angular 6 中的 URL 段
- android - Unity - iOS/Android 构建大小太高/太大,原因不明