Commit d77687a6 authored by Benjamin Graham's avatar Benjamin Graham Committed by Benjamin Thomas Graham
Browse files

Rename ValidConvolutions to SubmanifoldConvolutions, update for PyTorch 0.4 Tensor/Variable merge

parent 297e04c0
...@@ -6,12 +6,13 @@ ...@@ -6,12 +6,13 @@
import sparseconvnet import sparseconvnet
import torch.nn.functional as F import torch.nn.functional as F
from torch.autograd import Function, Variable from torch.autograd import Function
from torch.nn import Module, Parameter from torch.nn import Module, Parameter
from .utils import * from .utils import *
from .sparseConvNetTensor import SparseConvNetTensor from .sparseConvNetTensor import SparseConvNetTensor
from .batchNormalization import BatchNormalization from .batchNormalization import BatchNormalization
class Sigmoid(Module): class Sigmoid(Module):
def forward(self, input): def forward(self, input):
output = SparseConvNetTensor() output = SparseConvNetTensor()
...@@ -20,6 +21,7 @@ class Sigmoid(Module): ...@@ -20,6 +21,7 @@ class Sigmoid(Module):
output.spatial_size = input.spatial_size output.spatial_size = input.spatial_size
return output return output
class Tanh(Module): class Tanh(Module):
def forward(self, input): def forward(self, input):
output = SparseConvNetTensor() output = SparseConvNetTensor()
...@@ -28,6 +30,7 @@ class Tanh(Module): ...@@ -28,6 +30,7 @@ class Tanh(Module):
output.spatial_size = input.spatial_size output.spatial_size = input.spatial_size
return output return output
class ReLU(Module): class ReLU(Module):
def forward(self, input): def forward(self, input):
output = SparseConvNetTensor() output = SparseConvNetTensor()
...@@ -36,6 +39,7 @@ class ReLU(Module): ...@@ -36,6 +39,7 @@ class ReLU(Module):
output.spatial_size = input.spatial_size output.spatial_size = input.spatial_size
return output return output
class ELU(Module): class ELU(Module):
def forward(self, input): def forward(self, input):
output = SparseConvNetTensor() output = SparseConvNetTensor()
......
...@@ -4,11 +4,12 @@ ...@@ -4,11 +4,12 @@
# This source code is licensed under the license found in the # This source code is licensed under the license found in the
# LICENSE file in the root directory of this source tree. # LICENSE file in the root directory of this source tree.
from torch.autograd import Function, Variable from torch.autograd import Function
from torch.nn import Module from torch.nn import Module
from .utils import * from .utils import *
from .sparseConvNetTensor import SparseConvNetTensor from .sparseConvNetTensor import SparseConvNetTensor
class AveragePoolingFunction(Function): class AveragePoolingFunction(Function):
@staticmethod @staticmethod
def forward( def forward(
...@@ -21,15 +22,11 @@ class AveragePoolingFunction(Function): ...@@ -21,15 +22,11 @@ class AveragePoolingFunction(Function):
pool_size, pool_size,
pool_stride, pool_stride,
nFeaturesToDrop): nFeaturesToDrop):
ctx.input_features=input_features ctx.input_metadata = input_metadata
ctx.input_metadata=input_metadata
ctx.input_spatial_size = input_spatial_size
ctx.output_spatial_size = output_spatial_size
ctx.dimension = dimension ctx.dimension = dimension
ctx.pool_size = pool_size
ctx.pool_stride = pool_stride
ctx.nFeaturesToDrop = nFeaturesToDrop ctx.nFeaturesToDrop = nFeaturesToDrop
output_features = input_features.new() output_features = input_features.new()
dim_typed_fn(dimension, input_features, 'AveragePooling_updateOutput')( dim_typed_fn(dimension, input_features, 'AveragePooling_updateOutput')(
input_spatial_size, input_spatial_size,
output_spatial_size, output_spatial_size,
...@@ -40,23 +37,36 @@ class AveragePoolingFunction(Function): ...@@ -40,23 +37,36 @@ class AveragePoolingFunction(Function):
output_features, output_features,
nFeaturesToDrop, nFeaturesToDrop,
torch.cuda.IntTensor() if input_features.is_cuda else nullptr) torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
ctx.save_for_backward(input_features,
output_features,
input_spatial_size,
output_spatial_size,
pool_size,
pool_stride)
return output_features return output_features
@staticmethod @staticmethod
def backward(ctx, grad_output): def backward(ctx, grad_output):
grad_input=Variable(grad_output.data.new()) 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( dim_typed_fn(
ctx.dimension, ctx.input_features, 'AveragePooling_updateGradInput')( ctx.dimension, input_features, 'AveragePooling_updateGradInput')(
ctx.input_spatial_size, input_spatial_size,
ctx.output_spatial_size, output_spatial_size,
ctx.pool_size, pool_size,
ctx.pool_stride, pool_stride,
ctx.input_metadata.ffi, ctx.input_metadata.ffi,
ctx.input_features, input_features,
grad_input.data, grad_input,
grad_output.data.contiguous(), grad_output.contiguous(),
ctx.nFeaturesToDrop, ctx.nFeaturesToDrop,
torch.cuda.IntTensor() if ctx.input_features.is_cuda else nullptr) 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
...@@ -67,29 +77,39 @@ class AveragePooling(Module): ...@@ -67,29 +77,39 @@ class AveragePooling(Module):
self.pool_size = toLongTensor(dimension, pool_size) self.pool_size = toLongTensor(dimension, pool_size)
self.pool_stride = toLongTensor(dimension, pool_stride) self.pool_stride = toLongTensor(dimension, pool_stride)
self.nFeaturesToDrop = nFeaturesToDrop self.nFeaturesToDrop = nFeaturesToDrop
def forward(self, input): def forward(self, input):
output = SparseConvNetTensor() output = SparseConvNetTensor()
output.metadata = input.metadata output.metadata = input.metadata
output.spatial_size = ( output.spatial_size = (
input.spatial_size - self.pool_size) / self.pool_stride + 1 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() assert ((output.spatial_size - 1) * self.pool_stride +
output.features = AveragePoolingFunction().apply( self.pool_size == input.spatial_size).all()
input.features, input.metadata, input.spatial_size, output.features = AveragePoolingFunction.apply(
output.spatial_size, self.dimension,self.pool_size,self.pool_stride, input.features,
input.metadata,
input.spatial_size,
output.spatial_size,
self.dimension,
self.pool_size,
self.pool_stride,
self.nFeaturesToDrop) self.nFeaturesToDrop)
return output return output
def input_spatial_size(self, out_size): def input_spatial_size(self, out_size):
return (out_size - 1) * self.pool_stride + self.pool_size return (out_size - 1) * self.pool_stride + self.pool_size
def __repr__(self): def __repr__(self):
s = 'AveragePooling' s = 'AveragePooling'
if self.pool_size.max() == self.pool_size.min() and\ if self.pool_size.max() == self.pool_size.min() and\
self.pool_stride.max() == self.pool_stride.min(): self.pool_stride.max() == self.pool_stride.min():
s = s + str(self.pool_size[0]) + '/' + str(self.pool_stride[0]) s = s + str(self.pool_size[0].item()) + \
'/' + str(self.pool_stride[0].item())
else: else:
s = s + '(' + str(self.pool_size[0]) s = s + '(' + str(self.pool_size[0].item())
for i in self.pool_size[1:]: for i in self.pool_size[1:]:
s = s + ',' + str(i) s = s + ',' + str(i)
s = s + ')/(' + str(self.pool_stride[0]) s = s + ')/(' + str(self.pool_stride[0].item())
for i in self.pool_stride[1:]: for i in self.pool_stride[1:]:
s = s + ',' + str(i) s = s + ',' + str(i)
s = s + ')' s = s + ')'
......
...@@ -15,11 +15,12 @@ leakiness : Apply activation def inplace: 0<=leakiness<=1. ...@@ -15,11 +15,12 @@ leakiness : Apply activation def inplace: 0<=leakiness<=1.
0 for ReLU, values in (0,1) for LeakyReLU, 1 for no activation def. 0 for ReLU, values in (0,1) for LeakyReLU, 1 for no activation def.
""" """
from torch.autograd import Function, Variable from torch.autograd import Function
from torch.nn import Module, Parameter from torch.nn import Module, Parameter
from .utils import * from .utils import *
from .sparseConvNetTensor import SparseConvNetTensor from .sparseConvNetTensor import SparseConvNetTensor
class BatchNormalizationFunction(Function): class BatchNormalizationFunction(Function):
@staticmethod @staticmethod
def forward( def forward(
...@@ -33,60 +34,72 @@ class BatchNormalizationFunction(Function): ...@@ -33,60 +34,72 @@ class BatchNormalizationFunction(Function):
momentum, momentum,
train, train,
leakiness): leakiness):
ctx.nPlanes=runningMean.shape[0] ctx.nPlanes = runningMean.shape[0]
ctx.input_features=input_features ctx.train = train
ctx.weight=weight ctx.leakiness = leakiness
ctx.bias=bias output_features = input_features.new()
ctx.runningMean=runningMean saveMean = input_features.new().resize_(ctx.nPlanes)
ctx.runningVar=runningVar saveInvStd = runningMean.clone().resize_(ctx.nPlanes)
ctx.train=train
ctx.leakiness=leakiness
ctx.output_features = input_features.new()
ctx.saveMean = input_features.new().resize_(ctx.nPlanes)
ctx.saveInvStd = runningMean.clone().resize_(ctx.nPlanes)
typed_fn(input_features, 'BatchNormalization_updateOutput')( typed_fn(input_features, 'BatchNormalization_updateOutput')(
input_features, input_features,
ctx.output_features, output_features,
ctx.saveMean, saveMean,
ctx.saveInvStd, saveInvStd,
ctx.runningMean, runningMean,
ctx.runningVar, runningVar,
ctx.weight if ctx.weight is not None else nullptr, weight if weight is not None else nullptr,
ctx.bias if ctx.bias is not None else nullptr, bias if bias is not None else nullptr,
eps, eps,
momentum, momentum,
ctx.train, ctx.train,
ctx.leakiness) ctx.leakiness)
return ctx.output_features ctx.save_for_backward(input_features,
output_features,
weight,
bias,
runningMean,
runningVar,
saveMean,
saveInvStd)
return output_features
@staticmethod @staticmethod
def backward(ctx, grad_output): def backward(ctx, grad_output):
input_features,\
output_features,\
weight,\
bias,\
runningMean,\
runningVar,\
saveMean,\
saveInvStd = ctx.saved_tensors
assert ctx.train assert ctx.train
grad_input=Variable(grad_output.data.new()) grad_input = grad_output.new()
if ctx.weight is None: if weight is None:
grad_weight=None grad_weight = None
else: else:
grad_weight=Variable(ctx.input_features.new().resize_(ctx.nPlanes).zero_()) grad_weight = input_features.new().resize_(ctx.nPlanes).zero_()
if ctx.bias is None: if bias is None:
grad_bias=None grad_bias = None
else: else:
grad_bias=Variable(ctx.input_features.new().resize_(ctx.nPlanes).zero_()) grad_bias = input_features.new().resize_(ctx.nPlanes).zero_()
typed_fn(ctx.input_features, 'BatchNormalization_backward')( typed_fn(input_features, 'BatchNormalization_backward')(
ctx.input_features, input_features,
grad_input.data, grad_input,
ctx.output_features, output_features,
grad_output.data.contiguous(), grad_output.contiguous(),
ctx.saveMean, saveMean,
ctx.saveInvStd, saveInvStd,
ctx.runningMean, runningMean,
ctx.runningVar, runningVar,
ctx.weight if ctx.weight is not None else nullptr, weight if weight is not None else nullptr,
ctx.bias if ctx.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.data if grad_weight is not None else nullptr,
grad_bias.data if grad_bias is not None else nullptr, grad_bias.data 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): class BatchNormalization(Module):
def __init__( def __init__(
self, self,
...@@ -109,12 +122,13 @@ class BatchNormalization(Module): ...@@ -109,12 +122,13 @@ class BatchNormalization(Module):
else: else:
self.weight = None self.weight = None
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.ndimension() == 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
output.features = BatchNormalizationFunction().apply( output.features = BatchNormalizationFunction.apply(
input.features, input.features,
self.weight, self.weight,
self.bias, self.bias,
...@@ -125,8 +139,10 @@ class BatchNormalization(Module): ...@@ -125,8 +139,10 @@ class BatchNormalization(Module):
self.training, self.training,
self.leakiness) self.leakiness)
return output return output
def input_spatial_size(self, out_size): def input_spatial_size(self, out_size):
return out_size return out_size
def __repr__(self): def __repr__(self):
s = 'BatchNorm(' + str(self.nPlanes) + ',eps=' + str(self.eps) + \ s = 'BatchNorm(' + str(self.nPlanes) + ',eps=' + str(self.eps) + \
',momentum=' + str(self.momentum) + ',affine=' + str(self.affine) ',momentum=' + str(self.momentum) + ',affine=' + str(self.affine)
...@@ -135,17 +151,21 @@ class BatchNormalization(Module): ...@@ -135,17 +151,21 @@ class BatchNormalization(Module):
s = s + ')' s = s + ')'
return s return s
class BatchNormReLU(BatchNormalization): class BatchNormReLU(BatchNormalization):
def __init__(self, nPlanes, eps=1e-4, momentum=0.9): def __init__(self, nPlanes, eps=1e-4, momentum=0.9):
BatchNormalization.__init__(self, nPlanes, eps, momentum, True, 0) BatchNormalization.__init__(self, nPlanes, eps, momentum, True, 0)
def __repr__(self): def __repr__(self):
s = 'BatchNormReLU(' + str(self.nPlanes) + ',eps=' + str(self.eps) + \ s = 'BatchNormReLU(' + str(self.nPlanes) + ',eps=' + str(self.eps) + \
',momentum=' + str(self.momentum) + ',affine=' + str(self.affine) + ')' ',momentum=' + str(self.momentum) + ',affine=' + str(self.affine) + ')'
return s return s
class BatchNormLeakyReLU(BatchNormalization): class BatchNormLeakyReLU(BatchNormalization):
def __init__(self, nPlanes, eps=1e-4, momentum=0.9): def __init__(self, nPlanes, eps=1e-4, momentum=0.9):
BatchNormalization.__init__(self, nPlanes, eps, momentum, True, 0.333) BatchNormalization.__init__(self, nPlanes, eps, momentum, True, 0.333)
def __repr__(self): def __repr__(self):
s = 'BatchNormReLU(' + str(self.nPlanes) + ',eps=' + str(self.eps) + \ s = 'BatchNormReLU(' + str(self.nPlanes) + ',eps=' + str(self.eps) + \
',momentum=' + str(self.momentum) + ',affine=' + str(self.affine) + ')' ',momentum=' + str(self.momentum) + ',affine=' + str(self.affine) + ')'
......
...@@ -8,7 +8,6 @@ import torch ...@@ -8,7 +8,6 @@ import torch
import torch.nn as nn import torch.nn as nn
import torch.optim as optim import torch.optim as optim
import torch.nn.functional as F import torch.nn.functional as F
from torch.autograd import Variable
import sparseconvnet as s import sparseconvnet as s
import time import time
import os import os
...@@ -16,26 +15,28 @@ import math ...@@ -16,26 +15,28 @@ import math
import numpy as np import numpy as np
from PIL import Image from PIL import Image
def updateStats(stats, output, target, loss): def updateStats(stats, output, target, loss):
batchSize = output.size(0) batchSize = output.size(0)
nClasses= output.size(1) nClasses = output.size(1)
if not stats: if not stats:
stats['top1'] = 0 stats['top1'] = 0
stats['top5'] = 0 stats['top5'] = 0
stats['n'] = 0 stats['n'] = 0
stats['nll'] = 0 stats['nll'] = 0
stats['confusion matrix'] = output.new().resize_(nClasses,nClasses).zero_() stats['confusion matrix'] = output.new().resize_(
nClasses, nClasses).zero_()
stats['n'] = stats['n'] + batchSize stats['n'] = stats['n'] + batchSize
stats['nll'] = stats['nll'] + loss * batchSize stats['nll'] = stats['nll'] + loss * batchSize
_, predictions = output.float().sort(1, True) _, predictions = output.float().sort(1, True)
correct = predictions.eq( correct = predictions.eq(
target[:,None].expand_as(output)) target[:, None].expand_as(output))
# Top-1 score # Top-1 score
stats['top1'] += correct.narrow(1, 0, 1).sum() stats['top1'] += correct[:, :1].long().sum().item()
# Top-5 score # Top-5 score
l = min(5, correct.size(1)) l = min(5, correct.size(1))
stats['top5'] += correct.narrow(1, 0, l).sum() stats['top5'] += correct[:, :l].long().sum().item()
stats['confusion matrix'].index_add_(0,target,F.softmax(Variable(output),1).data) stats['confusion matrix'].index_add_(0, target, F.softmax(output, 1).data)
def ClassificationTrainValidate(model, dataset, p): def ClassificationTrainValidate(model, dataset, p):
...@@ -59,10 +60,10 @@ def ClassificationTrainValidate(model, dataset, p): ...@@ -59,10 +60,10 @@ def ClassificationTrainValidate(model, dataset, p):
if 'test_reps' not in p: if 'test_reps' not in p:
p['test_reps'] = 1 p['test_reps'] = 1
optimizer = optim.SGD(model.parameters(), optimizer = optim.SGD(model.parameters(),
lr=p['initial_lr'], lr=p['initial_lr'],
momentum = p['momentum'], momentum=p['momentum'],
weight_decay = p['weight_decay'], weight_decay=p['weight_decay'],
nesterov=True) nesterov=True)
if p['check_point'] and os.path.isfile('epoch.pth'): if p['check_point'] and os.path.isfile('epoch.pth'):
p['epoch'] = torch.load('epoch.pth') + 1 p['epoch'] = torch.load('epoch.pth') + 1
print('Restarting at epoch ' + print('Restarting at epoch ' +
...@@ -70,7 +71,7 @@ def ClassificationTrainValidate(model, dataset, p): ...@@ -70,7 +71,7 @@ def ClassificationTrainValidate(model, dataset, p):
' from model.pth ..') ' from model.pth ..')
model.load_state_dict(torch.load('model.pth')) model.load_state_dict(torch.load('model.pth'))
else: else:
p['epoch']=1 p['epoch'] = 1
print(p) print(p)
print('#parameters', sum([x.nelement() for x in model.parameters()])) print('#parameters', sum([x.nelement() for x in model.parameters()]))
for epoch in range(p['epoch'], p['n_epochs'] + 1): for epoch in range(p['epoch'], p['n_epochs'] + 1):
...@@ -78,18 +79,16 @@ def ClassificationTrainValidate(model, dataset, p): ...@@ -78,18 +79,16 @@ def ClassificationTrainValidate(model, dataset, p):
stats = {} stats = {}
for param_group in optimizer.param_groups: for param_group in optimizer.param_groups:
param_group['lr'] = p['initial_lr'] * \ param_group['lr'] = p['initial_lr'] * \
math.exp((1 - epoch) * p['lr_decay']) math.exp((1 - epoch) * p['lr_decay'])
start = time.time() start = time.time()
for batch in dataset['train'](): for batch in dataset['train']():
if p['use_gpu']: if p['use_gpu']:
batch['input']=batch['input'].cuda() batch['input'] = batch['input'].cuda()
batch['target'] = batch['target'].cuda() batch['target'] = batch['target'].cuda()
batch['input'].to_variable(requires_grad=True)
batch['target'] = Variable(batch['target'])
optimizer.zero_grad() optimizer.zero_grad()
output = model(batch['input']) output = model(batch['input'])
loss = criterion(output, batch['target']) loss = criterion(output, batch['target'])
updateStats(stats, output.data, batch['target'].data, loss.data[0]) updateStats(stats, output, batch['target'], loss.item())
loss.backward() loss.backward()
optimizer.step() optimizer.step()
print(epoch, 'train: top1=%.2f%% top5=%.2f%% nll:%.2f time:%.1fs' % print(epoch, 'train: top1=%.2f%% top5=%.2f%% nll:%.2f time:%.1fs' %
...@@ -102,73 +101,80 @@ def ClassificationTrainValidate(model, dataset, p): ...@@ -102,73 +101,80 @@ def ClassificationTrainValidate(model, dataset, p):
stats['n']), stats['nll'] / stats['n']), stats['nll'] /
stats['n'], time.time() - stats['n'], time.time() -
start)) start))
cm=stats['confusion matrix'].cpu().numpy() cm = stats['confusion matrix'].cpu().numpy()
np.savetxt('train confusion matrix.csv',cm,delimiter=',') np.savetxt('train confusion matrix.csv', cm, delimiter=',')
cm*=255/(cm.sum(1,keepdims=True)+1e-9) cm *= 255 / (cm.sum(1, keepdims=True) + 1e-9)
Image.fromarray(cm.astype('uint8'),mode='L').save('train confusion matrix.png') Image.fromarray(cm.astype('uint8'), mode='L').save(
'train confusion matrix.png')
if p['check_point']: if p['check_point']:
torch.save(epoch, 'epoch.pth') torch.save(epoch, 'epoch.pth')
torch.save(model.state_dict(),'model.pth') torch.save(model.state_dict(), 'model.pth')
model.eval() model.eval()
s.forward_pass_multiplyAdd_count = 0 s.forward_pass_multiplyAdd_count = 0
s.forward_pass_hidden_states = 0 s.forward_pass_hidden_states = 0
start = time.time() start = time.time()
if p['test_reps'] ==1: if p['test_reps'] == 1:
stats = {} stats = {}
for batch in dataset['val'](): for batch in dataset['val']():
if p['use_gpu']: if p['use_gpu']:
batch['input']=batch['input'].cuda() batch['input'] = batch['input'].cuda()
batch['target'] = batch['target'].cuda() batch['target'] = batch['target'].cuda()
batch['input'].to_variable()
batch['target'] = Variable(batch['target'])
output = model(batch['input']) output = model(batch['input'])
loss = criterion(output, batch['target']) loss = criterion(output, batch['target'])
updateStats(stats, output.data, batch['target'].data, loss.data[0]) updateStats(stats, output, batch['target'], loss.item())
print(epoch, 'test: top1=%.2f%% top5=%.2f%% nll:%.2f time:%.1fs' %( print(epoch, 'test: top1=%.2f%% top5=%.2f%% nll:%.2f time:%.1fs' %
100 * (1 - 1.0 * stats['top1'] / stats['n']), (100 *
100 * (1 - 1.0 * stats['top5'] / stats['n']), (1 -
stats['nll'] / stats['n'], 1.0 *
time.time() - start), stats['top1'] /
'%.3e MultiplyAdds/sample %.3e HiddenStates/sample' % ( stats['n']), 100 *
s.forward_pass_multiplyAdd_count / stats['n'], (1 -
s.forward_pass_hidden_states / stats['n'])) 1.0 *
stats['top5'] /
stats['n']), stats['nll'] /
stats['n'], time.time() -
start), '%.3e MultiplyAdds/sample %.3e HiddenStates/sample' %
(s.forward_pass_multiplyAdd_count /
stats['n'], s.forward_pass_hidden_states /
stats['n']))
else: else:
for rep in range(1,p['test_reps']+1): for rep in range(1, p['test_reps'] + 1):
pr=[] pr = []
ta=[] ta = []
idxs=[] idxs = []
for batch in dataset['val'](): for batch in dataset['val']():
if p['use_gpu']: if p['use_gpu']:
batch['input']=batch['input'].cuda() batch['input'] = batch['input'].cuda()
batch['target'] = batch['target'].cuda() batch['target'] = batch['target'].cuda()
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.data)
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)
ta=torch.cat(ta,0) ta = torch.cat(ta, 0)
idxs=torch.cat(idxs,0) idxs = torch.cat(idxs, 0)
if rep==1: if rep == 1:
predictions=pr.new().resize_as_(pr).zero_().index_add_(0,idxs,pr) predictions = pr.new().resize_as_(pr).zero_().index_add_(0, idxs, pr)
targets=ta.new().resize_as_(ta).zero_().index_add_(0,idxs,ta) targets = ta.new().resize_as_(ta).zero_().index_add_(0, idxs, ta)
else: else:
predictions.index_add_(0,idxs,pr) predictions.index_add_(0, idxs, pr)
loss = criterion(predictions/rep, targets) loss = criterion(predictions / rep, targets)
stats = {} stats = {}
updateStats(stats, predictions, targets, loss.data[0]) updateStats(stats, predictions, targets, loss.item())
print(epoch, 'test rep ', rep, print(epoch, 'test rep ', rep,
': top1=%.2f%% top5=%.2f%% nll:%.2f time:%.1fs' %( ': top1=%.2f%% top5=%.2f%% nll:%.2f time:%.1fs' % (
100 * (1 - 1.0 * stats['top1'] / stats['n']), 100 * (1 - 1.0 * stats['top1'] / stats['n']),
100 * (1 - 1.0 * stats['top5'] / stats['n']), 100 * (1 - 1.0 * stats['top5'] / stats['n']),
stats['nll'] / stats['n'], stats['nll'] / stats['n'],
time.time() - start), time.time() - start),
'%.3e MultiplyAdds/sample %.3e HiddenStates/sample' % ( '%.3e MultiplyAdds/sample %.3e HiddenStates/sample' % (
s.forward_pass_multiplyAdd_count / stats['n'], s.forward_pass_multiplyAdd_count / stats['n'],
s.forward_pass_hidden_states / stats['n'])) s.forward_pass_hidden_states / stats['n']))
cm=stats['confusion matrix'].cpu().numpy() cm = stats['confusion matrix'].cpu().numpy()
np.savetxt('test confusion matrix.csv',cm,delimiter=',') np.savetxt('test confusion matrix.csv', cm, delimiter=',')
cm*=255/(cm.sum(1,keepdims=True)+1e-9) cm *= 255 / (cm.sum(1, keepdims=True) + 1e-9)
Image.fromarray(cm.astype('uint8'),mode='L').save('test confusion matrix.png') Image.fromarray(cm.astype('uint8'), mode='L').save(
'test confusion matrix.png')
...@@ -5,35 +5,42 @@ ...@@ -5,35 +5,42 @@
# LICENSE file in the root directory of this source tree. # LICENSE file in the root directory of this source tree.
import sparseconvnet import sparseconvnet
from torch.autograd import Function, Variable from torch.autograd import Function
from torch.nn import Module, Parameter from torch.nn import Module, Parameter
from .utils import * from .utils import *
from .sparseConvNetTensor import SparseConvNetTensor from .sparseConvNetTensor import SparseConvNetTensor
class ConvolutionFunction(Function): class ConvolutionFunction(Function):
@staticmethod @staticmethod
def forward( def forward(
ctx, ctx,
input_features, input_features,
weight, weight,
bias, bias,
input_metadata, input_metadata,
input_spatial_size, input_spatial_size,
output_spatial_size, output_spatial_size,
dimension, dimension,
filter_size, filter_size,
filter_stride): filter_stride):
output_features=input_features.new() output_features = input_features.new()
ctx.input_features=input_features ctx.input_metadata = input_metadata
ctx.input_metadata=input_metadata ctx.dimension = dimension
ctx.input_spatial_size=input_spatial_size # ctx.weight=weight
ctx.weight=weight # ctx.bias=bias
ctx.bias=bias # ctx.output_spatial_size=output_spatial_size
ctx.output_features=input_features.new() # ctx.filter_size=filter_size
ctx.output_spatial_size=output_spatial_size # ctx.filter_stride=filter_stride
ctx.dimension=dimension # bias??
ctx.filter_size=filter_size ctx.save_for_backward(
ctx.filter_stride=filter_stride input_features,
input_spatial_size,
weight,
bias,
output_spatial_size,
filter_size,
filter_stride)
sparseconvnet.forward_pass_multiplyAdd_count +=\ sparseconvnet.forward_pass_multiplyAdd_count +=\
dim_typed_fn( dim_typed_fn(
dimension, input_features, 'Convolution_updateOutput')( dimension, input_features, 'Convolution_updateOutput')(
...@@ -46,35 +53,38 @@ class ConvolutionFunction(Function): ...@@ -46,35 +53,38 @@ 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) 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
@staticmethod @staticmethod
def backward(ctx, grad_output): def backward(ctx, grad_output):
grad_input=Variable(grad_output.data.new()) input_features, input_spatial_size, weight, bias, output_spatial_size, filter_size, filter_stride = ctx.saved_tensors
grad_weight=Variable(grad_output.data.new().resize_as_(ctx.weight).zero_()) grad_input = grad_output.new()
if ctx.bias is None: grad_weight = grad_output.new().resize_as_(weight).zero_()
grad_bias=None if bias is None:
grad_bias = None
else: else:
grad_bias = Variable(grad_output.data.new().resize_as_(ctx.bias).zero_()) grad_bias = grad_output.new().resize_as_(bias).zero_()
dim_typed_fn( dim_typed_fn(
ctx.dimension, ctx.input_features, 'Convolution_backward')( ctx.dimension, input_features, 'Convolution_backward')(
ctx.input_spatial_size, input_spatial_size,
ctx.output_spatial_size, output_spatial_size,
ctx.filter_size, filter_size,
ctx.filter_stride, filter_stride,
ctx.input_metadata.ffi, ctx.input_metadata.ffi,
ctx.input_features, input_features,
grad_input.data, grad_input,
grad_output.data.contiguous(), grad_output.contiguous(),
ctx.weight, weight,
grad_weight.data, grad_weight,
grad_bias.data if grad_bias is not None else nullptr, grad_bias.data if grad_bias is not None else nullptr,
0, #remove this parameter 0, # remove this parameter
torch.cuda.IntTensor() if ctx.input_features.is_cuda else nullptr) 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
class Convolution(Module): class Convolution(Module):
def __init__(self, dimension, nIn, nOut, filter_size, filter_stride, bias): def __init__(self, dimension, nIn, nOut, filter_size, filter_stride, bias):
Module.__init__(self) Module.__init__(self)
...@@ -82,7 +92,7 @@ class Convolution(Module): ...@@ -82,7 +92,7 @@ class Convolution(Module):
self.nIn = nIn self.nIn = nIn
self.nOut = nOut self.nOut = nOut
self.filter_size = toLongTensor(dimension, filter_size) self.filter_size = toLongTensor(dimension, filter_size)
self.filter_volume = self.filter_size.prod() self.filter_volume = self.filter_size.prod().item()
self.filter_stride = toLongTensor(dimension, filter_stride) self.filter_stride = toLongTensor(dimension, filter_stride)
std = (2.0 / nIn / self.filter_volume)**0.5 std = (2.0 / nIn / self.filter_volume)**0.5
self.weight = Parameter(torch.Tensor( self.weight = Parameter(torch.Tensor(
...@@ -92,15 +102,17 @@ class Convolution(Module): ...@@ -92,15 +102,17 @@ class Convolution(Module):
if bias: if bias:
self.bias = Parameter(torch.Tensor(nOut).zero_()) self.bias = Parameter(torch.Tensor(nOut).zero_())
else: else:
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.ndimension() == 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 =\
(input.spatial_size - self.filter_size) / self.filter_stride + 1 (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() assert ((output.spatial_size - 1) * self.filter_stride +
output.features=ConvolutionFunction().apply( self.filter_size == input.spatial_size).all()
output.features = ConvolutionFunction.apply(
input.features, input.features,
self.weight, self.weight,
self.bias, self.bias,
...@@ -117,12 +129,13 @@ class Convolution(Module): ...@@ -117,12 +129,13 @@ class Convolution(Module):
s = 'Convolution ' + str(self.nIn) + '->' + str(self.nOut) + ' C' s = 'Convolution ' + str(self.nIn) + '->' + str(self.nOut) + ' C'
if self.filter_size.max() == self.filter_size.min() and\ if self.filter_size.max() == self.filter_size.min() and\
self.filter_stride.max() == self.filter_stride.min(): self.filter_stride.max() == self.filter_stride.min():
s = s + str(self.filter_size[0]) + '/' + str(self.filter_stride[0]) s = s + str(self.filter_size[0].item()) + \
'/' + str(self.filter_stride[0].item())
else: else:
s = s + '(' + str(self.filter_size[0]) s = s + '(' + str(self.filter_size[0].item())
for i in self.filter_size[1:]: for i in self.filter_size[1:]:
s = s + ',' + str(i) s = s + ',' + str(i)
s = s + ')/(' + str(self.filter_stride[0]) s = s + ')/(' + str(self.filter_stride[0].item())
for i in self.filter_stride[1:]: for i in self.filter_stride[1:]:
s = s + ',' + str(i) s = s + ',' + str(i)
s = s + ')' s = s + ')'
......
...@@ -5,34 +5,29 @@ ...@@ -5,34 +5,29 @@
# LICENSE file in the root directory of this source tree. # LICENSE file in the root directory of this source tree.
import sparseconvnet import sparseconvnet
from torch.autograd import Function, Variable from torch.autograd import Function
from torch.nn import Module, Parameter from torch.nn import Module, Parameter
from .utils import * from .utils import *
from .sparseConvNetTensor import SparseConvNetTensor from .sparseConvNetTensor import SparseConvNetTensor
class DeconvolutionFunction(Function): class DeconvolutionFunction(Function):
@staticmethod @staticmethod
def forward( def forward(
ctx, ctx,
input_features, input_features,
weight, weight,
bias, bias,
input_metadata, input_metadata,
input_spatial_size, input_spatial_size,
output_spatial_size, output_spatial_size,
dimension, dimension,
filter_size, filter_size,
filter_stride): filter_stride):
ctx.input_features=input_features ctx.input_metadata = input_metadata
ctx.input_metadata=input_metadata output_features = input_features.new()
ctx.input_spatial_size=input_spatial_size ctx.dimension = dimension
ctx.weight=weight
ctx.bias=bias
ctx.output_features=input_features.new()
ctx.output_spatial_size=output_spatial_size
ctx.dimension=dimension
ctx.filter_size=filter_size
ctx.filter_stride=filter_stride
sparseconvnet.forward_pass_multiplyAdd_count +=\ sparseconvnet.forward_pass_multiplyAdd_count +=\
dim_typed_fn( dim_typed_fn(
dimension, input_features, 'Deconvolution_updateOutput')( dimension, input_features, 'Deconvolution_updateOutput')(
...@@ -42,38 +37,56 @@ class DeconvolutionFunction(Function): ...@@ -42,38 +37,56 @@ class DeconvolutionFunction(Function):
filter_stride, filter_stride,
input_metadata.ffi, input_metadata.ffi,
input_features, input_features,
ctx.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) torch.cuda.IntTensor() if input_features.is_cuda else nullptr)
sparseconvnet.forward_pass_hidden_states += ctx.output_features.nelement() sparseconvnet.forward_pass_hidden_states += output_features.nelement()
return ctx.output_features ctx.save_for_backward(input_features,
output_features,
input_spatial_size,
weight,
bias,
output_spatial_size,
filter_size,
filter_stride)
return output_features
@staticmethod @staticmethod
def backward(ctx, grad_output): def backward(ctx, grad_output):
grad_input=Variable(grad_output.data.new()) input_features,\
grad_weight=Variable(grad_output.data.new().resize_as_(ctx.weight).zero_()) output_features,\
if ctx.bias is None: input_spatial_size,\
grad_bias=None 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: else:
grad_bias = Variable(grad_output.data.new().resize_as_(ctx.bias).zero_()) grad_bias = grad_output.new().resize_as_(bias).zero_()
dim_typed_fn( dim_typed_fn(
ctx.dimension, ctx.input_features, 'Deconvolution_backward')( ctx.dimension, input_features, 'Deconvolution_backward')(
ctx.input_spatial_size, input_spatial_size,
ctx.output_spatial_size, output_spatial_size,
ctx.filter_size, filter_size,
ctx.filter_stride, filter_stride,
ctx.input_metadata.ffi, ctx.input_metadata.ffi,
ctx.input_features, input_features,
grad_input.data, grad_input,
grad_output.data.contiguous(), grad_output.contiguous(),
ctx.weight, weight,
grad_weight.data, grad_weight,
grad_bias.data if grad_bias is not None else nullptr, grad_bias.data if grad_bias is not None else nullptr,
0, #remove this parameter 0, # remove this parameter
torch.cuda.IntTensor() if ctx.input_features.is_cuda else nullptr) 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
class Deconvolution(Module): class Deconvolution(Module):
def __init__(self, dimension, nIn, nOut, filter_size, filter_stride, bias): def __init__(self, dimension, nIn, nOut, filter_size, filter_stride, bias):
Module.__init__(self) Module.__init__(self)
...@@ -81,7 +94,7 @@ class Deconvolution(Module): ...@@ -81,7 +94,7 @@ class Deconvolution(Module):
self.nIn = nIn self.nIn = nIn
self.nOut = nOut self.nOut = nOut
self.filter_size = toLongTensor(dimension, filter_size) self.filter_size = toLongTensor(dimension, filter_size)
self.filter_volume = self.filter_size.prod() self.filter_volume = self.filter_size.prod().item()
self.filter_stride = toLongTensor(dimension, filter_stride) self.filter_stride = toLongTensor(dimension, filter_stride)
std = (2.0 / nIn / self.filter_volume)**0.5 std = (2.0 / nIn / self.filter_volume)**0.5
self.weight = Parameter(torch.Tensor( self.weight = Parameter(torch.Tensor(
...@@ -91,14 +104,15 @@ class Deconvolution(Module): ...@@ -91,14 +104,15 @@ class Deconvolution(Module):
if bias: if bias:
self.bias = Parameter(torch.Tensor(nOut).zero_()) self.bias = Parameter(torch.Tensor(nOut).zero_())
else: else:
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.ndimension() == 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 =\
(input.spatial_size - 1) * self.filter_stride + self.filter_size (input.spatial_size - 1) * self.filter_stride + self.filter_size
output.features=DeconvolutionFunction().apply( output.features = DeconvolutionFunction.apply(
input.features, input.features,
self.weight, self.weight,
self.bias, self.bias,
...@@ -115,12 +129,13 @@ class Deconvolution(Module): ...@@ -115,12 +129,13 @@ class Deconvolution(Module):
s = 'Deconvolution ' + str(self.nIn) + '->' + str(self.nOut) + ' C' s = 'Deconvolution ' + str(self.nIn) + '->' + str(self.nOut) + ' C'
if self.filter_size.max() == self.filter_size.min() and\ if self.filter_size.max() == self.filter_size.min() and\
self.filter_stride.max() == self.filter_stride.min(): self.filter_stride.max() == self.filter_stride.min():
s = s + str(self.filter_size[0]) + '/' + str(self.filter_stride[0]) s = s + str(self.filter_size[0].item()) + \
'/' + str(self.filter_stride[0].item())
else: else:
s = s + '(' + str(self.filter_size[0]) s = s + '(' + str(self.filter_size[0].item())
for i in self.filter_size[1:]: for i in self.filter_size[1:]:
s = s + ',' + str(i) s = s + ',' + str(i)
s = s + ')/(' + str(self.filter_stride[0]) s = s + ')/(' + str(self.filter_stride[0].item())
for i in self.filter_stride[1:]: for i in self.filter_stride[1:]:
s = s + ',' + str(i) s = s + ',' + str(i)
s = s + ')' s = s + ')'
...@@ -128,5 +143,6 @@ class Deconvolution(Module): ...@@ -128,5 +143,6 @@ class Deconvolution(Module):
def input_spatial_size(self, out_size): def input_spatial_size(self, out_size):
in_size = (out_size - self.filter_size) / self.filter_stride + 1 in_size = (out_size - self.filter_size) / self.filter_stride + 1
assert ((in_size - 1) * self.filter_stride + self.filter_size == out_size).all() assert ((in_size - 1) * self.filter_stride +
self.filter_size == out_size).all()
return in_size return in_size
...@@ -12,12 +12,13 @@ Parameters: ...@@ -12,12 +12,13 @@ Parameters:
dimension : of the input field dimension : of the input field
""" """
from torch.autograd import Function, Variable from torch.autograd import Function
from torch.nn import Module from torch.nn import Module
from .utils import * from .utils import *
from .metadata import Metadata from .metadata import Metadata
from .sparseConvNetTensor import SparseConvNetTensor from .sparseConvNetTensor import SparseConvNetTensor
class DenseToSparseFunction(Function): class DenseToSparseFunction(Function):
@staticmethod @staticmethod
def forward( def forward(
...@@ -26,32 +27,36 @@ class DenseToSparseFunction(Function): ...@@ -26,32 +27,36 @@ class DenseToSparseFunction(Function):
output_metadata, output_metadata,
output_spatial_size, output_spatial_size,
dimension): dimension):
ctx.dimension=dimension ctx.dimension = dimension
a=input aa = input.permute(
aa=a.permute(*([0,]+list(range(2,2+dimension))+[1,])).clone() *([0, ] + list(range(2, 2 + dimension)) + [1, ])).clone()
ctx.aas=aa.size() aas = aa.size()
nz=aa.abs().sum(dimension+1).view(aa.size()[0:-1]) nz = aa.abs().sum(dimension + 1).view(aa.size()[0:-1])
s=torch.LongTensor(nz.stride()).view(1,dimension+1) s = torch.LongTensor(nz.stride()).view(1, dimension + 1)
nz=nz.nonzero() nz = nz.nonzero()
s=s.type_as(nz) s = s.type_as(nz)
aa=aa.view(-1,a.size(1)) aa = aa.view(-1, input.size(1))
ctx.aas2=aa.size() aas2 = aa.size()
ctx.r=(nz*s.expand_as(nz)).sum(1).view(-1) r = (nz * s.expand_as(nz)).sum(1).view(-1)
ctx.output_features=aa.index_select(0,ctx.r) output_features = aa.index_select(0, ctx.r)
dim_fn(dimension, 'createMetadataForDenseToSparse')( dim_fn(dimension, 'createMetadataForDenseToSparse')(
output_metadata.ffi, output_metadata.ffi,
output_spatial_size, output_spatial_size,
nz.cpu(), nz.cpu(),
input.size(0)) input.size(0))
return ctx.output_features ctx.save_for_backwards(output_features, aas, aas2, r)
return output_features
@staticmethod @staticmethod
def backward(ctx, grad_output): def backward(ctx, grad_output):
grad_input=Variable(grad_output.data.new().resize_(ctx.aas2).zero_().index_copy_(0,ctx.r,grad_output.data)) output_features, aas, aas2, r = ctx.saved_tensors
grad_input=grad_input.view(ctx.aas).permute(*([0,ctx.dimension+1]+list(range(1,ctx.dimension+1)))) grad_input = grad_output.new().resize_(
aas2).zero_().index_copy_(0, r, grad_output.data)
grad_input = grad_input.view(aas).permute(
*([0, ctx.dimension + 1] + list(range(1, ctx.dimension + 1))))
return grad_input, None, None, None return grad_input, None, None, None
class DenseToSparse(Module): class DenseToSparse(Module):
def __init__(self, dimension): def __init__(self, dimension):
Module.__init__(self) Module.__init__(self)
...@@ -60,8 +65,8 @@ class DenseToSparse(Module): ...@@ -60,8 +65,8 @@ class DenseToSparse(Module):
def forward(self, input): def forward(self, input):
output = SparseConvNetTensor() output = SparseConvNetTensor()
output.metadata = Metadata(self.dimension) output.metadata = Metadata(self.dimension)
output.spatial_size=torch.LongTensor(list(input.size()[2:])) output.spatial_size = torch.LongTensor(list(input.size()[2:]))
output.features=DenseToSparseFunction().apply( output.features = DenseToSparseFunction.apply(
input, input,
output.metadata, output.metadata,
output.spatial_size, output.spatial_size,
......
...@@ -4,43 +4,49 @@ ...@@ -4,43 +4,49 @@
# This source code is licensed under the license found in the # This source code is licensed under the license found in the
# LICENSE file in the root directory of this source tree. # LICENSE file in the root directory of this source tree.
from torch.autograd import Function, Variable from torch.autograd import Function
from torch.nn import Module from torch.nn import Module
from .utils import * from .utils import *
from .sparseConvNetTensor import SparseConvNetTensor from .sparseConvNetTensor import SparseConvNetTensor
class Dropout(Module): class Dropout(Module):
def __init__(self, p = 0.5): def __init__(self, p=0.5):
Module.__init__(self) Module.__init__(self)
self.p = p self.p = p
def forward(self, input): def forward(self, input):
output = SparseConvNetTensor() output = SparseConvNetTensor()
i = input.features i = input.features
if self.training: if self.training:
m = i.new().resize_(1).expand_as(i).fill_(1-self.p) m = i.new().resize_(1).expand_as(i).fill_(1 - self.p)
output.features = i * torch.bernoulli(m) output.features = i * torch.bernoulli(m)
else: else:
output.features = i * (1 - self.p) output.features = i * (1 - self.p)
output.metadata = input.metadata output.metadata = input.metadata
output.spatial_size = input.spatial_size output.spatial_size = input.spatial_size
return output return output
def input_spatial_size(self, out_size): def input_spatial_size(self, out_size):
return out_size return out_size
class BatchwiseDropout(Module): class BatchwiseDropout(Module):
def __init__(self, p = 0.5): def __init__(self, p=0.5):
Module.__init__(self) Module.__init__(self)
self.p = p self.p = p
def forward(self, input): def forward(self, input):
output = SparseConvNetTensor() output = SparseConvNetTensor()
i = input.features i = input.features
if self.training: if self.training:
m = i.new().resize_(1).expand(1,i.shape[1]).fill_(1-self.p) m = i.new().resize_(1).expand(1, i.shape[1]).fill_(1 - self.p)
output.features = i * torch.bernoulli(m) output.features = i * torch.bernoulli(m)
else: else:
output.features = i * (1 - self.p) output.features = i * (1 - self.p)
output.metadata = input.metadata output.metadata = input.metadata
output.spatial_size = input.spatial_size output.spatial_size = input.spatial_size
return output return output
def input_spatial_size(self, out_size): def input_spatial_size(self, out_size):
return out_size return out_size
...@@ -6,8 +6,10 @@ ...@@ -6,8 +6,10 @@
from torch.nn import Module from torch.nn import Module
class Identity(Module): class Identity(Module):
def forward(self, input): def forward(self, input):
return input return input
def input_spatial_size(self, out_size): def input_spatial_size(self, out_size):
return out_size return out_size
...@@ -9,6 +9,7 @@ from .metadata import Metadata ...@@ -9,6 +9,7 @@ from .metadata import Metadata
from .utils import toLongTensor, dim_fn from .utils import toLongTensor, dim_fn
from .sparseConvNetTensor import SparseConvNetTensor from .sparseConvNetTensor import SparseConvNetTensor
class InputBatch(SparseConvNetTensor): class InputBatch(SparseConvNetTensor):
def __init__(self, dimension, spatial_size): def __init__(self, dimension, spatial_size):
self.dimension = dimension self.dimension = dimension
...@@ -51,7 +52,7 @@ class InputBatch(SparseConvNetTensor): ...@@ -51,7 +52,7 @@ class InputBatch(SparseConvNetTensor):
to add point (1,2,3) to sample 7, and (4,5,6) to sample 9 (0-indexed). to add point (1,2,3) to sample 7, and (4,5,6) to sample 9 (0-indexed).
""" """
l = locations.narrow(1,0,self.dimension) l = locations[:, :self.dimension]
assert l.min() >= 0 and (self.spatial_size.expand_as(l) - l).min() > 0 assert l.min() >= 0 and (self.spatial_size.expand_as(l) - l).min() > 0
dim_fn(self.dimension, 'setInputSpatialLocations')( dim_fn(self.dimension, 'setInputSpatialLocations')(
self.metadata.ffi, self.features, locations, vectors, overwrite) self.metadata.ffi, self.features, locations, vectors, overwrite)
......
# 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, Parameter
from .utils import *
from .sparseConvNetTensor import SparseConvNetTensor
from .metadata import Metadata
class InputLayerFunction(Function):
@staticmethod
def forward(
ctx,
dimension,
metadata,
spatial_size,
coords,
input_features,
batch_size,
mode):
output_features = input_features.new()
ctx.dimension = dimension
ctx.metadata = metadata
ctx.dimension = dimension
dim_typed_fn(dimension, input_features, 'InputLayer_updateOutput')(
metadata.ffi,
spatial_size,
coords,
input_features,
output_features,
batch_size,
mode,
torch.cuda.IntTensor() if input_features.is_cuda else nullptr
)
return output_features
@staticmethod
def backward(ctx, grad_output):
grad_input = grad_output.data.new()
dim_typed_fn(
ctx.dimension,
grad_output.data,
'InputLayer_updateGradInput')(
ctx.metadata.ffi,
grad_input.data,
grad_output.contiguous().data,
torch.cuda.IntTensor() if grad_output.data.is_cuda else nullptr)
return None, None, None, None, grad_input, None, None
class InputLayer(Module):
def __init__(self, dimension, spatial_size, mode=3):
Module.__init__(self)
self.dimension = dimension
self.spatial_size = toLongTensor(dimension, spatial_size)
self.mode = mode
# (coords,input_features,batch_size or None) = input
def forward(self, input):
output = SparseConvNetTensor(
metadata=Metadata(
self.dimension),
spatial_size=self.spatial_size)
output.features = InputLayerFunction.apply(
self.dimension,
output.metadata,
self.spatial_size,
input[0],
input[1],
0 if len(input == 2) else input[2],
self.mode
)
return output
class BLInputLayerFunction(Function):
@staticmethod
def forward(
ctx,
dimension,
metadata,
spatial_size,
coords,
input_features,
mode):
output_features = input_features.new()
ctx.metadata = metadata
ctx.dimension = dimension
dim_typed_fn(dimension, input_features, 'BLInputLayer_updateOutput')(
metadata.ffi,
spatial_size,
coords,
input_features,
output_features,
mode,
torch.cuda.IntTensor() if input_features.is_cuda else nullptr
)
return output_features
@staticmethod
def backward(ctx, grad_output):
grad_input = grad_output.data.new()
dim_typed_fn(
ctx.dimension,
grad_output.data,
'BLInputLayer_updateGradInput')(
ctx.metadata.ffi,
grad_input.data,
grad_output.contiguous().data,
torch.cuda.IntTensor() if grad_output.data.is_cuda else nullptr)
return None, None, None, None, grad_input, None
class BLInputLayer(Module):
def __init__(self, dimension, spatial_size, mode=3):
Module.__init__(self)
self.dimension = dimension
self.spatial_size = toLongTensor(dimension, spatial_size)
self.mode = mode
# (coords,input_features) = input
def forward(self, input):
output = SparseConvNetTensor(
metadata=Metadata(
self.dimension),
spatial_size=self.spatial_size)
output.features = BLInputLayerFunction.apply(
self.dimension,
output.metadata,
self.spatial_size,
input[0],
input[1],
self.mode
)
return output
class BLOutputLayerFunction(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, 'BLOutputLayer_updateOutput')(
metadata.ffi,
input_features,
output_features,
torch.cuda.IntTensor() if input_features.is_cuda else nullptr
)
return output_features
@staticmethod
def backward(ctx, grad_output):
grad_input = grad_output.data.new()
dim_typed_fn(
ctx.dimension,
grad_output.data,
'BLOutputLayer_updateGradInput')(
ctx.metadata.ffi,
grad_input.data,
grad_output.contiguous().data,
torch.cuda.IntTensor() if grad_output.data.is_cuda else nullptr)
return None, None, grad_input
class BLOutputLayer(Module):
def __init__(self, dimension):
Module.__init__(self)
self.dimension = dimension
def forward(self, input):
output = BLOutputLayerFunction.apply(
self.dimension,
input.metadata,
input.features
)
return output
...@@ -25,7 +25,7 @@ from .networkInNetwork import NetworkInNetwork ...@@ -25,7 +25,7 @@ from .networkInNetwork import NetworkInNetwork
from .reLU import ReLU from .reLU import ReLU
from .sequential import Sequential from .sequential import Sequential
from .sparseToDense import SparseToDense from .sparseToDense import SparseToDense
from .validConvolution import ValidConvolution from .submanifoldConvolution import SubmanifoldConvolution
from .networkArchitectures import * from .networkArchitectures import *
from .classificationTrainValidate import ClassificationTrainValidate from .classificationTrainValidate import ClassificationTrainValidate
from .misc import * from .misc import *
...@@ -25,7 +25,10 @@ class AveragePooling(SparseModule): ...@@ -25,7 +25,10 @@ class AveragePooling(SparseModule):
self.output.metadata = input.metadata self.output.metadata = input.metadata
self.output.spatial_size =\ self.output.spatial_size =\
(input.spatial_size - self.pool_size) / self.pool_stride + 1 (input.spatial_size - self.pool_size) / self.pool_stride + 1
dim_typed_fn(self.dimension, input.features, 'AveragePooling_updateOutput')( dim_typed_fn(
self.dimension,
input.features,
'AveragePooling_updateOutput')(
input.spatial_size, input.spatial_size,
self.output.spatial_size, self.output.spatial_size,
self.pool_size, self.pool_size,
......
...@@ -21,6 +21,7 @@ from . import SparseModule ...@@ -21,6 +21,7 @@ from . import SparseModule
from ..utils import toLongTensor, typed_fn, optionalTensor, nullptr from ..utils import toLongTensor, typed_fn, optionalTensor, nullptr
from ..sparseConvNetTensor import SparseConvNetTensor from ..sparseConvNetTensor import SparseConvNetTensor
class BatchNormalization(SparseModule): class BatchNormalization(SparseModule):
def __init__( def __init__(
self, self,
...@@ -48,7 +49,7 @@ class BatchNormalization(SparseModule): ...@@ -48,7 +49,7 @@ class BatchNormalization(SparseModule):
self.gradInput = torch.Tensor() self.gradInput = torch.Tensor()
def updateOutput(self, input): def updateOutput(self, input):
assert input.features.ndimension()==0 or input.features.size(1) == self.nPlanes assert input.features.ndimension() == 0 or input.features.size(1) == self.nPlanes
self.output.metadata = input.metadata self.output.metadata = input.metadata
self.output.spatial_size = input.spatial_size self.output.spatial_size = input.spatial_size
typed_fn(input.features, 'BatchNormalization_updateOutput')( typed_fn(input.features, 'BatchNormalization_updateOutput')(
...@@ -112,6 +113,8 @@ class BatchNormReLU(BatchNormalization): ...@@ -112,6 +113,8 @@ class BatchNormReLU(BatchNormalization):
s = 'BatchNormReLU(' + str(self.nPlanes) + ',eps=' + str(self.eps) + \ s = 'BatchNormReLU(' + str(self.nPlanes) + ',eps=' + str(self.eps) + \
',momentum=' + str(self.momentum) + ',affine=' + str(self.affine) + ')' ',momentum=' + str(self.momentum) + ',affine=' + str(self.affine) + ')'
return s return s
class BatchNormLeakyReLU(BatchNormalization): class BatchNormLeakyReLU(BatchNormalization):
def __init__(self, nPlanes, eps=1e-4, momentum=0.9): def __init__(self, nPlanes, eps=1e-4, momentum=0.9):
BatchNormalization.__init__(self, nPlanes, eps, momentum, True, 0.333) BatchNormalization.__init__(self, nPlanes, eps, momentum, True, 0.333)
...@@ -121,6 +124,7 @@ class BatchNormLeakyReLU(BatchNormalization): ...@@ -121,6 +124,7 @@ class BatchNormLeakyReLU(BatchNormalization):
',momentum=' + str(self.momentum) + ',affine=' + str(self.affine) + ')' ',momentum=' + str(self.momentum) + ',affine=' + str(self.affine) + ')'
return s return s
class BatchNormalizationInTensor(BatchNormalization): class BatchNormalizationInTensor(BatchNormalization):
def __init__( def __init__(
self, self,
......
...@@ -21,6 +21,7 @@ from . import SparseModule ...@@ -21,6 +21,7 @@ from . import SparseModule
from ..utils import toLongTensor, typed_fn from ..utils import toLongTensor, typed_fn
from ..sparseConvNetTensor import SparseConvNetTensor from ..sparseConvNetTensor import SparseConvNetTensor
class BatchwiseDropout(SparseModule): class BatchwiseDropout(SparseModule):
def __init__( def __init__(
self, self,
...@@ -39,9 +40,9 @@ class BatchwiseDropout(SparseModule): ...@@ -39,9 +40,9 @@ class BatchwiseDropout(SparseModule):
def updateOutput(self, input): def updateOutput(self, input):
if self.train: if self.train:
self.noise.bernoulli_(1-self.p) self.noise.bernoulli_(1 - self.p)
else: else:
self.noise.fill_(1-self.p) self.noise.fill_(1 - self.p)
if self.inplace: if self.inplace:
self.output = input self.output = input
...@@ -50,11 +51,7 @@ class BatchwiseDropout(SparseModule): ...@@ -50,11 +51,7 @@ class BatchwiseDropout(SparseModule):
self.output.spatialSize = input.spatialSize self.output.spatialSize = input.spatialSize
typed_fn(input.features, 'BatchwiseMultiplicativeDropout_updateOutput')( typed_fn(input.features, 'BatchwiseMultiplicativeDropout_updateOutput')(
input.features, input.features, self.output.features, self.noise, self.leakiness)
self.output.features,
self.noise,
self.leakiness
)
return self.output return self.output
def updateGradInput(self, input, gradOutput): def updateGradInput(self, input, gradOutput):
...@@ -67,7 +64,7 @@ class BatchwiseDropout(SparseModule): ...@@ -67,7 +64,7 @@ class BatchwiseDropout(SparseModule):
gradOutput, gradOutput,
self.noise, self.noise,
self.leakiness self.leakiness
) )
return self.gradInput return self.gradInput
def type(self, t, tensorCache=None): def type(self, t, tensorCache=None):
...@@ -94,6 +91,7 @@ class BatchwiseDropout(SparseModule): ...@@ -94,6 +91,7 @@ class BatchwiseDropout(SparseModule):
s = s + ')' s = s + ')'
return s return s
class BatchwiseDropoutInTensor(BatchwiseDropout): class BatchwiseDropoutInTensor(BatchwiseDropout):
def __init__( def __init__(
self, self,
...@@ -106,9 +104,9 @@ class BatchwiseDropoutInTensor(BatchwiseDropout): ...@@ -106,9 +104,9 @@ class BatchwiseDropoutInTensor(BatchwiseDropout):
def updateOutput(self, input): def updateOutput(self, input):
if self.train: if self.train:
self.noise.bernoulli_(1-self.p) self.noise.bernoulli_(1 - self.p)
else: else:
self.noise.fill_(1-self.p) self.noise.fill_(1 - self.p)
self.output.metadata = input.metadata self.output.metadata = input.metadata
self.output.spatial_size = input.spatial_size self.output.spatial_size = input.spatial_size
...@@ -116,12 +114,13 @@ class BatchwiseDropoutInTensor(BatchwiseDropout): ...@@ -116,12 +114,13 @@ class BatchwiseDropoutInTensor(BatchwiseDropout):
o = self.output.features.narrow( o = self.output.features.narrow(
1, self.output_column_offset, self.nPlanes) 1, self.output_column_offset, self.nPlanes)
typed_fn(input.features, 'BatchwiseMultiplicativeDropout_updateOutput')( typed_fn(
input.features,
'BatchwiseMultiplicativeDropout_updateOutput')(
input.features, input.features,
o, o,
self.noise, self.noise,
self.leakiness self.leakiness)
)
return self.output return self.output
def updateGradInput(self, input, gradOutput): def updateGradInput(self, input, gradOutput):
...@@ -130,17 +129,12 @@ class BatchwiseDropoutInTensor(BatchwiseDropout): ...@@ -130,17 +129,12 @@ class BatchwiseDropoutInTensor(BatchwiseDropout):
d_o = gradOutput.narrow(1, self.output_column_offset, self.nPlanes) d_o = gradOutput.narrow(1, self.output_column_offset, self.nPlanes)
typed_fn(input.features, 'BatchwiseMultiplicativeDropout_updateGradInput')( typed_fn(input.features, 'BatchwiseMultiplicativeDropout_updateGradInput')(
input.features, input.features, self.gradInput, d_o, self.noise, self.leakiness)
self.gradInput,
d_o,
self.noise,
self.leakiness
)
return self.gradInput return self.gradInput
def __repr__(self): def __repr__(self):
s = 'BatchwiseDropoutInTensor(' + str(self.nPlanes) + ',p=' + str(self.p) + \ s = 'BatchwiseDropoutInTensor(' + str(self.nPlanes) + ',p=' + str(
',column_offset=' + str(self.output_column_offset) self.p) + ',column_offset=' + str(self.output_column_offset)
if self.leakiness > 0: if self.leakiness > 0:
s = s + ',leakiness=' + str(self.leakiness) s = s + ',leakiness=' + str(self.leakiness)
s = s + ')' s = s + ')'
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
""" """
Assume all the inputs have identical SparseGrids and input[i].nActive Assume all the inputs have identical SparseGrids and input[i].nActive
Assume input[0].nPlanes >= input[i].nPlanes for all i=1,#input Assume input[0].nPlanes >= input[i].nPlanes for all i=1,#input
output.validRules is taken from input[0].validRules (could do set union?) output.submanifoldRules is taken from input[0].submanifoldRules (could do set union?)
(for resnets, make sure the residual link is input[1]) (for resnets, make sure the residual link is input[1])
""" """
......
...@@ -36,7 +36,7 @@ class Convolution(SparseModule): ...@@ -36,7 +36,7 @@ class Convolution(SparseModule):
self.gradInput = torch.Tensor() self.gradInput = torch.Tensor()
def updateOutput(self, input): def updateOutput(self, input):
assert input.features.ndimension()==0 or input.features.size(1) == self.nIn assert input.features.ndimension() == 0 or input.features.size(1) == self.nIn
self.output.metadata = input.metadata self.output.metadata = input.metadata
self.output.spatial_size =\ self.output.spatial_size =\
(input.spatial_size - self.filter_size) / self.filter_stride + 1 (input.spatial_size - self.filter_size) / self.filter_stride + 1
......
...@@ -35,7 +35,7 @@ class Deconvolution(SparseModule): ...@@ -35,7 +35,7 @@ class Deconvolution(SparseModule):
self.gradInput = torch.Tensor() self.gradInput = torch.Tensor()
def updateOutput(self, input): def updateOutput(self, input):
assert input.features.ndimension()==0 or input.features.size(1) == self.nIn assert input.features.ndimension() == 0 or input.features.size(1) == self.nIn
self.output.metadata = input.metadata self.output.metadata = input.metadata
self.output.spatial_size =\ self.output.spatial_size =\
(input.spatial_size - 1) * self.filter_stride + self.filter_size (input.spatial_size - 1) * self.filter_stride + self.filter_size
......
...@@ -11,7 +11,7 @@ from ..utils import toLongTensor, typed_fn, optionalTensor, nullptr, set ...@@ -11,7 +11,7 @@ from ..utils import toLongTensor, typed_fn, optionalTensor, nullptr, set
from ..sparseConvNetTensor import SparseConvNetTensor from ..sparseConvNetTensor import SparseConvNetTensor
from .batchNormalization import * from .batchNormalization import *
from .affineReLUTrivialConvolution import AffineReLUTrivialConvolution from .affineReLUTrivialConvolution import AffineReLUTrivialConvolution
from .validConvolution import ValidConvolution from .submanifoldConvolution import SubmanifoldConvolution
import math import math
...@@ -43,7 +43,7 @@ class DenseNetBlock(Container): ...@@ -43,7 +43,7 @@ class DenseNetBlock(Container):
self.add(BatchNormalization(nFeaturesB)) self.add(BatchNormalization(nFeaturesB))
# Module 4*i+3 # Module 4*i+3
self.add( self.add(
ValidConvolution( SubmanifoldConvolution(
dimension, dimension,
nFeaturesB, nFeaturesB,
growthRate, growthRate,
......
...@@ -18,26 +18,28 @@ from ..utils import dim_fn, nullptr ...@@ -18,26 +18,28 @@ from ..utils import dim_fn, nullptr
from ..sparseConvNetTensor import SparseConvNetTensor from ..sparseConvNetTensor import SparseConvNetTensor
from ..metadata import Metadata from ..metadata import Metadata
class DenseToSparse(SparseModule): class DenseToSparse(SparseModule):
def __init__(self, dimension): def __init__(self, dimension):
SparseModule.__init__(self) SparseModule.__init__(self)
self.dimension = dimension self.dimension = dimension
self.output = SparseConvNetTensor(torch.Tensor(),Metadata(dimension)) self.output = SparseConvNetTensor(torch.Tensor(), Metadata(dimension))
self.gradInput = torch.Tensor() self.gradInput = torch.Tensor()
def updateOutput(self, input): def updateOutput(self, input):
a=input a = input
aa=a.permute(*([0,]+list(range(2,2+self.dimension))+[1,])).clone() aa = a.permute(
self.aas=aa.size() *([0, ] + list(range(2, 2 + self.dimension)) + [1, ])).clone()
nz=aa.abs().sum(self.dimension+1).view(aa.size()[0:-1]) self.aas = aa.size()
s=torch.LongTensor(nz.stride()).view(1,self.dimension+1) nz = aa.abs().sum(self.dimension + 1).view(aa.size()[0:-1])
nz=nz.nonzero() s = torch.LongTensor(nz.stride()).view(1, self.dimension + 1)
s=s.type_as(nz) nz = nz.nonzero()
aa=aa.view(-1,a.size(1)) s = s.type_as(nz)
self.aas2=aa.size() aa = aa.view(-1, a.size(1))
self.r=(nz*s.expand_as(nz)).sum(1).view(-1) self.aas2 = aa.size()
self.output.features=aa.index_select(0,self.r) self.r = (nz * s.expand_as(nz)).sum(1).view(-1)
self.output.spatial_size=torch.LongTensor(list(input.size()[2:])) self.output.features = aa.index_select(0, self.r)
self.output.spatial_size = torch.LongTensor(list(input.size()[2:]))
dim_fn(self.dimension, 'createMetadataForDenseToSparse')( dim_fn(self.dimension, 'createMetadataForDenseToSparse')(
self.output.metadata.ffi, self.output.metadata.ffi,
self.output.spatial_size, self.output.spatial_size,
...@@ -47,13 +49,15 @@ class DenseToSparse(SparseModule): ...@@ -47,13 +49,15 @@ class DenseToSparse(SparseModule):
def updateGradInput(self, input, gradOutput): def updateGradInput(self, input, gradOutput):
self.gradInput.resize_(self.aas2).zero_() self.gradInput.resize_(self.aas2).zero_()
self.gradInput.index_copy_(0,self.r,gradOutput) self.gradInput.index_copy_(0, self.r, gradOutput)
self.gradInput=self.gradInput.view(self.aas).permute(*([0,self.dimension+1]+list(range(1,self.dimension+1)))) self.gradInput = self.gradInput.view(self.aas).permute(
*([0, self.dimension + 1] + list(range(1, self.dimension + 1))))
return self.gradInput return self.gradInput
def clearState(self): def clearState(self):
SparseModule.clearState(self) SparseModule.clearState(self)
self.aas=None self.aas = None
self.r=None self.r = None
def __repr__(self): def __repr__(self):
return 'DenseToSparse(' + str(self.dimension) + ')' return 'DenseToSparse(' + str(self.dimension) + ')'
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