757 lines
34 KiB
Plaintext
757 lines
34 KiB
Plaintext
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// 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.
|
|
|
|
#include "paddle/common/enforce.h"
|
|
#include "paddle/common/layout.h"
|
|
#include "paddle/phi/backends/gpu/gpu_context.h"
|
|
#include "paddle/phi/common/amp_type_traits.h"
|
|
#include "paddle/phi/core/kernel_registry.h"
|
|
#include "paddle/phi/kernels/cpu/conv_util.h"
|
|
#include "paddle/phi/kernels/full_kernel.h"
|
|
#include "paddle/phi/kernels/funcs/batch_norm_utils.h"
|
|
#include "paddle/phi/kernels/funcs/common_shape.h"
|
|
#include "paddle/phi/kernels/funcs/math_function.h"
|
|
#include "paddle/phi/kernels/gpu/depthwise_conv.h"
|
|
#include "paddle/phi/kernels/reduce_sum_kernel.h"
|
|
|
|
namespace phi {
|
|
|
|
constexpr int CUDA_NUM_THREADS = 1024;
|
|
constexpr int CUDA_WARP_SIZE = 32;
|
|
|
|
template <typename T,
|
|
typename AccT,
|
|
int kKnownKernelT,
|
|
int kKnownKernelH,
|
|
int kKnownKernelW,
|
|
int kKnownDilationT,
|
|
int kKnownDilationH,
|
|
int kKnownDilationW,
|
|
int kKnownStrideT,
|
|
int kKnownStrideH,
|
|
int kKnownStrideW>
|
|
__global__ void DWConv3dBwdInputKernel(const T* grad_output,
|
|
T* grad_input,
|
|
const T* weight,
|
|
const int batch_size,
|
|
const int input_channels,
|
|
const int input_depth,
|
|
int input_height,
|
|
int input_width,
|
|
const int output_channels,
|
|
const int output_depth,
|
|
int output_height,
|
|
int output_width,
|
|
const int kernel_t_in,
|
|
int kernel_h_in,
|
|
int kernel_w_in,
|
|
const int stride_t_in,
|
|
int stride_h_in,
|
|
int stride_w_in,
|
|
const int padding_t,
|
|
int padding_h,
|
|
int padding_w,
|
|
const int dilation_t_in,
|
|
int dilation_h_in,
|
|
int dilation_w_in) {
|
|
const int kernel_t = kKnownKernelT > 0 ? kKnownKernelT : kernel_t_in;
|
|
const int kernel_h = kKnownKernelH > 0 ? kKnownKernelH : kernel_h_in;
|
|
const int kernel_w = kKnownKernelW > 0 ? kKnownKernelW : kernel_w_in;
|
|
const int dilation_t = kKnownDilationT > 0 ? kKnownDilationT : dilation_t_in;
|
|
const int dilation_h = kKnownDilationH > 0 ? kKnownDilationH : dilation_h_in;
|
|
const int dilation_w = kKnownDilationW > 0 ? kKnownDilationW : dilation_w_in;
|
|
const int stride_t = kKnownStrideT > 0 ? kKnownStrideT : stride_t_in;
|
|
const int stride_h = kKnownStrideH > 0 ? kKnownStrideH : stride_h_in;
|
|
const int stride_w = kKnownStrideW > 0 ? kKnownStrideW : stride_w_in;
|
|
|
|
const int channel_multiplier = output_channels / input_channels;
|
|
const int64_t num_input = static_cast<int64_t>(batch_size) * input_channels *
|
|
input_depth * input_height * input_width;
|
|
|
|
const int64_t i_stride_c =
|
|
static_cast<int64_t>(input_depth) * input_height * input_width;
|
|
const int64_t i_stride_d = static_cast<int64_t>(input_height) * input_width;
|
|
const int i_stride_h = input_width;
|
|
|
|
const int64_t o_stride_c =
|
|
static_cast<int64_t>(output_depth) * output_height * output_width;
|
|
const int64_t o_stride_d = static_cast<int64_t>(output_height) * output_width;
|
|
const int o_stride_h = output_width;
|
|
|
|
const int64_t w_stride_c =
|
|
static_cast<int64_t>(kernel_t) * kernel_h * kernel_w;
|
|
|
|
for (int64_t index =
|
|
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
|
|
static_cast<int64_t>(threadIdx.x);
|
|
index < num_input;
|
|
index +=
|
|
static_cast<int64_t>(blockDim.x) * static_cast<int64_t>(gridDim.x)) {
|
|
int64_t temp = index;
|
|
const int in_col = temp % input_width;
|
|
temp /= input_width;
|
|
const int in_row = temp % input_height;
|
|
temp /= input_height;
|
|
const int in_frame = temp % input_depth;
|
|
temp /= input_depth;
|
|
const int in_channel = temp % input_channels;
|
|
const int batch = temp / input_channels;
|
|
|
|
const int64_t out_col_end = static_cast<int64_t>(in_col) + padding_w;
|
|
const int64_t out_row_end = static_cast<int64_t>(in_row) + padding_h;
|
|
const int64_t out_frame_end = static_cast<int64_t>(in_frame) + padding_t;
|
|
|
|
AccT sum = 0;
|
|
const T* weight_ptr = weight + static_cast<int64_t>(in_channel) *
|
|
channel_multiplier * w_stride_c;
|
|
const T* gout_base = grad_output + static_cast<int64_t>(batch) *
|
|
output_channels * o_stride_c;
|
|
|
|
for (int k_chn = 0; k_chn < channel_multiplier; ++k_chn) {
|
|
const T* gout_ptr =
|
|
gout_base +
|
|
(static_cast<int64_t>(in_channel) * channel_multiplier + k_chn) *
|
|
o_stride_c;
|
|
|
|
for (int k_frame = 0; k_frame < kernel_t; ++k_frame) {
|
|
const int64_t out_frame_raw =
|
|
out_frame_end - static_cast<int64_t>(k_frame) * dilation_t;
|
|
const int64_t out_frame = out_frame_raw / stride_t;
|
|
|
|
if (out_frame * stride_t == out_frame_raw && out_frame >= 0 &&
|
|
out_frame < output_depth) {
|
|
for (int k_row = 0; k_row < kernel_h; ++k_row) {
|
|
const int64_t out_row_raw =
|
|
out_row_end - static_cast<int64_t>(k_row) * dilation_h;
|
|
const int64_t out_row = out_row_raw / stride_h;
|
|
|
|
if (out_row * stride_h == out_row_raw && out_row >= 0 &&
|
|
out_row < output_height) {
|
|
for (int k_col = 0; k_col < kernel_w; ++k_col) {
|
|
const int64_t out_col_raw =
|
|
out_col_end - static_cast<int64_t>(k_col) * dilation_w;
|
|
const int64_t out_col = out_col_raw / stride_w;
|
|
|
|
if (out_col * stride_w == out_col_raw && out_col >= 0 &&
|
|
out_col < output_width) {
|
|
const int64_t w_offset =
|
|
k_chn * w_stride_c +
|
|
static_cast<int64_t>(k_frame) * kernel_h * kernel_w +
|
|
static_cast<int64_t>(k_row) * kernel_w + k_col;
|
|
T val_w = weight_ptr[w_offset];
|
|
|
|
const int64_t out_offset =
|
|
out_frame * o_stride_d + out_row * o_stride_h + out_col;
|
|
T val_go = gout_ptr[out_offset];
|
|
|
|
sum += static_cast<AccT>(val_w) * static_cast<AccT>(val_go);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
grad_input[index] = static_cast<T>(sum);
|
|
}
|
|
}
|
|
|
|
template <typename T, typename AccT, int kKnownStrideH, int kKnownStrideW>
|
|
__global__ void DWConv3dBwdWeightKernel(const T* grad_output,
|
|
const T* input,
|
|
T* grad_weight,
|
|
const int batch_size,
|
|
const int input_channels,
|
|
const int input_depth,
|
|
int input_height,
|
|
int input_width,
|
|
const int output_channels,
|
|
const int output_depth,
|
|
int output_height,
|
|
int output_width,
|
|
const int kernel_t,
|
|
int kernel_h,
|
|
int kernel_w,
|
|
const int stride_t,
|
|
int stride_h_in,
|
|
int stride_w_in,
|
|
const int padding_t,
|
|
int padding_h,
|
|
int padding_w,
|
|
const int dilation_t,
|
|
int dilation_h,
|
|
int dilation_w) {
|
|
const int stride_h = kKnownStrideH > 0 ? kKnownStrideH : stride_h_in;
|
|
const int stride_w = kKnownStrideW > 0 ? kKnownStrideW : stride_w_in;
|
|
|
|
const int k_c = output_channels;
|
|
const int k_col = blockIdx.x % kernel_w;
|
|
const int k_row = (blockIdx.x / kernel_w) % kernel_h;
|
|
const int k_frame = (blockIdx.x / kernel_w / kernel_h) % kernel_t;
|
|
const int k_channel = blockIdx.x / kernel_w / kernel_h / kernel_t;
|
|
|
|
if (k_channel >= k_c) return;
|
|
|
|
const int channel_multiplier = output_channels / input_channels;
|
|
const int in_channel = k_channel / channel_multiplier;
|
|
|
|
extern __shared__ char smem_raw[];
|
|
T* sdata = reinterpret_cast<T*>(smem_raw);
|
|
|
|
const int laneid = threadIdx.x % CUDA_WARP_SIZE;
|
|
const int warpid = threadIdx.x / CUDA_WARP_SIZE;
|
|
const int nwarps = blockDim.x / CUDA_WARP_SIZE;
|
|
|
|
const int64_t i_stride_c =
|
|
static_cast<int64_t>(input_depth) * input_height * input_width;
|
|
const int64_t i_stride_d = static_cast<int64_t>(input_height) * input_width;
|
|
const int i_stride_h = input_width;
|
|
|
|
const int64_t o_stride_c =
|
|
static_cast<int64_t>(output_depth) * output_height * output_width;
|
|
const int64_t o_stride_d = static_cast<int64_t>(output_height) * output_width;
|
|
const int o_stride_h = output_width;
|
|
|
|
AccT grad = 0;
|
|
|
|
int64_t batch = warpid / output_depth;
|
|
int64_t gout_frame = warpid - batch * output_depth;
|
|
|
|
const int64_t total_outer_loops =
|
|
static_cast<int64_t>(batch_size) * output_depth;
|
|
|
|
for (int64_t outer_pos = warpid; outer_pos < total_outer_loops;
|
|
outer_pos += nwarps, gout_frame += nwarps) {
|
|
while (gout_frame >= output_depth) {
|
|
gout_frame -= output_depth;
|
|
batch++;
|
|
}
|
|
|
|
const int64_t in_frame = gout_frame * stride_t +
|
|
static_cast<int64_t>(k_frame) * dilation_t -
|
|
padding_t;
|
|
|
|
if (in_frame >= 0 && in_frame < input_depth) {
|
|
const T* gout_ptr = grad_output + batch * (output_channels * o_stride_c) +
|
|
k_channel * o_stride_c + gout_frame * o_stride_d;
|
|
|
|
const T* input_ptr = input + batch * (input_channels * i_stride_c) +
|
|
in_channel * i_stride_c + in_frame * i_stride_d;
|
|
|
|
const T* gout_ptr_lane = gout_ptr + laneid;
|
|
|
|
int gout_row = laneid / output_width;
|
|
int gout_col = laneid % output_width;
|
|
|
|
for (; gout_row < output_height;) {
|
|
const AccT op1 = static_cast<AccT>(*gout_ptr_lane);
|
|
gout_ptr_lane += CUDA_WARP_SIZE;
|
|
|
|
const int64_t in_col = static_cast<int64_t>(gout_col) * stride_w +
|
|
static_cast<int64_t>(k_col) * dilation_w -
|
|
padding_w;
|
|
const int64_t in_row = static_cast<int64_t>(gout_row) * stride_h +
|
|
static_cast<int64_t>(k_row) * dilation_h -
|
|
padding_h;
|
|
|
|
AccT op2 = 0;
|
|
if (in_row >= 0 && in_row < input_height && in_col >= 0 &&
|
|
in_col < input_width) {
|
|
const int64_t in_pos =
|
|
static_cast<int64_t>(in_row) * i_stride_h + in_col;
|
|
op2 = static_cast<AccT>(input_ptr[in_pos]);
|
|
}
|
|
|
|
grad += op1 * op2;
|
|
|
|
gout_col += CUDA_WARP_SIZE;
|
|
while (gout_col >= output_width) {
|
|
gout_col -= output_width;
|
|
gout_row++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sdata[threadIdx.x] = static_cast<T>(grad);
|
|
__syncthreads();
|
|
|
|
for (int i = blockDim.x / 2; i >= 1; i >>= 1) {
|
|
if (threadIdx.x < i) {
|
|
AccT val = static_cast<AccT>(sdata[threadIdx.x]) +
|
|
static_cast<AccT>(sdata[threadIdx.x + i]);
|
|
sdata[threadIdx.x] = static_cast<T>(val);
|
|
}
|
|
__syncthreads();
|
|
}
|
|
|
|
if (threadIdx.x == 0) {
|
|
grad_weight[blockIdx.x] = sdata[0];
|
|
}
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
void LaunchDepthwiseConv3dBackwardCompatible(const Context& dev_ctx,
|
|
const DenseTensor& input,
|
|
const DenseTensor& filter,
|
|
const DenseTensor& out_grad,
|
|
const std::vector<int>& strides,
|
|
const std::vector<int>& paddings,
|
|
const std::vector<int>& dilations,
|
|
const std::string& data_format,
|
|
DenseTensor* input_grad,
|
|
DenseTensor* filter_grad,
|
|
DenseTensor* bias_grad) {
|
|
const bool channel_last = (data_format == "NDHWC");
|
|
|
|
DenseTensor input_ncdhw;
|
|
DenseTensor out_grad_ncdhw;
|
|
const DenseTensor& filter_ncdhw = filter;
|
|
|
|
DenseTensor input_grad_ncdhw_tmp;
|
|
DenseTensor* input_grad_ncdhw_ptr = nullptr;
|
|
DenseTensor* filter_grad_ncdhw_ptr = nullptr;
|
|
|
|
if (channel_last) {
|
|
ResizeToChannelFirst<Context, T>(dev_ctx, &input, &input_ncdhw);
|
|
TransToChannelFirst<Context, T>(dev_ctx, &input, &input_ncdhw);
|
|
|
|
ResizeToChannelFirst<Context, T>(dev_ctx, &out_grad, &out_grad_ncdhw);
|
|
TransToChannelFirst<Context, T>(dev_ctx, &out_grad, &out_grad_ncdhw);
|
|
|
|
if (input_grad) {
|
|
ResizeToChannelFirst<Context, T>(
|
|
dev_ctx, input_grad, &input_grad_ncdhw_tmp);
|
|
dev_ctx.template Alloc<T>(&input_grad_ncdhw_tmp);
|
|
input_grad_ncdhw_ptr = &input_grad_ncdhw_tmp;
|
|
}
|
|
} else {
|
|
input_ncdhw.ShareDataWith(input);
|
|
out_grad_ncdhw.ShareDataWith(out_grad);
|
|
if (input_grad) input_grad_ncdhw_ptr = input_grad;
|
|
}
|
|
|
|
if (filter_grad) {
|
|
if (channel_last) dev_ctx.template Alloc<T>(filter_grad);
|
|
filter_grad_ncdhw_ptr = filter_grad;
|
|
}
|
|
|
|
const int64_t batch_size = input_ncdhw.dims()[0];
|
|
const int64_t in_channels = input_ncdhw.dims()[1];
|
|
const int64_t in_depth = input_ncdhw.dims()[2];
|
|
const int64_t in_height = input_ncdhw.dims()[3];
|
|
const int64_t in_width = input_ncdhw.dims()[4];
|
|
|
|
const int64_t out_channels = out_grad_ncdhw.dims()[1];
|
|
const int64_t out_depth = out_grad_ncdhw.dims()[2];
|
|
const int64_t out_height = out_grad_ncdhw.dims()[3];
|
|
const int64_t out_width = out_grad_ncdhw.dims()[4];
|
|
|
|
const int64_t kernel_t = filter_ncdhw.dims()[2];
|
|
const int64_t kernel_h = filter_ncdhw.dims()[3];
|
|
const int64_t kernel_w = filter_ncdhw.dims()[4];
|
|
std::vector<int> paddings_vec;
|
|
if (paddings.size() == 6) {
|
|
paddings_vec = {paddings[0], paddings[2], paddings[4]};
|
|
} else if (paddings.size() == 3) {
|
|
paddings_vec = paddings;
|
|
} else {
|
|
paddings_vec = {paddings[0], paddings[0], paddings[0]};
|
|
}
|
|
|
|
auto stream = dev_ctx.stream();
|
|
using AccT = typename MPTypeTrait<T>::Type;
|
|
|
|
PADDLE_ENFORCE_LE_INT_MAX(batch_size, "batch_size");
|
|
PADDLE_ENFORCE_LE_INT_MAX(in_channels, "in_channels");
|
|
PADDLE_ENFORCE_LE_INT_MAX(in_depth, "in_depth");
|
|
PADDLE_ENFORCE_LE_INT_MAX(in_height, "in_height");
|
|
PADDLE_ENFORCE_LE_INT_MAX(in_width, "in_width");
|
|
PADDLE_ENFORCE_LE_INT_MAX(out_channels, "out_channels");
|
|
PADDLE_ENFORCE_LE_INT_MAX(out_depth, "out_depth");
|
|
PADDLE_ENFORCE_LE_INT_MAX(out_height, "out_height");
|
|
PADDLE_ENFORCE_LE_INT_MAX(out_width, "out_width");
|
|
PADDLE_ENFORCE_LE_INT_MAX(kernel_t, "kernel_t");
|
|
PADDLE_ENFORCE_LE_INT_MAX(kernel_h, "kernel_h");
|
|
PADDLE_ENFORCE_LE_INT_MAX(kernel_w, "kernel_w");
|
|
|
|
const int batch_size_int = static_cast<int>(batch_size);
|
|
const int in_channels_int = static_cast<int>(in_channels);
|
|
const int in_depth_int = static_cast<int>(in_depth);
|
|
const int in_height_int = static_cast<int>(in_height);
|
|
const int in_width_int = static_cast<int>(in_width);
|
|
const int out_channels_int = static_cast<int>(out_channels);
|
|
const int out_depth_int = static_cast<int>(out_depth);
|
|
const int out_height_int = static_cast<int>(out_height);
|
|
const int out_width_int = static_cast<int>(out_width);
|
|
const int kernel_t_int = static_cast<int>(kernel_t);
|
|
const int kernel_h_int = static_cast<int>(kernel_h);
|
|
const int kernel_w_int = static_cast<int>(kernel_w);
|
|
|
|
const T* input_ptr = input_ncdhw.data<T>();
|
|
const T* grad_output_ptr = out_grad_ncdhw.data<T>();
|
|
const T* filter_ptr = filter_ncdhw.data<T>();
|
|
|
|
T* grad_input_ptr =
|
|
input_grad_ncdhw_ptr ? input_grad_ncdhw_ptr->data<T>() : nullptr;
|
|
T* grad_filter_ptr =
|
|
filter_grad_ncdhw_ptr ? filter_grad_ncdhw_ptr->data<T>() : nullptr;
|
|
|
|
// Input Gradient
|
|
if (grad_input_ptr) {
|
|
int64_t num_elements = input_grad_ncdhw_ptr->numel();
|
|
int block = 256;
|
|
const int64_t grid_num =
|
|
std::min((num_elements - 1) / block + 1, static_cast<int64_t>(65536));
|
|
dim3 grid(static_cast<unsigned int>(grid_num));
|
|
dim3 block_dim(block);
|
|
|
|
bool is_k3 = (kernel_t == 3 && kernel_h == 3 && kernel_w == 3);
|
|
bool is_d1 = (dilations[0] == 1 && dilations[1] == 1 && dilations[2] == 1);
|
|
bool is_s1 = (strides[0] == 1 && strides[1] == 1 && strides[2] == 1);
|
|
|
|
if (is_k3 && is_d1 && is_s1) {
|
|
DWConv3dBwdInputKernel<T, AccT, 3, 3, 3, 1, 1, 1, 1, 1, 1>
|
|
<<<grid, block_dim, 0, stream>>>(grad_output_ptr,
|
|
grad_input_ptr,
|
|
filter_ptr,
|
|
batch_size_int,
|
|
in_channels_int,
|
|
in_depth_int,
|
|
in_height_int,
|
|
in_width_int,
|
|
out_channels_int,
|
|
out_depth_int,
|
|
out_height_int,
|
|
out_width_int,
|
|
kernel_t_int,
|
|
kernel_h_int,
|
|
kernel_w_int,
|
|
strides[0],
|
|
strides[1],
|
|
strides[2],
|
|
paddings_vec[0],
|
|
paddings_vec[1],
|
|
paddings_vec[2],
|
|
dilations[0],
|
|
dilations[1],
|
|
dilations[2]);
|
|
} else if (is_k3 && is_d1) {
|
|
DWConv3dBwdInputKernel<T, AccT, 3, 3, 3, 1, 1, 1, -1, -1, -1>
|
|
<<<grid, block_dim, 0, stream>>>(grad_output_ptr,
|
|
grad_input_ptr,
|
|
filter_ptr,
|
|
batch_size_int,
|
|
in_channels_int,
|
|
in_depth_int,
|
|
in_height_int,
|
|
in_width_int,
|
|
out_channels_int,
|
|
out_depth_int,
|
|
out_height_int,
|
|
out_width_int,
|
|
kernel_t_int,
|
|
kernel_h_int,
|
|
kernel_w_int,
|
|
strides[0],
|
|
strides[1],
|
|
strides[2],
|
|
paddings_vec[0],
|
|
paddings_vec[1],
|
|
paddings_vec[2],
|
|
dilations[0],
|
|
dilations[1],
|
|
dilations[2]);
|
|
} else if (is_k3 && is_s1) {
|
|
DWConv3dBwdInputKernel<T, AccT, 3, 3, 3, -1, -1, -1, 1, 1, 1>
|
|
<<<grid, block_dim, 0, stream>>>(grad_output_ptr,
|
|
grad_input_ptr,
|
|
filter_ptr,
|
|
batch_size_int,
|
|
in_channels_int,
|
|
in_depth_int,
|
|
in_height_int,
|
|
in_width_int,
|
|
out_channels_int,
|
|
out_depth_int,
|
|
out_height_int,
|
|
out_width_int,
|
|
kernel_t_int,
|
|
kernel_h_int,
|
|
kernel_w_int,
|
|
strides[0],
|
|
strides[1],
|
|
strides[2],
|
|
paddings_vec[0],
|
|
paddings_vec[1],
|
|
paddings_vec[2],
|
|
dilations[0],
|
|
dilations[1],
|
|
dilations[2]);
|
|
} else if (is_k3) {
|
|
DWConv3dBwdInputKernel<T, AccT, 3, 3, 3, -1, -1, -1, -1, -1, -1>
|
|
<<<grid, block_dim, 0, stream>>>(grad_output_ptr,
|
|
grad_input_ptr,
|
|
filter_ptr,
|
|
batch_size_int,
|
|
in_channels_int,
|
|
in_depth_int,
|
|
in_height_int,
|
|
in_width_int,
|
|
out_channels_int,
|
|
out_depth_int,
|
|
out_height_int,
|
|
out_width_int,
|
|
kernel_t_int,
|
|
kernel_h_int,
|
|
kernel_w_int,
|
|
strides[0],
|
|
strides[1],
|
|
strides[2],
|
|
paddings_vec[0],
|
|
paddings_vec[1],
|
|
paddings_vec[2],
|
|
dilations[0],
|
|
dilations[1],
|
|
dilations[2]);
|
|
} else {
|
|
DWConv3dBwdInputKernel<T, AccT, -1, -1, -1, -1, -1, -1, -1, -1, -1>
|
|
<<<grid, block_dim, 0, stream>>>(grad_output_ptr,
|
|
grad_input_ptr,
|
|
filter_ptr,
|
|
batch_size_int,
|
|
in_channels_int,
|
|
in_depth_int,
|
|
in_height_int,
|
|
in_width_int,
|
|
out_channels_int,
|
|
out_depth_int,
|
|
out_height_int,
|
|
out_width_int,
|
|
kernel_t_int,
|
|
kernel_h_int,
|
|
kernel_w_int,
|
|
strides[0],
|
|
strides[1],
|
|
strides[2],
|
|
paddings_vec[0],
|
|
paddings_vec[1],
|
|
paddings_vec[2],
|
|
dilations[0],
|
|
dilations[1],
|
|
dilations[2]);
|
|
}
|
|
}
|
|
|
|
// Weight Gradient
|
|
if (grad_filter_ptr) {
|
|
int64_t num_weights = filter_grad_ncdhw_ptr->numel();
|
|
int block = 256;
|
|
PADDLE_ENFORCE_LE_UINT32_MAX(num_weights, "num_weights");
|
|
PADDLE_ENFORCE_LE(
|
|
num_weights,
|
|
static_cast<int64_t>(dev_ctx.GetCUDAMaxGridDimSize()[0]),
|
|
common::errors::InvalidArgument(
|
|
"depthwise conv3d bias grad grid.x exceeds device limit."));
|
|
dim3 grid(static_cast<unsigned int>(num_weights));
|
|
dim3 block_dim(block);
|
|
size_t smem = block * sizeof(T);
|
|
|
|
if (strides[1] == 1 && strides[2] == 1) {
|
|
DWConv3dBwdWeightKernel<T, AccT, 1, 1>
|
|
<<<grid, block_dim, smem, stream>>>(grad_output_ptr,
|
|
input_ptr,
|
|
grad_filter_ptr,
|
|
batch_size_int,
|
|
in_channels_int,
|
|
in_depth_int,
|
|
in_height_int,
|
|
in_width_int,
|
|
out_channels_int,
|
|
out_depth_int,
|
|
out_height_int,
|
|
out_width_int,
|
|
kernel_t_int,
|
|
kernel_h_int,
|
|
kernel_w_int,
|
|
strides[0],
|
|
strides[1],
|
|
strides[2],
|
|
paddings_vec[0],
|
|
paddings_vec[1],
|
|
paddings_vec[2],
|
|
dilations[0],
|
|
dilations[1],
|
|
dilations[2]);
|
|
} else if (strides[1] == 2 && strides[2] == 2) {
|
|
DWConv3dBwdWeightKernel<T, AccT, 2, 2>
|
|
<<<grid, block_dim, smem, stream>>>(grad_output_ptr,
|
|
input_ptr,
|
|
grad_filter_ptr,
|
|
batch_size_int,
|
|
in_channels_int,
|
|
in_depth_int,
|
|
in_height_int,
|
|
in_width_int,
|
|
out_channels_int,
|
|
out_depth_int,
|
|
out_height_int,
|
|
out_width_int,
|
|
kernel_t_int,
|
|
kernel_h_int,
|
|
kernel_w_int,
|
|
strides[0],
|
|
strides[1],
|
|
strides[2],
|
|
paddings_vec[0],
|
|
paddings_vec[1],
|
|
paddings_vec[2],
|
|
dilations[0],
|
|
dilations[1],
|
|
dilations[2]);
|
|
} else {
|
|
DWConv3dBwdWeightKernel<T, AccT, -1, -1>
|
|
<<<grid, block_dim, smem, stream>>>(grad_output_ptr,
|
|
input_ptr,
|
|
grad_filter_ptr,
|
|
batch_size_int,
|
|
in_channels_int,
|
|
in_depth_int,
|
|
in_height_int,
|
|
in_width_int,
|
|
out_channels_int,
|
|
out_depth_int,
|
|
out_height_int,
|
|
out_width_int,
|
|
kernel_t_int,
|
|
kernel_h_int,
|
|
kernel_w_int,
|
|
strides[0],
|
|
strides[1],
|
|
strides[2],
|
|
paddings_vec[0],
|
|
paddings_vec[1],
|
|
paddings_vec[2],
|
|
dilations[0],
|
|
dilations[1],
|
|
dilations[2]);
|
|
}
|
|
}
|
|
|
|
// Bias Gradient
|
|
if (bias_grad) {
|
|
dev_ctx.template Alloc<T>(bias_grad);
|
|
// Reduce N(0), D(2), H(3), W(4) -> C(1) for NCDHW
|
|
std::vector<int64_t> reduce_dims = {0, 2, 3, 4};
|
|
SumKernel<T, Context>(dev_ctx,
|
|
out_grad_ncdhw,
|
|
IntArray(reduce_dims),
|
|
CppTypeToDataType<T>::Type(),
|
|
false,
|
|
bias_grad);
|
|
}
|
|
|
|
if (input_grad && channel_last) {
|
|
TransToChannelLast<Context, T>(dev_ctx, input_grad_ncdhw_ptr, input_grad);
|
|
}
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
void DepthwiseConv3dBiasGradKernel(const Context& dev_ctx,
|
|
const DenseTensor& input,
|
|
const DenseTensor& filter,
|
|
const optional<DenseTensor>& bias,
|
|
const DenseTensor& out_grad,
|
|
const std::vector<int>& strides_t,
|
|
const std::vector<int>& paddings_t,
|
|
const std::string& padding_algorithm,
|
|
int groups,
|
|
const std::vector<int>& dilations_t,
|
|
const std::string& data_format,
|
|
DenseTensor* input_grad,
|
|
DenseTensor* filter_grad,
|
|
DenseTensor* bias_grad) {
|
|
if (!input_grad && !filter_grad && !bias_grad) return;
|
|
|
|
// 0-size Check
|
|
if (input.numel() == 0) {
|
|
if (input_grad) dev_ctx.template Alloc<T>(input_grad);
|
|
if (filter_grad) {
|
|
dev_ctx.template Alloc<T>(filter_grad);
|
|
Full<T, Context>(dev_ctx, filter_grad->dims(), 0, filter_grad);
|
|
}
|
|
if (bias_grad) {
|
|
dev_ctx.template Alloc<T>(bias_grad);
|
|
Full<T, Context>(dev_ctx, bias_grad->dims(), 0, bias_grad);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Alloc
|
|
if (input_grad) dev_ctx.template Alloc<T>(input_grad);
|
|
if (filter_grad) dev_ctx.template Alloc<T>(filter_grad);
|
|
|
|
std::vector<int> strides = strides_t;
|
|
std::vector<int> dilations = dilations_t;
|
|
std::vector<int> paddings = paddings_t;
|
|
|
|
// Update Padding
|
|
auto in_dims = input.dims();
|
|
auto filter_dims = filter.dims();
|
|
|
|
DDim in_data_dims;
|
|
const DataLayout data_layout = StringToDataLayout(data_format);
|
|
if (data_layout != DataLayout::NDHWC) {
|
|
in_data_dims = slice_ddim(in_dims, 2, in_dims.size());
|
|
} else {
|
|
in_data_dims = slice_ddim(in_dims, 1, in_dims.size() - 1);
|
|
}
|
|
|
|
DDim filter_data_dims = slice_ddim(filter_dims, 2, filter_dims.size());
|
|
std::vector<int> ksize = vectorize<int>(filter_data_dims);
|
|
|
|
UpdatePaddingAndDilation(
|
|
&paddings, &dilations, padding_algorithm, in_data_dims, strides, ksize);
|
|
|
|
bool is_sys_pad = strides.size() * 2 == paddings.size() ? false : true;
|
|
if (!is_sys_pad) {
|
|
for (size_t i = 0; i < strides.size(); ++i) {
|
|
paddings.erase(paddings.begin() + i + 1);
|
|
}
|
|
}
|
|
|
|
LaunchDepthwiseConv3dBackwardCompatible<T, Context>(dev_ctx,
|
|
input,
|
|
filter,
|
|
out_grad,
|
|
strides,
|
|
paddings,
|
|
dilations,
|
|
data_format,
|
|
input_grad,
|
|
filter_grad,
|
|
bias_grad);
|
|
}
|
|
|
|
} // namespace phi
|
|
|
|
PD_REGISTER_KERNEL(depthwise_conv3d_bias_grad,
|
|
GPU,
|
|
ALL_LAYOUT,
|
|
phi::DepthwiseConv3dBiasGradKernel,
|
|
float,
|
|
double,
|
|
phi::float16,
|
|
phi::bfloat16) {}
|