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):
self.bias = None
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.metadata = input.metadata
output.spatial_size = input.spatial_size
......@@ -164,7 +164,78 @@ class BatchNormalizationFunction(Function):
runningVar,
weight if weight 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_bias.data if grad_bias is not None else nullptr,
grad_weight if grad_weight is not None else nullptr,
grad_bias if grad_bias is not None else nullptr,
ctx.leakiness)
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):
# Top-5 score
l = min(5, correct.size(1))
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):
......@@ -150,7 +150,7 @@ def ClassificationTrainValidate(model, dataset, p):
batch['idx'] = batch['idx'].cuda()
batch['input'].to_variable()
output = model(batch['input'])
pr.append(output.data)
pr.append(output.detach())
ta.append(batch['target'])
idxs.append(batch['idx'])
pr = torch.cat(pr, 0)
......
......@@ -27,12 +27,6 @@ class ConvolutionFunction(Function):
output_features = input_features.new()
ctx.input_metadata = input_metadata
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(
input_features,
input_spatial_size,
......@@ -53,8 +47,7 @@ class ConvolutionFunction(Function):
output_features,
weight,
bias if bias is not None else nullptr,
0, # remove this parameter!!
torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
0) # remove this parameter!!
sparseconvnet.forward_pass_hidden_states += output_features.nelement()
return output_features
......@@ -79,9 +72,9 @@ class ConvolutionFunction(Function):
grad_output.contiguous(),
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
torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
)
return grad_input, grad_weight, grad_bias, None, None, None, None, None, None
......@@ -105,7 +98,7 @@ class Convolution(Module):
self.bias = None
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.metadata = input.metadata
output.spatial_size =\
......
......@@ -41,7 +41,7 @@ class DeconvolutionFunction(Function):
weight,
bias if bias is not None else nullptr,
0, # remove this parameter!!
torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
)
sparseconvnet.forward_pass_hidden_states += output_features.nelement()
ctx.save_for_backward(input_features,
output_features,
......@@ -81,9 +81,9 @@ class DeconvolutionFunction(Function):
grad_output.contiguous(),
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
torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
)
return grad_input, grad_weight, grad_bias, None, None, None, None, None, None
......@@ -107,7 +107,7 @@ class Deconvolution(Module):
self.bias = None
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.metadata = input.metadata
output.spatial_size =\
......
......@@ -73,7 +73,7 @@ class DenseToSparseFunction(Function):
print(r)
print(grad_output)
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(
*([0, ctx.dimension + 1] + list(range(1, ctx.dimension + 1))))
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 *
from .sparseConvNetTensor import SparseConvNetTensor
from .metadata import Metadata
class InputLayer(Module):
"""
Takes a tuple (coords, features, batch_size [optional])
......@@ -27,6 +28,7 @@ class InputLayer(Module):
some of the batch items are totally empty.
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 == 2 to keep the first item at each spatial location
mode == 3 to sum feature vectors sharing one spatial location
......@@ -57,6 +59,30 @@ class InputLayer(Module):
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):
"""
Takes a tuple (coords, features)
......@@ -109,7 +135,7 @@ class BLOutputLayer(Module):
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):
Module.__init__(self)
......@@ -123,6 +149,7 @@ class BLOutputLayer(Module):
)
return output
class InputLayerFunction(Function):
@staticmethod
def forward(
......@@ -145,25 +172,52 @@ class InputLayerFunction(Function):
input_features.contiguous(),
output_features,
batch_size,
mode,
torch.cuda.IntTensor() if input_features.is_cuda else nullptr
mode
)
return output_features
@staticmethod
def backward(ctx, grad_output):
grad_input = grad_output.data.new()
grad_input = grad_output.new()
dim_typed_fn(
ctx.dimension,
grad_output.data,
grad_output,
'InputLayer_updateGradInput')(
ctx.metadata.ffi,
grad_input.data,
grad_output.contiguous().data,
torch.cuda.IntTensor() if grad_output.data.is_cuda else nullptr)
grad_input,
grad_output.contiguous())
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):
......@@ -185,28 +239,23 @@ class BLInputLayerFunction(Function):
coords,
input_features.contiguous(),
output_features,
mode,
torch.cuda.IntTensor() if input_features.is_cuda else nullptr
mode
)
return output_features
@staticmethod
def backward(ctx, grad_output):
grad_input = grad_output.data.new()
grad_input = grad_output.new()
dim_typed_fn(
ctx.dimension,
grad_output.data,
grad_output,
'BLInputLayer_updateGradInput')(
ctx.metadata.ffi,
grad_input.data,
grad_output.contiguous().data,
torch.cuda.IntTensor() if grad_output.data.is_cuda else nullptr)
grad_input,
grad_output.contiguous())
return None, None, None, None, grad_input, None
class BLOutputLayerFunction(Function):
@staticmethod
def forward(
......@@ -220,20 +269,18 @@ class BLOutputLayerFunction(Function):
dim_typed_fn(dimension, input_features, 'BLOutputLayer_updateOutput')(
metadata.ffi,
input_features.contiguous(),
output_features,
torch.cuda.IntTensor() if input_features.is_cuda else nullptr
output_features
)
return output_features
@staticmethod
def backward(ctx, grad_output):
grad_input = grad_output.data.new()
grad_input = grad_output.new()
dim_typed_fn(
ctx.dimension,
grad_output.data,
grad_output,
'BLOutputLayer_updateGradInput')(
ctx.metadata.ffi,
grad_input.data,
grad_output.contiguous().data,
torch.cuda.IntTensor() if grad_output.data.is_cuda else nullptr)
grad_input,
grad_output.contiguous())
return None, None, grad_input
......@@ -34,8 +34,7 @@ class MaxPoolingFunction(Function):
input_metadata.ffi,
input_features,
output_features,
nFeaturesToDrop,
torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
nFeaturesToDrop)
ctx.save_for_backward(
input_features,
output_features,
......@@ -65,8 +64,7 @@ class MaxPoolingFunction(Function):
grad_input,
output_features,
grad_output,
ctx.nFeaturesToDrop,
torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
ctx.nFeaturesToDrop)
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):
input_features,
grad_output,
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
......@@ -72,7 +72,7 @@ class NetworkInNetwork(Module):
self.bias = None
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.metadata = input.metadata
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):
input_metadata.ffi,
input_features,
output,
torch.cuda.IntTensor() if input_features.is_cuda else nullptr,
nPlanes)
return output
......@@ -58,8 +57,7 @@ class SparseToDenseFunction(Function):
ctx.input_metadata.ffi,
input_features,
grad_input,
grad_output.contiguous(),
torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
grad_output.contiguous())
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