Skip to content

Commit 8f156fd

Browse files
authored
[Hackathon NO.74] 为 Paddle-TRT 添加 grid_sampler 算子 (#50934)
1 parent 5133109 commit 8f156fd

File tree

6 files changed

+219
-53
lines changed

6 files changed

+219
-53
lines changed

paddle/fluid/inference/api/analysis_predictor.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2539,6 +2539,9 @@ USE_TRT_CONVERTER(preln_groupnorm_act)
25392539
USE_TRT_CONVERTER(flash_multihead_matmul)
25402540
USE_TRT_CONVERTER(cross_multihead_matmul)
25412541
#endif
2542+
#if IS_TRT_VERSION_GE(8510)
2543+
USE_TRT_CONVERTER(grid_sampler)
2544+
#endif
25422545
#if IS_TRT_VERSION_GE(8200)
25432546
USE_TRT_CONVERTER(set_value)
25442547
#endif

paddle/fluid/inference/tensorrt/convert/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ list(
2727
multihead_matmul_roformer_op.cc
2828
flash_multihead_matmul_op.cc
2929
cross_multihead_matmul_op.cc
30+
grid_sampler_op.cc
3031
shuffle_channel_op.cc
3132
fill_any_like_op.cc
3233
where_op.cc
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#include "paddle/fluid/inference/tensorrt/convert/op_converter.h"
16+
17+
namespace paddle {
18+
namespace inference {
19+
namespace tensorrt {
20+
21+
/*
22+
* GridSampler Op
23+
*/
24+
class GridSamplerOpConverter : public OpConverter {
25+
public:
26+
void operator()(const framework::proto::OpDesc& op,
27+
const framework::Scope& scope,
28+
bool test_mode) override {
29+
#if IS_TRT_VERSION_GE(8510)
30+
VLOG(3) << "convert a fluid grid_sampler op to tensorrt GridSample layer";
31+
framework::OpDesc op_desc(op, nullptr);
32+
std::string input_x_name = op_desc.Input("X").front();
33+
std::string input_grid_name = op_desc.Input("Grid").front();
34+
std::string output_name = op_desc.Output("Output").front();
35+
auto* input_x_tensor = engine_->GetITensor(input_x_name);
36+
auto* input_grid_tensor = engine_->GetITensor(input_grid_name);
37+
38+
auto* layer = TRT_ENGINE_ADD_LAYER(
39+
engine_, GridSample, *input_x_tensor, *input_grid_tensor);
40+
41+
const std::string mode =
42+
PADDLE_GET_CONST(std::string, op_desc.GetAttr("mode"));
43+
const std::string padding_mode =
44+
PADDLE_GET_CONST(std::string, op_desc.GetAttr("padding_mode"));
45+
const bool align_corners =
46+
PADDLE_GET_CONST(bool, op_desc.GetAttr("align_corners"));
47+
48+
nvinfer1::InterpolationMode interpolationMode{
49+
nvinfer1::InterpolationMode::kNEAREST};
50+
if (mode == "nearest") {
51+
interpolationMode = nvinfer1::ResizeMode::kNEAREST;
52+
} else if (mode == "bilinear") {
53+
interpolationMode = nvinfer1::ResizeMode::kLINEAR;
54+
}
55+
56+
nvinfer1::SampleMode sampleMode{nvinfer1::SampleMode::kFILL};
57+
if (padding_mode == "zeros") {
58+
sampleMode = nvinfer1::SampleMode::kFILL;
59+
} else if (padding_mode == "border") {
60+
sampleMode = nvinfer1::SampleMode::kCLAMP;
61+
} else if (padding_mode == "reflection") {
62+
sampleMode = nvinfer1::SampleMode::kREFLECT;
63+
}
64+
65+
layer->setInterpolationMode(interpolationMode);
66+
layer->setSampleMode(sampleMode);
67+
layer->setAlignCorners(align_corners);
68+
69+
RreplenishLayerAndOutput(layer, "grid_sampler", {output_name}, test_mode);
70+
#else
71+
VLOG(3) << "grid_sampler is not supported when TensorRT < 8.5.1";
72+
#endif
73+
}
74+
};
75+
76+
} // namespace tensorrt
77+
} // namespace inference
78+
} // namespace paddle
79+
80+
REGISTER_TRT_OP_CONVERTER(grid_sampler, GridSamplerOpConverter);

paddle/fluid/inference/tensorrt/dynamic_shape_infermeta.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ nvinfer1::DimsExprs GridSamplerInferMeta(
377377
output.d[2] = grid_dims.d[1];
378378
output.d[3] = grid_dims.d[2];
379379
} else {
380-
output.nbDims = 4;
380+
output.nbDims = 5;
381381
output.d[0] = x_dims.d[0];
382382
output.d[1] = x_dims.d[1];
383383
output.d[2] = grid_dims.d[1];

paddle/fluid/inference/tensorrt/op_teller.cc

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2542,6 +2542,48 @@ struct SimpleOpTypeSetTeller : public Teller {
25422542
}
25432543
}
25442544

2545+
if (op_type == "grid_sampler") {
2546+
#if !IS_TRT_VERSION_GE(8510)
2547+
VLOG(3) << "grid_sampler is not supported when TensorRT < 8.5.1";
2548+
return false;
2549+
#else
2550+
if (!with_dynamic_shape) {
2551+
VLOG(3) << "the grid_sampler does not support "
2552+
"static shape yet";
2553+
return false;
2554+
}
2555+
2556+
if (!desc.HasAttr("mode") || !desc.HasAttr("padding_mode") ||
2557+
!desc.HasAttr("align_corners")) {
2558+
VLOG(3) << "grid_sampler need attributes : mode, padding_mode, "
2559+
"align_corners";
2560+
return false;
2561+
}
2562+
2563+
auto* block = desc.Block();
2564+
if (block == nullptr) {
2565+
VLOG(3) << "The block desc is nullptr, we can't continue to analyze. "
2566+
"Developers need to check whether block_desc is passed in "
2567+
"the pass.";
2568+
return false;
2569+
}
2570+
auto input_name = desc.Input("X")[0];
2571+
auto* input_desc = block->FindVar(input_name);
2572+
const auto input_shape = input_desc->GetShape();
2573+
2574+
auto grid_name = desc.Input("Grid")[0];
2575+
auto* grid_desc = block->FindVar(grid_name);
2576+
const auto grid_shape = grid_desc->GetShape();
2577+
2578+
if (input_shape.size() != 4 || grid_shape.size() != 4) {
2579+
VLOG(3) << "The input and grid tensors must be shape tensors of rank 4 "
2580+
"using TRT GridSample layer.";
2581+
return false;
2582+
}
2583+
2584+
#endif
2585+
}
2586+
25452587
if (use_no_calib_int8) {
25462588
return int8_teller_set.count(op_type);
25472589
} else {
@@ -2701,7 +2743,8 @@ struct SimpleOpTypeSetTeller : public Teller {
27012743
"expand_v2",
27022744
"fuse_eleadd_transpose",
27032745
"skip_groupnorm_act",
2704-
"preln_groupnorm_act"};
2746+
"preln_groupnorm_act",
2747+
"grid_sampler"};
27052748

27062749
std::unordered_set<std::string> teller_set{
27072750
"mul",
@@ -2853,7 +2896,8 @@ struct SimpleOpTypeSetTeller : public Teller {
28532896
"expand_v2",
28542897
"fuse_eleadd_transpose",
28552898
"skip_groupnorm_act",
2856-
"preln_groupnorm_act"};
2899+
"preln_groupnorm_act",
2900+
"grid_sampler"};
28572901
};
28582902

28592903
struct GenericPluginTeller : public Teller {

python/paddle/fluid/tests/unittests/ir/inference/test_trt_convert_grid_sampler.py

Lines changed: 88 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -25,61 +25,103 @@
2525

2626
class TrtConvertGridSampler(TrtLayerAutoScanTest):
2727
def is_program_valid(self, program_config: ProgramConfig) -> bool:
28+
self.trt_param.workspace_size = 1073741824
2829
return True
2930

3031
def sample_program_configs(self):
3132
def generate_input1():
32-
return np.random.random([1, 3, 32, 32]).astype(np.float32)
33+
if self.dims == 4:
34+
self.input_shape = [1, 3, 32, 32]
35+
return np.random.random([1, 3, 32, 32]).astype(np.float32)
36+
elif self.dims == 5:
37+
self.input_shape = [1, 3, 32, 32, 64]
38+
return np.random.random([1, 3, 32, 32, 64]).astype(np.float32)
3339

3440
def generate_input2():
35-
return np.random.random([1, 3, 3, 2]).astype(np.float32)
36-
37-
ops_config = [
38-
{
39-
"op_type": "grid_sampler",
40-
"op_inputs": {
41-
"X": ["input_data"],
42-
"Grid": ["grid_data"],
43-
},
44-
"op_outputs": {"Output": ["output_data"]},
45-
"op_attrs": {},
46-
}
47-
]
48-
49-
ops = self.generate_op_config(ops_config)
50-
for i in range(10):
51-
program_config = ProgramConfig(
52-
ops=ops,
53-
weights={},
54-
inputs={
55-
"input_data": TensorConfig(
56-
data_gen=partial(generate_input1)
57-
),
58-
"grid_data": TensorConfig(
59-
data_gen=partial(generate_input2)
60-
),
61-
},
62-
outputs=["output_data"],
63-
)
64-
65-
yield program_config
41+
if self.dims == 4:
42+
self.input_shape = [1, 3, 3, 2]
43+
return np.random.random([1, 3, 3, 2]).astype(np.float32)
44+
elif self.dims == 5:
45+
self.input_shape = [1, 3, 3, 2, 3]
46+
return np.random.random([1, 3, 3, 2, 3]).astype(np.float32)
47+
48+
mode = ["bilinear", "nearest"]
49+
padding_mode = ["zeros", "reflection", "border"]
50+
align_corners = [True, False]
51+
descs = []
52+
for m in mode:
53+
for p in padding_mode:
54+
for a in align_corners:
55+
descs.append(
56+
{
57+
"mode": m,
58+
"padding_mode": p,
59+
"align_corners": a,
60+
}
61+
)
62+
63+
for dims in [4, 5]:
64+
for desc in descs:
65+
self.dims = dims
66+
ops_config = [
67+
{
68+
"op_type": "grid_sampler",
69+
"op_inputs": {
70+
"X": ["input_data"],
71+
"Grid": ["grid_data"],
72+
},
73+
"op_outputs": {"Output": ["output_data"]},
74+
"op_attrs": desc,
75+
}
76+
]
77+
ops = self.generate_op_config(ops_config)
78+
79+
program_config = ProgramConfig(
80+
ops=ops,
81+
weights={},
82+
inputs={
83+
"input_data": TensorConfig(
84+
data_gen=partial(generate_input1)
85+
),
86+
"grid_data": TensorConfig(
87+
data_gen=partial(generate_input2)
88+
),
89+
},
90+
outputs=["output_data"],
91+
)
92+
93+
yield program_config
6694

6795
def sample_predictor_configs(
6896
self, program_config
6997
) -> (paddle_infer.Config, List[int], float):
70-
def generate_dynamic_shape(attrs):
71-
self.dynamic_shape.min_input_shape = {
72-
"input_data": [1, 3, 32, 32],
73-
"grid_data": [1, 3, 3, 2],
74-
}
75-
self.dynamic_shape.max_input_shape = {
76-
"input_data": [1, 3, 64, 64],
77-
"grid_data": [1, 3, 4, 4],
78-
}
79-
self.dynamic_shape.opt_input_shape = {
80-
"input_data": [1, 3, 32, 32],
81-
"grid_data": [1, 3, 3, 2],
82-
}
98+
def generate_dynamic_shape():
99+
if self.dims == 4:
100+
self.dynamic_shape.min_input_shape = {
101+
"input_data": [1, 3, 32, 32],
102+
"grid_data": [1, 3, 3, 2],
103+
}
104+
self.dynamic_shape.max_input_shape = {
105+
"input_data": [1, 3, 64, 64],
106+
"grid_data": [1, 3, 6, 2],
107+
}
108+
self.dynamic_shape.opt_input_shape = {
109+
"input_data": [1, 3, 32, 32],
110+
"grid_data": [1, 3, 3, 2],
111+
}
112+
elif self.dims == 5:
113+
self.dynamic_shape.min_input_shape = {
114+
"input_data": [1, 3, 32, 32, 64],
115+
"grid_data": [1, 3, 3, 2, 3],
116+
}
117+
self.dynamic_shape.max_input_shape = {
118+
"input_data": [1, 3, 64, 64, 128],
119+
"grid_data": [1, 3, 3, 6, 3],
120+
}
121+
self.dynamic_shape.opt_input_shape = {
122+
"input_data": [1, 3, 32, 32, 64],
123+
"grid_data": [1, 3, 3, 2, 3],
124+
}
83125

84126
def clear_dynamic_shape():
85127
self.dynamic_shape.max_input_shape = {}
@@ -92,13 +134,9 @@ def clear_dynamic_shape():
92134

93135
# for static_shape
94136
clear_dynamic_shape()
95-
self.trt_param.precision = paddle_infer.PrecisionType.Float32
96-
yield self.create_inference_config(), (0, 4), 1e-5
97-
self.trt_param.precision = paddle_infer.PrecisionType.Half
98-
yield self.create_inference_config(), (0, 4), 1e-3
99137

100138
# for dynamic_shape
101-
generate_dynamic_shape(attrs)
139+
generate_dynamic_shape()
102140
self.trt_param.precision = paddle_infer.PrecisionType.Float32
103141
yield self.create_inference_config(), (1, 3), 1e-5
104142
self.trt_param.precision = paddle_infer.PrecisionType.Half

0 commit comments

Comments
 (0)