#include "caffe2/operators/conv_op.h" #include "caffe2/operators/conv_op_impl.h" #include "caffe2/operators/conv_pool_op_base.h" namespace caffe2 { const char kConvDoc[] = R"DOC( The Conv2D operator computes a 2D convolution operation over an input blob $(X)$, with a filter blob $(filter)$ and a bias blob $(bias)$, and outputs a single output blob $(Y)$. Although there are several options for order, the convention is that the input $(X)$ is a blob of shape $(N,C_{in},H_{in},W_{in})$ and the output $(Y)$ is a blob of shape $(N,C_{out},H_{out},W_{out})$. Here, $N$ is the batch size, $C$ is the number of channels, $H$ is the spatial height, and $W$ is the spatial width. For example, if your input data was a batch of five, 100x120pixel RGB images, $X$ would have shape $(5,3,120,100)$. The $filter$ input blob may contain multiple filters and has shape $(M, C_{in}, K_H, K_W)$. Here, $M$ is the number of individual filters contained in the blob, $C_{in}$ is the number of channels of each filter (by convention in 2D convolution it is the same as the number of channels in the input), $K_H$ is the spatial height of the kernel, and $K_W$ is the spatial width of the kernel. The $bias$ blob is a vector of length $M$, where there is one bias for each filter in the $filter$ blob. Given the shape of the input blob and the filter blob, we can calculate the shape of the output blob as follows. The number of items in the batch $N$ will stay the same. The number of channels in the output will equal the number of kernels in the filter blob, so $C_{out} = M.$ With stride and pad defined below, the spatial height and width of the output ($H_{out}$ and $W_{out}$) are calculated as $$H_{out} = \left \lfloor{\frac{H_{in} - K_H + 2*pad}{stride}+1}\right \rfloor$$ $$W_{out} = \left \lfloor{\frac{W_{in} - K_W + 2*pad}{stride}+1}\right \rfloor$$ Github Links: - https://github.com/pytorch/pytorch/blob/master/caffe2/operators/conv_op.h - https://github.com/pytorch/pytorch/blob/master/caffe2/operators/conv_op.cc - https://github.com/pytorch/pytorch/blob/master/caffe2/operators/conv_pool_op_base.h
Example **Code** ``` workspace.ResetWorkspace() op = core.CreateOperator( "Conv", ["X", "filter", "bias"], ["Y"], kernel=5, pad=1, stride=2 ) // Create X: (N,C,H,W) data = np.random.randn(1,1,8,8).astype(np.float32) print("Data shape: ",data.shape) // Create W: (M,C,Kh,Kw) filters = np.random.randn(3,1,5,5).astype(np.float32) print("Filter shape: ",filters.shape) // Create b: M bias = np.array([1.,1.,1.]).astype(np.float32) print("Bias shape: ",bias.shape) // Put the inputs into the workspace workspace.FeedBlob("X", data) workspace.FeedBlob("filter", filters) workspace.FeedBlob("bias", bias) // Run the operator workspace.RunOperatorOnce(op) print("Y:\n", workspace.FetchBlob("Y")) ``` **Result** ``` Data shape: (1, 1, 8, 8) Filter shape: (3, 1, 5, 5) Bias shape: (3,) Y: [[[[ 0.6406407 0.8620521 0.56461596] [ -1.5042953 -0.79549205 -10.683343 ] [ -0.5240259 3.4538248 -3.9564204 ]] [[ 0.6876496 4.8328524 -1.9525816 ] [ 1.2995434 -2.3895378 7.2670045 ] [ 3.9929862 1.8126237 5.4699917 ]] [[ 3.55949 4.7934155 0.76086235] [ 3.9588015 -1.3251319 4.413117 ] [ -1.5296054 -1.4924102 -3.2552304 ]]]] ```
)DOC"; std::function ConvDocGenerator(const char* dim) { return [=](OpSchema& schema) { string doc = R"DOC( The convolution operator consumes an input vector, a {dim}filter blob and a bias blob and computes the output. {conv_doc})DOC"; c10::ReplaceAll(doc, "{dim}", dim); c10::ReplaceAll(doc, "{conv_doc}", kConvDoc); schema.SetDoc(doc); schema.Input( 0, "X", "Input data blob, of shape $(N, C_{in}, H_{in}, W_{in})$, to be convolved with the kernels in the filter blob." ); schema.Input( 1, "filter", "The filter blob, of shape $(M, C_{in}, K_H, K_W)$, containing the filters to be convolved with the data." ); schema.Input( 2, "bias", "The bias blob, of length $M$, containing the biases for the convolution, one bias per filter." ); schema.Output( 0, "Y", "Output data blob, of shape $(N, C_{out}, H_{out}, W_{out})$, that contains the result of the convolution." ); /* schema.Arg( "kernel", "*(type: int; default: 0)* Desired kernel size. If left at default the kernel size will be inferred from the input $filter$ blob.", 0 ); schema.Arg( "stride", "*(type: int; default: 1)* Controls the stride of the kernel as it traverses the input blob.", 0 ); schema.Arg( "dilation", "*(type: int; default: 1)* Controls spacing between kernel points. If dilation is greater than one, the kernel does not operate on a contiguous spatial region. For a visualization click [here](https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md).", 0 ); schema.Arg( "pad", "*(type: int; default: 0)* Controls the amount of padding to apply to the input feature map before computing the convolution.", 0 ); schema.Arg( "float16_compute", "*(type: bool; default: False)* Whether to use float-16 compute kernel.", 0 ); schema.Arg( "group", "*(type: int; default: 1)* Controls level of group convolution. For more info click [here](https://blog.yani.io/filter-group-tutorial/).", 0 ); schema.Arg( "order", "*(type: string; default: \"NCHW\")* Specifies the order of the input data blob, where $N$ is batch size, $C$ is number of channels, $H$ is spatial height, and $W$ is spatial width. The only other valid option is \"NHWC\".", 0 ); schema.Arg( "shared_buffer", "*(type: int; default: 0)*", 0 ); */ }; } REGISTER_CPU_OPERATOR(Conv, ConvOp); OPERATOR_SCHEMA(Conv) .NumInputs(2, 3) .NumOutputs(1) .TensorInferenceFunction(ConvPoolOpBase::TensorInferenceForConv) .CostInferenceFunction(OpSchema::CostInferenceFunctionType( ConvPoolOpBase::CostInferenceForConv)) .FillUsing(ConvDocGenerator("")) .InheritOnnxSchema(); REGISTER_CPU_OPERATOR(Conv1D, ConvOp); OPERATOR_SCHEMA(Conv1D) .NumInputs(2, 3) .NumOutputs(1) .TensorInferenceFunction(ConvPoolOpBase::TensorInferenceForConv) .FillUsing(ConvDocGenerator("1D ")) .InheritOnnxSchema("Conv"); REGISTER_CPU_OPERATOR(Conv2D, ConvOp); OPERATOR_SCHEMA(Conv2D) .NumInputs(2, 3) .NumOutputs(1) .CostInferenceFunction(OpSchema::CostInferenceFunctionType( ConvPoolOpBase::CostInferenceForConv)) .TensorInferenceFunction(ConvPoolOpBase::TensorInferenceForConv) .FillUsing(ConvDocGenerator("2D ")) .InheritOnnxSchema("Conv"); REGISTER_CPU_OPERATOR(Conv3D, ConvOp); OPERATOR_SCHEMA(Conv3D) .NumInputs(2, 3) .NumOutputs(1) .CostInferenceFunction(OpSchema::CostInferenceFunctionType( ConvPoolOpBase::CostInferenceForConv)) .TensorInferenceFunction(ConvPoolOpBase::TensorInferenceForConv) .FillUsing(ConvDocGenerator("3D ")) .InheritOnnxSchema("Conv"); } // namespace caffe2