orient_express 2.3.0__tar.gz → 2.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {orient_express-2.3.0 → orient_express-2.4.0}/PKG-INFO +27 -18
- {orient_express-2.3.0 → orient_express-2.4.0}/README.md +25 -17
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/predictors/__init__.py +1 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/predictors/predictor.py +1 -1
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/predictors/vector_index.py +55 -32
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/vertex.py +18 -11
- {orient_express-2.3.0 → orient_express-2.4.0}/pyproject.toml +2 -1
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/__init__.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/deployment.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/model_wrapper.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/predictors/classification.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/predictors/feature_extraction.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/predictors/instance_segmentation.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/predictors/multi_label_classification.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/predictors/object_detection.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/predictors/semantic_segmentation.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/sklearn_pipeline.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/utils/colors.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/utils/gs.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/utils/image_processor.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/utils/paths.py +0 -0
- {orient_express-2.3.0 → orient_express-2.4.0}/orient_express/utils/retry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: orient_express
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: A library to simplify model deployment to Vertex AI
|
|
5
5
|
Author: Alexey Zankevich
|
|
6
6
|
Author-email: alex.zankevich@shiftsmart.com
|
|
@@ -21,6 +21,7 @@ Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
|
21
21
|
Requires-Dist: scikit-learn (>=1.5.0,<2.0.0)
|
|
22
22
|
Requires-Dist: torch (>=2.5.0)
|
|
23
23
|
Requires-Dist: torchvision (>=0.20.0)
|
|
24
|
+
Requires-Dist: tqdm (>=4.67.1)
|
|
24
25
|
Requires-Dist: xgboost (>=2.0.0,<3.0.0)
|
|
25
26
|
Description-Content-Type: text/markdown
|
|
26
27
|
|
|
@@ -45,11 +46,13 @@ pip install orient_express
|
|
|
45
46
|
```
|
|
46
47
|
|
|
47
48
|
For local development:
|
|
49
|
+
|
|
48
50
|
```bash
|
|
49
51
|
pip install -e .
|
|
50
52
|
```
|
|
51
53
|
|
|
52
54
|
Or with Poetry:
|
|
55
|
+
|
|
53
56
|
```bash
|
|
54
57
|
poetry install
|
|
55
58
|
```
|
|
@@ -101,8 +104,8 @@ vertex_model.deploy_to_endpoint(
|
|
|
101
104
|
|
|
102
105
|
# remote prediction API depends on the endpoint container deployed with the model
|
|
103
106
|
predictions = vertex_model.remote_predict(
|
|
104
|
-
|
|
105
|
-
|
|
107
|
+
"my-classifier-endpoint",
|
|
108
|
+
[{"image": "https://storage.googleapis.com/ssm-media-uploads/example.jpg"}],
|
|
106
109
|
)
|
|
107
110
|
```
|
|
108
111
|
|
|
@@ -178,19 +181,20 @@ predictions = local_predictor.predict(X_test)
|
|
|
178
181
|
### Platform Support Matrix
|
|
179
182
|
|
|
180
183
|
| Platform | Architecture | ONNX Runtime Package | CUDA Available |
|
|
181
|
-
|
|
182
|
-
| Linux | x86_64 | onnxruntime-gpu
|
|
183
|
-
| Linux | aarch64 | onnxruntime
|
|
184
|
-
| Windows | x64 (AMD64) | onnxruntime-gpu
|
|
185
|
-
| Windows | ARM64 | onnxruntime
|
|
186
|
-
| macOS | x86_64 | onnxruntime
|
|
187
|
-
| macOS | arm64 | onnxruntime
|
|
184
|
+
| -------- | ------------ | -------------------- | -------------- |
|
|
185
|
+
| Linux | x86_64 | onnxruntime-gpu | Yes |
|
|
186
|
+
| Linux | aarch64 | onnxruntime | No |
|
|
187
|
+
| Windows | x64 (AMD64) | onnxruntime-gpu | Yes |
|
|
188
|
+
| Windows | ARM64 | onnxruntime | No |
|
|
189
|
+
| macOS | x86_64 | onnxruntime | No |
|
|
190
|
+
| macOS | arm64 | onnxruntime | No |
|
|
188
191
|
|
|
189
192
|
The appropriate package is installed automatically based on your platform.
|
|
190
193
|
|
|
191
194
|
### Selecting CPU vs CUDA Execution
|
|
192
195
|
|
|
193
196
|
When loading a predictor, use the `device` parameter to specify the execution provider:
|
|
197
|
+
|
|
194
198
|
```python
|
|
195
199
|
from orient_express.predictors import ObjectDetectionPredictor
|
|
196
200
|
|
|
@@ -202,6 +206,7 @@ predictor = ObjectDetectionPredictor("/path/to/model", classes, device="cuda")
|
|
|
202
206
|
```
|
|
203
207
|
|
|
204
208
|
When using a Vertex AI model:
|
|
209
|
+
|
|
205
210
|
```python
|
|
206
211
|
# CPU inference
|
|
207
212
|
predictor = model.get_local_predictor(device="cpu")
|
|
@@ -539,11 +544,8 @@ index = build_vector_index(
|
|
|
539
544
|
num_workers=8, # parallel image loading
|
|
540
545
|
)
|
|
541
546
|
|
|
542
|
-
# Aggregate to one centroid per label
|
|
543
|
-
centroid_index = index.aggregate()
|
|
544
|
-
|
|
545
547
|
# Save and load
|
|
546
|
-
|
|
548
|
+
index.dump("/path/to/artifact_dir")
|
|
547
549
|
|
|
548
550
|
from orient_express.predictors import get_predictor
|
|
549
551
|
loaded_index = get_predictor("/path/to/artifact_dir")
|
|
@@ -559,16 +561,23 @@ batch_results = loaded_index.search_batch(query_matrix, k=5)
|
|
|
559
561
|
|
|
560
562
|
#### Multi-label support
|
|
561
563
|
|
|
562
|
-
Vectors can have
|
|
564
|
+
Vectors can have composite labels (use tuples). This is useful when a single visual cluster maps to multiple things:
|
|
563
565
|
|
|
564
566
|
```python
|
|
565
567
|
index = VectorIndex(
|
|
566
568
|
vectors=feature_matrix,
|
|
567
|
-
labels=[
|
|
569
|
+
labels=[("sku_101", "sku_102"), ("sku_103",)],
|
|
568
570
|
)
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
#### Per-label aggregation
|
|
569
574
|
|
|
570
|
-
|
|
571
|
-
|
|
575
|
+
Vector indices in which labels are not unique can be aggregated so that each label has a single centroid.
|
|
576
|
+
If `per_label=True` and the labels are composite (tuples), then the labels will be unpacked and aggregated separately.
|
|
577
|
+
|
|
578
|
+
```python
|
|
579
|
+
aggregated = index.aggregate(per_label=True) # 3 vectors, one per label element ["sku_101", "sku_102", "sku_103"]
|
|
580
|
+
aggregated = index.aggregate(per_label=False) # 2 vectors, one per composite label [("sku_101", "sku_102"), ("sku_103")]
|
|
572
581
|
```
|
|
573
582
|
|
|
574
583
|
#### Output Structure
|
|
@@ -19,11 +19,13 @@ pip install orient_express
|
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
For local development:
|
|
22
|
+
|
|
22
23
|
```bash
|
|
23
24
|
pip install -e .
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
Or with Poetry:
|
|
28
|
+
|
|
27
29
|
```bash
|
|
28
30
|
poetry install
|
|
29
31
|
```
|
|
@@ -75,8 +77,8 @@ vertex_model.deploy_to_endpoint(
|
|
|
75
77
|
|
|
76
78
|
# remote prediction API depends on the endpoint container deployed with the model
|
|
77
79
|
predictions = vertex_model.remote_predict(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
"my-classifier-endpoint",
|
|
81
|
+
[{"image": "https://storage.googleapis.com/ssm-media-uploads/example.jpg"}],
|
|
80
82
|
)
|
|
81
83
|
```
|
|
82
84
|
|
|
@@ -152,19 +154,20 @@ predictions = local_predictor.predict(X_test)
|
|
|
152
154
|
### Platform Support Matrix
|
|
153
155
|
|
|
154
156
|
| Platform | Architecture | ONNX Runtime Package | CUDA Available |
|
|
155
|
-
|
|
156
|
-
| Linux | x86_64 | onnxruntime-gpu
|
|
157
|
-
| Linux | aarch64 | onnxruntime
|
|
158
|
-
| Windows | x64 (AMD64) | onnxruntime-gpu
|
|
159
|
-
| Windows | ARM64 | onnxruntime
|
|
160
|
-
| macOS | x86_64 | onnxruntime
|
|
161
|
-
| macOS | arm64 | onnxruntime
|
|
157
|
+
| -------- | ------------ | -------------------- | -------------- |
|
|
158
|
+
| Linux | x86_64 | onnxruntime-gpu | Yes |
|
|
159
|
+
| Linux | aarch64 | onnxruntime | No |
|
|
160
|
+
| Windows | x64 (AMD64) | onnxruntime-gpu | Yes |
|
|
161
|
+
| Windows | ARM64 | onnxruntime | No |
|
|
162
|
+
| macOS | x86_64 | onnxruntime | No |
|
|
163
|
+
| macOS | arm64 | onnxruntime | No |
|
|
162
164
|
|
|
163
165
|
The appropriate package is installed automatically based on your platform.
|
|
164
166
|
|
|
165
167
|
### Selecting CPU vs CUDA Execution
|
|
166
168
|
|
|
167
169
|
When loading a predictor, use the `device` parameter to specify the execution provider:
|
|
170
|
+
|
|
168
171
|
```python
|
|
169
172
|
from orient_express.predictors import ObjectDetectionPredictor
|
|
170
173
|
|
|
@@ -176,6 +179,7 @@ predictor = ObjectDetectionPredictor("/path/to/model", classes, device="cuda")
|
|
|
176
179
|
```
|
|
177
180
|
|
|
178
181
|
When using a Vertex AI model:
|
|
182
|
+
|
|
179
183
|
```python
|
|
180
184
|
# CPU inference
|
|
181
185
|
predictor = model.get_local_predictor(device="cpu")
|
|
@@ -513,11 +517,8 @@ index = build_vector_index(
|
|
|
513
517
|
num_workers=8, # parallel image loading
|
|
514
518
|
)
|
|
515
519
|
|
|
516
|
-
# Aggregate to one centroid per label
|
|
517
|
-
centroid_index = index.aggregate()
|
|
518
|
-
|
|
519
520
|
# Save and load
|
|
520
|
-
|
|
521
|
+
index.dump("/path/to/artifact_dir")
|
|
521
522
|
|
|
522
523
|
from orient_express.predictors import get_predictor
|
|
523
524
|
loaded_index = get_predictor("/path/to/artifact_dir")
|
|
@@ -533,16 +534,23 @@ batch_results = loaded_index.search_batch(query_matrix, k=5)
|
|
|
533
534
|
|
|
534
535
|
#### Multi-label support
|
|
535
536
|
|
|
536
|
-
Vectors can have
|
|
537
|
+
Vectors can have composite labels (use tuples). This is useful when a single visual cluster maps to multiple things:
|
|
537
538
|
|
|
538
539
|
```python
|
|
539
540
|
index = VectorIndex(
|
|
540
541
|
vectors=feature_matrix,
|
|
541
|
-
labels=[
|
|
542
|
+
labels=[("sku_101", "sku_102"), ("sku_103",)],
|
|
542
543
|
)
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
#### Per-label aggregation
|
|
543
547
|
|
|
544
|
-
|
|
545
|
-
|
|
548
|
+
Vector indices in which labels are not unique can be aggregated so that each label has a single centroid.
|
|
549
|
+
If `per_label=True` and the labels are composite (tuples), then the labels will be unpacked and aggregated separately.
|
|
550
|
+
|
|
551
|
+
```python
|
|
552
|
+
aggregated = index.aggregate(per_label=True) # 3 vectors, one per label element ["sku_101", "sku_102", "sku_103"]
|
|
553
|
+
aggregated = index.aggregate(per_label=False) # 2 vectors, one per composite label [("sku_101", "sku_102"), ("sku_103")]
|
|
546
554
|
```
|
|
547
555
|
|
|
548
556
|
#### Output Structure
|
|
@@ -99,6 +99,7 @@ def load_vector_index(dir: str, metadata: dict | None = None):
|
|
|
99
99
|
data = np.load(artifact_path, allow_pickle=False)
|
|
100
100
|
vectors = data["vectors"]
|
|
101
101
|
labels = json.loads(str(data["labels_json"]))
|
|
102
|
+
labels = [tuple(label) if isinstance(label, list) else label for label in labels]
|
|
102
103
|
index = VectorIndex(vectors=vectors, labels=labels)
|
|
103
104
|
index.model_path = artifact_path
|
|
104
105
|
return index
|
|
@@ -46,7 +46,7 @@ class ImagePredictor(Predictor):
|
|
|
46
46
|
self.model_path = model_path
|
|
47
47
|
|
|
48
48
|
def get_serving_container_image_uri(self):
|
|
49
|
-
return "us-west1-docker.pkg.dev/shiftsmart-api/orient-express/image-onnx:v2.
|
|
49
|
+
return "us-west1-docker.pkg.dev/shiftsmart-api/orient-express/image-onnx:v2.4.0"
|
|
50
50
|
|
|
51
51
|
def get_serving_container_health_route(self, model_name):
|
|
52
52
|
return f"/v1/models/{model_name}"
|
|
@@ -2,6 +2,7 @@ import os
|
|
|
2
2
|
import json
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
import warnings
|
|
5
|
+
from typing import Any
|
|
5
6
|
|
|
6
7
|
import yaml
|
|
7
8
|
import numpy as np
|
|
@@ -37,7 +38,7 @@ class VectorIndex(Predictor):
|
|
|
37
38
|
def __init__(
|
|
38
39
|
self,
|
|
39
40
|
vectors: np.ndarray,
|
|
40
|
-
labels: list[
|
|
41
|
+
labels: list[int | str | tuple],
|
|
41
42
|
normalize: bool = False,
|
|
42
43
|
):
|
|
43
44
|
if vectors.ndim != 2:
|
|
@@ -53,8 +54,16 @@ class VectorIndex(Predictor):
|
|
|
53
54
|
# Avoid division by zero for zero vectors
|
|
54
55
|
norms = np.where(norms == 0, 1, norms)
|
|
55
56
|
vectors = vectors / norms
|
|
57
|
+
|
|
56
58
|
self.vectors = vectors
|
|
57
59
|
self.labels = labels
|
|
60
|
+
self.label_to_idx = {}
|
|
61
|
+
|
|
62
|
+
for i, label in enumerate(labels):
|
|
63
|
+
if label not in self.label_to_idx:
|
|
64
|
+
self.label_to_idx[label] = []
|
|
65
|
+
self.label_to_idx[label].append(i)
|
|
66
|
+
|
|
58
67
|
self.model_path = None
|
|
59
68
|
|
|
60
69
|
def search(self, query: np.ndarray, k: int = 1) -> list[SearchResult]:
|
|
@@ -86,38 +95,63 @@ class VectorIndex(Predictor):
|
|
|
86
95
|
)
|
|
87
96
|
return all_results
|
|
88
97
|
|
|
89
|
-
def
|
|
98
|
+
def get_by_idx(self, idx: int) -> np.ndarray:
|
|
99
|
+
return self.vectors[idx]
|
|
100
|
+
|
|
101
|
+
def get_by_idxs(self, idxs: list[int]) -> np.ndarray:
|
|
102
|
+
return self.vectors[idxs]
|
|
103
|
+
|
|
104
|
+
def get_by_label(self, label: Any) -> np.ndarray:
|
|
105
|
+
return self.vectors[self.label_to_idx[label]]
|
|
106
|
+
|
|
107
|
+
def get_by_labels(self, labels: list[Any]) -> np.ndarray:
|
|
108
|
+
idxs = []
|
|
109
|
+
for label in labels:
|
|
110
|
+
idxs.extend(self.label_to_idx[label])
|
|
111
|
+
return self.vectors[idxs]
|
|
112
|
+
|
|
113
|
+
def aggregate(self, per_label: bool = False) -> "VectorIndex":
|
|
90
114
|
"""
|
|
91
|
-
Aggregate the vectors into a single centroid per label.
|
|
115
|
+
Aggregate the vectors into a single centroid per unique label group.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
per_label: if True, create one centroid per individual label
|
|
119
|
+
(splitting label groups (tuples) apart). If False (default),
|
|
120
|
+
create one centroid per unique label group.
|
|
92
121
|
"""
|
|
93
|
-
|
|
94
|
-
for i,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
122
|
+
key_to_indices: dict = {}
|
|
123
|
+
for i, label in enumerate(self.labels):
|
|
124
|
+
if per_label and isinstance(label, tuple):
|
|
125
|
+
keys = label
|
|
126
|
+
else:
|
|
127
|
+
keys = [label]
|
|
128
|
+
for key in keys:
|
|
129
|
+
if key not in key_to_indices:
|
|
130
|
+
key_to_indices[key] = []
|
|
131
|
+
key_to_indices[key].append(i)
|
|
132
|
+
|
|
133
|
+
sorted_keys = sorted(key_to_indices.keys(), key=str)
|
|
101
134
|
centroids = np.empty(
|
|
102
|
-
(len(
|
|
135
|
+
(len(sorted_keys), self.vectors.shape[1]), dtype=self.vectors.dtype
|
|
103
136
|
)
|
|
104
137
|
|
|
105
|
-
|
|
106
|
-
|
|
138
|
+
new_labels = []
|
|
139
|
+
for i, key in enumerate(sorted_keys):
|
|
140
|
+
indices = key_to_indices[key]
|
|
107
141
|
centroid = self.vectors[indices].mean(axis=0)
|
|
108
142
|
norm = np.linalg.norm(centroid)
|
|
109
143
|
if norm > 0:
|
|
110
144
|
centroid = centroid / norm
|
|
111
145
|
centroids[i] = centroid
|
|
146
|
+
new_labels.append(key)
|
|
112
147
|
|
|
113
|
-
new_labels = [[label] for label in unique_labels]
|
|
114
148
|
return VectorIndex(vectors=centroids, labels=new_labels)
|
|
115
149
|
|
|
116
150
|
def get_serving_container_image_uri(self) -> str:
|
|
117
151
|
warnings.warn(
|
|
118
152
|
"VectorIndex does not support serving via a container. Returning incompatible image URI."
|
|
119
153
|
)
|
|
120
|
-
return "us-west1-docker.pkg.dev/
|
|
154
|
+
return "us-west1-docker.pkg.dev/shiftsmart-api/orient-express/image-onnx:v2.1.2"
|
|
121
155
|
|
|
122
156
|
def get_serving_container_health_route(self, model_name) -> str:
|
|
123
157
|
return f"/v1/models/{model_name}"
|
|
@@ -151,12 +185,9 @@ class VectorIndex(Predictor):
|
|
|
151
185
|
return self.vectors.shape[0]
|
|
152
186
|
|
|
153
187
|
def __repr__(self) -> str:
|
|
154
|
-
unique_labels = set()
|
|
155
|
-
for group in self.labels:
|
|
156
|
-
unique_labels.update(group)
|
|
157
188
|
return (
|
|
158
189
|
f"VectorIndex({len(self)} vectors, dim={self.dim}, "
|
|
159
|
-
f"{len(
|
|
190
|
+
f"{len(set(self.labels))} unique labels)"
|
|
160
191
|
)
|
|
161
192
|
|
|
162
193
|
|
|
@@ -187,11 +218,10 @@ def _collate_pil(batch):
|
|
|
187
218
|
|
|
188
219
|
def build_vector_index(
|
|
189
220
|
crops: list[Image.Image | str | CropSpec],
|
|
190
|
-
labels: list,
|
|
221
|
+
labels: list[int | str | tuple],
|
|
191
222
|
feature_extractor,
|
|
192
223
|
batch_size: int = 128,
|
|
193
224
|
normalize: bool = True,
|
|
194
|
-
multi_label: bool = False,
|
|
195
225
|
num_workers: int = 0,
|
|
196
226
|
) -> VectorIndex:
|
|
197
227
|
"""Build a VectorIndex by extracting features from crops.
|
|
@@ -200,14 +230,12 @@ def build_vector_index(
|
|
|
200
230
|
crops: images to extract features from. Each element can be a PIL
|
|
201
231
|
Image (used directly), a file path string (loaded as a whole
|
|
202
232
|
image), or a CropSpec (loaded and cropped to the specified bbox).
|
|
203
|
-
labels: one label per crop.
|
|
204
|
-
|
|
205
|
-
of labels.
|
|
233
|
+
labels: one label per crop. labels can be any hashable type, includeing
|
|
234
|
+
iterables like tuples.
|
|
206
235
|
feature_extractor: anything with a .predict(list[Image]) method that
|
|
207
236
|
returns objects with a .feature attribute (e.g. FeatureExtractionPredictor).
|
|
208
237
|
batch_size: number of crops to process at once.
|
|
209
238
|
normalize: whether to L2-normalize the resulting feature vectors.
|
|
210
|
-
multi_label: if True, labels are already lists of labels per crop.
|
|
211
239
|
num_workers: number of DataLoader workers for parallel image loading.
|
|
212
240
|
"""
|
|
213
241
|
if len(crops) != len(labels):
|
|
@@ -215,11 +243,6 @@ def build_vector_index(
|
|
|
215
243
|
f"crops length ({len(crops)}) must match labels length ({len(labels)})"
|
|
216
244
|
)
|
|
217
245
|
|
|
218
|
-
if multi_label:
|
|
219
|
-
index_labels = [list(group) for group in labels]
|
|
220
|
-
else:
|
|
221
|
-
index_labels = [[label] for label in labels]
|
|
222
|
-
|
|
223
246
|
loader = DataLoader(
|
|
224
247
|
_CropDataset(crops),
|
|
225
248
|
batch_size=batch_size,
|
|
@@ -233,4 +256,4 @@ def build_vector_index(
|
|
|
233
256
|
all_features.extend([r.feature for r in results])
|
|
234
257
|
|
|
235
258
|
vectors = np.vstack(all_features)
|
|
236
|
-
return VectorIndex(vectors=vectors, labels=
|
|
259
|
+
return VectorIndex(vectors=vectors, labels=labels, normalize=normalize)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import tempfile
|
|
3
|
+
import warnings
|
|
3
4
|
|
|
4
5
|
import yaml
|
|
5
6
|
import joblib
|
|
@@ -287,7 +288,6 @@ def get_vertex_model(
|
|
|
287
288
|
):
|
|
288
289
|
vertex_init(project_name, region)
|
|
289
290
|
models = aiplatform.Model.list(filter=f"display_name={model_name}")
|
|
290
|
-
|
|
291
291
|
if not models:
|
|
292
292
|
if raise_exception:
|
|
293
293
|
raise Exception(
|
|
@@ -295,19 +295,26 @@ def get_vertex_model(
|
|
|
295
295
|
)
|
|
296
296
|
else:
|
|
297
297
|
return None
|
|
298
|
+
if len(models) > 1:
|
|
299
|
+
warnings.warn(
|
|
300
|
+
f"Multiple models found with name '{model_name}'. Using the latest one."
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
latest_model = sorted(models, key=lambda x: x.update_time, reverse=True)[0]
|
|
304
|
+
resource_name = latest_model.resource_name
|
|
298
305
|
|
|
299
306
|
if version is None:
|
|
300
|
-
latest_model = sorted(models, key=lambda x: x.update_time, reverse=True)[0]
|
|
301
307
|
return VertexModel(
|
|
302
308
|
latest_model, model_name, project_name, region, int(latest_model.version_id)
|
|
303
309
|
)
|
|
304
310
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
311
|
+
if version is not None:
|
|
312
|
+
try:
|
|
313
|
+
model = aiplatform.Model(model_name=resource_name, version=str(version))
|
|
314
|
+
except Exception:
|
|
315
|
+
if raise_exception:
|
|
316
|
+
raise Exception(
|
|
317
|
+
f"Failed to fetch model '{model_name}' with version '{version}' in registry for project '{project_name}' region '{region}'"
|
|
318
|
+
)
|
|
319
|
+
return None
|
|
320
|
+
return VertexModel(model, model_name, project_name, region, version)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "orient_express"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.4.0"
|
|
4
4
|
description = "A library to simplify model deployment to Vertex AI"
|
|
5
5
|
authors = ["Alexey Zankevich <alex.zankevich@shiftsmart.com>", "Ian Myers <ian.myers@shiftsmart.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -18,6 +18,7 @@ Pillow = ">=10.0.1"
|
|
|
18
18
|
gcsfs = "^2024.10.0"
|
|
19
19
|
scikit-learn = "^1.5.0"
|
|
20
20
|
xgboost = "^2.0.0"
|
|
21
|
+
tqdm = ">=4.67.1"
|
|
21
22
|
onnxruntime-gpu = {version = "^1.20", markers = "sys_platform == 'linux' and platform_machine == 'x86_64' or sys_platform == 'win32' and platform_machine == 'AMD64'"}
|
|
22
23
|
onnxruntime = {version = "^1.20", markers = "sys_platform == 'darwin' or platform_machine == 'aarch64' or platform_machine == 'arm64' or sys_platform == 'win32' and platform_machine == 'ARM64'"}
|
|
23
24
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{orient_express-2.3.0 → orient_express-2.4.0}/orient_express/predictors/feature_extraction.py
RENAMED
|
File without changes
|
{orient_express-2.3.0 → orient_express-2.4.0}/orient_express/predictors/instance_segmentation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{orient_express-2.3.0 → orient_express-2.4.0}/orient_express/predictors/semantic_segmentation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|