已合并
Implement D and H dimension swapping for Conv3D backward operation #4483
jiangqi创建于 4月30日
Implement D and H dimension swapping for Conv3D backward operation #4483
已合并
共 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 ¶ms, | ||
| 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 ¶ms, | ||
| 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 | + | ||
| 2687 | static aclnnStatus CalculateConv3DBp(ConvolutionBackwardInputTensor &inputTensor, | 2829 | static aclnnStatus CalculateConv3DBp(ConvolutionBackwardInputTensor &inputTensor, |
| 2688 | ConvolutionBackwardResult &outputTensor, | 2830 | ConvolutionBackwardResult &outputTensor, |
| 2689 | ConvolutionBackwardParams ¶ms, | 2831 | ConvolutionBackwardParams ¶ms, |
| @@ -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 l0op | 144 | } // namespace l0op |
| 137 | 145 | ||
| 138 | 146 | ||
| @@ -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 ¶ms, | ||
| 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 ¶ms) | ||
| 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 | + | ||
| 135 | static bool CheckV2Stride(const ConvBackpropParams ¶ms) | 182 | static bool CheckV2Stride(const ConvBackpropParams ¶ms) |
| 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 ¶ms); | ||
| 50 | + | ||
| 47 | // Conv2dBackpropInput | 51 | // Conv2dBackpropInput |
| 48 | // 5HD->FZ with Fp16 | 52 | // 5HD->FZ with Fp16 |
| 49 | const aclTensor *Conv2DBackpropInputFp162Fp16(const aclTensor *input, const aclTensor *weight, | 53 | const 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 | } |