已合并
Implement D and H dimension swapping for Conv3D backward operation #4483
Implement D and H dimension swapping for Conv3D backward operation #4483
已合并
jiangqi创建于 4月30日
5 个文件变更+248-0
@@ -2684,6 +2684,148 @@ static bool isConv2dTo3d(const ConvolutionBackwardInputTensor &inputTensor,
2684 return false;2684 return false;
2685}2685}
2686 2686 
2687+// Swap D and H dimensions for a tensor (N, C, D, H, W) -> (N, C, H, D, W)
2688+static const aclTensor *SwapDHDimensions(const aclTensor *input, aclOpExecutor *executor)
2689+{
2690+ auto inputShape = input->GetViewShape();
2691+ int64_t nDim = inputShape.GetDim(NCDHW_N_DIM);
2692+ int64_t cDim = inputShape.GetDim(NCDHW_C_DIM);
2693+ int64_t dDim = inputShape.GetDim(NCDHW_D_DIM);
2694+ int64_t hDim = inputShape.GetDim(NCDHW_H_DIM);
2695+ int64_t wDim = inputShape.GetDim(NCDHW_W_DIM);
2696+
2697+ // Create new shape with D and H swapped
2698+ op::Shape newShape = op::Shape({nDim, cDim, hDim, dDim, wDim});
2699+
2700+ auto contiguousInput = l0op::Contiguous(input, executor);
2701+ CHECK_RET(contiguousInput != nullptr, nullptr);
2702+ 
2703+ std::vector<int64_t> shapeVec;
2704+ for (size_t i = 0; i < newShape.GetDimNum(); i++) {
2705+ shapeVec.push_back(newShape.GetDim(i));
2706+ }
2707+
2708+ auto *shapeArray = executor->AllocIntArray(shapeVec.data(), newShape.GetDimNum());
2709+ CHECK_RET(shapeArray != nullptr, nullptr);
2710+
2711+ auto reshapedTensor = l0op::Reshape(contiguousInput, shapeArray, executor);
2712+ CHECK_RET(reshapedTensor != nullptr, nullptr);
2713+
2714+ return reshapedTensor;
2715+}
2716+ 
2717+static const aclIntArray *SwapDHInArray3(const aclIntArray *arr, aclOpExecutor *executor)
2718+{
2719+ if (arr == nullptr || arr->Size() != 3) {
2720+ return arr;
2721+ }
2722+ int64_t elemD = (*arr)[0]; // depth element
2723+ int64_t elemH = (*arr)[1]; // height element
2724+ int64_t elemW = (*arr)[2]; // width element
2725+ int64_t newArray[] = {elemH, elemD, elemW}; // swap D and H
2726+ return executor->AllocIntArray(newArray, 3);
2727+}
2728+ 
2729+static const aclIntArray *SwapDHInPaddingArray6(const aclIntArray *arr, aclOpExecutor *executor)
2730+{
2731+ if (arr == nullptr || arr->Size() != 6) {
2732+ return arr;
2733+ }
2734+ int64_t padDHead = (*arr)[0];
2735+ int64_t padDTail = (*arr)[1];
2736+ int64_t padHTop = (*arr)[2];
2737+ int64_t padHBottom = (*arr)[3];
2738+ int64_t padWLeft = (*arr)[4];
2739+ int64_t padWRight = (*arr)[5];
2740+ int64_t newArray[] = {padHTop, padHBottom, padDHead, padDTail, padWLeft, padWRight}; // swap D and H
2741+ return executor->AllocIntArray(newArray, 6);
2742+}
2743+ 
2744+// Apply D-H swap on input tensors and parameters before calculation
2745+static aclnnStatus ApplySwapDHBeforeCalculation(ConvolutionBackwardInputTensor &inputTensor,
2746+ ConvolutionBackwardParams &params,
2747+ aclOpExecutor *executor)
2748+{
2749+ OP_LOGD("Conv3DBackward: Swapping D and H dimensions before calculation");
2750+
2751+ // Swap D and H for input tensors
2752+ inputTensor.input = SwapDHDimensions(inputTensor.input, executor);
2753+ OP_CHECK(inputTensor.input != nullptr,
2754+ OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "Swap D and H for input failed."),
2755+ return ACLNN_ERR_INNER_NULLPTR);
2756+
2757+ inputTensor.gradOutput = SwapDHDimensions(inputTensor.gradOutput, executor);
2758+ OP_CHECK(inputTensor.gradOutput != nullptr,
2759+ OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "Swap D and H for gradOutput failed."),
2760+ return ACLNN_ERR_INNER_NULLPTR);
2761+
2762+ inputTensor.weight = SwapDHDimensions(inputTensor.weight, executor);
2763+ OP_CHECK(inputTensor.weight != nullptr,
2764+ OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "Swap D and H for weight failed."),
2765+ return ACLNN_ERR_INNER_NULLPTR);
2766+
2767+ // Adjust stride, padding, dilation parameters by swapping D and H indices
2768+ if (params.stride->Size() == 3) {
2769+ params.stride = SwapDHInArray3(params.stride, executor);
2770+ OP_CHECK(params.stride != nullptr,
2771+ OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "AllocIntArray for stride failed."),
2772+ return ACLNN_ERR_INNER_NULLPTR);
2773+ }
2774+
2775+ if (params.dilation->Size() == 3) {
2776+ params.dilation = SwapDHInArray3(params.dilation, executor);
2777+ OP_CHECK(params.dilation != nullptr,
2778+ OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "AllocIntArray for dilation failed."),
2779+ return ACLNN_ERR_INNER_NULLPTR);
2780+ }
2781+
2782+ if (params.padding->Size() == 6) {
2783+ params.padding = SwapDHInPaddingArray6(params.padding, executor);
2784+ OP_CHECK(params.padding != nullptr,
2785+ OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "AllocIntArray for padding failed."),
2786+ return ACLNN_ERR_INNER_NULLPTR);
2787+ } else if (params.padding->Size() == 3) {
2788+ params.padding = SwapDHInArray3(params.padding, executor);
2789+ OP_CHECK(params.padding != nullptr,
2790+ OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "AllocIntArray for padding failed."),
2791+ return ACLNN_ERR_INNER_NULLPTR);
2792+ }
2793+
2794+ if (params.outputPadding->Size() == 3) {
2795+ params.outputPadding = SwapDHInArray3(params.outputPadding, executor);
2796+ OP_CHECK(params.outputPadding != nullptr,
2797+ OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "AllocIntArray for outputPadding failed."),
2798+ return ACLNN_ERR_INNER_NULLPTR);
2799+ }
2800+
2801+ return ACLNN_SUCCESS;
2802+}
2803+ 
2804+// Restore D and H dimensions on output tensors after calculation
2805+// Returns ACLNN_SUCCESS on success, error code on failure
2806+static aclnnStatus RestoreDHAfterCalculation(ConvolutionBackwardResult &outputTensor,
2807+ const ConvolutionBackwardParams &params,
2808+ aclOpExecutor *executor)
2809+{
2810+ OP_LOGD("Conv3DBackward: Restoring D and H dimensions after calculation");
2811+
2812+ if ((*params.outputMask)[0] && outputTensor.gradInput != nullptr) {
2813+ outputTensor.gradInput = SwapDHDimensions(outputTensor.gradInput, executor);
2814+ OP_CHECK(outputTensor.gradInput != nullptr,
2815+ OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "Restore D and H for gradInput failed."),
2816+ return ACLNN_ERR_INNER_NULLPTR);
2817+ }
2818+
2819+ if ((*params.outputMask)[1] && outputTensor.gradWeight != nullptr) {
2820+ outputTensor.gradWeight = SwapDHDimensions(outputTensor.gradWeight, executor);
2821+ OP_CHECK(outputTensor.gradWeight != nullptr,
2822+ OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "Restore D and H for gradWeight failed."),
2823+ return ACLNN_ERR_INNER_NULLPTR);
2824+ }
2825+
2826+ return ACLNN_SUCCESS;
2827+}
2828+ 
2687static aclnnStatus CalculateConv3DBp(ConvolutionBackwardInputTensor &inputTensor,2829static aclnnStatus CalculateConv3DBp(ConvolutionBackwardInputTensor &inputTensor,
2688 ConvolutionBackwardResult &outputTensor,2830 ConvolutionBackwardResult &outputTensor,
2689 ConvolutionBackwardParams &params,2831 ConvolutionBackwardParams &params,
@@ -2698,6 +2840,16 @@ static aclnnStatus CalculateConv3DBp(ConvolutionBackwardInputTensor &inputTensor
2698 if (!(Ops::NN::AclnnUtil::IsRegbase(curArch))) {2840 if (!(Ops::NN::AclnnUtil::IsRegbase(curArch))) {
2699 CHECK_RET(CheckSupportedForConv3dBackpropFilter(inputTensor, outputTensor, params), ACLNN_ERR_PARAM_INVALID);2841 CHECK_RET(CheckSupportedForConv3dBackpropFilter(inputTensor, outputTensor, params), ACLNN_ERR_PARAM_INVALID);
2700 }2842 }
2843+
2844+ // Check if need to swap D and H dimensions for Conv3D backward
2845+ bool needSwapDH = l0op::NeedSwapDHForConv3DBackward(inputTensor, params);
2846+
2847+ // Apply D-H swap before calculation if needed
2848+ if (needSwapDH) {
2849+ ret = ApplySwapDHBeforeCalculation(inputTensor, params, executor);
2850+ CHECK_RET(ret == ACLNN_SUCCESS, ret);
2851+ }
2852+
2701 if (!params.transposed) {2853 if (!params.transposed) {
2702 OP_LOGD("Entering CalculateConv3DBackward");2854 OP_LOGD("Entering CalculateConv3DBackward");
2703 ret = CalculateConv3DBackward(inputTensor, outputTensor, params, executor);2855 ret = CalculateConv3DBackward(inputTensor, outputTensor, params, executor);
@@ -2705,6 +2857,12 @@ static aclnnStatus CalculateConv3DBp(ConvolutionBackwardInputTensor &inputTensor
2705 OP_LOGD("Entering CalculateConv3DTransposeBackward");2857 OP_LOGD("Entering CalculateConv3DTransposeBackward");
2706 ret = CalculateConv3DTransposeBackward(inputTensor, outputTensor, params, executor);2858 ret = CalculateConv3DTransposeBackward(inputTensor, outputTensor, params, executor);
2707 }2859 }
2860+
2861+ // Restore original D and H dimensions after calculation if we swapped them
2862+ if (needSwapDH) {
2863+ aclnnStatus restoreRet = RestoreDHAfterCalculation(outputTensor, params, executor);
2864+ CHECK_RET(restoreRet == ACLNN_SUCCESS, restoreRet);
2865+ }
2708 2866 
2709 return ret;2867 return ret;
2710}2868}
@@ -133,6 +133,14 @@ const std::vector<std::vector<int64_t>> CONV3D_BACKPROP_FILTER_V2_TRANSDATA_MERG
133 {op::DataType::DT_BF16, 1, 8, 5, 32, 32, 8, 8, 1, 1, 1, 1, 8, 5, 32, 32, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1},133 {op::DataType::DT_BF16, 1, 8, 5, 32, 32, 8, 8, 1, 1, 1, 1, 8, 5, 32, 32, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1},
134};134};
135 135 
136+// D-H swap white list for Conv3D backward
137+// Format: [dtype, N_input, C_in, D_input, H_input, W_input, C_out, C_in/groups, Kd, Kh, Kw,
138+// N_gradOutput, C_out, D_gradOutput, H_gradOutput, W_gradOutput, strideD, strideH, strideW,
139+// padD, padH, padW, dilationD, dilationH, dilationW, groups]
140+const std::vector<std::vector<int64_t>> CONV3D_BACKPROP_DH_SWAP_WHITE_LIST = {
141+ {op::DataType::DT_FLOAT, 1, 512, 4099, 1, 16, 512, 1, 4, 1, 1, 1, 512, 4096, 1, 16, 1, 1, 1, 0, 0, 0, 1, 1, 1, 512},
142+};
143+ 
136} // namespace l0op144} // namespace l0op
137 145 
138#endif // OP_API_CONVOLUTION_BACKWARD_WHITE_LIST_H_146#endif // OP_API_CONVOLUTION_BACKWARD_WHITE_LIST_H_
@@ -132,6 +132,53 @@ static bool IsConv2DV2WhiteListCase(const vector<int64_t> &caseInfo, const vecto
132 return false;132 return false;
133}133}
134 134 
135+// Construct case info for D-H swap white list from ConvolutionBackwardInputTensor and ConvolutionBackwardParams
136+static void ConstructDHSwapCaseInfo(const ConvolutionBackwardInputTensor &inputTensor,
137+ const ConvolutionBackwardParams &params,
138+ vector<int64_t> &caseInfo)
139+{
140+ caseInfo.reserve(CONV3D_BACKPROP_WHITE_LIST_CASE_SIZE);
141+ auto inputDataType = inputTensor.input->GetDataType();
142+ caseInfo.push_back(static_cast<int64_t>(inputDataType));
143+ AddTensorShapeToCaseInfo(*(inputTensor.input), caseInfo);
144+ AddTensorShapeToCaseInfo(*(inputTensor.weight), caseInfo);
145+ AddTensorShapeToCaseInfo(*(inputTensor.gradOutput), caseInfo);
146+ AddAclIntArrayToCaseInfo(*(params.stride), caseInfo);
147+ AddAclIntArrayToCaseInfo(*(params.padding), caseInfo);
148+ AddAclIntArrayToCaseInfo(*(params.dilation), caseInfo);
149+ caseInfo.push_back(params.groups);
150+}
151+ 
152+// Check if need to swap D and H dimensions for Conv3D backward
153+bool NeedSwapDHForConv3DBackward(const ConvolutionBackwardInputTensor &inputTensor,
154+ const ConvolutionBackwardParams &params)
155+{
156+ NpuArch npuArch = GetCurrentPlatformInfo().GetCurNpuArch();
157+ if (npuArch != NpuArch::DAV_2201) {
158+ return false;
159+ }
160+
161+ // Check format - all tensors must be NCDHW
162+ if (inputTensor.input->GetOriginalFormat() != op::Format::FORMAT_NCDHW) {
163+ OP_LOGD("Conv3DBackward: NeedSwapDH skip: input format is not NCDHW");
164+ return false;
165+ }
166+ if (inputTensor.weight->GetOriginalFormat() != op::Format::FORMAT_NCDHW) {
167+ OP_LOGD("Conv3DBackward: NeedSwapDH skip: weight format is not NCDHW");
168+ return false;
169+ }
170+ if (inputTensor.gradOutput->GetOriginalFormat() != op::Format::FORMAT_NCDHW) {
171+ OP_LOGD("Conv3DBackward: NeedSwapDH skip: gradOutput format is not NCDHW");
172+ return false;
173+ }
174+
175+ vector<int64_t> caseInfo;
176+ ConstructDHSwapCaseInfo(inputTensor, params, caseInfo);
177+
178+ // Use IsConv2DV2WhiteListCase for exact match
179+ return IsConv2DV2WhiteListCase(caseInfo, CONV3D_BACKPROP_DH_SWAP_WHITE_LIST);
180+}
181+ 
135static bool CheckV2Stride(const ConvBackpropParams &params)182static bool CheckV2Stride(const ConvBackpropParams &params)
136{183{
137 const aclIntArray &stride = *(params.stride);184 const aclIntArray &stride = *(params.stride);
@@ -44,6 +44,10 @@ struct ConvolutionBackwardParams {
44 const int8_t cubeMathType;44 const int8_t cubeMathType;
45};45};
46 46 
47+// Check if need to swap D and H dimensions for Conv3D backward
48+bool NeedSwapDHForConv3DBackward(const ConvolutionBackwardInputTensor &inputTensor,
49+ const ConvolutionBackwardParams &params);
50+ 
47// Conv2dBackpropInput51// Conv2dBackpropInput
48// 5HD->FZ with Fp1652// 5HD->FZ with Fp16
49const aclTensor *Conv2DBackpropInputFp162Fp16(const aclTensor *input, const aclTensor *weight,53const aclTensor *Conv2DBackpropInputFp162Fp16(const aclTensor *input, const aclTensor *weight,
@@ -1934,4 +1934,35 @@ TEST_F(convolution_backward_test, ascend950_test_ConvBack3D_all_valid_type) {
1934 EXPECT_EQ(aclRet, ACLNN_SUCCESS);1934 EXPECT_EQ(aclRet, ACLNN_SUCCESS);
1935 }1935 }
1936}1936}
1937+ 
1938+TEST_F(convolution_backward_test, ascend910B2_test_Conv3DBackward_DH_Swap) {
1939+ auto input_tensor_desc = TensorDesc({1, 512, 4099, 1, 16}, ACL_FLOAT, ACL_FORMAT_NCDHW);
1940+ auto weight_tensor_desc = TensorDesc({512, 1, 4, 1, 1}, ACL_FLOAT, ACL_FORMAT_NCDHW);
1941+ auto grad_output_tensor_desc = TensorDesc({1, 512, 4096, 1, 16}, ACL_FLOAT, ACL_FORMAT_NCDHW);
1942+ 
1943+ auto bias_sizes_desc = IntArrayDesc(vector<int64_t>{512});
1944+ auto stride_desc = IntArrayDesc(vector<int64_t>{1, 1, 1});
1945+ auto padding_desc = IntArrayDesc(vector<int64_t>{0, 0, 0});
1946+ auto dilation_desc = IntArrayDesc(vector<int64_t>{1, 1, 1});
1947+ bool transposed = false;
1948+ auto output_padding_desc = IntArrayDesc(vector<int64_t>{0, 0, 0});
1949+ int groups = 512;
1950+ auto output_mask = BoolArrayDesc(vector<bool>{true, true, true});
1951+ auto gradInput = TensorDesc({1, 512, 4099, 1, 16}, ACL_FLOAT, ACL_FORMAT_NCDHW);
1952+ auto gradWeight = TensorDesc({512, 1, 4, 1, 1}, ACL_FLOAT, ACL_FORMAT_NCDHW);
1953+ auto gradBias = TensorDesc({512}, ACL_FLOAT, ACL_FORMAT_ND);
1954+ 
1955+ int8_t cubeMathType = 0;
1956+ 
1957+ auto ut =
1958+ OP_API_UT(aclnnConvolutionBackward,
1959+ INPUT(grad_output_tensor_desc, input_tensor_desc, weight_tensor_desc, bias_sizes_desc, stride_desc,
1960+ padding_desc, dilation_desc, transposed, output_padding_desc, groups, output_mask, cubeMathType),
1961+ OUTPUT(gradInput, gradWeight, gradBias));
1962+ 
1963+ // SAMPLE: only test GetWorkspaceSize
1964+ uint64_t workspace_size = 0;
1965+ aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspace_size);
1966+ EXPECT_EQ(aclRet, ACLNN_SUCCESS);
1967+}
1937}1968}