Browse Source

implement depthwise deconvolution vulkan, fix top blob state

tags/20190320
nihuini 7 years ago
parent
commit
37413ea95c
5 changed files with 517 additions and 0 deletions
  1. +3
    -0
      src/layer/convolutiondepthwise.cpp
  2. +276
    -0
      src/layer/deconvolutiondepthwise.cpp
  3. +24
    -0
      src/layer/deconvolutiondepthwise.h
  4. +105
    -0
      src/layer/shader/deconvolutiondepthwise.comp
  5. +109
    -0
      src/layer/shader/deconvolutiondepthwise_pack4.comp

+ 3
- 0
src/layer/convolutiondepthwise.cpp View File

@@ -760,6 +760,9 @@ int ConvolutionDepthWise::forward(const VkMat& bottom_blob, VkMat& top_blob, VkC
return 0;
}

// record
cmd.record_prepare_compute_barrier(top_blob);

const int channels_g = channels / group;
const int num_output_g = num_output / packing / group;



+ 276
- 0
src/layer/deconvolutiondepthwise.cpp View File

@@ -13,6 +13,7 @@
// specific language governing permissions and limitations under the License.

#include "deconvolutiondepthwise.h"
#include "layer_type.h"

namespace ncnn {

@@ -22,6 +23,22 @@ DeconvolutionDepthWise::DeconvolutionDepthWise()
{
one_blob_only = true;
support_inplace = false;
support_vulkan = true;

#if NCNN_VULKAN
pipeline_deconvolutiondepthwise = 0;
pipeline_deconvolutiondepthwise_pack4 = 0;
#endif // NCNN_VULKAN
}

DeconvolutionDepthWise::~DeconvolutionDepthWise()
{
#if NCNN_VULKAN
for (int i=0; i<(int)deconvolution_group_ops.size(); i++)
delete deconvolution_group_ops[i];

deconvolution_group_ops.clear();
#endif // NCNN_VULKAN
}

int DeconvolutionDepthWise::load_param(const ParamDict& pd)
@@ -55,6 +72,65 @@ int DeconvolutionDepthWise::load_model(const ModelBin& mb)
return -100;
}

#if NCNN_VULKAN
const int maxk = kernel_w * kernel_h;
int channels = (weight_data_size / group) / maxk / (num_output / group) * group;

// group deconvolution
if (!(channels == group && group == num_output))
{
// create Deconvolution op for each group

for (int i=0; i<(int)deconvolution_group_ops.size(); i++)
delete deconvolution_group_ops[i];

deconvolution_group_ops.clear();

const int channels_g = channels / group;
const int num_output_g = num_output / group;

deconvolution_group_ops.resize(group);

for (int g=0; g<group; g++)
{
Mat weight_data_g = weight_data.range(maxk * channels_g * num_output_g * g, maxk * channels_g * num_output_g);
Mat bias_data_g;
if (bias_term)
bias_data_g = bias_data.range(num_output_g * g, num_output_g);

ncnn::Layer* op = ncnn::create_layer(ncnn::LayerType::Deconvolution);
op->vkdev = vkdev;

// set param
ncnn::ParamDict pd;
pd.set(0, num_output_g);// num_output
pd.set(1, kernel_w);
pd.set(11, kernel_h);
pd.set(2, dilation_w);
pd.set(12, dilation_h);
pd.set(3, stride_w);
pd.set(13, stride_h);
pd.set(4, 0);// pad_w
pd.set(14, 0);// pad_h
pd.set(5, bias_term);
pd.set(6, maxk * channels_g * num_output_g);// weight_data_size

pd.use_vulkan_compute = 1;

op->load_param(pd);

// set weights
ncnn::Mat weights[2];
weights[0] = weight_data_g;
weights[1] = bias_data_g;

op->load_model(ModelBinFromMatArray(weights));

deconvolution_group_ops[g] = op;
}
}
#endif // NCNN_VULKAN

return 0;
}

@@ -212,4 +288,204 @@ int DeconvolutionDepthWise::forward(const Mat& bottom_blob, Mat& top_blob, const
return 0;
}

#if NCNN_VULKAN
int DeconvolutionDepthWise::upload_model(VkTransfer& cmd)
{
const int maxk = kernel_w * kernel_h;
int channels = (weight_data_size / group) / maxk / (num_output / group) * group;

Mat weight_data_transposed(weight_data.w);
{
float* pt = weight_data_transposed;
const float* p = weight_data;

for (int i=0; i<(channels/group)*(num_output/group)*group; i++)
{
for (int k=0; k<maxk; k++)
{
pt[maxk-1 - k] = p[k];
}

p += maxk;
pt += maxk;
}
}

// depth-wise
if (channels == group && group == num_output)
{
cmd.record_upload(weight_data_transposed, weight_data_gpu);

if (bias_term)
{
cmd.record_upload(bias_data, bias_data_gpu);
}

// pack4
if (channels % 4 == 0 && num_output % 4 == 0)
{
const int maxk = kernel_w * kernel_h;

Mat weight_data_pack4;
Mat weight_data_r2 = weight_data_transposed.reshape(maxk, group);
convert_packing(weight_data_r2, weight_data_pack4, 4);

weight_data_pack4 = weight_data_pack4.reshape(maxk * (group/4));
cmd.record_upload(weight_data_pack4, weight_data_gpu_pack4);

if (bias_term)
{
Mat bias_data_pack4;
convert_packing(bias_data, bias_data_pack4, 4);
cmd.record_upload(bias_data_pack4, bias_data_gpu_pack4);
}
}

return 0;
}

for (int g=0; g<group; g++)
{
deconvolution_group_ops[g]->upload_model(cmd);
}

return 0;
}

int DeconvolutionDepthWise::create_pipeline()
{
const int maxk = kernel_w * kernel_h;
int channels = (weight_data_size / group) / maxk / (num_output / group) * group;

// depth-wise
if (channels == group && group == num_output)
{
pipeline_deconvolutiondepthwise = new Pipeline(vkdev);
pipeline_deconvolutiondepthwise->set_optimal_local_size_xyz(32, 32, num_output);

std::vector<vk_specialization_type> specializations(8);
specializations[0].i = kernel_w;
specializations[1].i = kernel_h;
specializations[2].i = dilation_w;
specializations[3].i = dilation_h;
specializations[4].i = stride_w;
specializations[5].i = stride_h;
specializations[6].i = bias_term;
specializations[7].i = group;

pipeline_deconvolutiondepthwise->create("deconvolutiondepthwise", specializations, 4, 10);

// pack4
if (num_output % 4 == 0)
{
pipeline_deconvolutiondepthwise_pack4 = new Pipeline(vkdev);
pipeline_deconvolutiondepthwise_pack4->set_optimal_local_size_xyz(32, 32, std::max(1, num_output / 4));
pipeline_deconvolutiondepthwise_pack4->create("deconvolutiondepthwise_pack4", specializations, 4, 10);
}

return 0;
}

for (int g=0; g<group; g++)
{
deconvolution_group_ops[g]->create_pipeline();
}

return 0;
}

int DeconvolutionDepthWise::destroy_pipeline()
{
for (int g=0; g<(int)deconvolution_group_ops.size(); g++)
{
deconvolution_group_ops[g]->destroy_pipeline();
}

delete pipeline_deconvolutiondepthwise;
pipeline_deconvolutiondepthwise = 0;

delete pipeline_deconvolutiondepthwise_pack4;
pipeline_deconvolutiondepthwise_pack4 = 0;

return 0;
}

int DeconvolutionDepthWise::forward(const VkMat& bottom_blob, VkMat& top_blob, VkCompute& cmd, const Option& opt) const
{
int w = bottom_blob.w;
int h = bottom_blob.h;
int channels = bottom_blob.c;
size_t elemsize = bottom_blob.elemsize;
int packing = bottom_blob.packing;

const int kernel_extent_w = dilation_w * (kernel_w - 1) + 1;
const int kernel_extent_h = dilation_h * (kernel_h - 1) + 1;

int outw = (w - 1) * stride_w + kernel_extent_w;
int outh = (h - 1) * stride_h + kernel_extent_h;

// TODO assert num_output % packing == 0

top_blob.create(outw, outh, num_output / packing, elemsize, packing, opt.blob_vkallocator, opt.staging_vkallocator);
if (top_blob.empty())
return -100;

// fprintf(stderr, "DeconvolutionDepthWise::forward %p %p\n", bottom_blob.buffer(), top_blob.buffer());

// depth-wise
if (channels == group / packing && group / packing == num_output / packing)
{
std::vector<VkMat> bindings(4);
bindings[0] = bottom_blob;
bindings[1] = top_blob;
bindings[2] = packing == 4 ? weight_data_gpu_pack4 : weight_data_gpu;
bindings[3] = bias_term ? (packing == 4 ? bias_data_gpu_pack4 : bias_data_gpu) : weight_data_gpu;// TODO use dummy buffer

std::vector<vk_constant_type> constants(10);
constants[0].i = bottom_blob.dims;
constants[1].i = bottom_blob.w;
constants[2].i = bottom_blob.h;
constants[3].i = bottom_blob.c;
constants[4].i = bottom_blob.cstep;
constants[5].i = top_blob.dims;
constants[6].i = top_blob.w;
constants[7].i = top_blob.h;
constants[8].i = top_blob.c;
constants[9].i = top_blob.cstep;

const Pipeline* pipeline = packing == 4 ? pipeline_deconvolutiondepthwise_pack4 : pipeline_deconvolutiondepthwise;

// record
cmd.record_prepare_compute_barrier(bottom_blob);
cmd.record_prepare_compute_barrier(top_blob);
cmd.record_pipeline(pipeline, bindings, constants, top_blob);

return 0;
}

// record
cmd.record_prepare_compute_barrier(top_blob);

const int channels_g = channels / group;
const int num_output_g = num_output / packing / group;

for (int g=0; g<group; g++)
{
VkMat bottom_blob_bordered_g = bottom_blob.channel_range(channels_g * g, channels_g);
VkMat top_blob_g = top_blob.channel_range(num_output_g * g, num_output_g);

const ncnn::Layer* op = deconvolution_group_ops[g];

ncnn::Option opt_g = opt;
opt_g.blob_vkallocator = top_blob.allocator;
opt_g.staging_vkallocator = top_blob.staging_allocator;

// forward
op->forward(bottom_blob_bordered_g, top_blob_g, cmd, opt_g);
}

return 0;
}
#endif // NCNN_VULKAN

} // namespace ncnn

+ 24
- 0
src/layer/deconvolutiondepthwise.h View File

@@ -23,6 +23,7 @@ class DeconvolutionDepthWise : public Layer
{
public:
DeconvolutionDepthWise();
~DeconvolutionDepthWise();

virtual int load_param(const ParamDict& pd);

@@ -30,6 +31,15 @@ public:

virtual int forward(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const;

#if NCNN_VULKAN
virtual int upload_model(VkTransfer& cmd);

virtual int create_pipeline();
virtual int destroy_pipeline();

virtual int forward(const VkMat& bottom_blob, VkMat& top_blob, VkCompute& cmd, const Option& opt) const;
#endif // NCNN_VULKAN

public:
// param
int num_output;
@@ -49,6 +59,20 @@ public:
// model
Mat weight_data;
Mat bias_data;

#if NCNN_VULKAN
VkMat weight_data_gpu;
VkMat bias_data_gpu;

std::vector<ncnn::Layer*> deconvolution_group_ops;

Pipeline* pipeline_deconvolutiondepthwise;

VkMat weight_data_gpu_pack4;
VkMat bias_data_gpu_pack4;
Pipeline* pipeline_deconvolutiondepthwise_pack4;
#endif // NCNN_VULKAN

};

} // namespace ncnn


+ 105
- 0
src/layer/shader/deconvolutiondepthwise.comp View File

@@ -0,0 +1,105 @@
// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#version 450

layout (constant_id = 0) const int kernel_w = 1;
layout (constant_id = 1) const int kernel_h = 1;
layout (constant_id = 2) const int dilation_w = 1;
layout (constant_id = 3) const int dilation_h = 1;
layout (constant_id = 4) const int stride_w = 1;
layout (constant_id = 5) const int stride_h = 1;
layout (constant_id = 6) const int bias_term = 0;
layout (constant_id = 7) const int group = 1;

layout (local_size_x_id = 233) in;
layout (local_size_y_id = 234) in;
layout (local_size_z_id = 235) in;

layout (binding = 0) readonly buffer bottom_blob { float bottom_blob_data[]; };
layout (binding = 1) writeonly buffer top_blob { float top_blob_data[]; };
layout (binding = 2) readonly buffer weight_blob { float weight_data[]; };
layout (binding = 3) readonly buffer bias_blob { float bias_data[]; };

layout (push_constant) uniform parameter
{
int dims;
int w;
int h;
int c;
int cstep;

int outdims;
int outw;
int outh;
int outc;
int outcstep;
} p;

void main()
{
int gx = int(gl_GlobalInvocationID.x);
int gy = int(gl_GlobalInvocationID.y);
int gz = int(gl_GlobalInvocationID.z);

if (gx >= p.outw || gy >= p.outh || gz >= p.outc)
return;

float sum;

if (bias_term == 1)
{
sum = bias_data[gz];
}
else
{
sum = 0.f;
}

const int kernel_extent_w = dilation_w * (kernel_w - 1) + 1;
const int kernel_extent_h = dilation_h * (kernel_h - 1) + 1;

// depth-wise deconvolution
int v_offset_0 = gz * p.cstep;
int w_offset_0 = gz * kernel_w * kernel_h;

for (int y = 0; y < kernel_h; y++)
{
int sys = (gy + y * dilation_h - (kernel_extent_h - 1));
if (sys % stride_h != 0)
continue;

int sy = sys / stride_h;
if (sy < 0 || sy >= p.h)
continue;

for (int x = 0; x < kernel_w; x++)
{
int sxs = (gx + x * dilation_w - (kernel_extent_w - 1));
if (sxs % stride_w != 0)
continue;

int sx = sxs / stride_w;
if (sx < 0 || sx >= p.w)
continue;

int v_offset = v_offset_0 + sy * p.w + sx;
int w_offset = w_offset_0 + y * kernel_w + x;

sum += weight_data[w_offset] * bottom_blob_data[v_offset];
}
}

top_blob_data[gz * p.outcstep + gy * p.outw + gx] = sum;
}

+ 109
- 0
src/layer/shader/deconvolutiondepthwise_pack4.comp View File

@@ -0,0 +1,109 @@
// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#version 450

layout (constant_id = 0) const int kernel_w = 1;
layout (constant_id = 1) const int kernel_h = 1;
layout (constant_id = 2) const int dilation_w = 1;
layout (constant_id = 3) const int dilation_h = 1;
layout (constant_id = 4) const int stride_w = 1;
layout (constant_id = 5) const int stride_h = 1;
layout (constant_id = 6) const int bias_term = 0;
layout (constant_id = 7) const int group = 1;

layout (local_size_x_id = 233) in;
layout (local_size_y_id = 234) in;
layout (local_size_z_id = 235) in;

layout (binding = 0) readonly buffer bottom_blob { vec4 bottom_blob_data[]; };
layout (binding = 1) writeonly buffer top_blob { vec4 top_blob_data[]; };
layout (binding = 2) readonly buffer weight_blob { vec4 weight_data[]; };
layout (binding = 3) readonly buffer bias_blob { vec4 bias_data[]; };

layout (push_constant) uniform parameter
{
int dims;
int w;
int h;
int c;
int cstep;

int outdims;
int outw;
int outh;
int outc;
int outcstep;
} p;

void main()
{
int gx = int(gl_GlobalInvocationID.x);
int gy = int(gl_GlobalInvocationID.y);
int gz = int(gl_GlobalInvocationID.z);

if (gx >= p.outw || gy >= p.outh || gz >= p.outc)
return;

vec4 sum;

if (bias_term == 1)
{
sum = bias_data[gz];
}
else
{
sum = vec4(0.f);
}

const int kernel_extent_w = dilation_w * (kernel_w - 1) + 1;
const int kernel_extent_h = dilation_h * (kernel_h - 1) + 1;

// depth-wise deconvolution
int v_offset_0 = gz * p.cstep;
int w_offset_0 = gz * kernel_w * kernel_h;

for (int y = 0; y < kernel_h; y++)
{
int sys = (gy + y * dilation_h - (kernel_extent_h - 1));
if (sys % stride_h != 0)
continue;

int sy = sys / stride_h;
if (sy < 0 || sy >= p.h)
continue;

for (int x = 0; x < kernel_w; x++)
{
int sxs = (gx + x * dilation_w - (kernel_extent_w - 1));
if (sxs % stride_w != 0)
continue;

int sx = sxs / stride_w;
if (sx < 0 || sx >= p.w)
continue;

int v_offset = v_offset_0 + sy * p.w + sx;
int w_offset = w_offset_0 + y * kernel_w + x;

vec4 v = bottom_blob_data[v_offset];

vec4 k = weight_data[w_offset];

sum += v * k;
}
}

top_blob_data[gz * p.outcstep + gy * p.outw + gx] = sum;
}

Loading…
Cancel
Save