diff options
-rw-r--r-- | SpatialAveragePooling.lua | 20 | ||||
-rwxr-xr-x | doc/convolution.md | 12 | ||||
-rw-r--r-- | generic/SpatialAveragePooling.c | 190 | ||||
-rw-r--r-- | init.c | 5 | ||||
-rw-r--r-- | init.lua | 1 | ||||
-rw-r--r-- | test.lua | 69 |
6 files changed, 292 insertions, 5 deletions
diff --git a/SpatialAveragePooling.lua b/SpatialAveragePooling.lua new file mode 100644 index 0000000..13b6b45 --- /dev/null +++ b/SpatialAveragePooling.lua @@ -0,0 +1,20 @@ +local SpatialAveragePooling, parent = torch.class('nn.SpatialAveragePooling', 'nn.Module') + +function SpatialAveragePooling:__init(kW, kH, dW, dH) + parent.__init(self) + + self.kW = kW + self.kH = kH + self.dW = dW or 1 + self.dH = dH or 1 +end + +function SpatialAveragePooling:updateOutput(input) + return input.nn.SpatialAveragePooling_updateOutput(self, input) +end + +function SpatialAveragePooling:updateGradInput(input, gradOutput) + if self.gradInput then + return input.nn.SpatialAveragePooling_updateGradInput(self, input, gradOutput) + end +end diff --git a/doc/convolution.md b/doc/convolution.md index 5571b19..c65222d 100755 --- a/doc/convolution.md +++ b/doc/convolution.md @@ -12,6 +12,7 @@ A convolution is an integral that expresses the amount of overlap of one functio * [SpatialConvolution](#nn.SpatialConvolution) : a 2D convolution over an input image ; * [SpatialSubSampling](#nn.SpatialSubSampling) : a 2D sub-sampling over an input image ; * [SpatialMaxPooling](#nn.SpatialMaxPooling) : a 2D max-pooling operation over an input image ; + * [SpatialAveragePooling](#nn.SpatialAveragePooling) : a 2D average-pooling operation over an input image ; * [SpatialLPPooling](#nn.SpatialLPPooling) : computes the `p` norm in a convolutional manner on a set of input images ; * [SpatialConvolutionMap](#nn.SpatialConvolutionMap) : a 2D convolution that uses a generic connection table ; * [SpatialZeroPadding](#nn.SpatialZeroPadding) : padds a feature map with specified number of zeros ; @@ -356,6 +357,17 @@ Applies 2D max-pooling operation in `kWxkH` regions by step size `dWxdH` steps. The number of output features is equal to the number of input planes. +<a name="nn.SpatialAveragePooling"/> +### SpatialAveragePooling ### + +```lua +module = nn.SpatialAveragePooling(kW, kH [, dW, dH]) +``` + +Applies 2D average-pooling operation in `kWxkH` regions by step size +`dWxdH` steps. The number of output features is equal to the number of +input planes. + <a name="nn.SpatialSubSampling"/> ### SpatialSubSampling ### diff --git a/generic/SpatialAveragePooling.c b/generic/SpatialAveragePooling.c new file mode 100644 index 0000000..2052d05 --- /dev/null +++ b/generic/SpatialAveragePooling.c @@ -0,0 +1,190 @@ +#ifndef TH_GENERIC_FILE +#define TH_GENERIC_FILE "generic/SpatialAveragePooling.c" +#else + +static int nn_(SpatialAveragePooling_updateOutput)(lua_State *L) +{ + THTensor *input = luaT_checkudata(L, 2, torch_Tensor); + 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 *output = luaT_getfieldcheckudata(L, 1, "output", torch_Tensor); + + real *output_data; + real *input_data; + + int dimw = 2; + int dimh = 1; + int dimc = 0; + long nbatch = 1; + + long inputWidth; + long inputHeight; + long outputWidth; + long outputHeight; + long nInputPlane; // number of channels (or colors) + + long k; + + luaL_argcheck(L, input->nDimension == 3 || input->nDimension == 4, 2, "3D or 4D(batch mode) tensor expected"); + + if (input->nDimension == 4) { + nbatch = input->size[0]; + dimw++; + dimh++; + dimc++; + } + + inputWidth = input->size[dimw]; + inputHeight = input->size[dimh]; + nInputPlane = input->size[dimc]; + outputWidth = (inputWidth - kW) / dW + 1; + outputHeight = (inputHeight - kH) / dH + 1; + + luaL_argcheck(L, inputWidth >= kW && inputHeight >= kH, 2, "input image smaller than kernel size"); + + if (input->nDimension == 3) + THTensor_(resize3d)(output, nInputPlane, outputHeight, outputWidth); + else + THTensor_(resize4d)(output, input->size[0], nInputPlane, outputHeight, outputWidth); + + input = THTensor_(newContiguous)(input); + input_data = THTensor_(data)(input); + output_data = THTensor_(data)(output); + +#pragma omp parallel for private(k) + for(k = 0; k < nInputPlane; k++) + { + long p; + for(p = 0; p < nbatch; p++) + { + long xx, yy; + /* For all output pixels... */ + real *ptr_output = output_data + p*nInputPlane*outputWidth*outputHeight + k*outputWidth*outputHeight; + long i; + for(i = 0; i < outputWidth*outputHeight; i++) + ptr_output[i] = 0; + + for(yy = 0; yy < outputHeight; yy++) + { + for(xx = 0; xx < outputWidth; xx++) + { + /* Compute the mean of the input image... */ + real *ptr_input = input_data + p*nInputPlane*inputWidth*inputHeight + k*inputWidth*inputHeight + yy*dH*inputWidth+xx*dW; + real sum = 0; + long kx, ky; + + for(ky = 0; ky < kH; ky++) + { + for(kx = 0; kx < kW; kx++) + sum += ptr_input[kx]; + ptr_input += inputWidth; /* next input line */ + } + /* Update output */ + *ptr_output++ += sum; + } + } + } + } + THTensor_(free)(input); + + return 1; +} + +static int nn_(SpatialAveragePooling_updateGradInput)(lua_State *L) +{ + THTensor *input = luaT_checkudata(L, 2, torch_Tensor); + THTensor *gradOutput = luaT_checkudata(L, 3, torch_Tensor); + 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 *gradInput = luaT_getfieldcheckudata(L, 1, "gradInput", torch_Tensor); + + int dimw = 2; + int dimh = 1; + int dimc = 0; + long nbatch = 1; + + long inputWidth; + long inputHeight; + long outputWidth; + long outputHeight; + long nInputPlane; // number of channels (or colors) + + real *gradOutput_data; + real *input_data, *gradInput_data; + + long k; + + if (input->nDimension == 4) { + nbatch = input->size[0]; + dimw++; + dimh++; + dimc++; + } + + inputWidth = input->size[dimw]; + inputHeight = input->size[dimh]; + nInputPlane = input->size[dimc]; + outputWidth = (inputWidth - kW) / dW + 1; + outputHeight = (inputHeight - kH) / dH + 1; + + input_data = THTensor_(data)(input); + + THTensor_(resizeAs)(gradInput, input); + gradInput_data = THTensor_(data)(gradInput); + gradOutput_data = THTensor_(data)(gradOutput); + +#pragma omp parallel for private(k) + for(k = 0; k < nInputPlane; k++) + { + long p; + for(p = 0; p < nbatch; p++) + { + real *ptr_gradOutput = gradOutput_data + p*nInputPlane*outputHeight*outputWidth + k*outputWidth*outputHeight; + long xx, yy; + + real* ptr_gi = gradInput_data + p*nInputPlane*inputWidth*inputHeight + k*inputWidth*inputHeight; + long i; + for(i=0; i<inputWidth*inputHeight; i++) + ptr_gi[i] = 0.0; + + for(yy = 0; yy < outputHeight; yy++) + { + for(xx = 0; xx < outputWidth; xx++) + { + real *ptr_gradInput = gradInput_data + p*nInputPlane*inputWidth*inputHeight + k*inputWidth*inputHeight + yy*dH*inputWidth+xx*dW; + real z = *ptr_gradOutput++; + long kx, ky; + + for(ky = 0; ky < kH; ky++) + { + for(kx = 0; kx < kW; kx++) + ptr_gradInput[kx] += z; + ptr_gradInput += inputWidth; + } + } + } + } + } + + return 1; +} + +static const struct luaL_Reg nn_(SpatialAveragePooling__) [] = { + {"SpatialAveragePooling_updateOutput", nn_(SpatialAveragePooling_updateOutput)}, + {"SpatialAveragePooling_updateGradInput", nn_(SpatialAveragePooling_updateGradInput)}, + {NULL, NULL} +}; + +static void nn_(SpatialAveragePooling_init)(lua_State *L) +{ + luaT_pushmetatable(L, torch_Tensor); + luaT_registeratname(L, nn_(SpatialAveragePooling__), "nn"); + lua_pop(L,1); +} + +#endif @@ -98,6 +98,9 @@ #include "generic/SpatialMaxPooling.c" #include "THGenerateFloatTypes.h" +#include "generic/SpatialAveragePooling.c" +#include "THGenerateFloatTypes.h" + #include "generic/VolumetricConvolution.c" #include "THGenerateFloatTypes.h" @@ -155,6 +158,7 @@ int luaopen_libnn(lua_State *L) nn_FloatSpatialConvolutionMap_init(L); nn_FloatSpatialSubSampling_init(L); nn_FloatSpatialMaxPooling_init(L); + nn_FloatSpatialAveragePooling_init(L); nn_FloatVolumetricConvolution_init(L); nn_FloatVolumetricMaxPooling_init(L); nn_FloatMultiMarginCriterion_init(L); @@ -193,6 +197,7 @@ int luaopen_libnn(lua_State *L) nn_DoubleSpatialConvolutionMap_init(L); nn_DoubleSpatialSubSampling_init(L); nn_DoubleSpatialMaxPooling_init(L); + nn_DoubleSpatialAveragePooling_init(L); nn_DoubleVolumetricConvolution_init(L); nn_DoubleVolumetricMaxPooling_init(L); nn_DoubleMultiMarginCriterion_init(L); @@ -74,6 +74,7 @@ include('SpatialSubSampling.lua') include('SpatialMaxPooling.lua') include('SpatialMaxPoolingCUDA.lua') include('SpatialLPPooling.lua') +include('SpatialAveragePooling.lua') include('TemporalConvolution.lua') include('TemporalSubSampling.lua') include('TemporalMaxPooling.lua') @@ -1349,7 +1349,6 @@ function nntest.SpatialSubSampling() 'error on bias [%s]', t)) end - --verbose = true local batch = math.random(2,5) outi = math.random(4,8) outj = math.random(4,8) @@ -1358,10 +1357,6 @@ function nntest.SpatialSubSampling() module = nn.SpatialSubSampling(from, ki, kj, si, sj) input = torch.Tensor(batch,from,inj,ini):zero() --- print(from, to, ki, kj, si, sj, batch, ini, inj) --- print(module.weight:size()) --- print(module.gradWeight:size()) - local err = jac.testJacobian(module, input) mytester:assertlt(err, precision, 'batch error on state ') @@ -1427,6 +1422,70 @@ function nntest.SpatialMaxPooling() end +function nntest.SpatialAveragePooling() + local from = math.random(1,6) + local ki = math.random(1,5) + local kj = math.random(1,5) + local si = math.random(1,4) + local sj = math.random(1,4) + local outi = math.random(6,10) + local outj = math.random(6,10) + local ini = (outi-1)*si+ki + local inj = (outj-1)*sj+kj + local module = nn.SpatialAveragePooling(ki, kj, si, sj) + local input = torch.Tensor(from, inj, ini):zero() + + local err = jac.testJacobian(module, input) + mytester:assertlt(err, precision, 'error on state ') + + local ferr, berr = jac.testIO(module, input) + mytester:asserteq(ferr, 0, torch.typename(module) .. ' - i/o forward err ') + mytester:asserteq(berr, 0, torch.typename(module) .. ' - i/o backward err ') + + local sap = nn.SpatialSubSampling(from, ki, kj, si, sj) + sap.weight:fill(1.0) + sap.bias:fill(0.0) + + local output = module:forward(input) + local gradInput = module:backward(input, output) + local output2 = sap:forward(input) + local gradInput2 = sap:updateGradInput(input, output) + + mytester:assertTensorEq(output, output2, 0.000001, torch.typename(module) .. ' forward err ') + mytester:assertTensorEq(gradInput, gradInput2, 0.000001, torch.typename(module) .. ' backward err ') + + local batch = math.random(2,5) + outi = math.random(4,8) + outj = math.random(4,8) + ini = (outi-1)*si+ki + inj = (outj-1)*sj+kj + module = nn.SpatialAveragePooling(ki, kj, si, sj) + input = torch.Tensor(batch,from,inj,ini):zero() + + local err = jac.testJacobian(module, input) + mytester:assertlt(err, precision, 'batch error on state ') + + local ferr, berr = jac.testIO(module, input) + mytester:asserteq(0, ferr, torch.typename(module) .. ' - i/o forward err ') + mytester:asserteq(0, berr, torch.typename(module) .. ' - i/o backward err ') + + local ferr, berr = jac.testIO(module, input) + mytester:asserteq(ferr, 0, torch.typename(module) .. ' - i/o forward err (Batch) ') + mytester:asserteq(berr, 0, torch.typename(module) .. ' - i/o backward err (Batch) ') + + local sap = nn.SpatialSubSampling(from, ki, kj, si, sj) + sap.weight:fill(1.0) + sap.bias:fill(0.0) + + local output = module:forward(input) + local gradInput = module:backward(input, output) + local output2 = sap:forward(input) + local gradInput2 = sap:updateGradInput(input, output) + + mytester:assertTensorEq(output, output2, 0.000001, torch.typename(module) .. ' forward err (Batch) ') + mytester:assertTensorEq(gradInput, gradInput2, 0.000001, torch.typename(module) .. ' backward err (Batch) ') +end + function nntest.SpatialLPPooling() local fanin = math.random(1,4) local osizex = math.random(1,4) |