Lua Torch7 и OpenResty: попытка проиндексировать нулевое значение

У меня есть Lusty (фреймворк для OpenResty) API, который обертывает классификатор Torch. До сих пор мне удавалось заставить работать один запрос, однако каждый последующий запрос к API вызывает следующую ошибку без подробной трассировки стека:

attempt to index a nil value

Ошибка появляется, когда я звоню:

net:add(SpatialConvolution(3, 96, 7, 7, 2, 2))

Поведение успешного завершения 1-го запроса при сбое с каждым дополнительным запросом является ключом к проблеме.

Я вставил полный код ниже для app/requests/classify.lua. Похоже, это какая-то проблема с кэшированием/инициализацией переменных, хотя мои ограниченные знания Lua не помогают мне решить проблему. Я пытался сделать несколько вещей, в том числе изменить мой импорт на локализованные переменные, такие как local torch = require('torch'), а также переместить этот импорт внутрь функции classifyImage().

torch = require 'torch'
nn = require 'nn'
image = require 'image'
ParamBank = require 'ParamBank'
label     = require 'classifier_label'
torch.setdefaulttensortype('torch.FloatTensor')

function classifyImage()

  local opt = {
    inplace = false,
    network = "big",
    backend = "nn",
    save = "model.t7",
    img = context.input.image,
    spatial = false,
    threads = 4
  }
  torch.setnumthreads(opt.threads)

  require(opt.backend)
  local SpatialConvolution = nn.SpatialConvolutionMM
  local SpatialMaxPooling = nn.SpatialMaxPooling
  local ReLU = nn.ReLU
  local SpatialSoftMax = nn.SpatialSoftMax

  local net = nn.Sequential()

  print('==> init a big overfeat network')
  net:add(SpatialConvolution(3, 96, 7, 7, 2, 2))
  net:add(ReLU(opt.inplace))
  net:add(SpatialMaxPooling(3, 3, 3, 3))
  net:add(SpatialConvolution(96, 256, 7, 7, 1, 1))
  net:add(ReLU(opt.inplace))
  net:add(SpatialMaxPooling(2, 2, 2, 2))
  net:add(SpatialConvolution(256, 512, 3, 3, 1, 1, 1, 1))
  net:add(ReLU(opt.inplace))
  net:add(SpatialConvolution(512, 512, 3, 3, 1, 1, 1, 1))
  net:add(ReLU(opt.inplace))
  net:add(SpatialConvolution(512, 1024, 3, 3, 1, 1, 1, 1))
  net:add(ReLU(opt.inplace))
  net:add(SpatialConvolution(1024, 1024, 3, 3, 1, 1, 1, 1))
  net:add(ReLU(opt.inplace))
  net:add(SpatialMaxPooling(3, 3, 3, 3))
  net:add(SpatialConvolution(1024, 4096, 5, 5, 1, 1))
  net:add(ReLU(opt.inplace))
  net:add(SpatialConvolution(4096, 4096, 1, 1, 1, 1))
  net:add(ReLU(opt.inplace))
  net:add(SpatialConvolution(4096, 1000, 1, 1, 1, 1))
  net:add(nn.View(1000))
  net:add(SpatialSoftMax())
  -- print(net)

  -- init file pointer
  print('==> overwrite network parameters with pre-trained weigts')
  ParamBank:init("net_weight_1")
  ParamBank:read(        0, {96,3,7,7},      net:get(1).weight)
  ParamBank:read(    14112, {96},            net:get(1).bias)
  ParamBank:read(    14208, {256,96,7,7},    net:get(4).weight)
  ParamBank:read(  1218432, {256},           net:get(4).bias)
  ParamBank:read(  1218688, {512,256,3,3},   net:get(7).weight)
  ParamBank:read(  2398336, {512},           net:get(7).bias)
  ParamBank:read(  2398848, {512,512,3,3},   net:get(9).weight)
  ParamBank:read(  4758144, {512},           net:get(9).bias)
  ParamBank:read(  4758656, {1024,512,3,3},  net:get(11).weight)
  ParamBank:read(  9477248, {1024},          net:get(11).bias)
  ParamBank:read(  9478272, {1024,1024,3,3}, net:get(13).weight)
  ParamBank:read( 18915456, {1024},          net:get(13).bias)
  ParamBank:read( 18916480, {4096,1024,5,5}, net:get(16).weight)
  ParamBank:read(123774080, {4096},          net:get(16).bias)
  ParamBank:read(123778176, {4096,4096,1,1}, net:get(18).weight)
  ParamBank:read(140555392, {4096},          net:get(18).bias)
  ParamBank:read(140559488, {1000,4096,1,1}, net:get(20).weight)
  ParamBank:read(144655488, {1000},          net:get(20).bias)

  ParamBank:close()

  -- load and preprocess image
  print('==> prepare an input image')
  local img = image.load(opt.img):mul(255)

  -- use image larger than the eye size in spatial mode
  if not opt.spatial then
     local dim = (opt.network == 'small') and 231 or 221
     local img_scale = image.scale(img, '^'..dim)
     local h = math.ceil((img_scale:size(2) - dim)/2)
     local w = math.ceil((img_scale:size(3) - dim)/2)
     img = image.crop(img_scale, w, h, w + dim, h + dim):floor()
  end

  -- memcpy from system RAM to GPU RAM if cuda enabled
  if opt.backend == 'cunn' or opt.backend == 'cudnn' then
    net:cuda()
    img = img:cuda()
  end

  -- save bare network (before its buffer filled with temp results)
  print('==> save model to:', opt.save)
  torch.save(opt.save, net)

  -- feedforward network
  print('==> feed the input image')
  timer = torch.Timer()
  img:add(-118.380948):div(61.896913)
  local out = net:forward(img)

  -- find output class name in non-spatial mode
  local results = {}
  local topN = 10
  local probs, idxs = torch.topk(out, topN, 1, true)

  for i=1,topN do
     print(label[idxs[i]], probs[i])
     local r = {}
     r.label = label[idxs[i]]
     r.prob = probs[i]
     results[i] = r
  end

  return results
end

function errorHandler(err)
  return tostring( err )
end

local success, result = xpcall(classifyImage, errorHandler)


context.template = {
  type = "mustache",
  name = "app/templates/layout",

  partials = {
    content = "app/templates/classify",
  }
}


context.output = {
  success = success,
  result = result,
  request = context.input
}

context.response.status = 200

Ценю твою помощь!

Обновление 1

Добавлено print( net ) до и после local net, а также после звонка net:add. Каждый раз перед инициализацией local net отображается значение nil. Как и ожидалось, после инициализации net в качестве значения отображается объект факела. Кажется, что-то внутри вызова :add создает ошибку, поэтому я добавил следующее сразу после объявления моей функции classifyImage:

print(tostring(torch))
print(tostring(nn))
print(tostring(net))

После добавления этих новых операторов печати я получаю следующее по первому запросу:

nil
nil
nil

И затем по второму запросу:

table: 0x41448a08
table: 0x413bdb10
nil

И по третьему запросу:

table: 0x41448a08
table: 0x413bdb10
nil

Они выглядят как указатели на объект в памяти, поэтому можно ли предположить, что Torch создает свой собственный глобальный объект?


person crockpotveggies    schedule 15.08.2016    source источник
comment
Попробуйте поставить print(net) до и после звонков.   -  person hjpotter92    schedule 15.08.2016
comment
Сделано и добавление деталей в вопрос в ближайшее время. По сути, прежде чем я объявляю local net в обоих 1-м и 2-м вызовах, я успешно получаю nil. После инициализации net я также получаю новый объект. Только когда я вызываю add, он терпит неудачу. Вы думаете, это что-то связанное с самим torch или nn?   -  person crockpotveggies    schedule 15.08.2016
comment
@hjpotter92 добавил дополнительную информацию, похоже, что torch сам создает глобальные объекты в памяти, которые мешают коду?   -  person crockpotveggies    schedule 15.08.2016
comment
Нашел исправление, публикую ответ...   -  person crockpotveggies    schedule 15.08.2016


Ответы (1)


Когда требуются torch и его модули, в конечном итоге создается глобальный экземпляр самого себя, который остается в памяти на протяжении всего жизненного цикла процесса. Исправление, которое сработало для меня, заключалось в том, чтобы сослаться на Torch в основном файле app.lua в Lusty и вставить следующее вверху:

require 'torch'
require 'nn'

image = require 'image'
ParamBank = require 'ParamBank'
label     = require 'classifier_label'
torch.setdefaulttensortype('torch.FloatTensor')
torch.setnumthreads(4)

SpatialConvolution = nn.SpatialConvolutionMM
SpatialMaxPooling = nn.SpatialMaxPooling
ReLU = nn.ReLU
SpatialSoftMax = nn.SpatialSoftMax

Переменные находятся в области действия classifyImage, и теперь каждый запрос выполняется успешно. Это грязное исправление, но, поскольку Torch поддерживает свои собственные глобальные объекты, я не вижу способа обойти это.

person crockpotveggies    schedule 15.08.2016