Skip to content

Commit abeae6f

Browse files
authored
[Feat][Deploy] Add ultrainfer and paddlex-hpi (PaddlePaddle#2625)
1 parent 4d6b62f commit abeae6f

827 files changed

Lines changed: 106390 additions & 3 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.precommit/check_custom.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import os
1516
import re
1617
import sys
1718

@@ -40,9 +41,12 @@ def check(file_path):
4041
if not content.startswith(LICENSE_TEXT):
4142
print(f"License header missing in {file_path}")
4243
return False
43-
if "import paddle" in content or "from paddle import " in content:
44-
print(f"Please use `lazy_paddle` instead `paddle` when import in {file_path}")
45-
return False
44+
if "paddlex" in file_path.split(os.sep):
45+
if "import paddle" in content or "from paddle import " in content:
46+
print(
47+
f"Please use `lazy_paddle` instead `paddle` when import in {file_path}"
48+
)
49+
return False
4650
return True
4751

4852

libs/paddlex-hpi/MANIFEST.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
include src/paddlex_hpi/py.typed
2+
include src/paddlex_hpi/model_info_collection.json

libs/paddlex-hpi/README.md

Whitespace-only changes.

libs/paddlex-hpi/pyproject.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[build-system]
2+
requires = ["setuptools >= 69"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "paddlex-hpi"
7+
version = "3.0.0.b2"
8+
description = ""
9+
readme = "README.md"
10+
authors = []
11+
dynamic = ["dependencies", "optional-dependencies"]
12+
13+
[tool.setuptools]
14+
include-package-data = true
15+
16+
[tool.setuptools.dynamic]
17+
dependencies = {file = ["requirements.txt"]}
18+
optional-dependencies.test = {file = ["test_requirements.txt"]}

libs/paddlex-hpi/requirements.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# ultrainfer
2+
# paddlex
3+
importlib-resources >= 6.4
4+
numpy >= 1.21
5+
pandas >= 1.3.3
6+
pydantic >= 2
7+
typing-extensions >= 4.11
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env bash
2+
3+
python -m pip wheel -w wheels/original --no-deps .
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env bash
2+
3+
python -m pytest tests
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve.
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+
__version__ = "3.0.0.beta2"
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve.
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+
import warnings
16+
from pathlib import Path
17+
from typing import Any, Dict, List, Mapping, Optional, Tuple, Type, Union
18+
19+
import ultrainfer as ui
20+
from paddlex.utils import logging
21+
from pydantic import BaseModel, ConfigDict, Field, field_validator
22+
from typing_extensions import Annotated, TypeAlias, TypedDict, assert_never
23+
24+
from paddlex_hpi._model_info import get_model_info
25+
from paddlex_hpi._utils.typing import Backend, DeviceType
26+
27+
28+
class _BackendConfig(BaseModel):
29+
def update_ui_option(self, option: ui.RuntimeOption, model_dir: Path) -> None:
30+
raise NotImplementedError
31+
32+
33+
class PaddleInferConfig(_BackendConfig):
34+
cpu_num_threads: int = 8
35+
enable_mkldnn: bool = True
36+
enable_trt: bool = False
37+
trt_dynamic_shapes: Optional[Dict[str, List[List[int]]]] = None
38+
trt_dynamic_shape_input_data: Optional[Dict[str, List[List[float]]]] = None
39+
enable_log_info: bool = False
40+
41+
def update_ui_option(self, option: ui.RuntimeOption, model_dir: Path) -> None:
42+
option.use_paddle_infer_backend()
43+
option.set_cpu_thread_num(self.cpu_num_threads)
44+
option.paddle_infer_option.enable_mkldnn = self.enable_mkldnn
45+
option.paddle_infer_option.enable_trt = self.enable_trt
46+
option.trt_option.serialize_file = str(model_dir / "trt_serialized.trt")
47+
if self.trt_dynamic_shapes is not None:
48+
for name, shapes in self.trt_dynamic_shapes.items():
49+
option.trt_option.set_shape(name, *shapes)
50+
if self.trt_dynamic_shape_input_data is not None:
51+
for name, data in self.trt_dynamic_shape_input_data.items():
52+
option.trt_option.set_input_data(name, *data)
53+
if self.enable_trt:
54+
option.paddle_infer_option.collect_trt_shape = True
55+
option.paddle_infer_option.collect_trt_shape_by_device = True
56+
option.paddle_infer_option.enable_log_info = self.enable_log_info
57+
58+
59+
class OpenVINOConfig(_BackendConfig):
60+
cpu_num_threads: int = 8
61+
62+
def update_ui_option(self, option: ui.RuntimeOption, model_dir: Path) -> None:
63+
option.use_openvino_backend()
64+
option.set_cpu_thread_num(self.cpu_num_threads)
65+
66+
67+
class ONNXRuntimeConfig(_BackendConfig):
68+
cpu_num_threads: int = 8
69+
70+
def update_ui_option(self, option: ui.RuntimeOption, model_dir: Path) -> None:
71+
option.use_ort_backend()
72+
option.set_cpu_thread_num(self.cpu_num_threads)
73+
74+
75+
class TensorRTConfig(_BackendConfig):
76+
dynamic_shapes: Optional[Dict[str, List[List[int]]]] = None
77+
78+
def update_ui_option(self, option: ui.RuntimeOption, model_dir: Path) -> None:
79+
option.use_trt_backend()
80+
option.trt_option.serialize_file = str(model_dir / "trt_serialized.trt")
81+
if self.dynamic_shapes is not None:
82+
for name, shapes in self.dynamic_shapes.items():
83+
option.trt_option.set_shape(name, *shapes)
84+
85+
86+
class PaddleTensorRTConfig(_BackendConfig):
87+
dynamic_shapes: Dict[str, List[List[int]]]
88+
dynamic_shape_input_data: Optional[Dict[str, List[List[float]]]] = None
89+
enable_log_info: bool = False
90+
91+
def update_ui_option(self, option: ui.RuntimeOption, model_dir: Path) -> None:
92+
option.use_paddle_infer_backend()
93+
option.paddle_infer_option.enable_trt = True
94+
option.trt_option.serialize_file = str(model_dir / "trt_serialized.trt")
95+
if self.dynamic_shapes is not None:
96+
option.paddle_infer_option.collect_trt_shape = True
97+
# TODO: Support setting collect_trt_shape_by_device
98+
for name, shapes in self.dynamic_shapes.items():
99+
option.trt_option.set_shape(name, *shapes)
100+
if self.dynamic_shape_input_data is not None:
101+
for name, data in self.dynamic_shape_input_data.items():
102+
option.trt_option.set_input_data(name, *data)
103+
option.paddle_infer_option.enable_log_info = self.enable_log_info
104+
105+
106+
# Should we use tagged unions?
107+
BackendConfig: TypeAlias = Union[
108+
PaddleInferConfig,
109+
OpenVINOConfig,
110+
ONNXRuntimeConfig,
111+
TensorRTConfig,
112+
]
113+
114+
115+
def get_backend_config_type(backend: Backend, /) -> Type[BackendConfig]:
116+
backend_config_type: Type[BackendConfig]
117+
if backend == "paddle_infer":
118+
backend_config_type = PaddleInferConfig
119+
elif backend == "openvino":
120+
backend_config_type = OpenVINOConfig
121+
elif backend == "onnx_runtime":
122+
backend_config_type = ONNXRuntimeConfig
123+
elif backend == "tensorrt":
124+
backend_config_type = TensorRTConfig
125+
else:
126+
assert_never(backend)
127+
return backend_config_type
128+
129+
130+
# Can I create this dynamically and automatically?
131+
class BackendConfigs(TypedDict, total=False):
132+
paddle_infer: PaddleInferConfig
133+
openvino: OpenVINOConfig
134+
onnx_runtime: ONNXRuntimeConfig
135+
tensorrt: TensorRTConfig
136+
paddle_tensorrt: PaddleTensorRTConfig
137+
138+
139+
class HPIConfig(BaseModel):
140+
model_config = ConfigDict(populate_by_name=True)
141+
142+
selected_backends: Optional[Dict[DeviceType, Backend]] = None
143+
# For backward compatilibity
144+
backend_configs: Annotated[
145+
Optional[BackendConfigs], Field(validation_alias="backend_config")
146+
] = None
147+
148+
def get_backend_and_config(
149+
self, model_name: str, device_type: DeviceType
150+
) -> Tuple[Backend, BackendConfig]:
151+
# Do we need an extensible selector?
152+
model_info = get_model_info(model_name, device_type)
153+
if model_info:
154+
backend_config_pairs = model_info["backend_config_pairs"]
155+
else:
156+
backend_config_pairs = []
157+
config_dict: Dict[str, Any] = {}
158+
if self.selected_backends and device_type in self.selected_backends:
159+
backend = self.selected_backends[device_type]
160+
for pair in backend_config_pairs:
161+
# Use the first one
162+
if pair[0] == self.selected_backends[device_type]:
163+
config_dict.update(pair[1])
164+
break
165+
else:
166+
if backend_config_pairs:
167+
# Currently we select the first one
168+
backend = backend_config_pairs[0][0]
169+
config_dict.update(backend_config_pairs[0][1])
170+
else:
171+
backend = "paddle_infer"
172+
if self.backend_configs and backend in self.backend_configs:
173+
config_dict.update(
174+
self.backend_configs[backend].model_dump(exclude_unset=True)
175+
)
176+
backend_config_type = get_backend_config_type(backend)
177+
backend_config = backend_config_type.model_validate(config_dict)
178+
return backend, backend_config
179+
180+
# XXX: For backward compatilibity
181+
@field_validator("selected_backends", mode="before")
182+
@classmethod
183+
def _hack_selected_backends(cls, data: Any) -> Any:
184+
if isinstance(data, Mapping):
185+
new_data = dict(data)
186+
for device_type in new_data:
187+
if new_data[device_type] == "paddle_tensorrt":
188+
warnings.warn(
189+
"`paddle_tensorrt` is deprecated. Please use `paddle_infer` instead.",
190+
FutureWarning,
191+
)
192+
new_data[device_type] = "paddle_infer"
193+
return new_data
194+
195+
@field_validator("backend_configs", mode="before")
196+
@classmethod
197+
def _hack_backend_configs(cls, data: Any) -> Any:
198+
if isinstance(data, Mapping):
199+
new_data = dict(data)
200+
if new_data and "paddle_tensorrt" in new_data:
201+
warnings.warn(
202+
"`paddle_tensorrt` is deprecated. Please use `paddle_infer` instead.",
203+
FutureWarning,
204+
)
205+
if "paddle_infer" not in new_data:
206+
new_data["paddle_infer"] = {}
207+
pptrt_cfg = new_data["paddle_tensorrt"]
208+
logging.warning("`paddle_infer.enable_trt` will be set to `True`.")
209+
new_data["paddle_infer"]["enable_trt"] = True
210+
new_data["paddle_infer"]["trt_dynamic_shapes"] = pptrt_cfg[
211+
"dynamic_shapes"
212+
]
213+
if "dynamic_shape_input_data" in pptrt_cfg:
214+
new_data["paddle_infer"]["trt_dynamic_shape_input_data"] = (
215+
pptrt_cfg["dynamic_shape_input_data"]
216+
)
217+
logging.warning("`paddle_tensorrt.enable_log_info` will be ignored.")
218+
return new_data
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve.
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+
import json
16+
import platform
17+
from functools import lru_cache
18+
from typing import Optional
19+
20+
from importlib_resources import files
21+
from paddlex.utils import logging
22+
23+
from paddlex_hpi._utils.typing import DeviceType
24+
25+
_DB_PATH: str = "model_info_collection.json"
26+
27+
28+
@lru_cache(1)
29+
def _get_model_info_collection() -> dict:
30+
with files("paddlex_hpi").joinpath(_DB_PATH).open("r", encoding="utf-8") as f:
31+
_model_info_collection = json.load(f)
32+
return _model_info_collection
33+
34+
35+
def get_model_info(model_name: str, device_type: DeviceType) -> Optional[dict]:
36+
# TODO: Typed model info and nearest referents
37+
model_info_collection = _get_model_info_collection()
38+
uname = platform.uname()
39+
arch = uname.machine.lower()
40+
if arch not in model_info_collection:
41+
return None
42+
logging.debug("Getting model information for arch: %s", arch)
43+
model_info_collection = model_info_collection[arch]
44+
os = uname.system.lower()
45+
if os not in model_info_collection:
46+
return None
47+
logging.debug("Getting model information for OS: %s", os)
48+
model_info_collection = model_info_collection[os]
49+
if device_type == "cpu":
50+
device = "cpu"
51+
elif device_type == "gpu":
52+
device = "gpu_cuda118_cudnn86"
53+
else:
54+
return None
55+
logging.debug("Getting model information for device: %s", device)
56+
model_info_collection = model_info_collection[device]
57+
if model_name not in model_info_collection:
58+
return None
59+
return model_info_collection[model_name]

0 commit comments

Comments
 (0)