require 'image'
require 'pl'
-----------------------------------------------------------------------
-
-local opt = lapp[[
- --seed (default 1) random seed
-
- --learningStateFile (default '')
- --dataDir (default './data/10p-mg/')
- --resultDir (default '/tmp/dyncnn')
-
- --learningRate (default -1)
- --momentum (default -1)
- --nbEpochs (default -1) nb of epochs for the heavy setting
-
- --heavy use the heavy configuration
- --nbChannels (default -1) nb of channels in the internal layers
- --resultFreq (default 100)
-
- --noLog supress logging
-
- --exampleInternals (default -1)
-]]
+require 'img'
----------------------------------------------------------------------
-commandLine=''
-for i = 0, #arg do
- commandLine = commandLine .. ' \'' .. arg[i] .. '\''
+function printf(f, ...)
+ print(string.format(f, unpack({...})))
end
-----------------------------------------------------------------------
-
colors = sys.COLORS
-global = {}
-
-function logString(s, c)
- if global.logFile then
- global.logFile:write(s)
- global.logFile:flush()
- end
- local c = c or colors.black
- io.write(c .. s)
- io.flush()
+function printfc(c, f, ...)
+ printf(c .. string.format(f, unpack({...})) .. colors.black)
end
function logCommand(c)
- logString('[' .. c .. '] -> [' .. sys.execute(c) .. ']\n', colors.blue)
-end
-
-logString('commandline: ' .. commandLine .. '\n', colors.blue)
-
-logCommand('mkdir -v -p ' .. opt.resultDir)
-
-if not opt.noLog then
- global.logName = opt.resultDir .. '/log'
- global.logFile = io.open(global.logName, 'a')
+ print(colors.blue .. '[' .. c .. '] -> [' .. sys.execute(c) .. ']' .. colors.black)
end
----------------------------------------------------------------------
+-- Environment and command line arguments
-alreadyLoggedString = {}
+local defaultNbThreads = 1
+local defaultUseGPU = false
-function logOnce(s)
- local l = debug.getinfo(1).currentline
- if not alreadyLoggedString[l] then
- logString('@line ' .. l .. ' ' .. s, colors.red)
- alreadyLoggedString[l] = s
- end
+if os.getenv('TORCH_NB_THREADS') then
+ defaultNbThreads = os.getenv('TORCH_NB_THREADS')
+ print('Environment variable TORCH_NB_THREADS is set and equal to ' .. defaultNbThreads)
+else
+ print('Environment variable TORCH_NB_THREADS is not set')
end
-----------------------------------------------------------------------
-
-nbThreads = os.getenv('TORCH_NB_THREADS') or 1
-
-useGPU = os.getenv('TORCH_USE_GPU') == 'yes'
-
-for _, c in pairs({ 'date',
- 'uname -a',
- 'git log -1 --format=%H'
- })
-do
- logCommand(c)
+if os.getenv('TORCH_USE_GPU') then
+ defaultUseGPU = os.getenv('TORCH_USE_GPU') == 'yes'
+ print('Environment variable TORCH_USE_GPU is set and evaluated as ' .. tostring(defaultUseGPU))
+else
+ print('Environment variable TORCH_USE_GPU is not set.')
end
-logString('useGPU is \'' .. tostring(useGPU) .. '\'.\n')
-
-logString('nbThreads is \'' .. nbThreads .. '\'.\n')
-
----------------------------------------------------------------------
-torch.setnumthreads(nbThreads)
-torch.setdefaulttensortype('torch.FloatTensor')
-torch.manualSeed(opt.seed)
+local cmd = torch.CmdLine()
-mynn = {}
+cmd:text('')
+cmd:text('General setup')
--- To deal elegantly with CPU/GPU
-local mt = {}
-function mt.__index(table, key)
- return (cudnn and cudnn[key]) or (cunn and cunn[key]) or nn[key]
-end
-setmetatable(mynn, mt)
+cmd:option('-seed', 1, 'initial random seed')
+cmd:option('-nbThreads', defaultNbThreads, 'how many threads (environment variable TORCH_NB_THREADS)')
+cmd:option('-useGPU', defaultUseGPU, 'should we use cuda (environment variable TORCH_USE_GPU)')
--- These are the tensors that can be kept on the CPU
-mynn.SlowTensor = torch.Tensor
--- These are the tensors that should be moved to the GPU
-mynn.FastTensor = torch.Tensor
+cmd:text('')
+cmd:text('Log')
-----------------------------------------------------------------------
+cmd:option('-resultFreq', 100, 'at which epoch frequency should we save result images')
+cmd:option('-exampleInternals', -1, 'should we save inner activation images')
+cmd:option('-noLog', false, 'should we prevent logging')
+cmd:option('-rundir', '', 'the directory for results')
-if useGPU then
- require 'cutorch'
- require 'cunn'
- require 'cudnn'
+cmd:text('')
+cmd:text('Training')
- mynn.FastTensor = torch.CudaTensor
+cmd:option('-nbEpochs', 1000, 'nb of epochs for the heavy setting')
+cmd:option('-learningRate', 0.1, 'learning rate')
+cmd:option('-batchSize', 128, 'size of the mini-batches')
+cmd:option('-filterSize', 5, 'convolution filter size')
+cmd:option('-nbTrainSamples', 32768)
+cmd:option('-nbValidationSamples', 1024)
+cmd:option('-nbTestSamples', 1024)
- if cudnn then
- cudnn.benchmark = true
- cudnn.fastest = true
- end
-end
+cmd:text('')
+cmd:text('Problem to solve')
-----------------------------------------------------------------------
+cmd:option('-dataDir', './data/10p-mg', 'data directory')
-config = {}
-config.learningRate = 0.1
-config.momentum = 0
-config.batchSize = 128
-config.filterSize = 5
+cmd:text('')
+cmd:text('Network structure')
-if opt.heavy then
+cmd:option('-nbChannels', 16)
+cmd:option('-nbBlocks', 8)
- logString('Using the heavy configuration.\n')
- config.nbChannels = 16
- config.nbBlocks = 4
- config.nbEpochs = 250
- config.nbEpochsInit = 100
- config.nbTrainSamples = 32768
- config.nbValidationSamples = 1024
- config.nbTestSamples = 1024
+------------------------------
+-- Log and stuff
-else
+cmd:addTime('DYNCNN','%F %T')
- logString('Using the light configuration.\n')
- config.nbChannels = 2
- config.nbBlocks = 2
- config.nbEpochs = 6
- config.nbEpochsInit = 3
- config.nbTrainSamples = 1024
- config.nbValidationSamples = 1024
- config.nbTestSamples = 1024
+params = cmd:parse(arg)
+if params.rundir == '' then
+ params.rundir = cmd:string('exp', params, { })
end
-if opt.nbEpochs > 0 then
- config.nbEpochs = opt.nbEpochs
-end
+paths.mkdir(params.rundir)
-if opt.nbChannels > 0 then
- config.nbChannels = opt.nbChannels
+if not params.noLog then
+ -- Append to the log if there is one
+ cmd:log(io.open(params.rundir .. '/log', 'a'), params)
end
-if opt.learningRate > 0 then
- config.learningRate = opt.learningRate
-end
+----------------------------------------------------------------------
+-- The experiment per se
-if opt.momentum >= 0 then
- config.momentum = opt.momentum
+if params.predictGrasp then
+ params.targetDepth = 2
+else
+ params.targetDepth = 1
end
----------------------------------------------------------------------
+-- Initializations
-function tensorCensus(tensorType, model)
+torch.setnumthreads(params.nbThreads)
+torch.setdefaulttensortype('torch.FloatTensor')
+torch.manualSeed(params.seed)
- local nb = {}
+----------------------------------------------------------------------
+-- Dealing with the CPU/GPU
- local function countThings(m)
- for k, i in pairs(m) do
- if torch.type(i) == tensorType then
- nb[k] = (nb[k] or 0) + i:nElement()
- end
- end
- end
+-- mynn will take entries in that order: mynn, cudnn, cunn, nn
- model:apply(countThings)
+mynn = {}
- return nb
+setmetatable(mynn,
+ {
+ __index = function(table, key)
+ return (cudnn and cudnn[key]) or (cunn and cunn[key]) or nn[key]
+ end
+ }
+)
+-- These are the tensors that can be kept on the CPU
+mynn.SlowTensor = torch.Tensor
+
+-- These are the tensors that should be moved to the GPU
+mynn.FastTensor = torch.Tensor
+
+if params.useGPU then
+ require 'cutorch'
+ require 'cunn'
+ require 'cudnn'
+ cudnn.benchmark = true
+ cudnn.fastest = true
+ mynn.FastTensor = torch.CudaTensor
end
----------------------------------------------------------------------
function loadData(first, nb, name)
- logString('Loading data `' .. name .. '\'.\n')
-
- local persistentFileName = string.format('%s/persistent_%d_%d.dat',
- opt.dataDir,
- first,
- nb)
-
- -- This is at what framerate we work. It is greater than 1 so that
- -- we can keep on disk sequences at a higher frame rate for videos
- -- and explaining materials
-
- local frameRate = 4
-
- local data
-
- if not path.exists(persistentFileName) then
- logString(string.format('No persistent data structure, creating it (%d samples).\n', nb))
- local data = {}
- data.name = name
- data.nbSamples = nb
- data.width = 64
- data.height = 64
- data.input = mynn.SlowTensor(data.nbSamples, 2, data.height, data.width)
- data.target = mynn.SlowTensor(data.nbSamples, 1, data.height, data.width)
-
- for i = 1, data.nbSamples do
- local n = i-1 + first-1
- local prefix = string.format('%s/%03d/dyn_%06d',
- opt.dataDir,
- math.floor(n/1000), n)
-
- function localLoad(filename, tensor)
- local tmp
- tmp = image.load(filename)
- tmp:mul(-1.0):add(1.0)
- tensor:copy(torch.max(tmp, 1))
- end
+ print('Loading data `' .. name .. '\'.')
- localLoad(prefix .. '_world_000.png', data.input[i][1])
- localLoad(prefix .. '_grab.png', data.input[i][2])
- localLoad(string.format('%s_world_%03d.png', prefix, frameRate),
- data.target[i][1])
- end
+ local data = {}
- data.persistentFileName = persistentFileName
+ data.name = name
+ data.nbSamples = nb
+ data.width = 64
+ data.height = 64
- torch.save(persistentFileName, data)
- end
+ data.input = mynn.SlowTensor(data.nbSamples, 2, data.height, data.width)
+ data.target = mynn.SlowTensor(data.nbSamples, 1, data.height, data.width)
- logCommand('sha256sum -b ' .. persistentFileName)
+ for i = 1, data.nbSamples do
+ local n = i-1 + first-1
+ local frame = image.load(string.format('%s/%03d/dyn_%06d.png',
+ params.dataDir,
+ math.floor(n/1000), n))
- data = torch.load(persistentFileName)
+ frame:mul(-1.0):add(1.0)
+ frame = frame:max(1):select(1, 1)
- return data
-end
+ data.input[i][1]:copy(frame:sub(0 * data.height + 1, 1 * data.height,
+ 1 * data.width + 1, 2 * data.width))
-----------------------------------------------------------------------
+ data.input[i][2]:copy(frame:sub(0 * data.height + 1, 1 * data.height,
+ 0 * data.width + 1, 1 * data.width))
--- This function gets as input a list of tensors of arbitrary
--- dimensions each, but whose two last dimension stands for height x
--- width. It creates an image tensor (2d, one channel) with each
--- argument tensor unfolded per row.
-
-function imageFromTensors(bt, signed)
- local gap = 1
- local tgap = -1
- local width = 0
- local height = gap
-
- for _, t in pairs(bt) do
- -- print(t:size())
- local d = t:dim()
- local h, w = t:size(d - 1), t:size(d)
- local n = t:nElement() / (w * h)
- width = math.max(width, gap + n * (gap + w))
- height = height + gap + tgap + gap + h
+ data.target[i][1]:copy(frame:sub(1 * data.height + 1, 2 * data.height,
+ 1 * data.width + 1, 2 * data.width))
end
- local e = torch.Tensor(3, height, width):fill(1.0)
- local y0 = 1 + gap
-
- for _, t in pairs(bt) do
- local d = t:dim()
- local h, w = t:size(d - 1), t:size(d)
- local n = t:nElement() / (w * h)
- local z = t:norm() / math.sqrt(t:nElement())
-
- local x0 = 1 + gap + math.floor( (width - n * (w + gap)) /2 )
- local u = torch.Tensor(t:size()):copy(t):resize(n, h, w)
- for m = 1, n do
-
- for c = 1, 3 do
- for y = 0, h+1 do
- e[c][y0 + y - 1][x0 - 1] = 0.0
- e[c][y0 + y - 1][x0 + w ] = 0.0
- end
- for x = 0, w+1 do
- e[c][y0 - 1][x0 + x - 1] = 0.0
- e[c][y0 + h ][x0 + x - 1] = 0.0
- end
- end
-
- for y = 1, h do
- for x = 1, w do
- local v = u[m][y][x] / z
- local r, g, b
- if signed then
- if v < -1 then
- r, g, b = 0.0, 0.0, 1.0
- elseif v > 1 then
- r, g, b = 1.0, 0.0, 0.0
- elseif v >= 0 then
- r, g, b = 1.0, 1.0 - v, 1.0 - v
- else
- r, g, b = 1.0 + v, 1.0 + v, 1.0
- end
- else
- if v <= 0 then
- r, g, b = 1.0, 1.0, 1.0
- elseif v > 1 then
- r, g, b = 0.0, 0.0, 0.0
- else
- r, g, b = 1.0 - v, 1.0 - v, 1.0 - v
- end
- end
- e[1][y0 + y - 1][x0 + x - 1] = r
- e[2][y0 + y - 1][x0 + x - 1] = g
- e[3][y0 + y - 1][x0 + x - 1] = b
- end
- end
- x0 = x0 + w + gap
- end
- y0 = y0 + h + gap + tgap + gap
- end
-
- return e
+ return data
end
+----------------------------------------------------------------------
+
function collectAllOutputs(model, collection, which)
if torch.type(model) == 'nn.Sequential' then
for i = 1, #model.modules do
collectAllOutputs(model.modules[i], collection, which)
end
elseif not which or which[torch.type(model)] then
- local t = torch.type(model.output)
- if t == 'torch.FloatTensor' or t == 'torch.CudaTensor' then
+ if torch.isTensor(model.output) then
collection.nb = collection.nb + 1
collection.outputs[collection.nb] = model.output
end
collection.nb = 1
collection.outputs[collection.nb] = input
- local which = {}
- which['nn.ReLU'] = true
- collectAllOutputs(model, collection, which)
+ collectAllOutputs(model, collection,
+ {
+ ['nn.ReLU'] = true,
+ ['cunn.ReLU'] = true,
+ ['cudnn.ReLU'] = true,
+ }
+ )
if collection.outputs[collection.nb] ~= model.output then
collection.nb = collection.nb + 1
end
local fileName = string.format('%s/internals_%s_%06d.png',
- opt.resultDir,
+ params.rundir,
data.name, n)
- logString('Saving ' .. fileName .. '\n')
+ print('Saving ' .. fileName)
image.save(fileName, imageFromTensors(collection.outputs))
end
----------------------------------------------------------------------
-function saveResultImage(model, data, prefix, nbMax, highlight)
- local l2criterion = nn.MSECriterion()
+function saveResultImage(model, data, nbMax)
+ local criterion = nn.MSECriterion()
- if useGPU then
- logString('Moving the criterion to the GPU.\n')
- l2criterion:cuda()
+ if params.useGPU then
+ print('Moving the criterion to the GPU.')
+ criterion:cuda()
end
- local prefix = prefix or 'result'
- local result = torch.Tensor(data.height * 4 + 5, data.width + 2)
local input = mynn.FastTensor(1, 2, data.height, data.width)
local target = mynn.FastTensor(1, 1, data.height, data.width)
model:evaluate()
- logString(string.format('Write %d result images `%s\' for set `%s\' in %s.\n',
- nb, prefix, data.name,
- opt.resultDir))
+ printf('Write %d result images for `%s\'.', nb, data.name)
+
+ local lossFile = io.open(params.rundir .. '/result_' .. data.name .. '_losses.dat', 'w')
for n = 1, nb do
target:copy(data.target:narrow(1, n, 1))
local output = model:forward(input)
+ local loss = criterion:forward(output, target)
+
+ output = mynn.SlowTensor(output:size()):copy(output)
+
+ -- We use our magical img.lua to create the result images
+
+ local comp = {
+ {
+ { pad = 1, data.input[n][1] },
+ { pad = 1, data.input[n][2] },
+ { pad = 1, data.target[n][1] },
+ { pad = 1, output[1][1] },
+ }
+ }
+
+ --[[
+ local comp = {
+ {
+ vertical = true,
+ { pad = 1, data.input[n][1] },
+ { pad = 1, data.input[n][2] }
+ },
+ torch.Tensor(4, 4):fill(1.0),
+ {
+ vertical = true,
+ { pad = 1, data.target[n][1] },
+ { pad = 1, output[1][1] },
+ { pad = 1, torch.csub(data.target[n][1], output[1][1]):abs() }
+ }
+ }
+ ]]--
+
+local result = combineImages(1.0, comp)
+
+result:mul(-1.0):add(1.0)
+
+local fileName = string.format('result_%s_%06d.png', data.name, n)
+image.save(params.rundir .. '/' .. fileName, result)
+lossFile:write(string.format('%f %s\n', loss, fileName))
+end
+end
- local loss = l2criterion:forward(output, target)
-
- result:fill(1.0)
-
- if highlight then
- for i = 1, data.height do
- for j = 1, data.width do
- local v = data.input[n][1][i][j]
- result[1 + i + 0 * (data.height + 1)][1 + j] = data.input[n][2][i][j]
- result[1 + i + 1 * (data.height + 1)][1 + j] = v
- local a = data.target[n][1][i][j]
- local b = output[1][1][i][j]
- result[1 + i + 2 * (data.height + 1)][1 + j] =
- a * math.min(1, 0.1 + 2.0 * math.abs(a - v))
- result[1 + i + 3 * (data.height + 1)][1 + j] =
- b * math.min(1, 0.1 + 2.0 * math.abs(b - v))
- end
- end
- else
- for i = 1, data.height do
- for j = 1, data.width do
- result[1 + i + 0 * (data.height + 1)][1 + j] = data.input[n][2][i][j]
- result[1 + i + 1 * (data.height + 1)][1 + j] = data.input[n][1][i][j]
- result[1 + i + 2 * (data.height + 1)][1 + j] = data.target[n][1][i][j]
- result[1 + i + 3 * (data.height + 1)][1 + j] = output[1][1][i][j]
- end
- end
- end
+----------------------------------------------------------------------
- result:mul(-1.0):add(1.0)
+function createTower(filterSize, nbChannels, nbBlocks)
- local fileName = string.format('%s/%s_%s_%06d.png',
- opt.resultDir,
- prefix,
- data.name, n)
+ local tower
- logString(string.format('LOSS_ON_SAMPLE %f %s\n', loss, fileName))
+ if nbBlocks == 0 then
- image.save(fileName, result)
- end
-end
+ tower = nn.Identity()
-----------------------------------------------------------------------
+ else
-function createTower(filterSize, nbChannels, nbBlocks)
- local tower = mynn.Sequential()
+ tower = mynn.Sequential()
- for b = 1, nbBlocks do
- local block = mynn.Sequential()
+ for b = 1, nbBlocks do
+ local block = mynn.Sequential()
- block:add(mynn.SpatialConvolution(nbChannels,
- nbChannels,
- filterSize, filterSize,
- 1, 1,
- (filterSize - 1) / 2, (filterSize - 1) / 2))
- block:add(mynn.SpatialBatchNormalization(nbChannels))
- block:add(mynn.ReLU(true))
+ block:add(mynn.SpatialConvolution(nbChannels,
+ nbChannels,
+ filterSize, filterSize,
+ 1, 1,
+ (filterSize - 1) / 2, (filterSize - 1) / 2))
+ block:add(mynn.SpatialBatchNormalization(nbChannels))
+ block:add(mynn.ReLU(true))
- block:add(mynn.SpatialConvolution(nbChannels,
- nbChannels,
- filterSize, filterSize,
- 1, 1,
- (filterSize - 1) / 2, (filterSize - 1) / 2))
+ block:add(mynn.SpatialConvolution(nbChannels,
+ nbChannels,
+ filterSize, filterSize,
+ 1, 1,
+ (filterSize - 1) / 2, (filterSize - 1) / 2))
- local parallel = mynn.ConcatTable()
- parallel:add(block):add(mynn.Identity())
+ local parallel = mynn.ConcatTable()
+ parallel:add(block):add(mynn.Identity())
- tower:add(parallel):add(mynn.CAddTable(true))
+ tower:add(parallel):add(mynn.CAddTable(true))
+
+ tower:add(mynn.SpatialBatchNormalization(nbChannels))
+ tower:add(mynn.ReLU(true))
+ end
- tower:add(mynn.SpatialBatchNormalization(nbChannels))
- tower:add(mynn.ReLU(true))
end
return tower
+
end
-function createModel(filterSize, nbChannels, nbBlocks)
+function createModel(imageWidth, imageHeight,
+ filterSize, nbChannels, nbBlocks)
+
local model = mynn.Sequential()
+ -- Encode the two input channels (grasping image and starting
+ -- configuration) into the internal number of channels
model:add(mynn.SpatialConvolution(2,
nbChannels,
filterSize, filterSize,
model:add(mynn.SpatialBatchNormalization(nbChannels))
model:add(mynn.ReLU(true))
- local towerCode = createTower(filterSize, nbChannels, nbBlocks)
- local towerDecode = createTower(filterSize, nbChannels, nbBlocks)
+ -- Add the resnet modules
+ model:add(createTower(filterSize, nbChannels, nbBlocks))
- model:add(towerCode)
- model:add(towerDecode)
-
- -- Decode to a single channel, which is the final image
+ -- Decode down to a single channel, which is the final image
model:add(mynn.SpatialConvolution(nbChannels,
1,
filterSize, filterSize,
----------------------------------------------------------------------
-function fillBatch(data, first, nb, batch, permutation)
- for k = 1, nb do
+function fillBatch(data, first, batch, permutation)
+ local actualBatchSize = math.min(params.batchSize, data.input:size(1) - first + 1)
+
+ if actualBatchSize ~= batch.input:size(1) then
+ local size = batch.input:size()
+ size[1] = actualBatchSize
+ batch.input:resize(size)
+ end
+
+ if actualBatchSize ~= batch.target:size(1) then
+ local size = batch.target:size()
+ size[1] = actualBatchSize
+ batch.target:resize(size)
+ end
+
+ for k = 1, batch.input:size(1) do
local i
if permutation then
i = permutation[first + k - 1]
end
end
-function trainModel(model,
- trainData, validationData, nbEpochs, learningRate,
- learningStateFile)
+function trainModel(model, trainData, validationData)
- local l2criterion = nn.MSECriterion()
- local batchSize = config.batchSize
-
- if useGPU then
- logString('Moving the criterion to the GPU.\n')
- l2criterion:cuda()
- end
+ local criterion = nn.MSECriterion()
+ local batchSize = params.batchSize
local batch = {}
batch.input = mynn.FastTensor(batchSize, 2, trainData.height, trainData.width)
torch.setRNGState(model.RNGState)
end
- logString('Starting training.\n')
+ if params.useGPU then
+ print('Moving the model and criterion to the GPU.')
+ model:cuda()
+ criterion:cuda()
+ end
+
+ print('Starting training.')
local parameters, gradParameters = model:getParameters()
- logString(string.format('model has %d parameters.\n', parameters:storage():size(1)))
+ printf('The model has %d parameters.', parameters:storage():size(1))
local averageTrainLoss, averageValidationLoss
local trainTime, validationTime
+ ----------------------------------------------------------------------
+
local sgdState = {
- learningRate = config.learningRate,
- momentum = config.momentum,
+ learningRate = params.learningRate,
+ momentum = 0,
learningRateDecay = 0
}
- for e = startingEpoch, nbEpochs do
+ for e = startingEpoch, params.nbEpochs do
model:training()
for b = 1, trainData.nbSamples, batchSize do
- fillBatch(trainData, b, batchSize, batch, permutation)
+ fillBatch(trainData, b, batch, permutation)
local opfunc = function(x)
- -- Surprisingly copy() needs this check
+ -- Surprisingly, copy() needs this check
if x ~= parameters then
parameters:copy(x)
end
local output = model:forward(batch.input)
- local loss = l2criterion:forward(output, batch.target)
- local dLossdOutput = l2criterion:backward(output, batch.target)
+ local loss = criterion:forward(output, batch.target)
+ local dLossdOutput = criterion:backward(output, batch.target)
+
gradParameters:zero()
model:backward(batch.input, dLossdOutput)
----------------------------------------------------------------------
-- Validation losses
+
do
model:evaluate()
local startTime = sys.clock()
for b = 1, validationData.nbSamples, batchSize do
- fillBatch(validationData, b, batchSize, batch)
+ fillBatch(validationData, b, batch)
local output = model:forward(batch.input)
- accLoss = accLoss + l2criterion:forward(output, batch.target)
+ accLoss = accLoss + criterion:forward(output, batch.target)
nbBatches = nbBatches + 1
end
averageValidationLoss = accLoss / nbBatches;
end
- logString(string.format('Epoch train %0.2fs (%0.2fms / sample), validation %0.2fs (%0.2fms / sample).\n',
- trainTime,
- 1000 * trainTime / trainData.nbSamples,
- validationTime,
- 1000 * validationTime / validationData.nbSamples))
+ printf('Epoch train %0.2fs (%0.2fms / sample), validation %0.2fs (%0.2fms / sample).',
+ trainTime,
+ 1000 * trainTime / trainData.nbSamples,
+ validationTime,
+ 1000 * validationTime / validationData.nbSamples)
- logString(string.format('LOSS %d %f %f\n', e, averageTrainLoss, averageValidationLoss),
- colors.green)
+ printfc(colors.green, 'LOSS %d %f %f', e, averageTrainLoss, averageValidationLoss)
----------------------------------------------------------------------
-- Save a persistent state so that we can restart from there
- if learningStateFile then
- model.RNGState = torch.getRNGState()
- model.epoch = e
- model:clearState()
- logString('Writing ' .. learningStateFile .. '.\n')
- torch.save(learningStateFile, model)
- end
+ model:clearState()
+ model.RNGState = torch.getRNGState()
+ model.epoch = e
+ torch.save(params.rundir .. '/model_last.t7', model)
----------------------------------------------------------------------
-- Save a duplicate of the persistent state from time to time
- if opt.resultFreq > 0 and e%opt.resultFreq == 0 then
- torch.save(string.format('%s/epoch_%05d_model', opt.resultDir, e), model)
+ if params.resultFreq > 0 and e%params.resultFreq == 0 then
+ torch.save(string.format('%s/model_%04d.t7', params.rundir, e), model)
saveResultImage(model, trainData)
saveResultImage(model, validationData)
end
function createAndTrainModel(trainData, validationData)
- local model
+ -- Load the current training state, or create a new model from
+ -- scratch
- local learningStateFile = opt.learningStateFile
-
- if learningStateFile == '' then
- learningStateFile = opt.resultDir .. '/learning.state'
- end
+ if pcall(function () model = torch.load(params.rundir .. '/model_last.t7') end) then
- local gotlearningStateFile
+ printfc(colors.red,
+ 'Found a learning state with %d epochs finished, starting from there.',
+ model.epoch)
- logString('Using the learning state file ' .. learningStateFile .. '\n')
-
- if pcall(function () model = torch.load(learningStateFile) end) then
-
- gotlearningStateFile = true
-
- else
-
- model = createModel(config.filterSize, config.nbChannels, config.nbBlocks)
-
- if useGPU then
- logString('Moving the model to the GPU.\n')
- model:cuda()
+ if params.exampleInternals > 0 then
+ saveInternalsImage(model, validationData, params.exampleInternals)
+ os.exit(0)
end
- end
+ else
- logString(tostring(model) .. '\n')
+ model = createModel(trainData.width, trainData.height,
+ params.filterSize, params.nbChannels,
+ params.nbBlocks)
- if gotlearningStateFile then
- logString(string.format('Found a learning state with %d epochs finished.\n', model.epoch),
- colors.red)
end
- if opt.exampleInternals > 0 then
- saveInternalsImage(model, validationData, opt.exampleInternals)
- os.exit(0)
- end
-
- trainModel(model,
- trainData, validationData,
- config.nbEpochs, config.learningRate,
- learningStateFile)
+ trainModel(model, trainData, validationData)
return model
end
-for i, j in pairs(config) do
- logString('config ' .. i .. ' = \'' .. j ..'\'\n')
+----------------------------------------------------------------------
+-- main
+
+for _, c in pairs({
+ 'date',
+ 'uname -a',
+ 'git log -1 --format=%H'
+ })
+do
+ logCommand(c)
end
-local trainData = loadData(1, config.nbTrainSamples, 'train')
-local validationData = loadData(config.nbTrainSamples + 1, config.nbValidationSamples, 'validation')
-local testData = loadData(config.nbTrainSamples + config.nbValidationSamples + 1, config.nbTestSamples, 'test')
+local trainData = loadData(1,
+ params.nbTrainSamples, 'train')
+
+local validationData = loadData(params.nbTrainSamples + 1,
+ params.nbValidationSamples, 'validation')
local model = createAndTrainModel(trainData, validationData)
+----------------------------------------------------------------------
+-- Test
+
+local testData = loadData(params.nbTrainSamples + params.nbValidationSamples + 1,
+ params.nbTestSamples, 'test')
+
+if params.useGPU then
+ print('Moving the model and criterion to the GPU.')
+ model:cuda()
+end
+
saveResultImage(model, trainData)
saveResultImage(model, validationData)
-saveResultImage(model, testData, nil, testData.nbSamples)
+saveResultImage(model, testData, 1024)
#include "universe.h"
#include "canvas_cairo.h"
-void generate_png(Universe *universe, scalar_t scale, FILE *file) {
- CanvasCairo canvas(scale, universe->width(), universe->height());
- canvas.set_line_width(1.0 / scale);
- universe->draw(&canvas);
- canvas.write_png(file);
-}
-
FILE *safe_fopen(const char *name, const char *mode) {
FILE *file = fopen(name, mode);
if(!file) {
exit(1);
}
+//////////////////////////////////////////////////////////////////////
+
+void draw_universe_on_canvas(CanvasCairo *canvas, scalar_t scaling,
+ Universe *universe) {
+ canvas->set_line_width(1.0 / scaling);
+ universe->draw(canvas);
+}
+
+void draw_grabbing_point_on_canvas(CanvasCairo *canvas, scalar_t scaling,
+ scalar_t xg, scalar_t yg,
+ scalar_t r, scalar_t g, scalar_t b) {
+ scalar_t radius = 1/scaling;
+ int n = 36;
+ scalar_t xp[n], yp[n];
+ for(int k = 0; k < n; k++) {
+ scalar_t alpha = 2 * M_PI * scalar_t(k) / scalar_t(n);
+ xp[k] = xg + radius * cos(alpha);
+ yp[k] = yg + radius * sin(alpha);
+ }
+ canvas->set_drawing_color(r, g, b);
+ canvas->set_line_width(2.0);
+ canvas->draw_polygon(1, n, xp, yp);
+}
+
+//////////////////////////////////////////////////////////////////////
+
int main(int argc, char **argv) {
const scalar_t world_width = 400;
const scalar_t world_height = 400;
- const scalar_t block_size = 80;
+ const scalar_t scaling = 0.16; // So that 400 * 0.16 = 64
+ const scalar_t shape_size = 80;
const scalar_t dt = 0.1;
- const int nb_iterations_per_steps = 20;
+ const int nb_iterations_per_steps = 5;
//////////////////////////////////////////////////////////////////////
- // We will generate images { 0, every_nth, 2 * every_nth, ..., nb_frames - 1 }
+ // We will generate images { 0, every_nth, 2 * every_nth, ..., k * every_nth < nb_frames }
// The framerate every_nth may be set to smaller value to generate
// nice materials for presentations or papers.
int every_nth = 4;
-
int nb_frames = 5;
-
int multi_grasp = 0;
int nb_shapes = 1;
char data_dir[1024] = "/tmp/";
+ int multi_images = 0;
+ int show_grabbing_point = 0;
+ int skip = -1;
//////////////////////////////////////////////////////////////////////
- Universe *universe;
- Polygon *grabbed_polygon;
-
if(argc < 2) {
print_help(argv[0]);
}
i++;
}
+ else if(strcmp(argv[i], "--multi_images") == 0) {
+ multi_images = 1;
+ i++;
+ }
+
+ else if(strcmp(argv[i], "--show_grabbing_point") == 0) {
+ show_grabbing_point = 1;
+ i++;
+ }
+
+ else if(strcmp(argv[i], "--skip") == 0) {
+ i++;
+ if(i == argc) { print_help(argv[0]);}
+ skip = atoi(argv[i]);
+ i++;
+ }
+
else {
cerr << "Unknown option " << argv[i] << "." << endl;
abort();
abort();
}
- universe = new Universe(nb_shapes, world_width, world_height);
-
for(int n = 0; n < nb_sequences; n++) {
- scalar_t grab_start_x = world_width * 0.5;
- scalar_t grab_start_y = world_height * 0.75;
+ Universe *universe;
+ Polygon *grabbed_polygon;
+
+ universe = new Universe(nb_shapes, world_width, world_height);
+
+ const int nb_saved_frames = (nb_frames + every_nth - 1) / every_nth;
+
+ CanvasCairo *canvases[nb_saved_frames * 2];
+
+ for(int s = 0; s < 2 * nb_saved_frames; s++) {
+ canvases[s] = new CanvasCairo(scaling, universe->width(), universe->height());
+ }
+
+ scalar_t grab_start_x, grab_start_y;
if(multi_grasp) {
grab_start_x = world_width * (0.1 + 0.8 * drand48());
grab_start_y = world_height * (0.1 + 0.8 * drand48());
+ } else {
+ grab_start_x = world_width * 0.5;
+ grab_start_y = world_height * 0.75;
}
if((n+1)%100 == 0) {
nb_attempts = 0;
do {
- scalar_t x[] = {
- - block_size * 0.4,
- + block_size * 0.4,
- + block_size * 0.4,
- - block_size * 0.4,
- };
-
- scalar_t y[] = {
- - block_size * 0.6,
- - block_size * 0.6,
- + block_size * 0.6,
- + block_size * 0.6,
- };
-
- scalar_t delta = block_size / sqrt(2.0);
+ scalar_t x[] = { - shape_size * 0.4, + shape_size * 0.4,
+ + shape_size * 0.4, - shape_size * 0.4 };
+
+ scalar_t y[] = { - shape_size * 0.6, - shape_size * 0.6,
+ + shape_size * 0.6, + shape_size * 0.6 };
+
+ scalar_t delta = shape_size / sqrt(2.0);
+
scalar_t object_center_x = delta + (world_width - 2 * delta) * drand48();
scalar_t object_center_y = delta + (world_height - 2 * delta) * drand48();
- scalar_t red, green, blue;
- red = 1.00;
- green = red;
- blue = red;
+
delete pol;
- pol = new Polygon(0.5,
- red, green, blue,
- x, y, sizeof(x)/sizeof(scalar_t));
+ pol = new Polygon(0.5, 1.0, 1.0, 1.0, x, y, sizeof(x)/sizeof(scalar_t));
pol->set_position(object_center_x, object_center_y, M_PI * 2 * drand48());
pol->set_speed(0, 0, 0);
+
universe->initialize_polygon(pol);
+
nb_attempts++;
} while(nb_attempts < nb_attempts_max && universe->collide(pol));
if(nb_attempts == nb_attempts_max) {
delete pol;
- u = 0;
+ u = -1;
universe->clear();
nb_attempts = 0;
} else {
grabbed_polygon = universe->pick_polygon(grab_start_x, grab_start_y);
} while(!grabbed_polygon);
- const scalar_t scaling = 0.16;
-
- CanvasCairo grab_trace(scaling, world_width, world_height);
-
- {
+ if(n%1000 == 0) {
char buffer[1024];
- sprintf(buffer, "%s/%03d/", data_dir, n/1000);
+ sprintf(buffer, "%s/%03d/", data_dir, n / 1000);
mkdir(buffer, 0777);
}
- scalar_t grab_relative_x = grabbed_polygon->relative_x(grab_start_x, grab_start_y);
- scalar_t grab_relative_y = grabbed_polygon->relative_y(grab_start_x, grab_start_y);
-
- {
- int n = 36;
- scalar_t xp[n], yp[n];
- for(int k = 0; k < n; k++) {
- scalar_t radius = 1/scaling;
- scalar_t alpha = 2 * M_PI * scalar_t(k) / scalar_t(n);
- xp[k] = grab_start_x + radius * cos(alpha);
- yp[k] = grab_start_y + radius * sin(alpha);
+ if(skip < 0 || n >= skip) {
+
+ scalar_t grab_relative_x = grabbed_polygon->relative_x(grab_start_x, grab_start_y);
+ scalar_t grab_relative_y = grabbed_polygon->relative_y(grab_start_x, grab_start_y);
+
+ for(int s = 0; s < nb_frames; s++) {
+ if(s % every_nth == 0) {
+ int t = s / every_nth;
+ scalar_t xf = grabbed_polygon->absolute_x(grab_relative_x, grab_relative_y);
+ scalar_t yf = grabbed_polygon->absolute_y(grab_relative_x, grab_relative_y);
+
+ canvases[2 * t + 0]->clear();
+ draw_grabbing_point_on_canvas(canvases[2 * t + 0], scaling,
+ xf, yf, 0.0, 0.0, 0.0);
+ canvases[2 * t + 1]->clear();
+ draw_universe_on_canvas(canvases[2 * t + 1], scaling, universe);
+
+ if(show_grabbing_point) {
+ draw_grabbing_point_on_canvas(canvases[2 * t + 1], scaling,
+ xf, yf, 1.0, 0.0, 0.0);
+ }
+ }
+
+ if(s < nb_frames - 1) {
+ // Run the simulation
+ for(int i = 0; i < nb_iterations_per_steps; i++) {
+ scalar_t xf = grabbed_polygon->absolute_x(grab_relative_x, grab_relative_y);
+ scalar_t yf = grabbed_polygon->absolute_y(grab_relative_x, grab_relative_y);
+ grabbed_polygon->apply_force(dt, xf, yf, 0.0, -1.0);
+ universe->update(dt);
+ }
+ }
}
- grab_trace.set_drawing_color(0.0, 0.0, 0.0);
- grab_trace.set_line_width(2.0);
- grab_trace.draw_polygon(1, n, xp, yp);
- }
- for(int s = 0; s < nb_frames; s++) {
- if(s % every_nth == 0) {
- char buffer[1024];
- sprintf(buffer, "%s/%03d/dyn_%06d_world_%03d.png", data_dir, n/1000, n, s);
+ char buffer[1024];
+
+ if(multi_images) {
+ for(int j = 0; j < nb_saved_frames; j++) {
+ FILE *file;
+ sprintf(buffer, "%s/%03d/dyn_%06d_grab_%02d.png", data_dir, n / 1000, n, j);
+ file = safe_fopen(buffer, "w");
+ canvases[j * 2 + 0]->write_png(file);
+ fclose(file);
+ sprintf(buffer, "%s/%03d/dyn_%06d_state_%02d.png", data_dir, n / 1000, n, j);
+ file = safe_fopen(buffer, "w");
+ canvases[j * 2 + 1]->write_png(file);
+ fclose(file);
+ }
+ } else {
+ CanvasCairo main_canvas(scaling, nb_saved_frames, 2, canvases);
+ sprintf(buffer, "%s/%03d/dyn_%06d.png", data_dir, n / 1000, n);
FILE *file = safe_fopen(buffer, "w");
- generate_png(universe, scaling, file);
+ main_canvas.write_png(file);
fclose(file);
}
-
- for(int i = 0; i < nb_iterations_per_steps; i++) {
- scalar_t xf = grabbed_polygon->absolute_x(grab_relative_x, grab_relative_y);
- scalar_t yf = grabbed_polygon->absolute_y(grab_relative_x, grab_relative_y);
- grabbed_polygon->apply_force(dt, xf, yf, 0.0, -1.0);
- universe->update(dt);
- }
}
- {
- char buffer[1024];
- sprintf(buffer, "%s/%03d/dyn_%06d_grab.png", data_dir, n/1000, n);
- FILE *file = safe_fopen(buffer, "w");
- grab_trace.write_png(file);
- fclose(file);
+ for(int t = 0; t < 2 * nb_saved_frames; t++) {
+ delete canvases[t];
}
- }
- delete universe;
+ delete universe;
+ }
}