这是我找到的解决方法。其他一切都相同,我得到的结果几乎与原始结果相同,只是由于某种原因出现了一些浮点精度错误。它节省了内存(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]:forward{x[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