Commit 909a0022 authored by Vitaliy Lyudvichenko's avatar Vitaliy Lyudvichenko

Adding of OCL and public interface for InnerProduct layer

parent bf9e9b81
...@@ -267,6 +267,14 @@ namespace dnn ...@@ -267,6 +267,14 @@ namespace dnn
static Ptr<SoftmaxLayer> create(int axis = 1); static Ptr<SoftmaxLayer> create(int axis = 1);
}; };
class CV_EXPORTS_W InnerProductLayer : public Layer
{
public:
int axis;
static Ptr<InnerProductLayer> create(int axis = 1);
};
//! @} //! @}
//! @} //! @}
......
...@@ -84,7 +84,7 @@ void initModule() ...@@ -84,7 +84,7 @@ void initModule()
REG_RUNTIME_LAYER_FUNC(Pooling, createPoolingLayerFromCaffe) REG_RUNTIME_LAYER_FUNC(Pooling, createPoolingLayerFromCaffe)
REG_RUNTIME_LAYER_CLASS(MVN, MVNLayer) REG_RUNTIME_LAYER_CLASS(MVN, MVNLayer)
REG_RUNTIME_LAYER_FUNC(LRN, createLRNLayerFromCaffe) REG_RUNTIME_LAYER_FUNC(LRN, createLRNLayerFromCaffe)
REG_RUNTIME_LAYER_CLASS(InnerProduct, FullyConnectedLayer) REG_RUNTIME_LAYER_FUNC(InnerProduct, createInnerProductLayerFromCaffe)
REG_RUNTIME_LAYER_CLASS(ReLU, ElementWiseLayer<ReLUFunctor>) REG_RUNTIME_LAYER_CLASS(ReLU, ElementWiseLayer<ReLUFunctor>)
REG_RUNTIME_LAYER_CLASS(TanH, ElementWiseLayer<TanHFunctor>) REG_RUNTIME_LAYER_CLASS(TanH, ElementWiseLayer<TanHFunctor>)
......
...@@ -43,73 +43,110 @@ ...@@ -43,73 +43,110 @@
#include "layers_common.hpp" #include "layers_common.hpp"
#include "fully_connected_layer.hpp" #include "fully_connected_layer.hpp"
#include "op_blas.hpp" #include "op_blas.hpp"
#include <opencv2/dnn/shape_utils.hpp>
#include <opencv2/core/ocl.hpp>
namespace cv namespace cv
{ {
namespace dnn namespace dnn
{ {
FullyConnectedLayer::FullyConnectedLayer(LayerParams &params) : Layer(params)
{
numOutputs = params.get<int>("num_output");
bias = params.get<bool>("bias_term", true);
axis_ = params.get<int>("axis", 1);
CV_Assert(blobs.size() == (bias ? 2U : 1U)); FullyConnectedLayerImpl::FullyConnectedLayerImpl(int axis_)
CV_Assert(blobs[0].dims() >= 2 && blobs[0].total() >= (size_t)numOutputs); {
CV_Assert(!bias || blobs[1].total() == (size_t)numOutputs); axis = axis_;
} }
void FullyConnectedLayer::allocate(const std::vector<Blob*> &input, std::vector<Blob> &output) void FullyConnectedLayerImpl::allocate(const std::vector<Blob*> &input, std::vector<Blob> &output)
{ {
CV_Assert(input.size() > 0); CV_Assert(input.size() > 0);
CV_Assert(1 <= blobs.size() && blobs.size() <= 2);
CV_Assert(blobs[0].dims() == 2);
axis = input[0]->canonicalAxis(axis_); bias = (blobs.size() >= 1);
innerSize = (int)input[0]->total(axis); axisCan = input[0]->canonicalAxis(axis);
dtype = input[0]->type();
numOutput = blobs[0].size(0);
innerSize = blobs[0].size(1);
outerSize = input[0]->total(0, axisCan);
CV_Assert((size_t)innerSize * (size_t)numOutputs == blobs[0].total()); CV_Assert((size_t)innerSize == input[0]->total(axisCan));
CV_Assert(blobs[0].size(-2) == numOutputs && blobs[0].size(-1) == innerSize); CV_Assert(!bias || (size_t)numOutput == blobs[1].total());
output.resize(input.size()); useOpenCL = ocl::useOpenCL();
for (size_t i = 0; i < input.size(); i++) int allocFlags = useOpenCL ? Blob::ALLOC_UMAT : Blob::ALLOC_UMAT;
{
if (i != 0)
CV_Assert(input[i]->equalShape(*input[0]));
this->reshape(*input[i], output[i]); biasOnesBlob.create(Shape(outerSize, 1), dtype, allocFlags);
} if (useOpenCL)
} biasOnesBlob.getRef<UMat>().setTo(1);
else
biasOnesBlob.getRef<Mat>().setTo(1);
void FullyConnectedLayer::reshape(const Blob &inp, Blob &out) output.resize(input.size());
for (size_t i = 0; i < input.size(); i++)
{ {
BlobShape inpShape = inp.shape(); CV_Assert(i == 0 || (input[i]->equalShape(*input[0]) && input[i]->type() == dtype));
BlobShape outShape(axis+1, inpShape.ptr()); Shape outShape = input[i]->shape().slice(0, axis) + Shape(numOutput);
outShape[axis] = numOutputs; output[i].create(outShape, dtype, allocFlags);
}
}
out.create(outShape, inp.type()); void FullyConnectedLayerImpl::forward(std::vector<Blob*> &input, std::vector<Blob> &output)
{
if (!useOpenCL)
forward_<Mat>(input, output);
else
forward_<UMat>(input, output);
}
template<typename XMat>
void FullyConnectedLayerImpl::forward_(std::vector<Blob *> &input, std::vector<Blob> &output)
{
const XMat &weight = blobs[0].getRefConst<XMat>();
const XMat *biasMat, *biasOnesMat;
if (bias)
{
biasOnesMat = &biasOnesBlob.getRefConst<XMat>();
biasMat = &blobs[1].getRefConst<XMat>();
} }
void FullyConnectedLayer::forward(std::vector<Blob*> &input, std::vector<Blob> &output) for (size_t i = 0; i < input.size(); i++)
{ {
for (size_t i = 0; i < input.size(); i++) const XMat srcMat = reshaped(input[i]->getRefConst<XMat>(), Shape(outerSize, innerSize));
{ XMat dstMat = reshaped(output[i].getRef<XMat>(), Shape(outerSize, numOutput));
int M = (int)input[i]->total(0, axis); dnn::gemm(srcMat, weight, 1, dstMat, 0, GEMM_2_T);
int N = numOutputs;
int K = innerSize; if (bias)
dnn::gemm(*biasOnesMat, *biasMat, 1, dstMat, 1);
Mat srcMat(M, K, input[i]->type(), input[i]->ptrf());
Mat weight(N, K, blobs[0].type(), blobs[0].ptrf());
Mat dstMat(M, N, output[i].type(), output[i].ptrf());
//important: for perfomance purposes Caffe stores weights as transposed array
gemmCPU(srcMat, weight, 1, dstMat, 0, GEMM_2_T);
if (bias)
{
Mat biasOnesMat = Mat::ones(M, 1, CV_32F);
Mat biasMat(1, N, CV_32F, blobs[1].ptrf());
gemmCPU(biasOnesMat, biasMat, 1, dstMat, 1);
}
}
} }
} }
Ptr<InnerProductLayer> InnerProductLayer::create(int axis)
{
return Ptr<InnerProductLayer>(new FullyConnectedLayerImpl(axis));
}
Ptr<Layer> createInnerProductLayerFromCaffe(LayerParams &params)
{
const std::vector<Blob> &blobs = params.blobs;
CV_Assert(1 <= blobs.size() && blobs.size() <= 2);
int numOutputs = params.get<int>("num_output");
int innerSize = (int)blobs[0].total() / numOutputs;
bool bias = params.get<bool>("bias_term", true);
int axis = params.get<int>("axis", 1);
CV_Assert(blobs[0].dims() >= 2 && (size_t)(innerSize * numOutputs) == blobs[0].total());
CV_Assert(!bias || (blobs.size() == 2 && (size_t)numOutputs == blobs[1].total()));
Ptr<InnerProductLayer> l = InnerProductLayer::create(axis);
l->setParamsFrom(params);
l->blobs[0].reshape(Shape(numOutputs, innerSize));
if (bias)
l->blobs[1].reshape(Shape(1, numOutputs));
return Ptr<Layer>(l);
}
}
} }
...@@ -42,26 +42,32 @@ ...@@ -42,26 +42,32 @@
#ifndef __OPENCV_DNN_LAYERS_FULLY_CONNECTED_LAYER_HPP__ #ifndef __OPENCV_DNN_LAYERS_FULLY_CONNECTED_LAYER_HPP__
#define __OPENCV_DNN_LAYERS_FULLY_CONNECTED_LAYER_HPP__ #define __OPENCV_DNN_LAYERS_FULLY_CONNECTED_LAYER_HPP__
#include "../precomp.hpp" #include "../precomp.hpp"
#include <opencv2/dnn/all_layers.hpp>
namespace cv namespace cv
{ {
namespace dnn namespace dnn
{ {
class FullyConnectedLayer : public Layer
{
bool bias;
int numOutputs;
int axis_, axis;
int innerSize; class FullyConnectedLayerImpl : public InnerProductLayer
{
int axisCan, dtype;
int numOutput, innerSize, outerSize;
bool bias, useOpenCL;
Blob biasOnesBlob;
template<typename XMat>
void forward_(std::vector<Blob*> &input, std::vector<Blob> &output);
public:
FullyConnectedLayerImpl(int axisCan = 1);
void allocate(const std::vector<Blob*> &input, std::vector<Blob> &output);
void forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs);
};
void reshape(const Blob &inp, Blob &out); Ptr<Layer> createInnerProductLayerFromCaffe(LayerParams &params);
public:
FullyConnectedLayer(LayerParams &params);
void allocate(const std::vector<Blob*> &input, std::vector<Blob> &output);
void forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs);
};
} }
} }
#endif #endif
...@@ -181,7 +181,7 @@ TEST(Layer_Test_Reshape, squeeze) ...@@ -181,7 +181,7 @@ TEST(Layer_Test_Reshape, squeeze)
rl->allocate(inpVec, outVec); rl->allocate(inpVec, outVec);
rl->forward(inpVec, outVec); rl->forward(inpVec, outVec);
EXPECT_EQ(outVec[0].shape(), BlobShape(Vec3i(4, 3, 2))); EXPECT_EQ(outVec[0].shape(), BlobShape(4, 3, 2));
} }
TEST(Layer_Test_Reshape_Split_Slice, Accuracy) TEST(Layer_Test_Reshape_Split_Slice, Accuracy)
......
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