diff options
-rw-r--r-- | SpatialMaxPooling.lua | 89 | ||||
-rw-r--r-- | generic/SpatialMaxPooling.c | 142 | ||||
-rw-r--r-- | init.c | 5 | ||||
-rw-r--r-- | init.lua | 1 | ||||
-rw-r--r-- | nnx-1.0-1.rockspec | 1 | ||||
-rw-r--r-- | test/test-all.lua | 19 | ||||
-rw-r--r-- | todo/SpatialMaxPooling.lua | 307 |
7 files changed, 257 insertions, 307 deletions
diff --git a/SpatialMaxPooling.lua b/SpatialMaxPooling.lua new file mode 100644 index 0000000..fe8bf54 --- /dev/null +++ b/SpatialMaxPooling.lua @@ -0,0 +1,89 @@ +local SpatialMaxPooling, parent = torch.class('nn.SpatialMaxPooling', 'nn.Module') + +local help_desc = +[[Applies a 2D sub-sampling over an input image composed of +several input planes. The input tensor in forward(input) is +expected to be a 3D tensor (nInputPlane x height x width). +The number of output planes will be the same as nInputPlane. + +Compared to the nn.SpatialSubSampling module, a max operator is +used to pool values in the kHxkW input neighborhood. + +Note that depending of the size of your kernel, several +(of the last) columns or rows of the input image might be lost. +It is up to the user to add proper padding in images. + +If the input image is a 3D tensor nInputPlane x height x width, +the output image size will be nInputPlane x oheight x owidth, where + +owidth = (width - kW) / dW + 1 +oheight = (height - kH) / dH + 1 . + +The parameters of the sub-sampling can be found in self.weight +(Tensor of size nInputPlane) and self.bias (Tensor of size nInputPlane). +The corresponding gradients can be found in self.gradWeight and self.gradBias. + +The output value of the layer can be precisely described as: + +output[i][j][k] = bias[k] + + weight[k] sum_{s=1}^kW sum_{t=1}^kH input[dW*(i-1)+s][dH*(j-1)+t][k] ]] + +function SpatialMaxPooling:__init(kW, kH, dW, dH) + parent.__init(self) + + -- usage + if not kW or not kH then + error(xlua.usage('nn.SpatialMaxPooling', help_desc, nil, + {type='number', help='kernel width', req=true}, + {type='number', help='kernel height', req=true}, + {type='number', help='stride width [default = kernel width]'}, + {type='number', help='stride height [default = kernel height]'})) + end + + dW = dW or kW + dH = dH or kH + + self.kW = kW + self.kH = kH + self.dW = dW + self.dH = dH + + self.indices = torch.Tensor() +end + +function SpatialMaxPooling:forward(input) + input.nn.SpatialMaxPooling_forward(self, input) + return self.output +end + +function SpatialMaxPooling:backward(input, gradOutput) + input.nn.SpatialMaxPooling_backward(self, input, gradOutput) + return self.gradInput +end + +function SpatialMaxPooling:empty() + self.gradInput:resize() + self.gradInput:storage():resize(0) + self.output:resize() + self.output:storage():resize(0) + self.indices:resize() + self.indices:storage():resize(0) +end + +function SpatialMaxPooling:write(file) + parent.write(self, file) + file:writeInt(self.kW) + file:writeInt(self.kH) + file:writeInt(self.dW) + file:writeInt(self.dH) + file:writeObject(self.indices) +end + +function SpatialMaxPooling:read(file) + parent.read(self, file) + self.kW = file:readInt() + self.kH = file:readInt() + self.dW = file:readInt() + self.dH = file:readInt() + self.indices = file:readObject() +end diff --git a/generic/SpatialMaxPooling.c b/generic/SpatialMaxPooling.c new file mode 100644 index 0000000..ee9a4be --- /dev/null +++ b/generic/SpatialMaxPooling.c @@ -0,0 +1,142 @@ +#ifndef TH_GENERIC_FILE +#define TH_GENERIC_FILE "generic/SpatialMaxPooling.c" +#else + +static int nn_(SpatialMaxPooling_forward)(lua_State *L) +{ + THTensor *input = luaT_checkudata(L, 2, torch_(Tensor_id)); + int kW = luaT_getfieldcheckint(L, 1, "kW"); + int kH = luaT_getfieldcheckint(L, 1, "kH"); + int dW = luaT_getfieldcheckint(L, 1, "dW"); + int dH = luaT_getfieldcheckint(L, 1, "dH"); + THTensor *indices = luaT_getfieldcheckudata(L, 1, "indices", torch_(Tensor_id)); + THTensor *output = luaT_getfieldcheckudata(L, 1, "output", torch_(Tensor_id)); + + luaL_argcheck(L, input->nDimension == 3, 2, "3D tensor expected"); + luaL_argcheck(L, input->size[2] >= kW && input->size[1] >= kH, 2, "input image smaller than kernel size"); + + THTensor *outputPlane, *inputPlane, *unfoldedInputPlane, *localInput; + int k,i,j; + + THTensor_(resize3d)(output, input->size[0], + (input->size[1] - kH) / dH + 1, + (input->size[2] - kW) / dW + 1 ); + + inputPlane = THTensor_(new)(); + outputPlane = THTensor_(new)(); + localInput = THTensor_(new)(); + unfoldedInputPlane = THTensor_(new)(); + + /* indices will contain i,j locatyions for each output point */ + THTensor_(resize4d)(indices, 2,output->size[0],output->size[1],output->size[2]); + + for (k = 0; k < input->size[0]; k++) + { + /* get input and output plane */ + THTensor_(select)(outputPlane, output, 0, k); + THTensor_(select)(inputPlane, input, 0, k); + + /* Unfold input to get each local window */ + THTensor_(unfold)(unfoldedInputPlane, inputPlane, 0, kH, dH); + THTensor_(unfold)(unfoldedInputPlane, NULL, 1, kW, dW); + + /* Calculate max points */ + for(i = 0; i < outputPlane->size[0]; i++) { + for(j = 0; j < outputPlane->size[1]; j++) { + long maxindex = -1; + double maxval = -THInf; + long tcntr = 0; + int x,y; + for(y = 0; y < unfoldedInputPlane->size[2]; y++) { + for(x = 0; x < unfoldedInputPlane->size[3]; x++) { + double val = THTensor_(get4d)(unfoldedInputPlane, i,j,y,x); + if (val > maxval) { + maxval = val; + maxindex = tcntr; + } + tcntr++; + } + } + + THTensor_(set4d)(indices,0,k,i,j, (int)(maxindex / dW)+1); + THTensor_(set4d)(indices,1,k,i,j, (maxindex % dW) +1); + THTensor_(set2d)(outputPlane,i,j,maxval); + } + } + } + THTensor_(free)(inputPlane); + THTensor_(free)(outputPlane); + THTensor_(free)(unfoldedInputPlane); + THTensor_(free)(localInput); + + return 1; +} + +static int nn_(SpatialMaxPooling_backward)(lua_State *L) +{ + THTensor *input = luaT_checkudata(L, 2, torch_(Tensor_id)); + THTensor *gradOutput = luaT_checkudata(L, 3, torch_(Tensor_id)); + int kW = luaT_getfieldcheckint(L, 1, "kW"); + int kH = luaT_getfieldcheckint(L, 1, "kH"); + int dW = luaT_getfieldcheckint(L, 1, "dW"); + int dH = luaT_getfieldcheckint(L, 1, "dH"); + + THTensor *indices = luaT_getfieldcheckudata(L, 1, "indices", torch_(Tensor_id)); + THTensor *gradInput = luaT_getfieldcheckudata(L, 1, "gradInput", torch_(Tensor_id)); + + THTensor *gradOutputPlane, *gradInputPlane, *unfoldedGradInputPlane, *gradLocalInput; + int k,i,j; + + THTensor_(resizeAs)(gradInput, input); + THTensor_(zero)(gradInput); + + gradInputPlane = THTensor_(new)(); + gradOutputPlane = THTensor_(new)(); + gradLocalInput = THTensor_(new)(); + unfoldedGradInputPlane = THTensor_(new)(); + + for (k = 0; k < input->size[0]; k++) + { + /* get input and output plane */ + THTensor_(select)(gradOutputPlane, gradOutput, 0, k); + THTensor_(select)(gradInputPlane, gradInput, 0, k); + + /* Unfold input to get each local window */ + THTensor_(unfold)(unfoldedGradInputPlane, gradInputPlane, 0, kH, dH); + THTensor_(unfold)(unfoldedGradInputPlane, NULL, 1, kW, dW); + + /* Calculate max points */ + for(i = 0; i < gradOutputPlane->size[0]; i++) { + for(j = 0; j < gradOutputPlane->size[1]; j++) { + THTensor_(select)(gradLocalInput, unfoldedGradInputPlane,0,i); + THTensor_(select)(gradLocalInput, NULL, 0,j); + long maxi = THTensor_(get4d)(indices,0,k,i,j)-1; + long maxj = THTensor_(get4d)(indices,1,k,i,j)-1; + double gi = THTensor_(get2d)(gradLocalInput,maxi,maxj)+THTensor_(get2d)(gradOutputPlane,i,j); + THTensor_(set2d)(gradLocalInput,maxi,maxj,gi); + } + } + } + + THTensor_(free)(gradInputPlane); + THTensor_(free)(gradOutputPlane); + THTensor_(free)(unfoldedGradInputPlane); + THTensor_(free)(gradLocalInput); + + return 1; +} + +static const struct luaL_Reg nn_(SpatialMaxPooling__) [] = { + {"SpatialMaxPooling_forward", nn_(SpatialMaxPooling_forward)}, + {"SpatialMaxPooling_backward", nn_(SpatialMaxPooling_backward)}, + {NULL, NULL} +}; + +static void nn_(SpatialMaxPooling_init)(lua_State *L) +{ + luaT_pushmetaclass(L, torch_(Tensor_id)); + luaT_registeratname(L, nn_(SpatialMaxPooling__), "nn"); + lua_pop(L,1); +} + +#endif @@ -20,6 +20,9 @@ static const void* torch_DoubleTensor_id = NULL; #include "generic/SpatialConvolutionTable.c" #include "THGenerateFloatTypes.h" +#include "generic/SpatialMaxPooling.c" +#include "THGenerateFloatTypes.h" + #include "generic/SpatialLogSoftMax.c" #include "THGenerateFloatTypes.h" @@ -37,6 +40,7 @@ DLL_EXPORT int luaopen_libnnx(lua_State *L) nn_FloatThreshold_init(L); nn_FloatSpatialConvolutionTable_init(L); nn_FloatSpatialLogSoftMax_init(L); + nn_FloatSpatialMaxPooling_init(L); nn_DoubleSpatialLinear_init(L); nn_DoubleHardShrink_init(L); @@ -44,6 +48,7 @@ DLL_EXPORT int luaopen_libnnx(lua_State *L) nn_DoubleThreshold_init(L); nn_DoubleSpatialConvolutionTable_init(L); nn_DoubleSpatialLogSoftMax_init(L); + nn_DoubleSpatialMaxPooling_init(L); return 1; } @@ -62,6 +62,7 @@ torch.include('nnx', 'Narrow.lua') torch.include('nnx', 'SpatialLinear.lua') torch.include('nnx', 'SpatialLogSoftMax.lua') torch.include('nnx', 'SpatialConvolutionTable.lua') +torch.include('nnx', 'SpatialMaxPooling.lua') -- criterions: torch.include('nnx', 'SuperCriterion.lua') diff --git a/nnx-1.0-1.rockspec b/nnx-1.0-1.rockspec index f3b2501..4fadf6b 100644 --- a/nnx-1.0-1.rockspec +++ b/nnx-1.0-1.rockspec @@ -58,6 +58,7 @@ build = { install_files(/lua/nnx Threshold.lua) install_files(/lua/nnx SpatialConvolutionTable.lua) install_files(/lua/nnx SpatialLogSoftMax.lua) + install_files(/lua/nnx SpatialMaxPooling.lua) install_files(/lua/nnx SpatialLinear.lua) install_files(/lua/nnx SuperCriterion.lua) install_files(/lua/nnx Trainer.lua) diff --git a/test/test-all.lua b/test/test-all.lua index 8ec3906..f584de2 100644 --- a/test/test-all.lua +++ b/test/test-all.lua @@ -34,6 +34,25 @@ function nnx.test_all() assert_equal(0, berr, 'error in backward after i/o') end + function test_SpatialMaxPooling() + local fanin = math.random(1,10) + local osizex = math.random(1,4) + local osizey = math.random(1,4) + local mx = math.random(2,6) + local my = math.random(2,6) + local sizex = osizex*mx + local sizey = osizey*my + local module = nn.SpatialMaxPooling(mx,my) + local input = lab.rand(fanin,sizey,sizex) + + local error = jac.test_jac(module, input) + assert_equal((error < precision), true, 'error on state: ' .. error) + + local ferr, berr = jac.test_io(module, input) + assert_equal(0, ferr, 'error in forward after i/o') + assert_equal(0, berr, 'error in backward after i/o') + end + function test_Power() local in1 = lab.rand(10,20) local mod = nn.Power(2) diff --git a/todo/SpatialMaxPooling.lua b/todo/SpatialMaxPooling.lua deleted file mode 100644 index 15fcdf4..0000000 --- a/todo/SpatialMaxPooling.lua +++ /dev/null @@ -1,307 +0,0 @@ -local SpatialMaxPooling, parent = torch.class('nn.SpatialMaxPooling', 'nn.Module') - -local help_desc = -[[Applies a 2D sub-sampling over an input image composed of -several input planes. The input tensor in forward(input) is -expected to be a 3D tensor (width x height x nInputPlane). -The number of output planes will be the same as nInputPlane. - -Compared to the nn.SpatialSubSampling module, a max operator is -used to pool values in the kWxkH input neighborhood. - -Note that depending of the size of your kernel, several -(of the last) columns or rows of the input image might be lost. -It is up to the user to add proper padding in images. - -If the input image is a 3D tensor width x height x nInputPlane, -the output image size will be owidth x oheight x nInputPlane where - -owidth = (width - kW) / dW + 1 -oheight = (height - kH) / dH + 1 . - -The parameters of the sub-sampling can be found in self.weight -(Tensor of size nInputPlane) and self.bias (Tensor of size nInputPlane). -The corresponding gradients can be found in self.gradWeight and self.gradBias. - -The output value of the layer can be precisely described as: - -output[i][j][k] = bias[k] - + weight[k] sum_{s=1}^kW sum_{t=1}^kH input[dW*(i-1)+s][dH*(j-1)+t][k] ]] - -function SpatialMaxPooling:__init(kW, kH, dW, dH) - parent.__init(self) - - -- usage - if not kW or not kH then - error(xlua.usage('nn.SpatialMaxPooling', help_desc, nil, - {type='number', help='kernel width', req=true}, - {type='number', help='kernel height', req=true}, - {type='number', help='stride width [default = kernel width]'}, - {type='number', help='stride height [default = kernel height]'})) - end - - dW = dW or kW - dH = dH or kH - - self.kW = kW - self.kH = kH - self.dW = dW - self.dH = dH - - self.indices = torch.Tensor() -end - -SpatialMaxPooling.forward_c = inline.load [[ - const void* torch_Tensor_id = luaT_checktypename2id(L, "torch.Tensor"); - THTensor *input = luaT_checkudata(L, 2, torch_Tensor_id); - int kW = luaT_getfieldcheckint(L, 1, "kW"); - int kH = luaT_getfieldcheckint(L, 1, "kH"); - int dW = luaT_getfieldcheckint(L, 1, "dW"); - int dH = luaT_getfieldcheckint(L, 1, "dH"); - THTensor *indices = luaT_getfieldcheckudata(L, 1, "indices", torch_Tensor_id); - THTensor *output = luaT_getfieldcheckudata(L, 1, "output", torch_Tensor_id); - - luaL_argcheck(L, input->nDimension == 3, 2, "3D tensor expected"); - luaL_argcheck(L, input->size[0] >= kW && input->size[1] >= kH, 2, "input image smaller than kernel size"); - - THTensor *outputPlane, *inputPlane, *unfoldedInputPlane, *localInput; - int k,i,j; - - THTensor_resize3d(output, - (input->size[0] - kW) / dW + 1, - (input->size[1] - kH) / dH + 1, - input->size[2]); - - inputPlane = THTensor_new(); - outputPlane = THTensor_new(); - localInput = THTensor_new(); - unfoldedInputPlane = THTensor_new(); - - - /* indices will contain i,j locatyions for each output point */ - THTensor_resize4d(indices, output->size[0],output->size[1],output->size[2],2); - - for (k = 0; k < input->size[2]; k++) - { - /* get input and output plane */ - THTensor_select(outputPlane, output, 2, k); - THTensor_select(inputPlane, input, 2, k); - - /* Unfold input to get each local window */ - THTensor_unfold(unfoldedInputPlane, inputPlane, 0, kW, dW); - THTensor_unfold(unfoldedInputPlane, NULL, 1, kH, dH); - - /* Calculate max points */ - for(j = 0; j < outputPlane->size[1]; j++) { - for(i = 0; i < outputPlane->size[0]; i++) { - long maxindex = -1; - double maxval = -THInf; - long tcntr = 0; - int x,y; - for(y = 0; y < unfoldedInputPlane->size[3]; y++) { - for(x = 0; x < unfoldedInputPlane->size[2]; x++) { - double val = THTensor_get4d(unfoldedInputPlane, i,j,x,y); - if (val > maxval) { - maxval = val; - maxindex = tcntr; - } - tcntr++; - } - } - - THTensor_set4d(indices,i,j,k,0, (maxindex %% dW) +1); - THTensor_set4d(indices,i,j,k,1, (int)(maxindex / dW)+1); - THTensor_set2d(outputPlane,i,j,maxval); - } - } - } - THTensor_free(inputPlane); - THTensor_free(outputPlane); - THTensor_free(unfoldedInputPlane); - THTensor_free(localInput); - - return 1; -]] - -inline.headers('omp.h') -inline.flags('-fopenmp') -SpatialMaxPooling.forward_c_omp = inline.load [[ - const void* torch_Tensor_id = luaT_checktypename2id(L, "torch.Tensor"); - THTensor *input = luaT_checkudata(L, 2, torch_Tensor_id); - int kW = luaT_getfieldcheckint(L, 1, "kW"); - int kH = luaT_getfieldcheckint(L, 1, "kH"); - int dW = luaT_getfieldcheckint(L, 1, "dW"); - int dH = luaT_getfieldcheckint(L, 1, "dH"); - THTensor *indices = luaT_getfieldcheckudata(L, 1, "indices", torch_Tensor_id); - THTensor *output = luaT_getfieldcheckudata(L, 1, "output", torch_Tensor_id); - - luaL_argcheck(L, input->nDimension == 3, 2, "3D tensor expected"); - luaL_argcheck(L, input->size[0] >= kW && input->size[1] >= kH, 2, "input image smaller than kernel size"); - - THTensor_resize3d(output, - (input->size[0] - kW) / dW + 1, - (input->size[1] - kH) / dH + 1, - input->size[2]); - - /* indices will contain i,j locatyions for each output point */ - THTensor_resize4d(indices, output->size[0],output->size[1],output->size[2],2); - - omp_lock_t lock_input,lock_output; - omp_init_lock(&lock_input); omp_init_lock(&lock_output); - - int k,i,j; - #pragma omp parallel for private(i,j,k) - for (k = 0; k < input->size[2]; k++) - { - THTensor *outputPlane, *inputPlane, *unfoldedInputPlane, *localInput; - omp_set_lock(&lock_input); - inputPlane = THTensor_new(); - THTensor_select(inputPlane, input, 2, k); - unfoldedInputPlane = THTensor_new(); - THTensor_unfold(unfoldedInputPlane, inputPlane, 0, kW, dW); - THTensor_unfold(unfoldedInputPlane, NULL, 1, kH, dH); - localInput = THTensor_new(); - omp_unset_lock(&lock_input); - - omp_set_lock(&lock_output); - outputPlane = THTensor_new(); - THTensor_select(outputPlane, output, 2, k); - omp_unset_lock(&lock_output); - - /* Calculate max points */ - for(j = 0; j < outputPlane->size[1]; j++) { - for(i = 0; i < outputPlane->size[0]; i++) { - long maxindex = -1; - double maxval = -THInf; - long tcntr = 0; - int x,y; - for(y = 0; y < unfoldedInputPlane->size[3]; y++) { - for(x = 0; x < unfoldedInputPlane->size[2]; x++) { - double val = THTensor_get4d(unfoldedInputPlane, i,j,x,y); - if (val > maxval) { - maxval = val; - maxindex = tcntr; - } - tcntr++; - } - } - - THTensor_set4d(indices,i,j,k,0, (maxindex %% dW) +1); - THTensor_set4d(indices,i,j,k,1, (int)(maxindex / dW)+1); - THTensor_set2d(outputPlane,i,j,maxval); - } - } - - //#pragma omp barrier - omp_set_lock(&lock_input); - THTensor_free(inputPlane); - THTensor_free(unfoldedInputPlane); - THTensor_free(localInput); - omp_unset_lock(&lock_input); - - omp_set_lock(&lock_output); - THTensor_free(outputPlane); - omp_unset_lock(&lock_output); - } - - omp_destroy_lock(&lock_input); omp_destroy_lock(&lock_output); - return 1; -]] - -SpatialMaxPooling.backward_c = inline.load [[ - const void* torch_Tensor_id = luaT_checktypename2id(L, "torch.Tensor"); - THTensor *input = luaT_checkudata(L, 2, torch_Tensor_id); - THTensor *gradOutput = luaT_checkudata(L, 3, torch_Tensor_id); - int kW = luaT_getfieldcheckint(L, 1, "kW"); - int kH = luaT_getfieldcheckint(L, 1, "kH"); - int dW = luaT_getfieldcheckint(L, 1, "dW"); - int dH = luaT_getfieldcheckint(L, 1, "dH"); - - THTensor *indices = luaT_getfieldcheckudata(L, 1, "indices", torch_Tensor_id); - THTensor *gradInput = luaT_getfieldcheckudata(L, 1, "gradInput", torch_Tensor_id); - - THTensor *gradOutputPlane, *gradInputPlane, *unfoldedGradInputPlane, *gradLocalInput; - int k,i,j; - - THTensor_resizeAs(gradInput, input); - THTensor_zero(gradInput); - - gradInputPlane = THTensor_new(); - gradOutputPlane = THTensor_new(); - gradLocalInput = THTensor_new(); - unfoldedGradInputPlane = THTensor_new(); - - for (k = 0; k < input->size[2]; k++) - { - /* get input and output plane */ - THTensor_select(gradOutputPlane, gradOutput, 2, k); - THTensor_select(gradInputPlane, gradInput, 2, k); - - /* Unfold input to get each local window */ - THTensor_unfold(unfoldedGradInputPlane, gradInputPlane, 0, kW, dW); - THTensor_unfold(unfoldedGradInputPlane, NULL, 1, kH, dH); - - /* Calculate max points */ - for(i = 0; i < gradOutputPlane->size[0]; i++) - { - for(j = 0; j < gradOutputPlane->size[1]; j++) - { - THTensor_select(gradLocalInput, unfoldedGradInputPlane,0,i); - THTensor_select(gradLocalInput, NULL, 0,j); - long maxi = THTensor_get4d(indices,i,j,k,0)-1; - long maxj = THTensor_get4d(indices,i,j,k,1)-1; - double gi = THTensor_get2d(gradLocalInput,maxi,maxj)+ - THTensor_get2d(gradOutputPlane,i,j); - THTensor_set2d(gradLocalInput,maxi,maxj,gi); - } - } - } - - THTensor_free(gradInputPlane); - THTensor_free(gradOutputPlane); - THTensor_free(unfoldedGradInputPlane); - THTensor_free(gradLocalInput); - - return 1; -]] - -function SpatialMaxPooling:forward(input) - if openmp and openmp.enabled then - self:forward_c_omp(input) - else - self:forward_c(input) - end - return self.output -end - -function SpatialMaxPooling:backward(input, gradOutput) - self:backward_c(input, gradOutput) - return self.gradInput -end - -function SpatialMaxPooling:empty() - self.gradInput:resize() - self.gradInput:storage():resize(0) - self.output:resize() - self.output:storage():resize(0) - self.indices:resize() - self.indices:storage():resize(0) -end - -function SpatialMaxPooling:write(file) - parent.write(self, file) - file:writeInt(self.kW) - file:writeInt(self.kH) - file:writeInt(self.dW) - file:writeInt(self.dH) - file:writeObject(self.indices) -end - -function SpatialMaxPooling:read(file) - parent.read(self, file) - self.kW = file:readInt() - self.kH = file:readInt() - self.dW = file:readInt() - self.dH = file:readInt() - self.indices = file:readObject() -end |