0

我使用 lasagne 构建了一个 LSTM 循环 NNet ,该NNet大致基于此博客文章中的架构。我的输入是一个包含大约 1,000,000 个句子和 2,000 个单词标记的词汇表的文本文件。通常,当我构建图像识别网络时,我的输入层将如下所示:

l_in = nn.layers.InputLayer((32, 3, 128, 128))

(其中尺寸是批量大小、通道、高度和宽度)这很方便,因为所有图像的大小都相同,所以我可以批量处理它们。由于我的 LSTM 网络中的每个实例都有不同的句子长度,我有一个如下所示的输入层:

l_in = nn.layers.InputLayer((None, None, 2000))

如上面引用的博客文章所述,

掩码
因为并非每个小批量中的所有序列都始终具有相同的长度,所以千层面中的所有循环层都接受一个单独的掩码输入,其形状为 (batch_size, n_time_steps) ,其填充使得掩码 [i, j] = 1 当 j <= (序列 i 的长度) 和 mask[i, j] = 0 当 j > (序列 i 的长度) 。当没有提供掩码时,假设小批量中的所有序列的长度为 n_time_steps。

我的问题是:有没有办法在使用掩码的情况下小批量处理这种类型的网络?


这是我的网络的简化版本。

# -*- coding: utf-8 -*-

import theano
import theano.tensor as T
import lasagne as nn

softmax = nn.nonlinearities.softmax

def build_model():
    l_in  = nn.layers.InputLayer((None, None, 2000))
    lstm  = nn.layers.LSTMLayer(l_in, 4096, grad_clipping=5)
    rs    = nn.layers.SliceLayer(lstm, 0, 0)
    dense = nn.layers.DenseLayer(rs, num_units=2000, nonlinearity=softmax)
    return l_in, dense

model = build_model()
l_in, l_out = model

all_params = nn.layers.get_all_params(l_out)
target_var = T.ivector("target_output")

output = nn.layers.get_output(l_out)
loss = T.nnet.categorical_crossentropy(output, target_var).sum()
updates = nn.updates.adagrad(loss, all_params, 0.005)

train = theano.function([l_in.input_var, target_var], cost, updates=updates)

从那里我有生成(X, y)对的生成器,我正在计算train(X, y)和更新每次迭代的梯度。我想做的是做N个训练步骤,然后用平均梯度更新参数。

为此,我尝试创建一个compute_gradient函数:

gradient = theano.grad(loss, all_params)

compute_gradient = theano.function(
    [l_in.input_var, target_var],
    output=gradient
  )

然后循环几个训练实例以创建一个“批处理”并将梯度计算收集到一个列表中:

grads = []
for _ in xrange(1024):
    X, y = train_gen.next()  # generator for producing training data
    grads.append(compute_gradient(X, y))

这会产生一个列表列表

>>> grads
[[<CudaNdarray at 0x7f83b5ff6d70>,
<CudaNdarray at 0x7f83b5ff69f0>,
<CudaNdarray at 0x7f83b5ff6270>,
<CudaNdarray at 0x7f83b5fc05f0>],
[<CudaNdarray at 0x7f83b5ff66f0>,
<CudaNdarray at 0x7f83b5ff6730>,
<CudaNdarray at 0x7f83b5ff6b70>,
<CudaNdarray at 0x7f83b5ff64f0>] ...

从这里我需要在每一层取梯度的平均值,然后更新模型参数。这可以像这样分批完成,梯度计算/参数更新是否需要在一个 theano 函数中全部发生?

谢谢。

4

1 回答 1

0

注意:这是一个解决方案,但我没有足够的经验来验证它的最佳效果,代码只是一个草率的例子

您需要 2 个 theano 函数。第一个是你似乎已经从你的问题中提供的信息判断的毕业生。

因此,在计算了批处理梯度后,您希望立即将它们作为输入参数反馈到另一个专用于更新共享变量的 theano 函数中。为此,您需要在神经网络的编译时指定预期的批量大小。所以你可以做这样的事情:(为简单起见,我假设你有一个全局列表变量,所有参数都存储在其中)

params #list of params you wish to update
BATCH_SIZE = 1024 #size of the expected training batch
G = [T.matrix() for i in range(BATCH_SIZE) for param in params] #placeholder for grads result flattened so they can be fed into a theano function

updates = [G[i] for i in range(len(params))] #starting with list of  param updates from first batch

for i in range(len(params)): #summing the gradients for each individual param
     for j in range(1, len(G)/len(params)):
         updates[i] += G[i*BATCH_SIZE + j]

for i in range(len(params)): #making a list of tuples for theano.function updates argument
     updates[i] = (params[i], updates[i]/BATCH_SIZE) 
update = theano.function([G], 0, updates=updates)

像这样,theano 将像往常一样采用梯度的平均值并更新参数

不知道你是否需要像我一样扁平化输入,但可能

编辑:从您编辑问题的方式收集,批次大小可以变化似乎很重要,在这种情况下,您可以将 2 个 theano 函数添加到现有的函数中:

  1. 第一个 theano 函数需要一批大小为 2 的参数并返回总和。你可以使用 python 的 reduce() 应用这个 theano 函数并获得整个批次梯度的总和
  2. 第二个 theano 函数将这些求和的参数梯度和一个缩放器(批量大小)作为输入,因此能够在求和梯度的平均值上更新 NN 参数。
于 2016-02-05T17:11:29.170 回答