RNN 代码的 luajit/lua5.1/lua5.2/lua5.3 内存问题

Posted

技术标签:

【中文标题】RNN 代码的 luajit/lua5.1/lua5.2/lua5.3 内存问题【英文标题】:luajit/lua5.1/lua5.2/lua5.3 memory issue for RNN code 【发布时间】:2016-11-26 03:51:39 【问题描述】:

我一直在运行一段代码,train.lua,在这里找到:https://github.com/karpathy/char-rnn/blob/master/train.lua

这是一个基于 SRNN/LSTM 的字符级语言预测模型。在我尝试实现逐字预测模型之前,它在带有 CPU 的 OSX 上一直运行良好。也就是说,网络预测下一个单词,而不是下一个字母表。词汇数量(可能的结果)上升到 13320,参数数量也增加到 39963。使用 Luajit,我收到一条错误消息“内存不足”,我正在四处寻找解决方案。我在这里发现了 Luajit 内存限制的问题:https://github.com/karpathy/char-rnn/issues/80

所以我移除了 torch 并安装了普通的 lua。但是,LUA51、LUA52 和 LUA53 都不起作用。我遇到了同样的内存问题。每次我运行训练代码时,它都会显示“Kill:9”。特别是,当我使用 util/model_utils.lua 文件中的“model_utils.clone_many_times”函数创建共享相同权重的 T(序列长度或时间步长)隐藏层时,就会出现问题。

在我的例子中,该函数运行到它克隆 7 个隐藏层,并终止那里的进程。我将 rnn_size 和 batch_size 都设置为 1。当然,我想运行更大的网络,但是代码仍然会因为这个小尺寸而失败。

更新: 这是我正在研究的解决方法。

克隆过程似乎有些多余,因为它存储了 T 个隐藏层。也许我们可以改变这个函数,让它只在单元中携带激活,而不是通过 T 个时间步长携带整个层。我觉得唯一的问题是反向传播。隐藏单元的激活级别由表init_state_global逐批传递。所以我们需要在多个批次上建立反向传播。

【问题讨论】:

它到底停在哪一步?你能明确哪个函数调用不起作用吗? Lua 5.3 使用更少的内存。试试看。 【参考方案1】:

这是我找到的解决方法。其他一切都相同,我得到的结果几乎与原始结果相同,只是由于某种原因出现了一些浮点精度错误。它节省了内存(seq_length 甚至不影响内存大小)。我将“model_utils.clone_many_times”函数中的克隆数设置为 1(所以我们可能甚至不再需要这个消耗内存的函数),并且只存储隐藏单元激活用于反向传播。

function feval(x)
if x ~= params then
    params:copy(x)
end
grad_params:zero()

------------------ get minibatch -------------------
local x, y = loader:next_batch(1)
x,y = prepro(x,y) -- seq_length by batch_size tensor
------------------- forward pass -------------------
local rnn_state = [0] = init_state_global
local predictions =            -- softmax outputs
local loss = 0
local hidden_units = 

for t=1,opt.seq_length do
    clones.rnn[1]:training() -- make sure we are in correct mode (this is cheap, sets flag)
    local lst = clones.rnn[1]:forwardx[t], unpack(rnn_state[t-1])
    rnn_state[t] = 
    for i=1,#init_state do table.insert(rnn_state[t], lst[i]) end -- extract the state, without output
    hidden_units[t] = 
    local j = 1
    for k = 1, #clones.rnn[1].modules do
         if clones.rnn[1].modules[k].output then
             if not (type(clones.rnn[1].modules[k].output) == 'table') then
                hidden_units[t][j] = clones.rnn[1].modules[k].output:clone() 
            else
                hidden_units[t][j] = 
                for l=1, #clones.rnn[1].modules[k].output do
                    hidden_units[t][j][l] = clones.rnn[1].modules[k].output[l]:clone() 
                end
            end
            j = j+1

         end
    end

    predictions[t] = lst[#lst] -- last element is the prediction
    loss = loss + clones.criterion[1]:forward(predictions[t], y[t])
end
loss = loss / opt.seq_length

------------------ backward pass -------------------
-- initialize gradient at time t to be zeros (there's no influence from future)
local drnn_state = [opt.seq_length] = clone_list(init_state, true) -- true also zeros the clones
for t=opt.seq_length,1,-1 do
    -- backprop through loss, and softmax/linear
    local j = 1

    for k = 1, #clones.rnn[1].modules do
         if clones.rnn[1].modules[k].output then
            clones.rnn[1].modules[k].output = hidden_units[t][j]
            j = j+1
         end
    end

    local doutput_t = clones.criterion[1]:backward(predictions[t], y[t])
    table.insert(drnn_state[t], doutput_t)
    local dlst = clones.rnn[1]:backward(x[t], unpack(rnn_state[t-1]), drnn_state[t])
    drnn_state[t-1] = 
    for k,v in pairs(dlst) do
         for k=1, #clones.rnn[1].modules[k].output do
                hidden_units[t][j][k] = clones.rnn[1].modules[k].output:clone() 
                end
            end
            j = j+1

         end
    end

    predictions[t] = lst[#lst] -- last element is the prediction
    loss = loss + clones.criterion[1]:forward(predictions[t], y[t])
end
loss = loss / opt.seq_length
------------------ backward pass -------------------
-- initialize gradient at time t to be zeros (there's no influence from future)
local drnn_state = [opt.seq_length] = clone_list(init_state, true) -- true also zeros the clones
for t=opt.seq_length,1,-1 do
    -- backprop through loss, and softmax/linear
    local j = 1

    for k = 1, #clones.rnn[1].modules do
         if clones.rnn[1].modules[k].output then
            clones.rnn[1].modules[k].output = hidden_units[t][j]
            j = j+1
         end
    end

    local doutput_t = clones.criterion[1]:backward(predictions[t], y[t])
    table.insert(drnn_state[t], doutput_t)
    local dlst = clones.rnn[1]:backward(x[t], unpack(rnn_state[t-1]), drnn_state[t])
    drnn_state[t-1] = 
           for k = 1, #clones.rnn[1].modules do
         if clones.rnn[1].modules[k].output then
            clones.rnn[1].modules[k].output = hidden_units[t][j]
            j = j+1
         end
    end

    local doutput_t = clones.criterion[1]:backward(predictions[t], y[t])
    table.insert(drnn_state[t], doutput_t)
    local dlst = clones.rnn[1]:backward(x[t], unpack(rnn_state[t-1]), drnn_state[t])
    drnn_state[t-1] = 
    for k,v in pairs(dlst) do
        if k > 1 then -- k == 1 is gradient on x, which we dont need
            -- note we do k-1 because first item is dembeddings, and then follow the 
            -- derivatives of the state, starting at index 2. I know...
            drnn_state[t-1][k-1] = v
        end
    end
end
------------------------ misc ----------------------
-- transfer final state to initial state (BPTT)
init_state_global = rnn_state[#rnn_state] -- NOTE: I don't think this needs to be a clone, right?
-- grad_params:div(opt.seq_length) -- this line should be here but since we use rmsprop it would have no effect. Removing for efficiency
-- clip gradient element-wise
--Lets not clip gradient this time grad_params:clamp(-opt.grad_clip, opt.grad_clip)
return loss, grad_params
end

【讨论】:

以上是关于RNN 代码的 luajit/lua5.1/lua5.2/lua5.3 内存问题的主要内容,如果未能解决你的问题,请参考以下文章

是否使用 nn.RNN 的代码差异

深度学习原理与框架-递归神经网络-RNN_exmaple(代码) 1.rnn.BasicLSTMCell(构造基本网络) 2.tf.nn.dynamic_rnn(执行rnn网络) 3.tf.expa

[Pytorch系列-51]:循环神经网络RNN - torch.nn.RNN类的参数详解与代码示例

character-RNN模型介绍以及代码解析

深度学习之六,基于RNN(GRU,LSTM)的语言模型分析与theano代码实现

Keras 函数(K.function)不适用于 RNN(提供的代码)