fast.ai 深度学习笔记(三)(2)

简介: fast.ai 深度学习笔记(三)(2)

fast.ai 深度学习笔记(三)(1)https://developer.aliyun.com/article/1482896

让我们创建我们的第一个 RNN[1:37:45]

我们可以简化上一个图表如下:


使用 1 到 n-1 个字符预测第 n 个字符

让我们实现这个。这次,我们将使用前 8 个字符来预测第 9 个。这是如何创建输入和输出的,就像上次一样:

cs = 8
c_in_dat = [[idx[i+j] for i in range(cs)] for j in range(len(idx)-cs)]
c_out_dat = [idx[j+cs] for j in range(len(idx)-cs)]
xs = np.stack(c_in_dat, axis=0)
y = np.stack(c_out_dat)
xs[:cs,:cs]
'''
array([[40, 42, 29, 30, 25, 27, 29,  1],
       [42, 29, 30, 25, 27, 29,  1,  1],
       [29, 30, 25, 27, 29,  1,  1,  1],
       [30, 25, 27, 29,  1,  1,  1, 43],
       [25, 27, 29,  1,  1,  1, 43, 45],
       [27, 29,  1,  1,  1, 43, 45, 40],
       [29,  1,  1,  1, 43, 45, 40, 40],
       [ 1,  1,  1, 43, 45, 40, 40, 39]])
'''
y[:cs]
'''
array([ 1,  1, 43, 45, 40, 40, 39, 43])
'''

请注意,它们是重叠的(即 0-7 预测 8,1-8 预测 9)。

val_idx = get_cv_idxs(len(idx)-cs-1)
md = ColumnarModelData.from_arrays('.', val_idx, xs, y, bs=512)

创建模型[1:43:03]

class CharLoopModel(nn.Module):
    # This is an RNN!
    def __init__(self, vocab_size, n_fac):
        super().__init__()
        self.e = nn.Embedding(vocab_size, n_fac)
        self.l_in = nn.Linear(n_fac, n_hidden)
        self.l_hidden = nn.Linear(n_hidden, n_hidden)
        self.l_out = nn.Linear(n_hidden, vocab_size)
    def forward(self, *cs):
        bs = cs[0].size(0)
        h = V(torch.zeros(bs, n_hidden).cuda())
        for c in cs:
            inp = F.relu(self.l_in(self.e(c)))
            h = F.tanh(self.l_hidden(h+inp))
        return F.log_softmax(self.l_out(h), dim=-1)

大部分代码与以前相同。您会注意到forward函数中有一个for循环。

双曲正切(Tanh)[1:43:43]

这是一个偏移的 sigmoid 函数。在隐藏状态到隐藏状态的转换中使用双曲正切是常见的,因为它可以阻止其飞得太高或太低。对于其他目的,relu 更常见。


现在这是一个相当深的网络,因为它使用 8 个字符而不是 2 个。随着网络变得更深,它们变得更难训练。

m = CharLoopModel(vocab_size, n_fac).cuda() 
opt = optim.Adam(m.parameters(), 1e-2)
fit(m, md, 1, opt, F.nll_loss)
set_lrs(opt, 0.001)
fit(m, md, 1, opt, F.nll_loss)

添加 vs.连接

现在我们将尝试为self.l_hidden(**h+inp**)[1:46:04]尝试其他方法。原因是输入状态和隐藏状态在质上是不同的。输入是字符的编码,h 是一系列字符的编码。因此,将它们相加,我们可能会丢失信息。让我们改为连接它们。不要忘记更改输入以匹配形状(n_fac+n_hidden而不是n_fac)。

class CharLoopConcatModel(nn.Module):
    def __init__(self, vocab_size, n_fac):
        super().__init__()
        self.e = nn.Embedding(vocab_size, n_fac)
        self.l_in = nn.Linear(n_fac+n_hidden, n_hidden)
        self.l_hidden = nn.Linear(n_hidden, n_hidden)
        self.l_out = nn.Linear(n_hidden, vocab_size)
    def forward(self, *cs):
        bs = cs[0].size(0)
        h = V(torch.zeros(bs, n_hidden).cuda())
        for c in cs:
            inp = torch.cat((h, self.e(c)), 1)
            inp = F.relu(self.l_in(inp))
            h = F.tanh(self.l_hidden(inp))
        return F.log_softmax(self.l_out(h), dim=-1)

这带来了一些改进。

使用 PyTorch 的 RNN[1:48:47]

PyTorch 将自动为我们编写for循环,还会编写线性输入层。

class CharRnn(nn.Module):
    def __init__(self, vocab_size, n_fac):
        super().__init__()
        self.e = nn.Embedding(vocab_size, n_fac)
        **self.rnn = nn.RNN(n_fac, n_hidden)**
        self.l_out = nn.Linear(n_hidden, vocab_size)
    def forward(self, *cs):
        bs = cs[0].size(0)
        h = V(torch.zeros(1, bs, n_hidden))
        inp = self.e(torch.stack(cs))
        **outp,h = self.rnn(inp, h)**
        return F.log_softmax(self.l_out(**outp[-1]**), dim=-1)
  • 出于以后会变得明显的原因,self.rnn将返回不仅输出,还有隐藏状态。
  • PyTorch 中的一个微小差异是self.rnn会将一个新的隐藏状态附加到张量上,而不是替换(换句话说,它会在图表中返回所有省略号)。我们只想要最后一个,所以我们做outp[-1]
m = CharRnn(vocab_size, n_fac).cuda() 
opt = optim.Adam(m.parameters(), 1e-3)
ht = V(torch.zeros(1, 512,n_hidden)) 
outp, hn = m.rnn(t, ht) 
outp.size(), hn.size()
'''
(torch.Size([8, 512, 256]), torch.Size([1, 512, 256]))
'''

在 PyTorch 版本中,隐藏状态是一个秩为 3 的张量h = V(torch.zeros(1, bs, n_hidden)(在我们的版本中,它是秩为 2 的张量)[1:51:58]。我们以后会学到更多关于这个,但事实证明你可以有第二个向后运行的 RNN。这个想法是它会更好地找到向后的关系——它被称为“双向 RNN”。你也可以有一个 RNN 馈送到一个 RNN,这被称为“多层 RNN”。对于这些 RNN,你将需要张量中的额外轴来跟踪额外层的隐藏状态。现在,我们只有 1 个,然后返回 1 个。

测试模型

def get_next(inp):
    idxs = T(np.array([char_indices[c] for c in inp]))
    p = m(*VV(idxs))
    i = np.argmax(to_np(p))
    return chars[i]
def get_next_n(inp, n):
    res = inp
    for i in range(n):
        c = get_next(inp)
        res += c
        inp = inp[1:]+c
    return res
get_next_n('for thos', 40) 
'''
'for those the same the same the same the same th'
'''

这次,我们循环n次,每次调用get_next,每次我们将我们的输入替换为删除第一个字符并添加我们刚预测的字符。

对于有趣的作业,尝试编写自己的nn.RNNJeremysRNN”,而不查看 PyTorch 源代码。

多输出[1:55:31]

从最后一个图表中,我们可以进一步简化,将字符 1 视为字符 2 到 n-1 相同。你会注意到三角形(输出)也移动到循环内部,换句话说,我们在每个字符之后创建一个预测。


使用字符 1 到 n-1 预测字符 2 到 n

我们可能想要这样做的原因之一是我们之前看到的冗余:

array([[40, 42, 29, 30, 25, 27, 29,  1],
       [42, 29, 30, 25, 27, 29,  1,  1],
       [29, 30, 25, 27, 29,  1,  1,  1],
       [30, 25, 27, 29,  1,  1,  1, 43],
       [25, 27, 29,  1,  1,  1, 43, 45],
       [27, 29,  1,  1,  1, 43, 45, 40],
       [29,  1,  1,  1, 43, 45, 40, 40],
       [ 1,  1,  1, 43, 45, 40, 40, 39]])

这次我们可以通过采用不重叠的字符集来使其更有效。因为我们正在进行多输出,对于输入字符 0 到 7,输出将是字符 1 到 8 的预测。

xs[:cs,:cs]
'''
array([[40, 42, 29, 30, 25, 27, 29,  1],
       [ 1,  1, 43, 45, 40, 40, 39, 43],
       [33, 38, 31,  2, 73, 61, 54, 73],
       [ 2, 44, 71, 74, 73, 61,  2, 62],
       [72,  2, 54,  2, 76, 68, 66, 54],
       [67,  9,  9, 76, 61, 54, 73,  2],
       [73, 61, 58, 67, 24,  2, 33, 72],
       [ 2, 73, 61, 58, 71, 58,  2, 67]])
'''
ys[:cs,:cs]
'''
array([[42, 29, 30, 25, 27, 29,  1,  1],
       [ 1, 43, 45, 40, 40, 39, 43, 33],
       [38, 31,  2, 73, 61, 54, 73,  2],
       [44, 71, 74, 73, 61,  2, 62, 72],
       [ 2, 54,  2, 76, 68, 66, 54, 67],
       [ 9,  9, 76, 61, 54, 73,  2, 73],
       [61, 58, 67, 24,  2, 33, 72,  2],
       [73, 61, 58, 71, 58,  2, 67, 68]])
'''

这不会使我们的模型更准确,但我们可以更有效地训练它。

class CharSeqRnn(nn.Module):
    def __init__(self, vocab_size, n_fac):
        super().__init__()
        self.e = nn.Embedding(vocab_size, n_fac)
        self.rnn = nn.RNN(n_fac, n_hidden)
        self.l_out = nn.Linear(n_hidden, vocab_size)
    def forward(self, *cs):
        bs = cs[0].size(0)
        h = V(torch.zeros(1, bs, n_hidden))
        inp = self.e(torch.stack(cs))
        outp,h = self.rnn(inp, h)
        return F.log_softmax(self.l_out(outp), dim=-1)

请注意,我们不再做outp[-1],因为我们想保留所有这些。但其他一切都是相同的。一个复杂性[2:00:37]是我们想要像以前一样使用负对数似然损失函数,但它期望两个秩为 2 的张量(两个矢量的小批量)。但在这里,我们有秩为 3 的张量:

  • 8 个字符(时间步)
  • 84 个概率
  • 对于 512 个小批量

让我们编写一个自定义损失函数[2:02:10]:

def nll_loss_seq(inp, targ):
    sl,bs,nh = inp.size()
    targ = targ.transpose(0,1).contiguous().view(-1)
    return F.nll_loss(inp.view(-1,nh), targ)
  • F.nll_loss是 PyTorch 的损失函数。
  • 展平我们的输入和目标。
  • 转置前两个轴,因为 PyTorch 期望 1.序列长度(多少个时间步),2.批量大小,3.隐藏状态本身。yt.size()是 512 乘以 8,而sl, bs是 8 乘以 512。
  • 当你做像“transpose”这样的事情时,PyTorch 通常不会实际洗牌内存顺序,而是保留一些内部元数据来处理它,就好像它被转置了。当你转置一个矩阵时,PyTorch 只是更新元数据。如果你看到一个错误说“这个张量不连续”,在它后面加上.contiguous(),错误就会消失。
  • .viewnp.reshape相同。-1表示它需要多长。
fit(m, md, 4, opt, null_loss_seq)

记住fit(...)是 fast.ai 实现训练循环的最低级抽象。因此,除了md是包装测试集、训练集和验证集的模型数据对象之外,所有参数都是标准的 PyTorch 东西。

问题[2:06:04]: 现在我们在循环内部放了一个三角形,我们需要更大的序列大小吗?

  • 如果我们有一个短序列像 8 这样,第一个字符没有任何依据。它从零开始的空隐藏状态。
  • 我们将学习如何避免这个问题下周。
  • 基本思想是“为什么我们每次都要将隐藏状态重置为零?”(见下面的代码)。如果我们可以以某种方式排列这些小批量,使得下一个小批量正确连接起来,代表尼采作品中的下一个字母,那么我们可以将h = V(torch.zeros(1, bs, n_hidden))移到构造函数中。
class CharSeqRnn(nn.Module):
    def __init__(self, vocab_size, n_fac):
        super().__init__()
        self.e = nn.Embedding(vocab_size, n_fac)
        self.rnn = nn.RNN(n_fac, n_hidden)
        self.l_out = nn.Linear(n_hidden, vocab_size)
    def forward(self, *cs):
        bs = cs[0].size(0)
        **h = V(torch.zeros(1, bs, n_hidden))**
        inp = self.e(torch.stack(cs))
        outp,h = self.rnn(inp, h)
        return F.log_softmax(self.l_out(outp), dim=-1)

梯度爆炸 [2:08:21]

self.rnn(inp, h) 是一个循环,一遍又一遍地应用相同的矩阵乘法。如果那个矩阵乘法倾向于每次增加激活,我们实际上是将其乘以 8 次 — 我们称之为梯度爆炸。我们希望确保初始的l_hidden不会导致我们的激活平均增加或减少。

一个很好的能做到这一点的矩阵被称为单位矩阵:


我们可以用单位矩阵覆盖随机初始化的隐藏-隐藏权重:

m.rnn.weight_hh_l0.data.copy_(torch.eye(n_hidden))

这是由 Geoffrey Hinton 等人在 2015 年介绍的(一种初始化修正线性单元循环网络的简单方法) — 在 RNN 存在几十年后。它效果非常好,你可以使用更高的学习率,因为它表现良好。

深度学习 2:第 1 部分第 7 课

原文:medium.com/@hiromi_suenaga/deep-learning-2-part-1-lesson-7-1b9503aff0c

译者:飞龙

协议:CC BY-NC-SA 4.0

来自 fast.ai 课程的个人笔记。随着我继续复习课程以“真正”理解它,这些笔记将继续更新和改进。非常感谢 JeremyRachel 给了我这个学习的机会。

第 7 课

第 1 部分的主题是:

  • 使用深度学习进行分类和回归
  • 识别和学习最佳和已建立的实践
  • 重点是分类和回归,即预测“一件事”(例如一个数字,少量标签)

课程的第 2 部分:

  • 重点是生成建模,这意味着预测“很多事情” — 例如,在神经翻译中创建句子,图像字幕或问题回答,同时创建图像,例如风格转移,超分辨率,分割等等。
  • 不是那么多的最佳实践,而是从最近的可能尚未完全测试的论文中更多的推测。

Char3Model 的回顾

提醒:RNN 在任何方面都不是不同或不寻常或神奇的 — 只是一个标准的全连接网络。


标准全连接网络

  • 箭头代表一个或多个层操作 —— 一般来说是线性后跟一个非线性函数,本例中是矩阵乘法后跟 relutanh
  • 相同颜色的箭头表示使用完全相同的权重矩阵。
  • 与以前的一个细微差别是第二层和第三层有输入进来。我们尝试了两种方法 —— 将这些输入连接或添加到当前激活中。
class Char3Model(nn.Module):
    def __init__(self, vocab_size, n_fac):
        super().__init__()
        self.e = nn.Embedding(vocab_size, n_fac)
        # The 'green arrow' from our diagram
        self.l_in = nn.Linear(n_fac, n_hidden)
        # The 'orange arrow' from our diagram
        self.l_hidden = nn.Linear(n_hidden, n_hidden)
        # The 'blue arrow' from our diagram
        self.l_out = nn.Linear(n_hidden, vocab_size)
    def forward(self, c1, c2, c3):
        in1 = F.relu(self.l_in(self.e(c1)))
        in2 = F.relu(self.l_in(self.e(c2)))
        in3 = F.relu(self.l_in(self.e(c3)))
        h = V(torch.zeros(in1.size()).cuda())
        h = F.tanh(self.l_hidden(h+in1))
        h = F.tanh(self.l_hidden(h+in2))
        h = F.tanh(self.l_hidden(h+in3))
        return F.log_softmax(self.l_out(h))
  • 通过使用 nn.Linear,我们免费获得了权重矩阵和偏置向量。
  • 为了解决第一个椭圆中没有橙色箭头的问题,我们发明了一个空矩阵
class CharLoopModel(nn.Module):
    # This is an RNN!
    def __init__(self, vocab_size, n_fac):
        super().__init__()
        self.e = nn.Embedding(vocab_size, n_fac)
        self.l_in = nn.Linear(n_fac, n_hidden)
        self.l_hidden = nn.Linear(n_hidden, n_hidden)
        self.l_out = nn.Linear(n_hidden, vocab_size)
    def forward(self, *cs):
        bs = cs[0].size(0)
        h = V(torch.zeros(bs, n_hidden).cuda())
        for c in cs:
            inp = F.relu(self.l_in(self.e(c)))
            h = F.tanh(self.l_hidden(h+inp))
        return F.log_softmax(self.l_out(h), dim=-1)
  • 几乎相同,除了 for 循环
class CharRnn(nn.Module):
    def __init__(self, vocab_size, n_fac):
        super().__init__()
        self.e = nn.Embedding(vocab_size, n_fac)
        self.rnn = nn.RNN(n_fac, n_hidden)
        self.l_out = nn.Linear(n_hidden, vocab_size)
    def forward(self, *cs):
        bs = cs[0].size(0)
        h = V(torch.zeros(1, bs, n_hidden))
        inp = self.e(torch.stack(cs))
        outp,h = self.rnn(inp, h)
        return F.log_softmax(self.l_out(outp[-1]), dim=-1)
  • PyTorch 版本 — nn.RNN 将创建循环并跟踪 h

  • 我们使用白色部分来预测绿色字符 —— 这似乎是浪费的,因为下一部分与当前部分大部分重叠。

  • 然后我们尝试在多输出模型中将其分割为不重叠的部分:
  • 在这种方法中,我们在处理每个部分后丢弃了我们的 h 激活,并开始了一个新的激活。为了在下一部分中使用第一个字符来预测第二个字符,它除了默认激活外没有其他信息。让我们不要丢弃 h

有状态的 RNN

class CharSeqStatefulRnn(nn.Module):
    def __init__(self, vocab_size, n_fac, bs):
        self.vocab_size = vocab_size
        super().__init__()
        self.e = nn.Embedding(vocab_size, n_fac)
        self.rnn = nn.RNN(n_fac, n_hidden)
        self.l_out = nn.Linear(n_hidden, vocab_size)
        self.init_hidden(bs)
    def forward(self, cs):
        bs = cs[0].size(0)
        if self.h.size(1) != bs: self.init_hidden(bs)
        outp,h = self.rnn(self.e(cs), self.h)
        self.h = repackage_var(h)
        return F.log_softmax(self.l_out(outp), dim=-1).view(-1, self.vocab_size)
    def init_hidden(self, bs): 
        self.h = V(torch.zeros(1, bs, n_hidden))
  • 构造函数中的一个额外行。self.init_hidden(bs)self.h 设置为一堆零。
  • 问题 #1 — 如果我们简单地执行 self.h = h,并在一个包含一百万个字符的文档上进行训练,那么 RNN 的展开版本的大小将有一百万层(椭圆)。一百万层全连接网络将非常占用内存,因为为了进行链式规则,我们必须在每个批次中乘以一百万层,同时记住所有一百万个梯度。
  • 为了避免这种情况,我们告诉它不时忘记它的历史。我们仍然可以记住状态(隐藏矩阵中的值)而不必记住如何到达那里的一切。
def repackage_var(h):
    return (
        Variable(h.data) 
        if type(h) == Variable 
        else tuple(repackage_var(v) for v in h)
    )
  • Variable h 中取出张量(记住,张量本身没有任何历史概念),并从中创建一个新的 Variable。新变量具有相同的值,但没有操作历史,因此当它尝试反向传播时,它将在那里停止。
  • forward将处理 8 个字符,然后通过 8 个层进行反向传播,跟踪隐藏状态中的值,但会丢弃其操作历史。这被称为时间反向传播(bptt)
  • 换句话说,在for循环之后,只需丢弃操作历史并重新开始。因此,我们保留了我们的隐藏状态,但没有保留我们的隐藏状态历史。
  • 不要通过太多层进行反向传播的另一个很好的理由是,如果您有任何梯度不稳定性(例如,梯度爆炸或梯度消失),您拥有的层数越多,网络训练就越困难(速度更慢,弹性更差)。
  • 另一方面,更长的bptt意味着您能够明确捕获更长的记忆和更多状态。
  • 皱纹#2[16:00] - 如何创建小批量。我们不想一次处理一个部分,而是一次并行处理一堆。
  • 当我们第一次开始研究 TorchText 时,我们谈到了它如何创建这些小批量。
  • Jeremy 说我们拿一整个由尼采的全部作品或所有 IMDB 评论连接在一起的长文档,将其分成 64 个相等大小的块(不是大小为 64 的块)。

  • 对于一个长度为 6400 万字符的文档,每个“块”将是 100 万个字符。我们将它们堆叠在一起,现在按bptt拆分它们 - 1 个小批次由 64 个bptt矩阵组成。
  • 第二块(第 100 万个字符)的第一个字符可能在一个句子的中间。但没关系,因为这只会在每一百万个字符中发生一次。

问题:这种数据集的数据增强?[20:34]

没有已知的好方法。最近有人通过进行数据增强赢得了一个 Kaggle 竞赛,随机插入不同行的部分 - 这样的方法可能在这里有用。但最近没有任何最先进的 NLP 论文在进行这种数据增强。

问题:我们如何选择 bptt 的大小?[21:36]

有几件事需要考虑:

  • 第一点是小批量矩阵的大小为bs(块数)乘以bptt,因此您的 GPU RAM 必须能够容纳嵌入矩阵。因此,如果您遇到 CUDA 内存不足错误,您需要减少其中一个。
  • 如果您的训练不稳定(例如,您的损失突然飙升到 NaN),那么您可以尝试减少您的bptt,因为您的层较少,梯度不会爆炸。
  • 如果速度太慢[22:44],尝试减少你的bptt,因为它会一次执行一个步骤。for循环不能并行化(对于当前版本)。最近有一种叫做 QRNN(准循环神经网络)的东西,它可以并行化,我们希望在第二部分中介绍。
  • 所以选择满足所有这些条件的最高数字。

有状态的 RNN 和 TorchText[23:23]

在使用期望数据符合特定格式的现有 API 时,您可以将数据更改为符合该格式,也可以编写自己的数据集子类来处理您的数据已经存在的格式。两者都可以,但在这种情况下,我们将把我们的数据放在 TorchText 已经支持的格式中。Fast.ai 对 TorchText 的包装器已经有了一些东西,您可以在每个路径中有一个训练路径和验证路径,并且每个路径中有一个或多个文本文件,其中包含一堆文本,这些文本被连接在一起用于您的语言模型。

from torchtext import vocab, data 
from fastai.nlp import * 
from fastai.lm_rnn import * 
PATH='data/nietzsche/' 
TRN_PATH = 'trn/' 
VAL_PATH = 'val/' 
TRN = f'{PATH}{TRN_PATH}' 
VAL = f'{PATH}{VAL_PATH}'
%ls {PATH}
'''
models/  nietzsche.txt  trn/  val/
'''
%ls {PATH}trn
'''
trn.txt
'''
  • 复制了尼采文件,粘贴到训练和验证目录中。然后从训练集中删除最后 20%的行,并删除验证集中除最后 20%之外的所有内容[25:15]。
  • 这样做的另一个好处是,似乎更现实地拥有一个验证集,它不是文本行的随机洗牌集,而是完全独立于语料库的一部分。
  • 当您进行语言模型时,您实际上不需要单独的文件。您可以有多个文件,但它们最终会被连接在一起。
TEXT = data.Field(lower=True, tokenize=list)
bs=64; bptt=8; n_fac=42; n_hidden=256
FILES = dict(train=TRN_PATH, validation=VAL_PATH, test=VAL_PATH)
md = LanguageModelData.from_text_files(PATH, TEXT, **FILES, bs=bs, bptt=bptt, min_freq=3)
len(md.trn_dl), md.nt, len(md.trn_ds), len(md.trn_ds[0].text)
'''
(963, 56, 1, 493747)
'''
  • 在 TorchText 中,我们创建了一个叫做Field的东西,最初Field只是关于如何进行文本预处理的描述。
  • lower - 我们告诉它将文本转换为小写
  • tokenize - 上次,我们使用了一个在空格上分割的函数,给我们一个单词模型。这次,我们想要一个字符模型,所以使用list函数来对字符串进行标记化。记住,在 Python 中,list('abc')将返回['a','b','c']
  • bs:批次大小,bptt:我们将其重命名为csn_fac:嵌入的大小,n_hidden:我们隐藏状态的大小
  • 我们没有单独的测试集,所以我们将只使用验证集进行测试
  • TorchText 每次都会稍微随机化bptt的长度。它并不总是给我们确切的 8 个字符;有 5%的概率,它会将其减半并添加一个小的标准偏差,使其略大或略小于 8。我们不能对数据进行洗牌,因为它需要是连续的,所以这是引入一些随机性的一种方式。
  • 问题:每个小批次的大小是否保持恒定?是的,我们需要用h权重矩阵进行矩阵乘法,因此小批次的大小必须保持恒定。但是序列长度可以改变,没有问题。
  • len(md.trn_dl):数据加载器的长度(即有多少个小批次),md.nt:标记的数量(即词汇表中有多少个唯一的东西)
  • 一旦运行LanguageModelData.from_text_filesTEXT将包含一个名为vocab的额外属性。TEXT.vocab.itos是词汇表中唯一项目的列表,TEXT.vocab.stoi是从每个项目到数字的反向映射。
class CharSeqStatefulRnn(nn.Module):
    def __init__(self, vocab_size, n_fac, bs):
        self.vocab_size = vocab_size
        super().__init__()
        self.e = nn.Embedding(vocab_size, n_fac)
        self.rnn = nn.RNN(n_fac, n_hidden)
        self.l_out = nn.Linear(n_hidden, vocab_size)
        self.init_hidden(bs)
    def forward(self, cs):
        bs = cs[0].size(0)
        if self.h.size(1) != bs: 
            self.init_hidden(bs)
        outp,h = self.rnn(self.e(cs), self.h)
        self.h = repackage_var(h)
        return F.log_softmax(self.l_out(outp), dim=-1).view(-1, self.vocab_size)
    def init_hidden(self, bs): 
        self.h = V(torch.zeros(1, bs, n_hidden))
  • 问题 #3:Jeremy 在说小批次大小保持恒定时对我们撒谎了。最后一个小批次很可能比其他小批次短,除非数据集恰好可以被bptt乘以bs整除。这就是为什么我们要检查self.h的第二维是否与输入的bs相同。如果不相同,将其设置回零,并使用输入的bs。这发生在周期结束和周期开始时(将其设置回完整的批次大小)。
  • 问题 #4:最后一个问题是关于 PyTorch 的一个小问题,也许有人可以友好地尝试通过 PR 来修复它。损失函数不喜欢接收一个三维张量(即三维数组)。它们不应该不喜欢接收一个三维张量(按序列长度、批次大小和结果计算损失 - 因此您可以为两个初始轴的每个计算损失)。对于二维或四维张量可以工作,但对于三维张量不行。
  • .view将三维张量重塑为二维的-1(必要时尽可能大)乘以vocab_size。TorchText 自动将目标展平,因此我们不需要为实际值这样做(当我们在第 4 课看到一个小批次时,我们注意到它被展平了。Jeremy 说我们以后会了解原因,现在就是时候了)。
  • PyTorch(截至 0.3 版),log_softmax要求我们指定我们要对 softmax 进行的轴(即我们要将其求和为 1 的轴)。在这种情况下,我们希望在最后一个轴dim = -1上进行。
m = CharSeqStatefulRnn(md.nt, n_fac, 512).cuda() 
opt = optim.Adam(m.parameters(), 1e-3)
fit(m, md, 4, opt, F.nll_loss)


fast.ai 深度学习笔记(三)(3)https://developer.aliyun.com/article/1482898

相关文章
|
1天前
|
机器学习/深度学习 人工智能 算法
AI大咖说-关于深度学习的一点思考
周志华教授探讨深度学习的成效,指出其关键在于大量数据、强大算力和训练技巧。深度学习依赖于函数可导性、梯度下降及反向传播算法,尽管硬件和数据集有显著进步,但核心原理保持不变。深度意味着增加模型复杂度,相较于拓宽,加深网络更能增强泛函表达能力,促进表示学习,通过逐层加工处理和内置特征变换实现抽象语义理解。周志华教授还提到了非神经网络的深度学习方法——深度森林。5月更文挑战第12天
26 5
|
1天前
|
机器学习/深度学习 人工智能 算法
构建高效AI系统:深度学习优化技术解析
【5月更文挑战第12天】 随着人工智能技术的飞速发展,深度学习已成为推动创新的核心动力。本文将深入探讨在构建高效AI系统中,如何通过优化算法、调整网络结构及使用新型硬件资源等手段显著提升模型性能。我们将剖析先进的优化策略,如自适应学习率调整、梯度累积技巧以及正则化方法,并讨论其对模型训练稳定性和效率的影响。文中不仅提供理论分析,还结合实例说明如何在实际项目中应用这些优化技术。
|
1天前
|
机器学习/深度学习 敏捷开发 人工智能
吴恩达 x Open AI ChatGPT ——如何写出好的提示词视频核心笔记
吴恩达 x Open AI ChatGPT ——如何写出好的提示词视频核心笔记
27 0
|
1天前
|
机器学习/深度学习 人工智能 算法
【AI 初识】讨论深度学习和机器学习之间的区别
【5月更文挑战第3天】【AI 初识】讨论深度学习和机器学习之间的区别
|
1天前
|
机器学习/深度学习 自然语言处理 PyTorch
fast.ai 深度学习笔记(三)(4)
fast.ai 深度学习笔记(三)(4)
25 0
|
1天前
|
机器学习/深度学习 算法 PyTorch
fast.ai 深度学习笔记(三)(3)
fast.ai 深度学习笔记(三)(3)
34 0
|
1天前
|
机器学习/深度学习 PyTorch 算法框架/工具
fast.ai 深度学习笔记(三)(1)
fast.ai 深度学习笔记(三)(1)
41 0
|
1天前
|
索引 机器学习/深度学习 计算机视觉
fast.ai 深度学习笔记(四)(3)
fast.ai 深度学习笔记(四)
47 0
|
1天前
|
机器学习/深度学习 固态存储 Python
fast.ai 深度学习笔记(四)(2)
fast.ai 深度学习笔记(四)
55 3
fast.ai 深度学习笔记(四)(2)
|
1天前
|
API 机器学习/深度学习 Python
fast.ai 深度学习笔记(四)(1)
fast.ai 深度学习笔记(四)
66 3
fast.ai 深度学习笔记(四)(1)

热门文章

最新文章