orient_express 0.3.2__tar.gz → 0.3.4__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-0.3.2 → orient_express-0.3.4}/PKG-INFO +2 -1
- {orient_express-0.3.2 → orient_express-0.3.4}/orient_express/model_wrapper.py +40 -25
- orient_express-0.3.4/orient_express/sklearn_pipeline.py +81 -0
- {orient_express-0.3.2 → orient_express-0.3.4}/pyproject.toml +2 -1
- {orient_express-0.3.2 → orient_express-0.3.4}/README.md +0 -0
- {orient_express-0.3.2 → orient_express-0.3.4}/orient_express/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: orient_express
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
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
|
|
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Requires-Dist: google-cloud-aiplatform
|
|
14
14
|
Requires-Dist: google-cloud-storage
|
|
15
|
+
Requires-Dist: jupyter (>=1.1.1,<2.0.0)
|
|
15
16
|
Requires-Dist: pandas
|
|
16
17
|
Description-Content-Type: text/markdown
|
|
17
18
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
2
4
|
import joblib
|
|
3
5
|
import logging
|
|
4
6
|
import pandas as pd
|
|
@@ -9,20 +11,20 @@ from google.cloud import aiplatform
|
|
|
9
11
|
class ModelExpress:
|
|
10
12
|
def __init__(
|
|
11
13
|
self,
|
|
12
|
-
model_name,
|
|
13
|
-
project_name,
|
|
14
|
-
bucket_name,
|
|
15
|
-
model_version=None,
|
|
16
|
-
model=None,
|
|
17
|
-
region="us-central1",
|
|
18
|
-
serialized_model_path="model.joblib",
|
|
19
|
-
serving_container_image_uri="us-west1-docker.pkg.dev/shiftsmart-api/orient-express/xgboost-scikit-learn:latest",
|
|
20
|
-
serving_container_predict_route="/v1/models/orient-express-model:predict",
|
|
21
|
-
serving_container_health_route="/v1/models/orient-express-model",
|
|
22
|
-
endpoint_name=None,
|
|
14
|
+
model_name: str,
|
|
15
|
+
project_name: str,
|
|
16
|
+
bucket_name: str,
|
|
17
|
+
model_version: Optional[int] = None,
|
|
18
|
+
model: object = None,
|
|
19
|
+
region: str = "us-central1",
|
|
20
|
+
serialized_model_path: str = "model.joblib",
|
|
21
|
+
serving_container_image_uri: str = "us-west1-docker.pkg.dev/shiftsmart-api/orient-express/xgboost-scikit-learn:latest",
|
|
22
|
+
serving_container_predict_route: str = "/v1/models/orient-express-model:predict",
|
|
23
|
+
serving_container_health_route: str = "/v1/models/orient-express-model",
|
|
24
|
+
endpoint_name: Optional[str] = None,
|
|
23
25
|
machine_type="n1-standard-4",
|
|
24
|
-
min_replica_count=1,
|
|
25
|
-
max_replica_count=1,
|
|
26
|
+
min_replica_count: int = 1,
|
|
27
|
+
max_replica_count: int = 1,
|
|
26
28
|
):
|
|
27
29
|
self.model = model
|
|
28
30
|
self.model_name = model_name
|
|
@@ -44,15 +46,22 @@ class ModelExpress:
|
|
|
44
46
|
else:
|
|
45
47
|
self.endpoint_name = endpoint_name
|
|
46
48
|
|
|
49
|
+
self._vertex_initialized = False
|
|
50
|
+
|
|
47
51
|
def colab_auth(self):
|
|
48
52
|
from google.colab import auth
|
|
49
53
|
|
|
50
54
|
auth.authenticate_user()
|
|
51
55
|
|
|
52
56
|
def _vertex_init(self):
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
if not self._vertex_initialized:
|
|
58
|
+
aiplatform.init(project=self.project_name, location=self.region)
|
|
59
|
+
self._vertex_initialized = True
|
|
60
|
+
|
|
61
|
+
def get_latest_vertex_model(self, model_name: str):
|
|
62
|
+
"""If there are a few models with the same name, load the most recent one.
|
|
63
|
+
It's highly recommended to keep only 1 model with the same name to avoid the confusion
|
|
64
|
+
"""
|
|
56
65
|
self._vertex_init()
|
|
57
66
|
|
|
58
67
|
# Search for models with the specified display name
|
|
@@ -89,7 +98,7 @@ class ModelExpress:
|
|
|
89
98
|
|
|
90
99
|
return self.create_model_version(new_version, last_model)
|
|
91
100
|
|
|
92
|
-
def get_artifacts_path(self, version, file_name=None):
|
|
101
|
+
def get_artifacts_path(self, version: int, file_name: str = None):
|
|
93
102
|
dir_name = f"models/{self.model_name}/{version}"
|
|
94
103
|
if file_name:
|
|
95
104
|
return f"{dir_name}/{file_name}"
|
|
@@ -152,7 +161,9 @@ class ModelExpress:
|
|
|
152
161
|
traffic_percentage=100,
|
|
153
162
|
)
|
|
154
163
|
|
|
155
|
-
def remote_predict(self, input_df):
|
|
164
|
+
def remote_predict(self, input_df: pd.DataFrame):
|
|
165
|
+
self._vertex_init()
|
|
166
|
+
|
|
156
167
|
if not self.endpoint:
|
|
157
168
|
endpoint = self.get_endpoint()
|
|
158
169
|
if not endpoint:
|
|
@@ -165,27 +176,31 @@ class ModelExpress:
|
|
|
165
176
|
predictions = self.endpoint.predict(instances=instances)
|
|
166
177
|
return predictions.predictions
|
|
167
178
|
|
|
168
|
-
def local_predict(self, input_df):
|
|
179
|
+
def local_predict(self, input_df: pd.DataFrame):
|
|
180
|
+
self._vertex_init()
|
|
181
|
+
|
|
169
182
|
if not self.model:
|
|
170
183
|
self.load_model_from_registry()
|
|
171
184
|
|
|
172
185
|
return self.model.predict(input_df)
|
|
173
186
|
|
|
174
|
-
def local_predict_proba(self, input_df):
|
|
187
|
+
def local_predict_proba(self, input_df: pd.DataFrame):
|
|
175
188
|
if not self.model:
|
|
176
189
|
self.load_model_from_registry()
|
|
177
190
|
|
|
178
191
|
return self.model.predict_proba(input_df)
|
|
179
192
|
|
|
180
193
|
def load_model_from_registry(self):
|
|
194
|
+
self._vertex_init()
|
|
195
|
+
|
|
196
|
+
vertex_model = self.get_latest_vertex_model(self.model_name)
|
|
197
|
+
|
|
181
198
|
if self.model_version:
|
|
199
|
+
# reload the model using a specific model version
|
|
182
200
|
vertex_model = aiplatform.Model(
|
|
183
|
-
model_name=
|
|
201
|
+
model_name=vertex_model.resource_name, version=str(self.model_version)
|
|
184
202
|
)
|
|
185
203
|
|
|
186
|
-
else:
|
|
187
|
-
vertex_model = self.get_latest_vertex_model(self.model_name)
|
|
188
|
-
|
|
189
204
|
if not vertex_model:
|
|
190
205
|
raise Exception(f"Model '{self.model_name}' not found in the registry.")
|
|
191
206
|
|
|
@@ -194,7 +209,7 @@ class ModelExpress:
|
|
|
194
209
|
|
|
195
210
|
self.model = joblib.load(self.serialized_model_path)
|
|
196
211
|
|
|
197
|
-
def download_artifacts(self, artifact_uri):
|
|
212
|
+
def download_artifacts(self, artifact_uri: str):
|
|
198
213
|
storage_client = storage.Client()
|
|
199
214
|
bucket_name, artifact_path = artifact_uri.replace("gs://", "").split("/", 1)
|
|
200
215
|
bucket = storage_client.bucket(bucket_name)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from sklearn.base import BaseEstimator, TransformerMixin
|
|
2
|
+
from sklearn.preprocessing import LabelEncoder
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LabelEncoderTransformer(BaseEstimator, TransformerMixin):
|
|
6
|
+
"""
|
|
7
|
+
A wrapper class that integrates a model and a label encoder to handle
|
|
8
|
+
encoded predictions and their corresponding probabilities.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
model (object): A machine learning model with `fit`, `predict`, and `predict_proba` methods.
|
|
12
|
+
label_encoder (object): A label encoder that maps encoded class labels to their original string labels.
|
|
13
|
+
|
|
14
|
+
Methods:
|
|
15
|
+
fit(X, y):
|
|
16
|
+
Trains the model on the provided features and labels.
|
|
17
|
+
predict(X):
|
|
18
|
+
Predicts the classes for the given features and returns the original string labels.
|
|
19
|
+
predict_proba(X):
|
|
20
|
+
Returns the probabilities for each class for the given features, along with their original class labels.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, model: BaseEstimator, label_encoder: LabelEncoder):
|
|
24
|
+
"""
|
|
25
|
+
Initializes the LabelEncoderTransformer.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
model (BaseEstimator): A machine learning model.
|
|
29
|
+
label_encoder (LabelEncoder): A label encoder with `fit` and `inverse_transform` methods.
|
|
30
|
+
"""
|
|
31
|
+
self.model = model
|
|
32
|
+
self.label_encoder = label_encoder
|
|
33
|
+
|
|
34
|
+
def fit(self, X, y):
|
|
35
|
+
"""
|
|
36
|
+
Fits the model to the training data.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
X (array-like): Feature matrix.
|
|
40
|
+
y (array-like): Target labels.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
self: Fitted LabelEncoderTransformer instance.
|
|
44
|
+
"""
|
|
45
|
+
self.model.fit(X, y)
|
|
46
|
+
return self
|
|
47
|
+
|
|
48
|
+
def predict(self, X):
|
|
49
|
+
"""
|
|
50
|
+
Predicts the target labels and returns the original string labels.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
X (array-like): Feature matrix for predictions.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
array-like: Predicted class labels in their original form.
|
|
57
|
+
"""
|
|
58
|
+
encoded_predictions = self.model.predict(X)
|
|
59
|
+
return self.label_encoder.inverse_transform(encoded_predictions)
|
|
60
|
+
|
|
61
|
+
def predict_proba(self, X):
|
|
62
|
+
"""
|
|
63
|
+
Returns class probabilities along with their original labels.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
X (array-like): Feature matrix for predictions.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
list: A list of lists, where each inner list contains `[class_name, probability]` pairs.
|
|
70
|
+
"""
|
|
71
|
+
# Get raw probabilities
|
|
72
|
+
probabilities = self.model.predict_proba(X)
|
|
73
|
+
|
|
74
|
+
# Get all class labels
|
|
75
|
+
class_names = self.label_encoder.classes_
|
|
76
|
+
# Combine class names with probabilities
|
|
77
|
+
combined_output = [
|
|
78
|
+
[[class_name, prob] for class_name, prob in zip(class_names, sample_probs)]
|
|
79
|
+
for sample_probs in probabilities
|
|
80
|
+
]
|
|
81
|
+
return combined_output
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "orient_express"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.4"
|
|
4
4
|
description = "A library to simplify model deployment to Vertex AI"
|
|
5
5
|
authors = ["Alexey Zankevich <alex.zankevich@shiftsmart.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -10,6 +10,7 @@ python = ">=3.9,<3.13"
|
|
|
10
10
|
google-cloud-aiplatform = "*"
|
|
11
11
|
google-cloud-storage = "*"
|
|
12
12
|
pandas = "*"
|
|
13
|
+
jupyter = "^1.1.1"
|
|
13
14
|
|
|
14
15
|
[tool.poetry.dev-dependencies]
|
|
15
16
|
black = "24.10.0"
|
|
File without changes
|
|
File without changes
|