Commit 1df7b845 authored by Benjamin Thomas Graham's avatar Benjamin Thomas Graham
Browse files

3d segmantation

parent f2e3800b
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
return function(sparseconvnet)
local C = sparseconvnet.C
local Convolution, parent = torch.class(
'sparseconvnet.Convolution', 'nn.Module', sparseconvnet)
function Convolution:__init(dimension, nInputPlanes, nOutputPlanes,
filterSize, filterStride, bias)
parent.__init(self)
self.dimension = dimension
self.nInputPlanes = nInputPlanes
self.nOutputPlanes = nOutputPlanes
self.filterSize = sparseconvnet.toLongTensor(filterSize,dimension)
self.filterStride = sparseconvnet.toLongTensor(filterStride,dimension)
self.filterVolume = self.filterSize:prod()
self.weight = torch.Tensor(nInputPlanes*self.filterVolume,nOutputPlanes)
self.gradWeight = torch.Tensor(nInputPlanes*self.filterVolume,nOutputPlanes)
if (type(bias) ~= 'boolean') or bias then
self.bias = torch.Tensor(nOutputPlanes)
self.gradBias = torch.Tensor(nOutputPlanes)
end
self.output = {
features = torch.Tensor(),
}
self.gradInput = {
features = torch.Tensor()
}
self:reset()
end
function Convolution:reset()
local stdv = math.sqrt(2/self.nInputPlanes/self.filterVolume)
self.weight:normal(0, stdv)
if self.bias then
self.bias:zero()
end
return self
end
function Convolution:updateOutput(input)
assert(input.features:size(2)==self.nInputPlanes)
self.output.metadata = input.metadata
self.output.spatialSize =
torch.cdiv(input.spatialSize-self.filterSize,self.filterStride)+1
self.shared.forwardPassMultiplyAddCount=
self.shared.forwardPassMultiplyAddCount+
C.dimTypedFn(self.dimension, self._type, 'Convolution_updateOutput')(
input.spatialSize:cdata(),
self.output.spatialSize:cdata(),
self.filterSize:cdata(),
self.filterStride:cdata(),
input.metadata.ffi,
input.features:cdata(),
self.output.features:cdata(),
self.weight:cdata(),
self.bias and self.bias:cdata(),
self.filterVolume,
self.shared.rulesBuffer and self.shared.rulesBuffer:cdata())
self.shared.forwardPassHiddenStates=
self.shared.forwardPassHiddenStates + self.output.features:nElement()
return self.output
end
function Convolution:backward(input, gradOutput)
C.dimTypedFn(self.dimension, self._type, 'Convolution_backward')(
input.spatialSize:cdata(),
self.output.spatialSize:cdata(),
self.filterSize:cdata(),
self.filterStride:cdata(),
input.metadata.ffi,
input.features:cdata(),
self.gradInput.features:cdata(),
gradOutput.features:cdata(),
self.weight:cdata(),
self.gradWeight:cdata(),
self.gradBias and self.gradBias:cdata(),
self.filterVolume,
self.shared.rulesBuffer and self.shared.rulesBuffer:cdata())
return self.gradInput
end
function Convolution:type(type,tensorCache)
if type==nil then
return self._type
else
self._type=type
self.weight = self.weight:type(type)
self.gradWeight =self.gradWeight:type(type)
if self.bias then
self.bias = self.bias:type(type)
self.gradBias =self.gradBias:type(type)
end
self.output.features=self.output.features:type(type)
self.gradInput.features=self.gradInput.features:type(type)
end
end
function Convolution:__tostring()
local s = 'Convolution ' .. self.nInputPlanes .. '->' .. self.nOutputPlanes..' C'
if self.filterSize:max()==self.filterSize:min() and
self.filterStride:max()==self.filterStride:min() then
s=s..self.filterSize[1] ..(self.filterStride[1]==1 and
'' or '/'..self.filterStride[1])
else
s=s..'('..self.filterSize[1]
for i=2,self.dimension do
s=s..','..self.filterSize[i]
end
s=s..')/('..self.filterStride[1]
for i=2,self.dimension do
s=s..','..self.filterStride[i]
end
s=s..')'
end
return s
end
function Convolution:clearState()
self.output={features=self.output.features:set()}
self.gradInput={features=self.gradInput.features:set()}
self.rules=nil
end
function Convolution:suggestInputSize(nOut)
if self.valid then
return nOut
else
return torch.cmul(nOut-1,self.filterStride)+self.filterSize
end
end
end
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
--Based on https://github.com/facebook/fb.resnet.torch/blob/master/dataloader.lua
return function(sparseconvnet)
local DataLoader = torch.class('sparseconvnet.DataLoader', sparseconvnet)
function DataLoader:__init(data, nSamples, batchSize, nThreads,
encode)
local threads = require 'threads'
self.nSamples = nSamples
self.batchSize = batchSize
self.nThreads=nThreads
if nThreads>0 then
self.threads=threads.Threads(nThreads,
function(threadid)
g_data=data
g_encode=encode
torch.manualSeed(torch.random())
end)
function self:epoch()
local perm=torch.randperm(self.nSamples)
local idx,sample = 1, nil
local function enqueue()
while idx <= self.nSamples and self.threads:acceptsjob() do
local indices = perm:narrow(1, idx, math.min(
self.batchSize, self.nSamples - idx + 1))
self.threads:addjob(
function(indices)
require 'nn'
batch=g_encode(g_data,indices:clone())
collectgarbage()
return batch
end,
function(batch)
require 'nn'
sample=batch
end,
indices)
idx = idx + self.batchSize
end
end
local function loop()
enqueue()
if not self.threads:hasjob() then
return nil
end
self.threads:dojob()
enqueue()
return sample
end
return loop
end
else --nThreads==0, for debugging
self._data=data
self._encode=encode
self._postSerialize=postSerialize or function (batch) end
self._postEpoch=postEpoch or function () end
function self:epoch()
perm=torch.randperm(self.nSamples)
local idx,sample = 1, nil
local function loop()
if idx <= self.nSamples then
local indices = perm:narrow(1, idx, math.min(
self.batchSize, self.nSamples - idx + 1))
batch=self._encode(self._data,indices:clone())
collectgarbage()
sample=batch
idx = idx + self.batchSize
return sample
else
return nil
end
end
return loop
end
end
end
end
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
return function(sparseconvnet)
local C = sparseconvnet.C
local Deconvolution, parent = torch.class(
'sparseconvnet.Deconvolution', 'nn.Module', sparseconvnet)
function Deconvolution:__init(dimension, nInputPlanes, nOutputPlanes,
filterSize, filterStride, bias)
parent.__init(self)
self.dimension = dimension
self.nInputPlanes = nInputPlanes
self.nOutputPlanes = nOutputPlanes
self.filterSize = sparseconvnet.toLongTensor(filterSize,dimension)
self.filterStride = sparseconvnet.toLongTensor(filterStride,dimension)
self.filterVolume = self.filterSize:prod()
self.weight = torch.Tensor(nInputPlanes*self.filterVolume,nOutputPlanes)
self.gradWeight = torch.Tensor(nInputPlanes*self.filterVolume,nOutputPlanes)
if (type(bias) ~= 'boolean') or bias then
self.bias = torch.Tensor(nOutputPlanes)
self.gradBias = torch.Tensor(nOutputPlanes)
end
self.output = {
features = torch.Tensor(),
}
self.gradInput = {
features = torch.Tensor()
}
self:reset()
end
function Deconvolution:reset()
local stdv = math.sqrt(2/self.nInputPlanes/self.filterVolume)
self.weight:normal(0, stdv)
if self.bias then
self.bias:zero()
end
return self
end
function Deconvolution:updateOutput(input)
assert(input.features:size(2)==self.nInputPlanes)
self.output.metadata = input.metadata
self.output.spatialSize =
torch.cmul(input.spatialSize-1,self.filterStride)+self.filterSize
self.shared.forwardPassMultiplyAddCount=
self.shared.forwardPassMultiplyAddCount+
C.dimTypedFn(self.dimension, self._type, 'Deconvolution_updateOutput')(
input.spatialSize:cdata(),
self.output.spatialSize:cdata(),
self.filterSize:cdata(),
self.filterStride:cdata(),
input.metadata.ffi,
input.features:cdata(),
self.output.features:cdata(),
self.weight:cdata(),
self.bias and self.bias:cdata() ,
self.filterVolume,
self.shared.rulesBuffer and self.shared.rulesBuffer:cdata())
self.shared.forwardPassHiddenStates=
self.shared.forwardPassHiddenStates + self.output.features:nElement()
return self.output
end
function Deconvolution:backward(input, gradOutput)
C.dimTypedFn(self.dimension, self._type, 'Deconvolution_backward')(
input.spatialSize:cdata(),
self.output.spatialSize:cdata(),
self.filterSize:cdata(),
self.filterStride:cdata(),
input.metadata.ffi,
input.features:cdata(),
self.gradInput.features:cdata(),
gradOutput.features:cdata(),
self.weight:cdata(),
self.gradWeight:cdata(),
self.gradBias and self.gradBias:cdata(),
self.filterVolume,
self.shared.rulesBuffer and self.shared.rulesBuffer:cdata())
return self.gradInput
end
function Deconvolution:type(type,tensorCache)
if type==nil then
return self._type
else
self._type=type
self.weight = self.weight:type(type)
self.gradWeight =self.gradWeight:type(type)
if self.bias then
self.bias = self.bias:type(type)
self.gradBias =self.gradBias:type(type)
end
self.output.features=self.output.features:type(type)
self.gradInput.features=self.gradInput.features:type(type)
end
end
function Deconvolution:__tostring()
local s = 'Deconvolution ' .. self.nInputPlanes .. '->' .. self.nOutputPlanes..' C'
if self.filterSize:max()==self.filterSize:min() and
self.filterStride:max()==self.filterStride:min() then
s=s..self.filterSize[1] ..(self.filterStride[1]==1 and
'' or '/'..self.filterStride[1])
else
s=s..'('..self.filterSize[1]
for i=2,self.dimension do
s=s..','..self.filterSize[i]
end
s=s..')/('..self.filterStride[1]
for i=2,self.dimension do
s=s..','..self.filterStride[i]
end
s=s..')'
end
return s
end
function Deconvolution:clearState()
self.output={features=self.output.features:set()}
self.gradInput={features=self.gradInput.features:set()}
self.rules=nil
end
function Deconvolution:suggestInputSize(nOut)
return torch.cmul(nOut-1,self.filterStride)+self.filterSize
end
end
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
return function(sparseconvnet)
local C = sparseconvnet.C
local DenseNetBlock, parent = torch.class(
'sparseconvnet.DenseNetBlock', 'nn.Container', sparseconvnet)
function DenseNetBlock:__init(dimension, nInputPlanes, nExtraLayers,
growthRate)
parent.__init(self)
self.dimension=dimensions
self.nInputPlanes=nInputPlanes
self.nExtraLayers=nExtraLayers or 2
self.growthRate=growthRate or 16
assert(self.nExtraLayers>=1)
self.nOutputPlanes=nInputPlanes+nExtraLayers*growthRate
self.output={
features=torch.Tensor(), --nActive x self.nOutputPlanes
}
--Module 1: Batchnorm the input into the start of self.output
self:add(sparseconvnet.BatchNormalizationInTensor(nInputPlanes,nil,nil,0))
self.modules[1].output=self.output
self.gradInput=self.modules[1].gradInput
for i = 1, nExtraLayers do
local nFeatures = self.nInputPlanes + (i-1)*growthRate
local nFeaturesB=4*growthRate
--Modules 4*i-2
self:add(sparseconvnet.AffineReluTrivialConvolution(nFeatures, nFeaturesB, true))
--Module 4*i-1
self:add(sparseconvnet.BatchNormalization(nFeaturesB,nil,nil,true,0))
--Module 4*i
self:add(sparseconvnet.SubmanifoldConvolution(dimension, nFeaturesB, growthRate,
3, false))
--Module 4*i+1
self:add(sparseconvnet.BatchNormalizationInTensor(growthRate,nil,nil,
self.nInputPlanes+(i-1)*growthRate))
self.modules[4*i+1].output=self.output
end
self.filterSize = self.modules[4].filterSize
self.filterStride = self.modules[4].filterStride
self.filterSizeString = self.modules[4].filterSizeString
end
function DenseNetBlock:updateOutput(input)
assert(input.features:size(2) == self.nInputPlanes)
self.output.spatialSize = input.spatialSize
self.output.metadata = input.metadata
self.output.features:resize(input.features:size(1),self.nOutputPlanes)
local i = input
for m = 1, 4*self.nExtraLayers+1 do
i=self.modules[m]:updateOutput(i)
end
return self.output
end
function DenseNetBlock:backward(input, gradOutput)
local g = gradOutput
for i = 1, self.nExtraLayers do
self.modules[4*i-2].gradInput=gradOutput
end
for m=4*self.nExtraLayers+1,2,-1 do
g = self.modules[m]:backward(self.modules[m-1].output,g)
end
self.modules[1]:backward(input,g)
return self.gradInput
end
function DenseNetBlock:type(type,tensorCache)
self._type=type
self.output.features=self.output.features:type(type)
for _,x in pairs(self.modules) do
x:type(type)
end
end
function DenseNetBlock:__tostring()
local s = 'DenseNetBlock('.. self.nInputPlanes .. '->' ..
self.nInputPlanes .. '+' .. self.nExtraLayers .. '*' ..
self.growthRate .. '=' .. self.nOutputPlanes .. ')'
return s
end
function DenseNetBlock:clearState()
for _,m in ipairs(self.modules) do
m:clearState()
end
self.output={
features=self.output.features:set(),
nPlanes=self.nOutputPlanes,
dimension=self.dimension
}
end
function DenseNetBlock:suggestInputSize(nOut)
return nOut
end
end
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
return function(sparseconvnet)
local Identity, _ = torch.class(
'sparseconvnet.Identity', 'nn.Identity', sparseconvnet)
function Identity:clearState()
self.output=nil
self.gradInput=nil
end
function Identity:suggestInputSize(nOut)
return nOut
end
end
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
return function(sparseconvnet)
local C = sparseconvnet.C
local InputBatch, parent = torch.class('sparseconvnet.InputBatch', sparseconvnet)
function InputBatch:__init(dimension, spatialSize)
self.dimension = dimension
self.features = torch.Tensor():type('torch.FloatTensor')
self.metadata = sparseconvnet.Metadata(dimension)
self.spatialSize = type(spatialSize)=='number' and torch.LongTensor(
dimension):fill(spatialSize) or spatialSize
C.dimensionFn(self.dimension,'setInputSpatialSize')(self.metadata.ffi,
self.spatialSize:cdata())
end
function InputBatch:addSample()
C.dimensionFn(self.dimension, 'batchAddSample')(self.metadata.ffi)
end
function InputBatch:addSampleFromTensor(tensor,offset,threshold)
C.dimensionFn(
self.dimension,'addSampleFromThresholdedTensor')(
self.metadata.ffi, self.features:cdata(), tensor:cdata(), offset:cdata(),
self.spatialSize:cdata(), threshold)
end
function InputBatch:setLocation(location, vector, overwrite)
--[[location is a self.dimensional length set of coordinates:
torch.LongStorage or a table]]
if type(location)=='table' then
local l=torch.LongStorage(self.dimension)
for i,x in ipairs(location) do
l[i]=x
end
location = l
end
assert(location:min()>=0 and (self.spatialSize-location):min()>0)
C.dimensionFn(self.dimension,'setInputSpatialLocation')(self.metadata.ffi,
self.features:cdata(), location:cdata(), vector:cdata(), overwrite)
end
function InputBatch:setLocations(locations, vectors, overwrite)
--[[locations is a n_locations x self.dimensional length set of coordinates:
torch.LongStorage or a 2-D table]]
if type(locations)=='table' then
locations = torch.LongStorage(locations)
end
local l = locations:narrow(2,1,self.dimension)
assert(l:min()>=0 and (self.spatialSize:view(1, self.dimension):expandAs(l)-l):min()>0)
C.dimensionFn(self.dimension,'setInputSpatialLocations')(self.metadata.ffi,
self.features:cdata(), locations:cdata(), vectors:cdata(), overwrite)
end
function InputBatch:precomputeMetadata(stride)
if stride==2 then
C.dimensionFn(self.dimension,'generateRuleBooks2s2')(self.metadata.ffi)
else
C.dimensionFn(self.dimension,'generateRuleBooks3s2')(self.metadata.ffi)
end
end
function InputBatch:type(t)
if t then
self.features = self.features:type(t)
else
return self.features:type()
end
end
end
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
--[[
Assume all the inputs have identical SparseGrids and input[i].features:size(1)
Assume input[1].features:size(2) >= input[i].features:size(2) for all i=1,#input
output.submanifoldRules is taken from input[1].submanifoldRules (could do set union?)
(for resnets, make sure the residual link is input[2])
]]
return function(sparseconvnet)
local JoinTable, parent = torch.class(
'sparseconvnet.JoinTable', 'nn.Module', sparseconvnet)
function JoinTable:__init(nPlanes)
parent.__init(self)
self.nOutputPlanes=0
self.nPlanes=nPlanes
self.gradInput = {}
for i,j in ipairs(nPlanes) do
self.gradInput[i]={features=torch.Tensor()}
self.nOutputPlanes=self.nOutputPlanes+j
end
self.output = {
features = torch.Tensor()
}
end
function JoinTable:updateOutput(input)
self.output.features:resize(input[1].features:size(1),self.nOutputPlanes)
self.output.metadata=input[1].metadata
self.output.spatialSize=input[1].spatialSize
local offset=0
for i,j in ipairs(self.nPlanes) do
assert(input[i].features:size(1)==input[1].features:size(1))
assert(input[i].features:size(2)==j)
self.output.features:narrow(2,1+offset,j):copy(input[i].features)
offset=offset+j
end
return self.output
end
function JoinTable:updateGradInput(input, gradOutput)
local offset=0
for i,j in ipairs(self.nPlanes) do
self.gradInput[i].features:resize(input[1].features:size(1),j):copy(gradOutput.features:narrow(2,1+offset,j))
offset=offset+j
end
return self.gradInput
end
function JoinTable:clearState()
self.output = {
features = self.output.features:set()
}
for i,j in ipairs(self.nPlanes) do
self.gradInput[i]={features=self.gradInput[i].features:set()}
end
end
function JoinTable:suggestInputSize(nOut)
return nOut
end
end
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
return function(sparseconvnet)
local C = sparseconvnet.C
local math = require 'math'
local LeakyReLU, parent = torch.class(
'sparseconvnet.LeakyReLU', 'nn.Module', sparseconvnet)
function LeakyReLU:__init(leakage,ip)
parent.__init(self)
self.inplace = type(ip)~='boolean' or ip --default to inplace
self.leakage = leakage
self.output = {
features = ip and "Recycle input.features" or torch.Tensor(),
}
self.gradInput = {
features = ip and "Recycle gradOutput.features" or torch.Tensor()
}
end
function LeakyReLU:updateOutput(input)
self.output.metadata=input.metadata
self.output.spatialSize = input.spatialSize
C.typedFn(self._type,'LeakyReLU_updateOutput')(
input.features:cdata(),
self.output.features:cdata(),
self.leakage)
return self.output
end
function LeakyReLU:updateGradInput(input, gradOutput)
if self.inplace then
self.gradInput.features = gradOutput.features
else
self.gradInput.features:resizeAs(gradOutput.features)
end
C.typedFn(self._type,'LeakyReLU_updateGradInput')(
input.features:cdata(),
self.gradInput.features:cdata(),
gradOutput.features:cdata(),
self.leakage)
return self.gradInput
end
function LeakyReLU:__tostring()
local s = 'LeakyReLU('..self.leakage..')'
return s
end
function LeakyReLU:clearState()
self.output = {
features = self.inplace and "Recycle input.features" or self.output.features:set(),
}
self.gradInput = {
features = self.inplace and "Recycle gradOutput.features" or self.gradInput.features:set()
}
end
function LeakyReLU:suggestInputSize(nOut)
return nOut
end
end
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
return function(sparseconvnet)
local C = sparseconvnet.C
local MaxPooling, parent = torch.class(
'sparseconvnet.MaxPooling', 'nn.Module', sparseconvnet)
function MaxPooling:__init(
dimension, poolSize, poolStride, nFeaturesToDrop)
parent.__init(self)
self.dimension = dimension
self.poolSize = sparseconvnet.toLongTensor(poolSize,dimension)
self.poolStride = sparseconvnet.toLongTensor(poolStride,dimension)
self.poolVolume = self.poolSize:prod()
self.nFeaturesToDrop = nFeaturesToDrop or 0
self.output = {
features = torch.FloatTensor(),
}
self.gradInput = {
features = torch.Tensor()
}
end
function MaxPooling:updateOutput(input)
self.output.metadata=input.metadata
self.output.spatialSize
= torch.cdiv(input.spatialSize-self.poolSize,self.poolStride)+1
C.dimTypedFn(self.dimension, self._type, 'MaxPooling_updateOutput')(
input.spatialSize:cdata(),
self.output.spatialSize:cdata(),
self.poolSize:cdata(),
self.poolStride:cdata(),
input.metadata.ffi,
input.features:cdata(),
self.output.features:cdata(),
self.nFeaturesToDrop,
self.shared.rulesBuffer and self.shared.rulesBuffer:cdata())
return self.output
end
function MaxPooling:updateGradInput(input, gradOutput)
C.dimTypedFn(self.dimension, self._type, 'MaxPooling_updateGradInput')(
input.spatialSize:cdata(),
self.output.spatialSize:cdata(),
self.poolSize:cdata(),
self.poolStride:cdata(),
input.metadata.ffi,
input.features:cdata(),
self.gradInput.features:cdata(),
self.output.features:cdata(),
gradOutput.features:cdata(),
self.nFeaturesToDrop,
self.shared.rulesBuffer and self.shared.rulesBuffer:cdata())
return self.gradInput
end
function MaxPooling:type(type,tensorCache)
if type==nil then
return self._type
end
self._type=type
self.output.features=self.output.features:type(type)
self.gradInput.features=self.gradInput.features:type(type)
--return parent.type(self,type,tensorCache)
end
function MaxPooling:__tostring()
local s = 'MaxPooling'
if self.poolSize:max()==self.poolSize:min()
and self.poolStride:max()==self.poolStride:min() then
s=s..self.poolSize[1] ..(self.poolStride[1]==1
and '' or '/'..self.poolStride[1])
else
s=s..'('..self.poolSize[1]
for i=2,self.dimension do
s=s..','..self.poolSize[i]
end
s=s..')/('..self.poolStride[1]
for i=2,self.dimension do
s=s..','..self.poolStride[i]
end
s=s..')'
end
if self.nFeaturesToDrop>0 then
s=s .. ' nFeaturesToDrop = ' .. self.nFeaturesToDrop
end
return s
end
function MaxPooling:clearState()
self.output = {
features = self.output.features:set(),
}
self.gradInput = {
features = self.gradInput.features:set()
}
end
function MaxPooling:suggestInputSize(nOut)
return torch.cmul(nOut-1,self.poolStride)+self.poolSize
end
end
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
--[[
Store Metadata relating to which spatial locations are active at each scale.
Convolutions and 'convolution reversing' deconvolutions
all coexist within the same MetaData object as long as each spatial size
only occurs once. Submanifold convolutions do not change the spatial structure.
Serialization is emulated by storing the pointer as an integer.
This is sufficient for mutithreaded batch preparation: each 'serialized'
object must be de-serialized exactly once.
]]
return function(sparseconvnet)
local ffi=require 'ffi'
local C = sparseconvnet.C
local Metadata = torch.class('sparseconvnet.Metadata', sparseconvnet)
function Metadata:__init(dimension)
self.ffi=ffi.new('void *[1]')
self.dimension=dimension
--[[ffi.gc to delete Metadata that need to be garbage collected.]]
ffi.gc(self.ffi, C.dimensionFn(self.dimension, 'freeMetadata'))
end
function Metadata:deactivateGC()
--[[FFI pointers cannot be serialized for passing from batch-creation
worker threads to the main learner thread. So we convert it to something
that can, ...]]
if self.ffi then
self.p = torch.LongStorage(1)
C.copyFfiPtrToLong(self.p,self.ffi)
ffi.gc(self.ffi,nil)
self.ffi=nil
end
end
function Metadata:reactivateGC()
--[[... and then convert them back as soon as possible after the thread
to thread transfer, and hope no-one notices.]]
if self.p then
self.ffi=ffi.new('void *[1]')
ffi.gc(self.ffi, C.dimensionFn(self.dimension, 'freeMetadata'))
C.copyLongToFfiPtr(self.ffi,self.p)
self.p=nil
end
end
function Metadata:write(file)
self:deactivateGC()
file:writeObject({self.dimension,self.p})
end
function Metadata:read(file)
local r = file:readObject()
self.dimension=r[1]
self.p=r[2]
self:reactivateGC()
end
end
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
--[[
Functions to build some networks
DeepCNet - cheap and cheerful
VggNet - deep
ResNet - deeper
DenseNet - deeper
]]
return function(sparseconvnet)
function sparseconvnet.DeepCNet(dimension,nInputPlanes,nPlanes,bn)
--[[
i.e. sparseconvnet(2,nInputPlanes,{16,32,48,64,80},4,32) maps
(batchSize,nInputPlanes,16n+32,16n+32)->(batchSize,80,n,n)
Regular (i.e. not 'valid') convolutions
https://arxiv.org/abs/1409.6070
Based on "Multi-column Deep Neural Networks for Image Classification",
Dan Ciresan, Ueli Meier, Jonathan Masci and Jurgen Schmidhuber
]]
bn = (type(bn) ~= 'boolean') or bn
m=sparseconvnet.Sequential()
local function c(nIn,nOut,size)
m:add(sparseconvnet.Convolution(
dimension,nIn,nOut,size,1,false))
if bn then
m:add(sparseconvnet.BatchNormReLU(nOut))
else
m:add(sparseconvnet.ReLU(true))
end
end
c(nInputPlanes,nPlanes[1],3)
for i = 2,#nPlanes do
m:add(sparseconvnet.MaxPooling(dimension,2,2))
c(nPlanes[i-1],nPlanes[i],2)
end
m.nOutputPlanes=nPlanes[#nPlanes]
return m
end
function sparseconvnet.SparseVggNet(dimension,nInputPlanes,layers,opts)
--[[
VGG style nets
Use submanifold convolutions
Also implements 'Plus'-augmented nets
]]
local nPlanes=nInputPlanes
local m=sparseconvnet.Sequential()
for i = 1,#layers do
x=layers[i]
if x == 'MP' then
m:add(sparseconvnet.MaxPooling(dimension,3,2))
elseif x[1] == 'MP' then
m:add(sparseconvnet.MaxPooling(dimension,x[2],x[3]))
elseif x[1]=='C' and #x==2 then
m:add(sparseconvnet.SubmanifoldConvolution(dimension,nPlanes,x[2],3,false))
nPlanes=x[2]
m:add(sparseconvnet.BatchNormReLU(nPlanes))
elseif x[1]=='C' and #x==3 then
m:add(sparseconvnet.ConcatTable()
:add(
sparseconvnet.Sequential()
:add(sparseconvnet.SubmanifoldConvolution(dimension,nPlanes,x[2],3,false))
)
:add(
sparseconvnet.Sequential()
:add(sparseconvnet.Convolution(dimension,nPlanes,x[3],3,2,false))
:add(sparseconvnet.BatchNormReLU(x[3]))
:add(sparseconvnet.SubmanifoldConvolution(dimension,x[3],x[3],3,false))
:add(sparseconvnet.BatchNormReLU(x[3]))
:add(sparseconvnet.Deconvolution(dimension,x[3],x[3],3,2,false))
))
:add(sparseconvnet.JoinTable({x[2],x[3]}))
nPlanes=x[2]+x[3]
m:add(sparseconvnet.BatchNormReLU(nPlanes))
elseif x[1]=='C' and #x==4 then
m:add(sparseconvnet.ConcatTable()
:add(
sparseconvnet.Sequential()
:add(sparseconvnet.SubmanifoldConvolution(dimension,nPlanes,x[2],3,false))
)
:add(
sparseconvnet.Sequential()
:add(sparseconvnet.Convolution(dimension,nPlanes,x[3],3,2,false))
:add(sparseconvnet.BatchNormReLU(x[3]))
:add(sparseconvnet.SubmanifoldConvolution(dimension,x[3],x[3],3,false))
:add(sparseconvnet.BatchNormReLU(x[3]))
:add(sparseconvnet.Deconvolution(dimension,x[3],x[3],3,2,false))
)
:add(sparseconvnet.Sequential()
:add(sparseconvnet.Convolution(dimension,nPlanes,x[4],3,2,false))
:add(sparseconvnet.BatchNormReLU(x[4]))
:add(sparseconvnet.SubmanifoldConvolution(dimension,x[4],x[4],3,false))
:add(sparseconvnet.BatchNormReLU(x[4]))
:add(sparseconvnet.Convolution(dimension,x[4],x[4],3,2,false))
:add(sparseconvnet.BatchNormReLU(x[4]))
:add(sparseconvnet.SubmanifoldConvolution(dimension,x[4],x[4],3,false))
:add(sparseconvnet.BatchNormReLU(x[4]))
:add(sparseconvnet.Deconvolution(dimension,x[4],x[4],3,2,false))
:add(sparseconvnet.BatchNormReLU(x[4]))
:add(sparseconvnet.SubmanifoldConvolution(dimension,x[4],x[4],3,false))
:add(sparseconvnet.BatchNormReLU(x[4]))
:add(sparseconvnet.Deconvolution(dimension,x[4],x[4],3,2,false))
))
:add(sparseconvnet.JoinTable({x[2],x[3],x[4]}))
nPlanes=x[2]+x[3]+x[4]
m:add(sparseconvnet.BatchNormReLU(nPlanes))
end
end
return m
end
function sparseconvnet.SparseResNet(dimension,nInputPlanes,layers,endNoise)
-- pre-activated ResNet
-- e.g. layers = {{'basic',16,2,1},{'basic',32,2},{'shortcut',64,2,2}}
local nPlanes=nInputPlanes
local m=sparseconvnet.Sequential()
local function residual(nIn,nOut,stride)
if stride>1 then
return sparseconvnet.Convolution(dimension,nIn,nOut,3,stride,false)
elseif nIn~=nOut then
return sparseconvnet.NetworkInNetwork(nIn,nOut,false)
else
return sparseconvnet.Identity()
end
end
for i = 1, #layers do
local blockType,n,reps,stride=table.unpack(layers[i])
for rep=1,reps do
if blockType:sub(1,1)=='b' then --basic block
if rep==1 then
m:add(sparseconvnet.BatchNormReLU(nPlanes))
m:add(sparseconvnet.ConcatTable()
:add( --convolutional connection
sparseconvnet.Sequential()
:add(stride==1 and
sparseconvnet.SubmanifoldConvolution(dimension,nPlanes,n,3,false) or
sparseconvnet.Convolution(dimension,nPlanes,n,3,stride,false))
:add(sparseconvnet.BatchNormReLU(n))
:add(sparseconvnet.SubmanifoldConvolution(
dimension,n,n,3,false)))
:add(residual(nPlanes,n,stride))
)
else --rep>1
m:add(sparseconvnet.ConcatTable()
:add( --convolutional connection
sparseconvnet.Sequential()
:add(sparseconvnet.BatchNormReLU(n))
:add(sparseconvnet.SubmanifoldConvolution(
dimension,nPlanes,n,3,false))
:add(sparseconvnet.BatchNormReLU(n))
:add(sparseconvnet.SubmanifoldConvolution(
dimension,n,n,3,false))
)
:add(sparseconvnet.Identity())
)
end
nPlanes=n
end
m:add(sparseconvnet.CAddTable(true))
end
end
m:add(sparseconvnet.BatchNormReLU(nPlanes))
return m
end
function sparseconvnet.SparseDenseNet(dimension,nInputPlanes,layers)
--[[
SparseConvNet meets DenseNets using submanifold convolutions
Could do with a less confusing name
]]
local nPlanes=nInputPlanes
local m=sparseconvnet.Sequential()
for i=1,#layers do
x=layers[i]
if x[1] == 'AP' then
local sz = x.size or 2
local str = x.stride or 2
local compression = x.compression or 0
local nDrop = 16*torch.floor(nPlanes*compression/16)
m:add(sparseconvnet.AveragePooling(dimension,sz,str,nDrop))
nPlanes = nPlanes - nDrop
elseif x[1] == 'BN-R-C-AP' then
local sz = x.size or 2
local str = x.stride or 2
local compression = x.compression or 0
local nDrop = 16*torch.floor(nPlanes*compression/16)
m:add(sparseconvnet.BatchNormReLU(nPlanes))
m:add(sparseconvnet.NetworkInNetwork(nPlanes,nPlanes-nDrop))
nPlanes = nPlanes - nDrop
m:add(sparseconvnet.AveragePooling(dimension,sz,str))
elseif x[1] == 'C-AP' then
local sz = x.size or 2
local str = x.stride or 2
local compression = x.compression or 0
local nDrop = 16*torch.floor(nPlanes*compression/16)
m:add(sparseconvnet.NetworkInNetwork(nPlanes,nPlanes-nDrop))
nPlanes = nPlanes - nDrop
m:add(sparseconvnet.AveragePooling(dimension,sz,str))
elseif x[1] == 'MP' then
local sz = x.size or 2
local str = x.stride or 2
local compression = x.compression or 0
local nDrop = 16*torch.floor(nPlanes*compression/16)
m:add(sparseconvnet.MaxPooling(dimension,sz,str,nDrop))
nPlanes = nPlanes - nDrop
else
x.nExtraLayers=x.nExtraLayers or 2
x.growthRate = x.growthRate or 16
m:add(sparseconvnet.DenseNetBlock(dimension, nPlanes,
x.nExtraLayers, x.growthRate, x.bottleNeckMode))
nPlanes = nPlanes + x.nExtraLayers * x.growthRate
end
end
m.nOutputPlanes=nPlanes
return m
end
end
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
return function(sparseconvnet)
local C = sparseconvnet.C
local math = require 'math'
local NetworkInNetwork, parent = torch.class(
'sparseconvnet.NetworkInNetwork', 'nn.Module', sparseconvnet)
function NetworkInNetwork:__init(nInputPlanes,nOutputPlanes,bias)
parent.__init(self)
self.nInputPlanes = nInputPlanes
self.nOutputPlanes = nOutputPlanes
self.weight = torch.Tensor(nInputPlanes,nOutputPlanes)
self.gradWeight = torch.Tensor(nInputPlanes,nOutputPlanes)
if (type(bias) ~= 'boolean') or bias then
self.bias = torch.Tensor(nOutputPlanes)
self.gradBias = torch.Tensor(nOutputPlanes)
end
self.output = {
features = torch.Tensor(),
}
self.gradInput = {
features = torch.Tensor()
}
self:reset()
end
function NetworkInNetwork:reset()
local stdv = math.sqrt(2/self.nInputPlanes)
if self._type=='torch.CudaDoubleTensor' then
self.weight = torch.CudaTensor(
self.weight:size()):normal(0,stdv):type(self._type)
else
self.weight:normal(0, stdv)
end
if self.bias then
self.bias:zero()
end
return self
end
function NetworkInNetwork:updateOutput(input)
self.output.metadata = input.metadata
self.output.spatialSize = input.spatialSize
self.shared.forwardPassMultiplyAddCount=
self.shared.forwardPassMultiplyAddCount+
C.typedFn(self._type,'NetworkInNetwork_updateOutput')(
input.features:cdata(),
self.output.features:cdata(),
self.weight:cdata(),
self.bias and self.bias:cdata())
self.shared.forwardPassHiddenStates =
self.shared.forwardPassHiddenStates + self.output.features:nElement()
return self.output
end
function NetworkInNetwork:updateGradInput(input, gradOutput)
C.typedFn(self._type, 'NetworkInNetwork_updateGradInput')(
self.gradInput.features:cdata(),
gradOutput.features:cdata(),
self.weight:cdata())
return self.gradInput
end
function NetworkInNetwork:accGradParameters(input, gradOutput, scale)
assert(not scale or scale==1)
C.typedFn(self._type, 'NetworkInNetwork_accGradParameters')(
input.features:cdata(),
gradOutput.features:cdata(),
self.gradWeight:cdata(),
self.gradBias and self.gradBias:cdata())
end
function NetworkInNetwork:__tostring()
local s = 'NetworkInNetwork('..self.nInputPlanes..','..
self.nOutputPlanes..')'
return s
end
function NetworkInNetwork:clearState()
self.output = {
features = self.output.features:set(),
}
self.gradInput = {
features = self.gradInput.features:set()
}
end
function NetworkInNetwork:suggestInputSize(nOut)
return nOut
end
end
-- Copyright 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the license found in the
-- LICENSE file in the root directory of this source tree.
--[[
Parameters
ip : operate in place (default true)
]]
return function(sparseconvnet)
local ReLU, parent = torch.class(
'sparseconvnet.ReLU', 'sparseconvnet.LeakyReLU', sparseconvnet)
function ReLU:__init(ip)
parent.__init(self,0,ip)
end
end
# Copyright 2016-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the license found in the
# LICENSE file in the root directory of this source tree.
# Borrowed from caffe
# https://github.com/BVLC/caffe/blob/master/cmake/Cuda.cmake
# Known NVIDIA GPU achitectures Torch can be compiled for.
# This list will be used for CUDA_ARCH_NAME = All option
SET(KNOWN_GPU_ARCHITECTURES "2.0 2.1(2.0) 3.0 3.5 5.0")
IF (CUDA_VERSION VERSION_GREATER "6.5")
SET(KNOWN_GPU_ARCHITECTURES "${KNOWN_GPU_ARCHITECTURES} 5.2")
ENDIF (CUDA_VERSION VERSION_GREATER "6.5")
################################################################################################
# Removes duplicates from LIST(s)
# Usage:
# LIST_UNIQUE(<list_variable> [<list_variable>] [...])
MACRO(LIST_UNIQUE)
FOREACH(__lst ${ARGN})
IF(${__lst})
LIST(REMOVE_DUPLICATES ${__lst})
ENDIF()
ENDFOREACH()
ENDMACRO()
################################################################################################
# A function for automatic detection of GPUs installed (IF autodetection is enabled)
# Usage:
# DETECT_INSTALLED_GPUS(OUT_VARIABLE)
FUNCTION(DETECT_INSTALLED_GPUS OUT_VARIABLE)
IF(NOT CUDA_GPU_DETECT_OUTPUT)
SET(__cufile ${PROJECT_BINARY_DIR}/detect_cuda_archs.cu)
file(WRITE ${__cufile} ""
"#include <cstdio>\n"
"int main()\n"
"{\n"
" int count = 0;\n"
" if (cudaSuccess != cudaGetDeviceCount(&count)) return -1;\n"
" if (count == 0) return -1;\n"
" for (int device = 0; device < count; ++device)\n"
" {\n"
" cudaDeviceProp prop;\n"
" if (cudaSuccess == cudaGetDeviceProperties(&prop, device))\n"
" std::printf(\"%d.%d \", prop.major, prop.minor);\n"
" }\n"
" return 0;\n"
"}\n")
EXECUTE_PROCESS(COMMAND "${CUDA_NVCC_EXECUTABLE}" "--run" "${__cufile}"
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/CMakeFiles/"
RESULT_VARIABLE __nvcc_res OUTPUT_VARIABLE __nvcc_out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
IF(__nvcc_res EQUAL 0)
STRING(REPLACE "2.1" "2.1(2.0)" __nvcc_out "${__nvcc_out}")
SET(CUDA_GPU_DETECT_OUTPUT ${__nvcc_out} CACHE INTERNAL "Returned GPU architetures from detect_gpus tool" FORCE)
ENDIF()
ENDIF()
IF(NOT CUDA_GPU_DETECT_OUTPUT)
message(STATUS "Automatic GPU detection failed. Building for all known architectures.")
SET(${OUT_VARIABLE} ${KNOWN_GPU_ARCHITECTURES} PARENT_SCOPE)
ELSE()
SET(${OUT_VARIABLE} ${CUDA_GPU_DETECT_OUTPUT} PARENT_SCOPE)
ENDIF()
ENDFUNCTION()
################################################################################################
# Function for selecting GPU arch flags for nvcc based on CUDA_ARCH_NAME
# Usage:
# SELECT_NVCC_ARCH_FLAGS(out_variable)
FUNCTION(SELECT_NVCC_ARCH_FLAGS out_variable)
# List of arch names
SET(__archs_names "Fermi" "Kepler" "Maxwell" "All" "Manual")
SET(__archs_name_default "All")
IF(NOT CMAKE_CROSSCOMPILING)
LIST(APPEND __archs_names "Auto")
SET(__archs_name_default "Auto")
ENDIF()
# SET CUDA_ARCH_NAME strings (so it will be seen as dropbox in CMake-Gui)
SET(CUDA_ARCH_NAME ${__archs_name_default} CACHE STRING "Select target NVIDIA GPU achitecture.")
SET_property( CACHE CUDA_ARCH_NAME PROPERTY STRINGS "" ${__archs_names} )
mark_as_advanced(CUDA_ARCH_NAME)
# verIFy CUDA_ARCH_NAME value
IF(NOT ";${__archs_names};" MATCHES ";${CUDA_ARCH_NAME};")
STRING(REPLACE ";" ", " __archs_names "${__archs_names}")
message(FATAL_ERROR "Only ${__archs_names} architeture names are supported.")
ENDIF()
IF(${CUDA_ARCH_NAME} STREQUAL "Manual")
SET(CUDA_ARCH_BIN ${KNOWN_GPU_ARCHITECTURES} CACHE STRING "SpecIFy 'real' GPU architectures to build binaries for, BIN(PTX) format is supported")
SET(CUDA_ARCH_PTX "50" CACHE STRING "SpecIFy 'virtual' PTX architectures to build PTX intermediate code for")
mark_as_advanced(CUDA_ARCH_BIN CUDA_ARCH_PTX)
else()
unSET(CUDA_ARCH_BIN CACHE)
unSET(CUDA_ARCH_PTX CACHE)
ENDIF()
# Allow a user to specify architecture from env
IF($ENV{CUDA_ARCH_BIN})
SET(CUDA_ARCH_NAME "Manual")
SET(CUDA_ARCH_BIN $ENV{CUDA_ARCH_BIN})
unSET(CUDA_ARCH_PTX)
ENDIF()
IF(${CUDA_ARCH_NAME} STREQUAL "Fermi")
SET(__cuda_arch_bin "2.0 2.1(2.0)")
elseIF(${CUDA_ARCH_NAME} STREQUAL "Kepler")
SET(__cuda_arch_bin "3.0 3.5")
elseIF(${CUDA_ARCH_NAME} STREQUAL "Maxwell")
SET(__cuda_arch_bin "5.0 5.2")
elseIF(${CUDA_ARCH_NAME} STREQUAL "All")
SET(__cuda_arch_bin ${KNOWN_GPU_ARCHITECTURES})
elseIF(${CUDA_ARCH_NAME} STREQUAL "Auto")
DETECT_INSTALLED_GPUS(__cuda_arch_bin)
else() # (${CUDA_ARCH_NAME} STREQUAL "Manual")
SET(__cuda_arch_bin ${CUDA_ARCH_BIN})
ENDIF()
MESSAGE(STATUS "Compiling for CUDA architecture: ${__cuda_arch_bin}")
# remove dots and convert to lists
STRING(REGEX REPLACE "\\." "" __cuda_arch_bin "${__cuda_arch_bin}")
STRING(REGEX REPLACE "\\." "" __cuda_arch_ptx "${CUDA_ARCH_PTX}")
STRING(REGEX MATCHALL "[0-9()]+" __cuda_arch_bin "${__cuda_arch_bin}")
STRING(REGEX MATCHALL "[0-9]+" __cuda_arch_ptx "${__cuda_arch_ptx}")
LIST_UNIQUE(__cuda_arch_bin __cuda_arch_ptx)
SET(__nvcc_flags "")
SET(__nvcc_archs_readable "")
# Tell NVCC to add binaries for the specIFied GPUs
FOREACH(__arch ${__cuda_arch_bin})
IF(__arch MATCHES "([0-9]+)\\(([0-9]+)\\)")
# User explicitly specIFied PTX for the concrete BIN
LIST(APPEND __nvcc_flags -gencode arch=compute_${CMAKE_MATCH_2},code=sm_${CMAKE_MATCH_1})
LIST(APPEND __nvcc_archs_readable sm_${CMAKE_MATCH_1})
else()
# User didn't explicitly specIFy PTX for the concrete BIN, we assume PTX=BIN
LIST(APPEND __nvcc_flags -gencode arch=compute_${__arch},code=sm_${__arch})
LIST(APPEND __nvcc_archs_readable sm_${__arch})
ENDIF()
ENDFOREACH()
# Tell NVCC to add PTX intermediate code for the specIFied architectures
FOREACH(__arch ${__cuda_arch_ptx})
LIST(APPEND __nvcc_flags -gencode arch=compute_${__arch},code=compute_${__arch})
LIST(APPEND __nvcc_archs_readable compute_${__arch})
ENDFOREACH()
STRING(REPLACE ";" " " __nvcc_archs_readable "${__nvcc_archs_readable}")
SET(${out_variable} ${__nvcc_flags} PARENT_SCOPE)
SET(${out_variable}_readable ${__nvcc_archs_readable} PARENT_SCOPE)
ENDFUNCTION()
// Copyright 2016-present, Facebook, Inc.
// All rights reserved.
//
// This source code is licensed under the license found in the
// LICENSE file in the root directory of this source tree.
// Helper function to draw pen strokes with
// nPlanes = 3, feature vector = (1,dx,dy)
extern "C" void scn_2_drawCurve(void **m, THFloatTensor *features,
THFloatTensor *stroke) {
auto location = THLongTensor_newWithSize1d(2);
auto location_ = THLongTensor_data(location);
auto vec = THFloatTensor_newWithSize1d(3);
auto vec_ = THFloatTensor_data(vec);
int n = THFloatTensor_size(stroke, 0) - 1;
float *s = THFloatTensor_data(stroke); // stroke is a [n+1,2] array
long idx = 0;
float x1, y1, x2, y2; // n line segments (x1,y1) to (x2,y2)
x2 = s[idx++];
y2 = s[idx++];
for (int i = 0; i < n; ++i) {
x1 = x2;
y1 = y2;
x2 = s[idx++];
y2 = s[idx++];
float inverse_length =
powf(1e-10 + (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1), -0.5);
vec_[0] = 1;
vec_[1] = (x2 - x1) * inverse_length;
vec_[2] = (y2 - y1) * inverse_length;
for (float a = 0; a < 1; a += inverse_length) {
location_[0] = x1 * a + x2 * (1 - a);
location_[1] = y1 * a + y2 * (1 - a);
scn_2_setInputSpatialLocation(m, features, location, vec, false);
}
}
THLongTensor_free(location);
THFloatTensor_free(vec);
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment