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

3d segmantation

parent f2e3800b
...@@ -44,7 +44,7 @@ class BatchNormalization(Module): ...@@ -44,7 +44,7 @@ class BatchNormalization(Module):
self.bias = None self.bias = None
def forward(self, input): def forward(self, input):
assert input.features.ndimension() == 0 or input.features.size(1) == self.nPlanes assert input.features.nelement() == 0 or input.features.size(1) == self.nPlanes
output = SparseConvNetTensor() output = SparseConvNetTensor()
output.metadata = input.metadata output.metadata = input.metadata
output.spatial_size = input.spatial_size output.spatial_size = input.spatial_size
...@@ -164,7 +164,78 @@ class BatchNormalizationFunction(Function): ...@@ -164,7 +164,78 @@ class BatchNormalizationFunction(Function):
runningVar, runningVar,
weight if weight is not None else nullptr, weight if weight is not None else nullptr,
bias if bias is not None else nullptr, bias if bias is not None else nullptr,
grad_weight.data if grad_weight is not None else nullptr, grad_weight if grad_weight is not None else nullptr,
grad_bias.data if grad_bias is not None else nullptr, grad_bias if grad_bias is not None else nullptr,
ctx.leakiness) ctx.leakiness)
return grad_input, grad_weight, grad_bias, None, None, None, None, None, None return grad_input, grad_weight, grad_bias, None, None, None, None, None, None
#
#
# class BatchNormalization(Module):
# """
# Parameters:
# nPlanes : number of input planes
# eps : small number used to stabilise standard deviation calculation
# momentum : for calculating running average for testing (default 0.9)
# affine : only 'true' is supported at present (default 'true')
# noise : add multiplicative and additive noise during training if >0.
# leakiness : Apply activation def inplace: 0<=leakiness<=1.
# 0 for ReLU, values in (0,1) for LeakyReLU, 1 for no activation def.
# """
# def __init__(
# self,
# nPlanes,
# eps=1e-4,
# momentum=0.9,
# affine=True,
# leakiness=1):
# Module.__init__(self)
# self.nPlanes = nPlanes
# self.eps = eps
# self.momentum = momentum
# self.affine = affine
# self.leakiness = leakiness
# self.register_buffer("runningMean", torch.Tensor(nPlanes).fill_(0))
# self.register_buffer("runningVar", torch.Tensor(nPlanes).fill_(1))
# if affine:
# self.weight = Parameter(torch.Tensor(nPlanes).fill_(1))
# self.bias = Parameter(torch.Tensor(nPlanes).fill_(0))
# else:
# self.weight = None
# self.bias = None
#
# def forward(self, input):
# output = SparseConvNetTensor()
# output.metadata = input.metadata
# output.spatial_size = input.spatial_size
# if input.features.ndimension() == 0:
# output.features = input.features
# else:
# output.features = input.features - input.features.mean(0, keepdim=True)
# if self.leakiness != 1:
# output.features = torch.nn.functional.leaky_relu(output.features, self.leakiness)
# return output
#
# def input_spatial_size(self, out_size):
# return out_size
#
# def __repr__(self):
# return str(self.bn)
#
# class BatchNormReLU(BatchNormalization):
# def __init__(self, nPlanes, eps=1e-4, momentum=0.9):
# BatchNormalization.__init__(self, nPlanes, eps, momentum, True, 0)
#
# def __repr__(self):
# s = 'BatchNormReLU(' + str(self.nPlanes) + ',eps=' + str(self.eps) + \
# ',momentum=' + str(self.momentum) + ',affine=' + str(self.affine) + ')'
# return s
#
#
# class BatchNormLeakyReLU(BatchNormalization):
# def __init__(self, nPlanes, eps=1e-4, momentum=0.9):
# BatchNormalization.__init__(self, nPlanes, eps, momentum, True, 0.333)
#
# def __repr__(self):
# s = 'BatchNormReLU(' + str(self.nPlanes) + ',eps=' + str(self.eps) + \
# ',momentum=' + str(self.momentum) + ',affine=' + str(self.affine) + ')'
# return s
...@@ -36,7 +36,7 @@ def updateStats(stats, output, target, loss): ...@@ -36,7 +36,7 @@ def updateStats(stats, output, target, loss):
# Top-5 score # Top-5 score
l = min(5, correct.size(1)) l = min(5, correct.size(1))
stats['top5'] += correct[:, :l].long().sum().item() stats['top5'] += correct[:, :l].long().sum().item()
stats['confusion matrix'].index_add_(0, target, F.softmax(output, 1).data) stats['confusion matrix'].index_add_(0, target, F.softmax(output, 1).detach())
def ClassificationTrainValidate(model, dataset, p): def ClassificationTrainValidate(model, dataset, p):
...@@ -150,7 +150,7 @@ def ClassificationTrainValidate(model, dataset, p): ...@@ -150,7 +150,7 @@ def ClassificationTrainValidate(model, dataset, p):
batch['idx'] = batch['idx'].cuda() batch['idx'] = batch['idx'].cuda()
batch['input'].to_variable() batch['input'].to_variable()
output = model(batch['input']) output = model(batch['input'])
pr.append(output.data) pr.append(output.detach())
ta.append(batch['target']) ta.append(batch['target'])
idxs.append(batch['idx']) idxs.append(batch['idx'])
pr = torch.cat(pr, 0) pr = torch.cat(pr, 0)
......
...@@ -27,12 +27,6 @@ class ConvolutionFunction(Function): ...@@ -27,12 +27,6 @@ class ConvolutionFunction(Function):
output_features = input_features.new() output_features = input_features.new()
ctx.input_metadata = input_metadata ctx.input_metadata = input_metadata
ctx.dimension = dimension ctx.dimension = dimension
# ctx.weight=weight
# ctx.bias=bias
# ctx.output_spatial_size=output_spatial_size
# ctx.filter_size=filter_size
# ctx.filter_stride=filter_stride
# bias??
ctx.save_for_backward( ctx.save_for_backward(
input_features, input_features,
input_spatial_size, input_spatial_size,
...@@ -53,8 +47,7 @@ class ConvolutionFunction(Function): ...@@ -53,8 +47,7 @@ class ConvolutionFunction(Function):
output_features, output_features,
weight, weight,
bias if bias is not None else nullptr, bias if bias is not None else nullptr,
0, # remove this parameter!! 0) # remove this parameter!!
torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
sparseconvnet.forward_pass_hidden_states += output_features.nelement() sparseconvnet.forward_pass_hidden_states += output_features.nelement()
return output_features return output_features
...@@ -79,9 +72,9 @@ class ConvolutionFunction(Function): ...@@ -79,9 +72,9 @@ class ConvolutionFunction(Function):
grad_output.contiguous(), grad_output.contiguous(),
weight, weight,
grad_weight, grad_weight,
grad_bias.data if grad_bias is not None else nullptr, grad_bias if grad_bias is not None else nullptr,
0, # remove this parameter 0, # remove this parameter
torch.cuda.IntTensor() if input_features.is_cuda else nullptr) )
return grad_input, grad_weight, grad_bias, None, None, None, None, None, None return grad_input, grad_weight, grad_bias, None, None, None, None, None, None
...@@ -105,7 +98,7 @@ class Convolution(Module): ...@@ -105,7 +98,7 @@ class Convolution(Module):
self.bias = None self.bias = None
def forward(self, input): def forward(self, input):
assert input.features.ndimension() == 0 or input.features.size(1) == self.nIn assert input.features.nelement() == 0 or input.features.size(1) == self.nIn
output = SparseConvNetTensor() output = SparseConvNetTensor()
output.metadata = input.metadata output.metadata = input.metadata
output.spatial_size =\ output.spatial_size =\
......
...@@ -41,7 +41,7 @@ class DeconvolutionFunction(Function): ...@@ -41,7 +41,7 @@ class DeconvolutionFunction(Function):
weight, weight,
bias if bias is not None else nullptr, bias if bias is not None else nullptr,
0, # remove this parameter!! 0, # remove this parameter!!
torch.cuda.IntTensor() if input_features.is_cuda else nullptr) )
sparseconvnet.forward_pass_hidden_states += output_features.nelement() sparseconvnet.forward_pass_hidden_states += output_features.nelement()
ctx.save_for_backward(input_features, ctx.save_for_backward(input_features,
output_features, output_features,
...@@ -81,9 +81,9 @@ class DeconvolutionFunction(Function): ...@@ -81,9 +81,9 @@ class DeconvolutionFunction(Function):
grad_output.contiguous(), grad_output.contiguous(),
weight, weight,
grad_weight, grad_weight,
grad_bias.data if grad_bias is not None else nullptr, grad_bias if grad_bias is not None else nullptr,
0, # remove this parameter 0, # remove this parameter
torch.cuda.IntTensor() if input_features.is_cuda else nullptr) )
return grad_input, grad_weight, grad_bias, None, None, None, None, None, None return grad_input, grad_weight, grad_bias, None, None, None, None, None, None
...@@ -107,7 +107,7 @@ class Deconvolution(Module): ...@@ -107,7 +107,7 @@ class Deconvolution(Module):
self.bias = None self.bias = None
def forward(self, input): def forward(self, input):
assert input.features.ndimension() == 0 or input.features.size(1) == self.nIn assert input.features.nelement() == 0 or input.features.size(1) == self.nIn
output = SparseConvNetTensor() output = SparseConvNetTensor()
output.metadata = input.metadata output.metadata = input.metadata
output.spatial_size =\ output.spatial_size =\
......
...@@ -73,7 +73,7 @@ class DenseToSparseFunction(Function): ...@@ -73,7 +73,7 @@ class DenseToSparseFunction(Function):
print(r) print(r)
print(grad_output) print(grad_output)
grad_input = grad_output.new().resize_( grad_input = grad_output.new().resize_(
ctx.aas2).zero_().index_copy_(0, r, grad_output.data) ctx.aas2).zero_().index_copy_(0, r, grad_output)
grad_input = grad_input.view(ctx.aas).permute( grad_input = grad_input.view(ctx.aas).permute(
*([0, ctx.dimension + 1] + list(range(1, ctx.dimension + 1)))) *([0, ctx.dimension + 1] + list(range(1, ctx.dimension + 1))))
return grad_input, None, None, None return grad_input, None, None, None
# 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.
import sparseconvnet
from torch.autograd import Function, Variable
from torch.nn import Module, Parameter
from .utils import *
from .sparseConvNetTensor import SparseConvNetTensor
from .metadata import Metadata
class FullConvolutionFunction(Function):
@staticmethod
def forward(
ctx,
input_features,
weight,
bias,
input_metadata,
output_metadata,
input_spatial_size,
output_spatial_size,
dimension,
filter_size,
filter_stride):
output_features=input_features.new()
ctx.input_metadata=input_metadata
ctx.output_metadata=output_metadata
ctx.dimension=dimension
ctx.save_for_backward(
input_features,
input_spatial_size,
weight,
bias,
output_spatial_size,
filter_size,
filter_stride)
sparseconvnet.forward_pass_multiplyAdd_count +=\
dim_typed_fn(
dimension, input_features, 'FullConvolution_updateOutput')(
input_spatial_size,
output_spatial_size,
filter_size,
filter_stride,
input_metadata.ffi,
output_metadata.ffi,
input_features,
output_features,
weight,
bias if bias is not None else nullptr,
0) #remove this parameter!!
sparseconvnet.forward_pass_hidden_states += output_features.nelement()
return output_features
@staticmethod
def backward(ctx, grad_output):
input_features, input_spatial_size, weight, bias, output_spatial_size, filter_size, filter_stride = ctx.saved_tensors
grad_input=grad_output.new()
grad_weight=grad_output.new().resize_as_(weight).zero_()
if bias is None:
grad_bias=None
else:
grad_bias = grad_output.data.new().resize_as_(bias).zero_()
dim_typed_fn(
ctx.dimension, input_features, 'FullConvolution_backward')(
input_spatial_size,
output_spatial_size,
filter_size,
filter_stride,
ctx.input_metadata.ffi,
ctx.output_metadata.ffi,
input_features,
grad_input,
grad_output.contiguous(),
weight,
grad_weight,
grad_bias if grad_bias is not None else nullptr,
0) #remove this parameter
return grad_input, grad_weight, grad_bias, None, None, None, None, None, None, None
class FullConvolution(Module):
def __init__(self, dimension, nIn, nOut, filter_size, filter_stride, bias):
Module.__init__(self)
self.dimension = dimension
self.nIn = nIn
self.nOut = nOut
self.filter_size = toLongTensor(dimension, filter_size)
self.filter_volume = self.filter_size.prod().item()
self.filter_stride = toLongTensor(dimension, filter_stride)
std = (2.0 / nIn / self.filter_volume)**0.5
self.weight = Parameter(torch.Tensor(
self.filter_volume * nIn, nOut).normal_(
0,
std))
if bias:
self.bias = Parameter(torch.Tensor(nOut).zero_())
else:
self.bias=None
def forward(self, input):
assert input.features.nelement()==0 or input.features.size(1) == self.nIn
output = SparseConvNetTensor()
output.metadata = Metadata(self.dimension)
output.spatial_size =\
(input.spatial_size - 1) * self.filter_stride + self.filter_size
output.features=FullConvolutionFunction().apply(
input.features,
self.weight,
self.bias,
input.metadata,
output.metadata,
input.spatial_size,
output.spatial_size,
self.dimension,
self.filter_size,
self.filter_stride,
)
return output
def __repr__(self):
s = 'FullConvolution ' + str(self.nIn) + '->' + str(self.nOut) + ' C'
if self.filter_size.max() == self.filter_size.min() and\
self.filter_stride.max() == self.filter_stride.min():
s = s + str(self.filter_size[0]) + '/' + str(self.filter_stride[0])
else:
s = s + '(' + str(self.filter_size[0])
for i in self.filter_size[1:]:
s = s + ',' + str(i)
s = s + ')/(' + str(self.filter_stride[0])
for i in self.filter_stride[1:]:
s = s + ',' + str(i)
s = s + ')'
return s
def input_spatial_size(self, out_size):
return (out_size - 1) * self.filter_stride + self.filter_size
...@@ -10,6 +10,7 @@ from .utils import * ...@@ -10,6 +10,7 @@ from .utils import *
from .sparseConvNetTensor import SparseConvNetTensor from .sparseConvNetTensor import SparseConvNetTensor
from .metadata import Metadata from .metadata import Metadata
class InputLayer(Module): class InputLayer(Module):
""" """
Takes a tuple (coords, features, batch_size [optional]) Takes a tuple (coords, features, batch_size [optional])
...@@ -27,6 +28,7 @@ class InputLayer(Module): ...@@ -27,6 +28,7 @@ class InputLayer(Module):
some of the batch items are totally empty. some of the batch items are totally empty.
In case of repetition in coords: In case of repetition in coords:
mode == 0 if the input is guaranteed to have no duplicates
mode == 1 to use the last item at each spatial location mode == 1 to use the last item at each spatial location
mode == 2 to keep the first item at each spatial location mode == 2 to keep the first item at each spatial location
mode == 3 to sum feature vectors sharing one spatial location mode == 3 to sum feature vectors sharing one spatial location
...@@ -57,6 +59,30 @@ class InputLayer(Module): ...@@ -57,6 +59,30 @@ class InputLayer(Module):
return output return output
class OutputLayer(Module):
"""
Used in conjunction with an InputLayer for 'autoencoder' style networks
Takes a SparseConvNetTensor and results a float Tensor of size
N x n_feature_planes
N is defined by the InputLayer
Behavior during forward-/back-propagation depends on the InputLayer's mode
"""
def __init__(self, dimension):
Module.__init__(self)
self.dimension = dimension
def forward(self, input):
output = OutputLayerFunction.apply(
self.dimension,
input.metadata,
input.features
)
return output
class BLInputLayer(Module): class BLInputLayer(Module):
""" """
Takes a tuple (coords, features) Takes a tuple (coords, features)
...@@ -109,7 +135,7 @@ class BLOutputLayer(Module): ...@@ -109,7 +135,7 @@ class BLOutputLayer(Module):
batch_size and length are defined by the BLInputLayer batch_size and length are defined by the BLInputLayer
Behavior during forward-/back-propagation depends on the BLInputLayer's Module mode Behavior during forward-/back-propagation depends on the BLInputLayer's mode
""" """
def __init__(self, dimension): def __init__(self, dimension):
Module.__init__(self) Module.__init__(self)
...@@ -123,6 +149,7 @@ class BLOutputLayer(Module): ...@@ -123,6 +149,7 @@ class BLOutputLayer(Module):
) )
return output return output
class InputLayerFunction(Function): class InputLayerFunction(Function):
@staticmethod @staticmethod
def forward( def forward(
...@@ -145,25 +172,52 @@ class InputLayerFunction(Function): ...@@ -145,25 +172,52 @@ class InputLayerFunction(Function):
input_features.contiguous(), input_features.contiguous(),
output_features, output_features,
batch_size, batch_size,
mode, mode
torch.cuda.IntTensor() if input_features.is_cuda else nullptr
) )
return output_features return output_features
@staticmethod @staticmethod
def backward(ctx, grad_output): def backward(ctx, grad_output):
grad_input = grad_output.data.new() grad_input = grad_output.new()
dim_typed_fn( dim_typed_fn(
ctx.dimension, ctx.dimension,
grad_output.data, grad_output,
'InputLayer_updateGradInput')( 'InputLayer_updateGradInput')(
ctx.metadata.ffi, ctx.metadata.ffi,
grad_input.data, grad_input,
grad_output.contiguous().data, grad_output.contiguous())
torch.cuda.IntTensor() if grad_output.data.is_cuda else nullptr)
return None, None, None, None, grad_input, None, None return None, None, None, None, grad_input, None, None
class OutputLayerFunction(Function):
@staticmethod
def forward(
ctx,
dimension,
metadata,
input_features):
output_features = input_features.new()
ctx.metadata = metadata
ctx.dimension = dimension
dim_typed_fn(dimension, input_features, 'OutputLayer_updateOutput')(
metadata.ffi,
input_features.contiguous(),
output_features
)
return output_features
@staticmethod
def backward(ctx, grad_output):
grad_input = grad_output.new()
grad_output=grad_output.contiguous()
dim_typed_fn(
ctx.dimension,
grad_output,
'OutputLayer_updateGradInput')(
ctx.metadata.ffi,
grad_input,
grad_output.contiguous())
return None, None, grad_input
class BLInputLayerFunction(Function): class BLInputLayerFunction(Function):
...@@ -185,28 +239,23 @@ class BLInputLayerFunction(Function): ...@@ -185,28 +239,23 @@ class BLInputLayerFunction(Function):
coords, coords,
input_features.contiguous(), input_features.contiguous(),
output_features, output_features,
mode, mode
torch.cuda.IntTensor() if input_features.is_cuda else nullptr
) )
return output_features return output_features
@staticmethod @staticmethod
def backward(ctx, grad_output): def backward(ctx, grad_output):
grad_input = grad_output.data.new() grad_input = grad_output.new()
dim_typed_fn( dim_typed_fn(
ctx.dimension, ctx.dimension,
grad_output.data, grad_output,
'BLInputLayer_updateGradInput')( 'BLInputLayer_updateGradInput')(
ctx.metadata.ffi, ctx.metadata.ffi,
grad_input.data, grad_input,
grad_output.contiguous().data, grad_output.contiguous())
torch.cuda.IntTensor() if grad_output.data.is_cuda else nullptr)
return None, None, None, None, grad_input, None return None, None, None, None, grad_input, None
class BLOutputLayerFunction(Function): class BLOutputLayerFunction(Function):
@staticmethod @staticmethod
def forward( def forward(
...@@ -220,20 +269,18 @@ class BLOutputLayerFunction(Function): ...@@ -220,20 +269,18 @@ class BLOutputLayerFunction(Function):
dim_typed_fn(dimension, input_features, 'BLOutputLayer_updateOutput')( dim_typed_fn(dimension, input_features, 'BLOutputLayer_updateOutput')(
metadata.ffi, metadata.ffi,
input_features.contiguous(), input_features.contiguous(),
output_features, output_features
torch.cuda.IntTensor() if input_features.is_cuda else nullptr
) )
return output_features return output_features
@staticmethod @staticmethod
def backward(ctx, grad_output): def backward(ctx, grad_output):
grad_input = grad_output.data.new() grad_input = grad_output.new()
dim_typed_fn( dim_typed_fn(
ctx.dimension, ctx.dimension,
grad_output.data, grad_output,
'BLOutputLayer_updateGradInput')( 'BLOutputLayer_updateGradInput')(
ctx.metadata.ffi, ctx.metadata.ffi,
grad_input.data, grad_input,
grad_output.contiguous().data, grad_output.contiguous())
torch.cuda.IntTensor() if grad_output.data.is_cuda else nullptr)
return None, None, grad_input return None, None, grad_input
...@@ -34,8 +34,7 @@ class MaxPoolingFunction(Function): ...@@ -34,8 +34,7 @@ class MaxPoolingFunction(Function):
input_metadata.ffi, input_metadata.ffi,
input_features, input_features,
output_features, output_features,
nFeaturesToDrop, nFeaturesToDrop)
torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
ctx.save_for_backward( ctx.save_for_backward(
input_features, input_features,
output_features, output_features,
...@@ -65,8 +64,7 @@ class MaxPoolingFunction(Function): ...@@ -65,8 +64,7 @@ class MaxPoolingFunction(Function):
grad_input, grad_input,
output_features, output_features,
grad_output, grad_output,
ctx.nFeaturesToDrop, ctx.nFeaturesToDrop)
torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
return grad_input, None, None, None, None, None, None, None return grad_input, None, None, None, None, None, None, None
......
# 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.
import sparseconvnet as scn
def SparseVggNet(dimension, nInputPlanes, layers):
"""
VGG style nets
Use submanifold convolutions
Also implements 'Plus'-augmented nets
"""
nPlanes = nInputPlanes
m = scn.Sequential()
for x in layers:
if x == 'MP':
m.add(scn.MaxPooling(dimension, 3, 2))
elif x[0] == 'MP':
m.add(scn.MaxPooling(dimension, x[1], x[2]))
elif x == 'C3/2':
m.add(scn.Convolution(dimension, nPlanes, nPlanes, 3, 2, False))
m.add(scn.BatchNormReLU(nPlanes))
elif x[0] == 'C3/2':
m.add(scn.Convolution(dimension, nPlanes, x[1], 3, 2, False))
nPlanes = x[1]
m.add(scn.BatchNormReLU(nPlanes))
elif x[0] == 'C' and len(x) == 2:
m.add(scn.SubmanifoldConvolution(dimension, nPlanes, x[1], 3, False))
nPlanes = x[1]
m.add(scn.BatchNormReLU(nPlanes))
elif x[0] == 'C' and len(x) == 3:
m.add(scn.ConcatTable()
.add(
scn.SubmanifoldConvolution(dimension, nPlanes, x[1], 3, False)
).add(
scn.Sequential()
.add(scn.Convolution(dimension, nPlanes, x[2], 3, 2, False))
.add(scn.BatchNormReLU(x[2]))
.add(scn.SubmanifoldConvolution(dimension, x[2], x[2], 3, False))
.add(scn.BatchNormReLU(x[2]))
.add(scn.Deconvolution(dimension, x[2], x[2], 3, 2, False))
)).add(scn.JoinTable())
nPlanes = x[1] + x[2]
m.add(scn.BatchNormReLU(nPlanes))
elif x[0] == 'C' and len(x) == 4:
m.add(scn.ConcatTable()
.add(
scn.SubmanifoldConvolution(dimension, nPlanes, x[1], 3, False)
)
.add(
scn.Sequential()
.add(scn.Convolution(dimension, nPlanes, x[2], 3, 2, False))
.add(scn.BatchNormReLU(x[2]))
.add(scn.SubmanifoldConvolution(dimension, x[2], x[2], 3, False))
.add(scn.BatchNormReLU(x[2]))
.add(scn.Deconvolution(dimension, x[2], x[2], 3, 2, False))
)
.add(scn.Sequential()
.add(scn.Convolution(dimension, nPlanes, x[3], 3, 2, False))
.add(scn.BatchNormReLU(x[3]))
.add(scn.SubmanifoldConvolution(dimension, x[3], x[3], 3, False))
.add(scn.BatchNormReLU(x[3]))
.add(scn.Convolution(dimension, x[3], x[3], 3, 2, False))
.add(scn.BatchNormReLU(x[3]))
.add(scn.SubmanifoldConvolution(dimension, x[3], x[3], 3, False))
.add(scn.BatchNormReLU(x[3]))
.add(scn.Deconvolution(dimension, x[3], x[3], 3, 2, False))
.add(scn.BatchNormReLU(x[3]))
.add(scn.SubmanifoldConvolution(dimension, x[3], x[3], 3, False))
.add(scn.BatchNormReLU(x[3]))
.add(scn.Deconvolution(dimension, x[3], x[3], 3, 2, False))
)).add(scn.JoinTable())
nPlanes = x[1] + x[2] + x[3]
m.add(scn.BatchNormReLU(nPlanes))
elif x[0] == 'C' and len(x) == 5:
m.add(scn.ConcatTable()
.add(
scn.SubmanifoldConvolution(dimension, nPlanes, x[1], 3, False)
)
.add(
scn.Sequential()
.add(scn.Convolution(dimension, nPlanes, x[2], 3, 2, False))
.add(scn.BatchNormReLU(x[2]))
.add(scn.SubmanifoldConvolution(dimension, x[2], x[2], 3, False))
.add(scn.BatchNormReLU(x[2]))
.add(scn.Deconvolution(dimension, x[2], x[2], 3, 2, False))
)
.add(scn.Sequential()
.add(scn.Convolution(dimension, nPlanes, x[3], 3, 2, False))
.add(scn.BatchNormReLU(x[3]))
.add(scn.SubmanifoldConvolution(dimension, x[3], x[3], 3, False))
.add(scn.BatchNormReLU(x[3]))
.add(scn.Convolution(dimension, x[3], x[3], 3, 2, False))
.add(scn.BatchNormReLU(x[3]))
.add(scn.SubmanifoldConvolution(dimension, x[3], x[3], 3, False))
.add(scn.BatchNormReLU(x[3]))
.add(scn.Deconvolution(dimension, x[3], x[3], 3, 2, False))
.add(scn.BatchNormReLU(x[3]))
.add(scn.SubmanifoldConvolution(dimension, x[3], x[3], 3, False))
.add(scn.BatchNormReLU(x[3]))
.add(scn.Deconvolution(dimension, x[3], x[3], 3, 2, False))
)
.add(scn.Sequential()
.add(scn.Convolution(dimension, nPlanes, x[4], 3, 2, False))
.add(scn.BatchNormReLU(x[4]))
.add(scn.SubmanifoldConvolution(dimension, x[4], x[4], 3, False))
.add(scn.BatchNormReLU(x[4]))
.add(scn.Convolution(dimension, x[4], x[4], 3, 2, False))
.add(scn.BatchNormReLU(x[4]))
.add(scn.SubmanifoldConvolution(dimension, x[4], x[4], 3, False))
.add(scn.BatchNormReLU(x[4]))
.add(scn.Convolution(dimension, x[4], x[4], 3, 2, False))
.add(scn.BatchNormReLU(x[4]))
.add(scn.SubmanifoldConvolution(dimension, x[4], x[4], 3, False))
.add(scn.BatchNormReLU(x[4]))
.add(scn.Deconvolution(dimension, x[4], x[4], 3, 2, False))
.add(scn.BatchNormReLU(x[4]))
.add(scn.SubmanifoldConvolution(dimension, x[4], x[4], 3, False))
.add(scn.BatchNormReLU(x[4]))
.add(scn.Deconvolution(dimension, x[4], x[4], 3, 2, False))
.add(scn.BatchNormReLU(x[4]))
.add(scn.SubmanifoldConvolution(dimension, x[4], x[4], 3, False))
.add(scn.BatchNormReLU(x[4]))
.add(scn.Deconvolution(dimension, x[4], x[4], 3, 2, False))
)).add(scn.JoinTable())
nPlanes = x[1] + x[2] + x[3] + x[4]
m.add(scn.BatchNormReLU(nPlanes))
return m
def SparseResNet(dimension, nInputPlanes, layers):
"""
pre-activated ResNet
e.g. layers = {{'basic',16,2,1},{'basic',32,2}}
"""
nPlanes = nInputPlanes
m = scn.Sequential()
def residual(nIn, nOut, stride):
if stride > 1:
return scn.Convolution(dimension, nIn, nOut, 3, stride, False)
elif nIn != nOut:
return scn.NetworkInNetwork(nIn, nOut, False)
else:
return scn.Identity()
for blockType, n, reps, stride in layers:
for rep in range(reps):
if blockType[0] == 'b': # basic block
if rep == 0:
m.add(scn.BatchNormReLU(nPlanes))
m.add(
scn.ConcatTable().add(
scn.Sequential().add(
scn.SubmanifoldConvolution(
dimension,
nPlanes,
n,
3,
False) if stride == 1 else scn.Convolution(
dimension,
nPlanes,
n,
3,
stride,
False)) .add(
scn.BatchNormReLU(n)) .add(
scn.SubmanifoldConvolution(
dimension,
n,
n,
3,
False))) .add(
residual(
nPlanes,
n,
stride)))
else:
m.add(
scn.ConcatTable().add(
scn.Sequential().add(
scn.BatchNormReLU(nPlanes)) .add(
scn.SubmanifoldConvolution(
dimension,
nPlanes,
n,
3,
False)) .add(
scn.BatchNormReLU(n)) .add(
scn.SubmanifoldConvolution(
dimension,
n,
n,
3,
False))) .add(
scn.Identity()))
nPlanes = n
m.add(scn.AddTable())
m.add(scn.BatchNormReLU(nPlanes))
return m
def UNet(dimension, reps, nPlanes, residual_blocks=False, downsample=[2, 2]):
"""
U-Net style network with VGG or ResNet-style blocks.
For voxel level prediction:
import sparseconvnet as scn
import torch.nn
class Model(nn.Module):
def __init__(self):
nn.Module.__init__(self)
self.sparseModel = scn.Sequential().add(
scn.SubmanifoldConvolution(3, nInputFeatures, 64, 3, False)).add(
scn.UNet(3, 2, [64, 128, 192, 256], residual_blocks=True, downsample=[2, 2]))
self.linear = nn.Linear(64, nClasses)
def forward(self,x):
x=self.sparseModel(x).features
x=self.linear(x)
return x
"""
def block(m, a, b):
if residual_blocks: #ResNet style blocks
m.add(scn.ConcatTable()
.add(scn.Identity() if a == b else scn.NetworkInNetwork(a, b, False))
.add(scn.Sequential()
.add(scn.BatchNormReLU(a))
.add(scn.SubmanifoldConvolution(dimension, a, b, 3, False))
.add(scn.BatchNormReLU(b))
.add(scn.SubmanifoldConvolution(dimension, b, b, 3, False)))
).add(scn.AddTable())
else: #VGG style blocks
m.add(scn.Sequential()
.add(scn.BatchNormReLU(a))
.add(scn.SubmanifoldConvolution(dimension, a, b, 3, False)))
def U(nPlanes): #Recursive function
m = scn.Sequential()
if len(nPlanes) == 1:
for _ in range(reps):
block(m, nPlanes[0], nPlanes[0])
else:
m = scn.Sequential()
for _ in range(reps):
block(m, nPlanes[0], nPlanes[0])
m.add(
scn.ConcatTable().add(
scn.Identity()).add(
scn.Sequential().add(
scn.BatchNormReLU(nPlanes[0])).add(
scn.Convolution(dimension, nPlanes[0], nPlanes[1],
downsample[0], downsample[1], False)).add(
U(nPlanes[1:])).add(
scn.BatchNormReLU(nPlanes[1])).add(
scn.Deconvolution(dimension, nPlanes[1], nPlanes[0],
downsample[0], downsample[1], False))))
m.add(scn.JoinTable())
for i in range(reps):
block(m, nPlanes[0] * (2 if i == 0 else 1), nPlanes[0])
return m
m = U(nPlanes)
return m
def FullyConvolutionalNet(dimension, reps, nPlanes, residual_blocks=False, downsample=[2, 2]):
"""
Fully-convolutional style network with VGG or ResNet-style blocks.
For voxel level prediction:
import sparseconvnet as scn
import torch.nn
class Model(nn.Module):
def __init__(self):
nn.Module.__init__(self)
self.sparseModel = scn.Sequential().add(
scn.SubmanifoldConvolution(3, nInputFeatures, 64, 3, False)).add(
scn.FullyConvolutionalNet(3, 2, [64, 128, 192, 256], residual_blocks=True, downsample=[2, 2]))
self.linear = nn.Linear(64+128+192+256, nClasses)
def forward(self,x):
x=self.sparseModel(x).features
x=self.linear(x)
return x
"""
def block(m, a, b):
if residual_blocks: #ResNet style blocks
m.add(scn.ConcatTable()
.add(scn.Identity() if a == b else scn.NetworkInNetwork(a, b, False))
.add(scn.Sequential()
.add(scn.BatchNormReLU(a))
.add(scn.SubmanifoldConvolution(dimension, a, b, 3, False))
.add(scn.BatchNormReLU(b))
.add(scn.SubmanifoldConvolution(dimension, b, b, 3, False)))
).add(scn.AddTable())
else: #VGG style blocks
m.add(scn.Sequential()
.add(scn.BatchNormReLU(a))
.add(scn.SubmanifoldConvolution(dimension, a, b, 3, False)))
def U(nPlanes): #Recursive function
m = scn.Sequential()
if len(nPlanes) == 1:
for _ in range(reps):
block(m, nPlanes[0], nPlanes[0])
else:
m = scn.Sequential()
for _ in range(reps):
block(m, nPlanes[0], nPlanes[0])
m.add(
scn.ConcatTable().add(
scn.Identity()).add(
scn.Sequential().add(
scn.BatchNormReLU(nPlanes[0])).add(
scn.Convolution(dimension, nPlanes[0], nPlanes[1],
downsample[0], downsample[1], False)).add(
U(nPlanes[1:])).add(
scn.UnPooling(dimension, downsample[0], downsample[1]))))
m.add(scn.JoinTable())
return m
m = U(nPlanes)
return m
...@@ -52,7 +52,7 @@ class NetworkInNetworkFunction(Function): ...@@ -52,7 +52,7 @@ class NetworkInNetworkFunction(Function):
input_features, input_features,
grad_output, grad_output,
grad_weight, grad_weight,
grad_bias.data if grad_bias is not None else nullptr) grad_bias if grad_bias is not None else nullptr)
return grad_input, grad_weight, grad_bias return grad_input, grad_weight, grad_bias
...@@ -72,7 +72,7 @@ class NetworkInNetwork(Module): ...@@ -72,7 +72,7 @@ class NetworkInNetwork(Module):
self.bias = None self.bias = None
def forward(self, input): def forward(self, input):
assert input.features.ndimension() == 0 or input.features.size(1) == self.nIn assert input.features.nelement() == 0 or input.features.size(1) == self.nIn
output = SparseConvNetTensor() output = SparseConvNetTensor()
output.metadata = input.metadata output.metadata = input.metadata
output.spatial_size = input.spatial_size output.spatial_size = input.spatial_size
......
# 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.
import sparseconvnet
from torch.autograd import Function
from torch.nn import Module, Parameter
from .utils import *
from .sparseConvNetTensor import SparseConvNetTensor
from .convolution import ConvolutionFunction
class RandomizedStrideConvolutionFunction(Function):
@staticmethod
def forward(
ctx,
input_features,
weight,
bias,
input_metadata,
input_spatial_size,
output_spatial_size,
dimension,
filter_size,
filter_stride):
output_features = input_features.new()
ctx.input_metadata = input_metadata
ctx.dimension = dimension
ctx.save_for_backward(
input_features,
input_spatial_size,
weight,
bias,
output_spatial_size,
filter_size,
filter_stride)
sparseconvnet.forward_pass_multiplyAdd_count +=\
dim_typed_fn(
dimension, input_features, 'RandomizedStrideConvolution_updateOutput')(
input_spatial_size,
output_spatial_size,
filter_size,
filter_stride,
input_metadata.ffi,
input_features,
output_features,
weight,
bias if bias is not None else nullptr,
0) # remove this parameter!!
sparseconvnet.forward_pass_hidden_states += output_features.nelement()
return output_features
@staticmethod
def backward(ctx, grad_output):
input_features, input_spatial_size, weight, bias, output_spatial_size, filter_size, filter_stride = ctx.saved_tensors
grad_input = grad_output.new()
grad_weight = grad_output.new().resize_as_(weight).zero_()
if bias is None:
grad_bias = None
else:
grad_bias = grad_output.new().resize_as_(bias).zero_()
dim_typed_fn(
ctx.dimension, input_features, 'RandomizedStrideConvolution_backward')(
input_spatial_size,
output_spatial_size,
filter_size,
filter_stride,
ctx.input_metadata.ffi,
input_features,
grad_input,
grad_output.contiguous(),
weight,
grad_weight,
grad_bias if grad_bias is not None else nullptr,
0, # remove this parameter
)
return grad_input, grad_weight, grad_bias, None, None, None, None, None, None
class RandomizedStrideConvolution(Module):
"""
A bit like Fractional Max Pooling during training, but at test time it
operates like a regular sparseconvnet.Convolution.
Don't use with scn.InputBatch.precomute_metadata()!!
Like regular a sparseconvnet.Convolution, is can be paired with a
sparseconvnet.Deconvolution module in an UNet style network, to restore
the input sparsity pattern.
"""
def __init__(self, dimension, nIn, nOut, filter_size, filter_stride, bias):
Module.__init__(self)
self.dimension = dimension
self.nIn = nIn
self.nOut = nOut
self.filter_size = toLongTensor(dimension, filter_size)
self.filter_volume = self.filter_size.prod().item()
self.filter_stride = toLongTensor(dimension, filter_stride)
std = (2.0 / nIn / self.filter_volume)**0.5
self.weight = Parameter(torch.Tensor(
self.filter_volume * nIn, nOut).normal_(
0,
std))
if bias:
self.bias = Parameter(torch.Tensor(nOut).zero_())
else:
self.bias = None
def forward(self, input):
assert input.features.ndimension() == 0 or input.features.size(1) == self.nIn
output = SparseConvNetTensor()
output.metadata = input.metadata
output.spatial_size =\
(input.spatial_size - self.filter_size) / self.filter_stride + 1
assert ((output.spatial_size - 1) * self.filter_stride +
self.filter_size == input.spatial_size).all()
output.features = (RandomizedStrideConvolutionFunction if self.training else ConvolutionFunction).apply(
#output.features = ConvolutionFunction.apply(
#output.features = RandomizedStrideConvolutionFunction.apply(
input.features,
self.weight,
self.bias,
input.metadata,
input.spatial_size,
output.spatial_size,
self.dimension,
self.filter_size,
self.filter_stride,
)
return output
def __repr__(self):
s = 'RandomizedStrideConvolution ' + str(self.nIn) + '->' + str(self.nOut) + ' C'
if self.filter_size.max().item() == self.filter_size.min().item() and\
self.filter_stride.max().item() == self.filter_stride.min().item():
s = s + str(self.filter_size[0].item()) + \
'/' + str(self.filter_stride[0].item())
else:
s = s + '(' + str(self.filter_size[0].item())
for i in self.filter_size[1:]:
s = s + ',' + str(i.item())
s = s + ')/(' + str(self.filter_stride[0].item())
for i in self.filter_stride[1:]:
s = s + ',' + str(i.item())
s = s + ')'
return s
def input_spatial_size(self, out_size):
return (out_size - 1) * self.filter_stride + self.filter_size
# 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.
from torch.autograd import Function
from torch.nn import Module
from .utils import *
from .sparseConvNetTensor import SparseConvNetTensor
from .maxPooling import MaxPoolingFunction
class RandomizedStrideMaxPoolingFunction(Function):
@staticmethod
def forward(
ctx,
input_features,
input_metadata,
input_spatial_size,
output_spatial_size,
dimension,
pool_size,
pool_stride,
nFeaturesToDrop):
ctx.input_metadata = input_metadata
ctx.dimension = dimension
ctx.nFeaturesToDrop = nFeaturesToDrop
output_features = input_features.new()
dim_typed_fn(dimension, input_features, 'RandomizedStrideMaxPooling_updateOutput')(
input_spatial_size,
output_spatial_size,
pool_size,
pool_stride,
input_metadata.ffi,
input_features,
output_features,
nFeaturesToDrop)
ctx.save_for_backward(
input_features,
output_features,
input_spatial_size,
output_spatial_size,
pool_size,
pool_stride)
return output_features
@staticmethod
def backward(ctx, grad_output):
input_features,\
output_features,\
input_spatial_size,\
output_spatial_size,\
pool_size,\
pool_stride = ctx.saved_tensors
grad_input = grad_output.new()
dim_typed_fn(
ctx.dimension, input_features, 'RandomizedStrideMaxPooling_updateGradInput')(
input_spatial_size,
output_spatial_size,
pool_size,
pool_stride,
ctx.input_metadata.ffi,
input_features,
grad_input,
output_features,
grad_output,
ctx.nFeaturesToDrop)
return grad_input, None, None, None, None, None, None, None
class RandomizedStrideMaxPooling(Module):
def __init__(self, dimension, pool_size, pool_stride, nFeaturesToDrop=0):
super(RandomizedStrideMaxPooling, self).__init__()
self.dimension = dimension
self.pool_size = toLongTensor(dimension, pool_size)
self.pool_stride = toLongTensor(dimension, pool_stride)
self.nFeaturesToDrop = nFeaturesToDrop
def forward(self, input):
output = SparseConvNetTensor()
output.metadata = input.metadata
output.spatial_size = (
input.spatial_size - self.pool_size) / self.pool_stride + 1
assert ((output.spatial_size - 1) * self.pool_stride +
self.pool_size == input.spatial_size).all()
output.features = (RandomizedStrideMaxPoolingFunction if self.training else MaxPoolingFunction).apply(
input.features,
input.metadata,
input.spatial_size,
output.spatial_size,
self.dimension,
self.pool_size,
self.pool_stride,
self.nFeaturesToDrop)
return output
def input_spatial_size(self, out_size):
return (out_size - 1) * self.pool_stride + self.pool_size
def __repr__(self):
s = 'RandomizedStrideMaxPooling'
if self.pool_size.max().item() == self.pool_size.min().item() and\
self.pool_stride.max().item() == self.pool_stride.min().item():
s = s + str(self.pool_size[0].item()) + \
'/' + str(self.pool_stride[0].item())
else:
s = s + '(' + str(self.pool_size[0].item())
for i in self.pool_size[1:]:
s = s + ',' + str(i.item())
s = s + ')/(' + str(self.pool_stride[0].item())
for i in self.pool_stride[1:]:
s = s + ',' + str(i.item())
s = s + ')'
if self.nFeaturesToDrop > 0:
s = s + ' nFeaturesToDrop = ' + self.nFeaturesToDrop
return s
...@@ -42,7 +42,6 @@ class SparseToDenseFunction(Function): ...@@ -42,7 +42,6 @@ class SparseToDenseFunction(Function):
input_metadata.ffi, input_metadata.ffi,
input_features, input_features,
output, output,
torch.cuda.IntTensor() if input_features.is_cuda else nullptr,
nPlanes) nPlanes)
return output return output
...@@ -58,8 +57,7 @@ class SparseToDenseFunction(Function): ...@@ -58,8 +57,7 @@ class SparseToDenseFunction(Function):
ctx.input_metadata.ffi, ctx.input_metadata.ffi,
input_features, input_features,
grad_input, grad_input,
grad_output.contiguous(), grad_output.contiguous())
torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
return grad_input, None, None, None, None return grad_input, None, None, None, None
......
# 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.
# 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.
import sparseconvnet
from torch.autograd import Function, Variable
from torch.nn import Module, Parameter
from .utils import *
from .sparseConvNetTensor import SparseConvNetTensor
from .metadata import Metadata
class Sparsify(Module):
def __init__(self, dimension):
Module.__init__(self)
self.dimension = dimension
def forward(self, input):
output = SparseConvNetTensor()
output.metadata = Metadata(self.dimension)
output.spatial_size = input.spatial_size
active = input.features[:,0]>0
output.features=input.features[active]
active=active.type('torch.LongTensor')
dim_fn(self.dimension, 'sparsifyMetadata')(
input.metadata.ffi,
output.metadata.ffi,
input.spatial_size,
active.byte(),
active.cumsum(0))
return output
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