mlrun 1.7.0rc28__py3-none-any.whl → 1.7.0rc55__py3-none-any.whl
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.
Potentially problematic release.
This version of mlrun might be problematic. Click here for more details.
- mlrun/__main__.py +4 -2
- mlrun/alerts/alert.py +75 -8
- mlrun/artifacts/base.py +1 -0
- mlrun/artifacts/manager.py +9 -2
- mlrun/common/constants.py +4 -1
- mlrun/common/db/sql_session.py +3 -2
- mlrun/common/formatters/__init__.py +1 -0
- mlrun/common/formatters/artifact.py +1 -0
- mlrun/{model_monitoring/application.py → common/formatters/feature_set.py} +20 -6
- mlrun/common/formatters/run.py +3 -0
- mlrun/common/helpers.py +0 -1
- mlrun/common/schemas/__init__.py +3 -1
- mlrun/common/schemas/alert.py +15 -12
- mlrun/common/schemas/api_gateway.py +6 -6
- mlrun/common/schemas/auth.py +5 -0
- mlrun/common/schemas/client_spec.py +0 -1
- mlrun/common/schemas/common.py +7 -4
- mlrun/common/schemas/frontend_spec.py +7 -0
- mlrun/common/schemas/function.py +7 -0
- mlrun/common/schemas/model_monitoring/__init__.py +4 -3
- mlrun/common/schemas/model_monitoring/constants.py +41 -26
- mlrun/common/schemas/model_monitoring/model_endpoints.py +23 -47
- mlrun/common/schemas/notification.py +69 -12
- mlrun/common/schemas/project.py +45 -12
- mlrun/common/schemas/workflow.py +10 -2
- mlrun/common/types.py +1 -0
- mlrun/config.py +91 -35
- mlrun/data_types/data_types.py +6 -1
- mlrun/data_types/spark.py +2 -2
- mlrun/data_types/to_pandas.py +57 -25
- mlrun/datastore/__init__.py +1 -0
- mlrun/datastore/alibaba_oss.py +3 -2
- mlrun/datastore/azure_blob.py +125 -37
- mlrun/datastore/base.py +42 -21
- mlrun/datastore/datastore.py +4 -2
- mlrun/datastore/datastore_profile.py +1 -1
- mlrun/datastore/dbfs_store.py +3 -7
- mlrun/datastore/filestore.py +1 -3
- mlrun/datastore/google_cloud_storage.py +85 -29
- mlrun/datastore/inmem.py +4 -1
- mlrun/datastore/redis.py +1 -0
- mlrun/datastore/s3.py +25 -12
- mlrun/datastore/sources.py +76 -4
- mlrun/datastore/spark_utils.py +30 -0
- mlrun/datastore/storeytargets.py +151 -0
- mlrun/datastore/targets.py +102 -131
- mlrun/datastore/v3io.py +1 -0
- mlrun/db/base.py +15 -6
- mlrun/db/httpdb.py +57 -28
- mlrun/db/nopdb.py +29 -5
- mlrun/errors.py +20 -3
- mlrun/execution.py +46 -5
- mlrun/feature_store/api.py +25 -1
- mlrun/feature_store/common.py +6 -11
- mlrun/feature_store/feature_vector.py +3 -1
- mlrun/feature_store/retrieval/job.py +4 -1
- mlrun/feature_store/retrieval/spark_merger.py +10 -39
- mlrun/feature_store/steps.py +8 -0
- mlrun/frameworks/_common/plan.py +3 -3
- mlrun/frameworks/_ml_common/plan.py +1 -1
- mlrun/frameworks/parallel_coordinates.py +2 -3
- mlrun/frameworks/sklearn/mlrun_interface.py +13 -3
- mlrun/k8s_utils.py +48 -2
- mlrun/launcher/client.py +6 -6
- mlrun/launcher/local.py +2 -2
- mlrun/model.py +215 -34
- mlrun/model_monitoring/api.py +38 -24
- mlrun/model_monitoring/applications/__init__.py +1 -2
- mlrun/model_monitoring/applications/_application_steps.py +60 -29
- mlrun/model_monitoring/applications/base.py +2 -174
- mlrun/model_monitoring/applications/context.py +197 -70
- mlrun/model_monitoring/applications/evidently_base.py +11 -85
- mlrun/model_monitoring/applications/histogram_data_drift.py +21 -16
- mlrun/model_monitoring/applications/results.py +4 -4
- mlrun/model_monitoring/controller.py +110 -282
- mlrun/model_monitoring/db/stores/__init__.py +8 -3
- mlrun/model_monitoring/db/stores/base/store.py +3 -0
- mlrun/model_monitoring/db/stores/sqldb/models/base.py +9 -7
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +18 -3
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +43 -23
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +48 -35
- mlrun/model_monitoring/db/tsdb/__init__.py +7 -2
- mlrun/model_monitoring/db/tsdb/base.py +147 -15
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +94 -55
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +0 -3
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +144 -38
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +44 -3
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +246 -57
- mlrun/model_monitoring/helpers.py +70 -50
- mlrun/model_monitoring/stream_processing.py +96 -195
- mlrun/model_monitoring/writer.py +13 -5
- mlrun/package/packagers/default_packager.py +2 -2
- mlrun/projects/operations.py +16 -8
- mlrun/projects/pipelines.py +126 -115
- mlrun/projects/project.py +286 -129
- mlrun/render.py +3 -3
- mlrun/run.py +38 -19
- mlrun/runtimes/__init__.py +19 -8
- mlrun/runtimes/base.py +4 -1
- mlrun/runtimes/daskjob.py +1 -1
- mlrun/runtimes/funcdoc.py +1 -1
- mlrun/runtimes/kubejob.py +6 -6
- mlrun/runtimes/local.py +12 -5
- mlrun/runtimes/nuclio/api_gateway.py +68 -8
- mlrun/runtimes/nuclio/application/application.py +307 -70
- mlrun/runtimes/nuclio/function.py +63 -14
- mlrun/runtimes/nuclio/serving.py +10 -10
- mlrun/runtimes/pod.py +25 -19
- mlrun/runtimes/remotesparkjob.py +2 -5
- mlrun/runtimes/sparkjob/spark3job.py +16 -17
- mlrun/runtimes/utils.py +34 -0
- mlrun/serving/routers.py +2 -5
- mlrun/serving/server.py +37 -19
- mlrun/serving/states.py +30 -3
- mlrun/serving/v2_serving.py +44 -35
- mlrun/track/trackers/mlflow_tracker.py +5 -0
- mlrun/utils/async_http.py +1 -1
- mlrun/utils/db.py +18 -0
- mlrun/utils/helpers.py +150 -36
- mlrun/utils/http.py +1 -1
- mlrun/utils/notifications/notification/__init__.py +0 -1
- mlrun/utils/notifications/notification/webhook.py +8 -1
- mlrun/utils/notifications/notification_pusher.py +1 -1
- mlrun/utils/v3io_clients.py +2 -2
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/METADATA +153 -66
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/RECORD +131 -134
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/WHEEL +1 -1
- mlrun/feature_store/retrieval/conversion.py +0 -271
- mlrun/model_monitoring/controller_handler.py +0 -37
- mlrun/model_monitoring/evidently_application.py +0 -20
- mlrun/model_monitoring/prometheus.py +0 -216
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/top_level.txt +0 -0
mlrun/model_monitoring/api.py
CHANGED
|
@@ -24,7 +24,6 @@ import mlrun.artifacts
|
|
|
24
24
|
import mlrun.common.helpers
|
|
25
25
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
26
26
|
import mlrun.feature_store
|
|
27
|
-
import mlrun.model_monitoring.application
|
|
28
27
|
import mlrun.model_monitoring.applications as mm_app
|
|
29
28
|
import mlrun.serving
|
|
30
29
|
from mlrun.data_types.infer import InferOptions, get_df_stats
|
|
@@ -147,8 +146,7 @@ def record_results(
|
|
|
147
146
|
on the provided `endpoint_id`.
|
|
148
147
|
:param function_name: If a new model endpoint is created, use this function name for generating the
|
|
149
148
|
function URI.
|
|
150
|
-
:param context: MLRun context. Note that the context is required
|
|
151
|
-
following the batch drift job.
|
|
149
|
+
:param context: MLRun context. Note that the context is required generating the model endpoint.
|
|
152
150
|
:param infer_results_df: DataFrame that will be stored under the model endpoint parquet target. Will be
|
|
153
151
|
used for doing the drift analysis. Please make sure that the dataframe includes
|
|
154
152
|
both feature names and label columns.
|
|
@@ -252,14 +250,31 @@ def _model_endpoint_validations(
|
|
|
252
250
|
In case of discrepancy between the provided `sample_set_statistics` and the
|
|
253
251
|
`model_endpoints.spec.feature_stats`, a warning will be presented to the user.
|
|
254
252
|
"""
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
253
|
+
|
|
254
|
+
# Model Path
|
|
255
|
+
if model_path:
|
|
256
|
+
# Generate the parsed model uri that is based on hash, key, iter, and tree
|
|
257
|
+
model_obj = mlrun.datastore.get_store_resource(model_path)
|
|
258
|
+
|
|
259
|
+
model_artifact_uri = mlrun.utils.helpers.generate_artifact_uri(
|
|
260
|
+
project=model_endpoint.metadata.project,
|
|
261
|
+
key=model_obj.key,
|
|
262
|
+
iter=model_obj.iter,
|
|
263
|
+
tree=model_obj.tree,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# Enrich the uri schema with the store prefix
|
|
267
|
+
model_artifact_uri = mlrun.datastore.get_store_uri(
|
|
268
|
+
kind=mlrun.utils.helpers.StorePrefix.Model, uri=model_artifact_uri
|
|
261
269
|
)
|
|
262
270
|
|
|
271
|
+
if model_endpoint.spec.model_uri != model_artifact_uri:
|
|
272
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
273
|
+
f"provided model store path {model_path} does not match "
|
|
274
|
+
f"the path that is stored under the existing model "
|
|
275
|
+
f"endpoint record: {model_endpoint.spec.model_uri}"
|
|
276
|
+
)
|
|
277
|
+
|
|
263
278
|
# Feature stats
|
|
264
279
|
if (
|
|
265
280
|
sample_set_statistics
|
|
@@ -545,8 +560,7 @@ def _create_model_monitoring_function_base(
|
|
|
545
560
|
func: typing.Union[str, None] = None,
|
|
546
561
|
application_class: typing.Union[
|
|
547
562
|
str,
|
|
548
|
-
|
|
549
|
-
mm_app.ModelMonitoringApplicationBaseV2,
|
|
563
|
+
mm_app.ModelMonitoringApplicationBase,
|
|
550
564
|
None,
|
|
551
565
|
] = None,
|
|
552
566
|
name: typing.Optional[str] = None,
|
|
@@ -560,19 +574,10 @@ def _create_model_monitoring_function_base(
|
|
|
560
574
|
Note: this is an internal API only.
|
|
561
575
|
This function does not set the labels or mounts v3io.
|
|
562
576
|
"""
|
|
563
|
-
if
|
|
564
|
-
application_class,
|
|
565
|
-
mlrun.model_monitoring.application.ModelMonitoringApplicationBase,
|
|
566
|
-
):
|
|
567
|
-
warnings.warn(
|
|
568
|
-
"The `ModelMonitoringApplicationBase` class is deprecated from version 1.7.0, "
|
|
569
|
-
"please use `ModelMonitoringApplicationBaseV2`. It will be removed in 1.9.0.",
|
|
570
|
-
FutureWarning,
|
|
571
|
-
)
|
|
572
|
-
if name in mm_constants.MonitoringFunctionNames.list():
|
|
577
|
+
if name in mm_constants._RESERVED_FUNCTION_NAMES:
|
|
573
578
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
574
|
-
|
|
575
|
-
f"{mm_constants.
|
|
579
|
+
"An application cannot have the following names: "
|
|
580
|
+
f"{mm_constants._RESERVED_FUNCTION_NAMES}"
|
|
576
581
|
)
|
|
577
582
|
if func is None:
|
|
578
583
|
func = ""
|
|
@@ -599,11 +604,20 @@ def _create_model_monitoring_function_base(
|
|
|
599
604
|
app_step = prepare_step.to(class_name=application_class, **application_kwargs)
|
|
600
605
|
else:
|
|
601
606
|
app_step = prepare_step.to(class_name=application_class)
|
|
607
|
+
|
|
602
608
|
app_step.__class__ = mlrun.serving.MonitoringApplicationStep
|
|
609
|
+
|
|
610
|
+
app_step.error_handler(
|
|
611
|
+
name="ApplicationErrorHandler",
|
|
612
|
+
class_name="mlrun.model_monitoring.applications._application_steps._ApplicationErrorHandler",
|
|
613
|
+
full_event=True,
|
|
614
|
+
project=project,
|
|
615
|
+
)
|
|
616
|
+
|
|
603
617
|
app_step.to(
|
|
604
618
|
class_name="mlrun.model_monitoring.applications._application_steps._PushToMonitoringWriter",
|
|
605
619
|
name="PushToMonitoringWriter",
|
|
606
620
|
project=project,
|
|
607
621
|
writer_application_name=mm_constants.MonitoringFunctionNames.WRITER,
|
|
608
|
-
)
|
|
622
|
+
)
|
|
609
623
|
return func_obj
|
|
@@ -13,12 +13,11 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
#
|
|
15
15
|
|
|
16
|
-
from .base import ModelMonitoringApplicationBase
|
|
16
|
+
from .base import ModelMonitoringApplicationBase
|
|
17
17
|
from .context import MonitoringApplicationContext
|
|
18
18
|
from .evidently_base import (
|
|
19
19
|
_HAS_EVIDENTLY,
|
|
20
20
|
SUPPORTED_EVIDENTLY_VERSION,
|
|
21
21
|
EvidentlyModelMonitoringApplicationBase,
|
|
22
|
-
EvidentlyModelMonitoringApplicationBaseV2,
|
|
23
22
|
)
|
|
24
23
|
from .results import ModelMonitoringApplicationMetric, ModelMonitoringApplicationResult
|
|
@@ -11,16 +11,17 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
+
|
|
14
15
|
import json
|
|
15
|
-
import
|
|
16
|
-
from typing import Optional
|
|
16
|
+
import traceback
|
|
17
|
+
from typing import Any, Optional, Union
|
|
17
18
|
|
|
18
|
-
import mlrun.common.
|
|
19
|
-
import mlrun.common.model_monitoring.helpers
|
|
19
|
+
import mlrun.common.schemas.alert as alert_objects
|
|
20
20
|
import mlrun.common.schemas.model_monitoring.constants as mm_constant
|
|
21
21
|
import mlrun.datastore
|
|
22
|
-
import mlrun.
|
|
22
|
+
import mlrun.model_monitoring
|
|
23
23
|
from mlrun.model_monitoring.helpers import get_stream_path
|
|
24
|
+
from mlrun.serving import GraphContext
|
|
24
25
|
from mlrun.serving.utils import StepToDict
|
|
25
26
|
from mlrun.utils import logger
|
|
26
27
|
|
|
@@ -33,8 +34,8 @@ class _PushToMonitoringWriter(StepToDict):
|
|
|
33
34
|
|
|
34
35
|
def __init__(
|
|
35
36
|
self,
|
|
36
|
-
project:
|
|
37
|
-
writer_application_name:
|
|
37
|
+
project: str,
|
|
38
|
+
writer_application_name: str,
|
|
38
39
|
stream_uri: Optional[str] = None,
|
|
39
40
|
name: Optional[str] = None,
|
|
40
41
|
):
|
|
@@ -59,7 +60,7 @@ class _PushToMonitoringWriter(StepToDict):
|
|
|
59
60
|
self,
|
|
60
61
|
event: tuple[
|
|
61
62
|
list[
|
|
62
|
-
|
|
63
|
+
Union[
|
|
63
64
|
ModelMonitoringApplicationResult, ModelMonitoringApplicationMetric
|
|
64
65
|
]
|
|
65
66
|
],
|
|
@@ -108,6 +109,7 @@ class _PushToMonitoringWriter(StepToDict):
|
|
|
108
109
|
f"Pushing data = {writer_event} \n to stream = {self.stream_uri}"
|
|
109
110
|
)
|
|
110
111
|
self.output_stream.push([writer_event])
|
|
112
|
+
logger.info(f"Pushed data to {self.stream_uri} successfully")
|
|
111
113
|
|
|
112
114
|
def _lazy_init(self):
|
|
113
115
|
if self.output_stream is None:
|
|
@@ -117,41 +119,70 @@ class _PushToMonitoringWriter(StepToDict):
|
|
|
117
119
|
|
|
118
120
|
|
|
119
121
|
class _PrepareMonitoringEvent(StepToDict):
|
|
120
|
-
def __init__(self, application_name: str):
|
|
122
|
+
def __init__(self, context: GraphContext, application_name: str) -> None:
|
|
121
123
|
"""
|
|
122
124
|
Class for preparing the application event for the application step.
|
|
123
125
|
|
|
124
126
|
:param application_name: Application name.
|
|
125
127
|
"""
|
|
128
|
+
self.graph_context = context
|
|
129
|
+
self.application_name = application_name
|
|
130
|
+
self.model_endpoints: dict[str, mlrun.model_monitoring.ModelEndpoint] = {}
|
|
126
131
|
|
|
127
|
-
|
|
128
|
-
self.model_endpoints = {}
|
|
129
|
-
|
|
130
|
-
def do(self, event: dict[str, dict]) -> MonitoringApplicationContext:
|
|
132
|
+
def do(self, event: dict[str, Any]) -> MonitoringApplicationContext:
|
|
131
133
|
"""
|
|
132
134
|
Prepare the application event for the application step.
|
|
133
135
|
|
|
134
136
|
:param event: Application event.
|
|
135
|
-
:return: Application
|
|
137
|
+
:return: Application context.
|
|
136
138
|
"""
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
application_context = MonitoringApplicationContext().from_dict(event)
|
|
139
|
+
application_context = MonitoringApplicationContext(
|
|
140
|
+
graph_context=self.graph_context,
|
|
141
|
+
application_name=self.application_name,
|
|
142
|
+
event=event,
|
|
143
|
+
model_endpoint_dict=self.model_endpoints,
|
|
144
|
+
)
|
|
145
|
+
|
|
145
146
|
self.model_endpoints.setdefault(
|
|
146
147
|
application_context.endpoint_id, application_context.model_endpoint
|
|
147
148
|
)
|
|
149
|
+
|
|
148
150
|
return application_context
|
|
149
151
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
|
|
153
|
+
class _ApplicationErrorHandler(StepToDict):
|
|
154
|
+
def __init__(self, project: str, name: Optional[str] = None):
|
|
155
|
+
self.project = project
|
|
156
|
+
self.name = name or "ApplicationErrorHandler"
|
|
157
|
+
|
|
158
|
+
def do(self, event):
|
|
159
|
+
"""
|
|
160
|
+
Handle model monitoring application error. This step will generate an event, describing the error.
|
|
161
|
+
|
|
162
|
+
:param event: Application event.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
exception_with_trace = "".join(
|
|
166
|
+
traceback.format_exception(None, event.error, event.error.__traceback__)
|
|
167
|
+
)
|
|
168
|
+
logger.error(f"Error in application step: {exception_with_trace}")
|
|
169
|
+
|
|
170
|
+
event_data = alert_objects.Event(
|
|
171
|
+
kind=alert_objects.EventKind.MM_APP_FAILED,
|
|
172
|
+
entity=alert_objects.EventEntities(
|
|
173
|
+
kind=alert_objects.EventEntityKind.MODEL_MONITORING_APPLICATION,
|
|
174
|
+
project=self.project,
|
|
175
|
+
ids=[f"{self.project}_{event.body.application_name}"],
|
|
176
|
+
),
|
|
177
|
+
value_dict={
|
|
178
|
+
"Error": event.error,
|
|
179
|
+
"Timestamp": event.timestamp,
|
|
180
|
+
"Application Class": event.body.application_name,
|
|
181
|
+
"Endpoint ID": event.body.endpoint_id,
|
|
182
|
+
},
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
mlrun.get_run_db().generate_event(
|
|
186
|
+
name=alert_objects.EventKind.MM_APP_FAILED, event_data=event_data
|
|
155
187
|
)
|
|
156
|
-
|
|
157
|
-
return context
|
|
188
|
+
logger.info("Event generated successfully")
|
|
@@ -13,18 +13,14 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
from abc import ABC, abstractmethod
|
|
16
|
-
from typing import Any, Union
|
|
16
|
+
from typing import Any, Union
|
|
17
17
|
|
|
18
|
-
import numpy as np
|
|
19
|
-
import pandas as pd
|
|
20
|
-
|
|
21
|
-
import mlrun
|
|
22
18
|
import mlrun.model_monitoring.applications.context as mm_context
|
|
23
19
|
import mlrun.model_monitoring.applications.results as mm_results
|
|
24
20
|
from mlrun.serving.utils import MonitoringApplicationToDict
|
|
25
21
|
|
|
26
22
|
|
|
27
|
-
class
|
|
23
|
+
class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
28
24
|
"""
|
|
29
25
|
A base class for a model monitoring application.
|
|
30
26
|
Inherit from this class to create a custom model monitoring application.
|
|
@@ -110,171 +106,3 @@ class ModelMonitoringApplicationBaseV2(MonitoringApplicationToDict, ABC):
|
|
|
110
106
|
each metric name is the key and the metric value is the corresponding value).
|
|
111
107
|
"""
|
|
112
108
|
raise NotImplementedError
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
116
|
-
"""
|
|
117
|
-
A base class for a model monitoring application.
|
|
118
|
-
Inherit from this class to create a custom model monitoring application.
|
|
119
|
-
|
|
120
|
-
example for very simple custom application::
|
|
121
|
-
|
|
122
|
-
class MyApp(ApplicationBase):
|
|
123
|
-
def do_tracking(
|
|
124
|
-
self,
|
|
125
|
-
sample_df_stats: mlrun.common.model_monitoring.helpers.FeatureStats,
|
|
126
|
-
feature_stats: mlrun.common.model_monitoring.helpers.FeatureStats,
|
|
127
|
-
start_infer_time: pd.Timestamp,
|
|
128
|
-
end_infer_time: pd.Timestamp,
|
|
129
|
-
schedule_time: pd.Timestamp,
|
|
130
|
-
latest_request: pd.Timestamp,
|
|
131
|
-
endpoint_id: str,
|
|
132
|
-
output_stream_uri: str,
|
|
133
|
-
) -> ModelMonitoringApplicationResult:
|
|
134
|
-
self.context.log_artifact(
|
|
135
|
-
TableArtifact(
|
|
136
|
-
"sample_df_stats", df=self.dict_to_histogram(sample_df_stats)
|
|
137
|
-
)
|
|
138
|
-
)
|
|
139
|
-
return ModelMonitoringApplicationResult(
|
|
140
|
-
name="data_drift_test",
|
|
141
|
-
value=0.5,
|
|
142
|
-
kind=mm_constant.ResultKindApp.data_drift,
|
|
143
|
-
status=mm_constant.ResultStatusApp.detected,
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
"""
|
|
148
|
-
|
|
149
|
-
kind = "monitoring_application"
|
|
150
|
-
|
|
151
|
-
def do(
|
|
152
|
-
self, monitoring_context: mm_context.MonitoringApplicationContext
|
|
153
|
-
) -> tuple[
|
|
154
|
-
list[mm_results.ModelMonitoringApplicationResult],
|
|
155
|
-
mm_context.MonitoringApplicationContext,
|
|
156
|
-
]:
|
|
157
|
-
"""
|
|
158
|
-
Process the monitoring event and return application results.
|
|
159
|
-
|
|
160
|
-
:param monitoring_context: (MonitoringApplicationContext) The monitoring context to process.
|
|
161
|
-
:returns: A tuple of:
|
|
162
|
-
[0] = list of application results that can be either from type
|
|
163
|
-
`ModelMonitoringApplicationResult` or from type
|
|
164
|
-
`ModelMonitoringApplicationResult`.
|
|
165
|
-
[1] = the original application event, wrapped in `MonitoringApplicationContext`
|
|
166
|
-
object
|
|
167
|
-
"""
|
|
168
|
-
resolved_event = self._resolve_event(monitoring_context)
|
|
169
|
-
if not (
|
|
170
|
-
hasattr(self, "context") and isinstance(self.context, mlrun.MLClientCtx)
|
|
171
|
-
):
|
|
172
|
-
self._lazy_init(monitoring_context)
|
|
173
|
-
results = self.do_tracking(*resolved_event)
|
|
174
|
-
results = results if isinstance(results, list) else [results]
|
|
175
|
-
return results, monitoring_context
|
|
176
|
-
|
|
177
|
-
def _lazy_init(self, monitoring_context: mm_context.MonitoringApplicationContext):
|
|
178
|
-
self.context = cast(mlrun.MLClientCtx, monitoring_context)
|
|
179
|
-
|
|
180
|
-
@abstractmethod
|
|
181
|
-
def do_tracking(
|
|
182
|
-
self,
|
|
183
|
-
application_name: str,
|
|
184
|
-
sample_df_stats: pd.DataFrame,
|
|
185
|
-
feature_stats: pd.DataFrame,
|
|
186
|
-
sample_df: pd.DataFrame,
|
|
187
|
-
start_infer_time: pd.Timestamp,
|
|
188
|
-
end_infer_time: pd.Timestamp,
|
|
189
|
-
latest_request: pd.Timestamp,
|
|
190
|
-
endpoint_id: str,
|
|
191
|
-
output_stream_uri: str,
|
|
192
|
-
) -> Union[
|
|
193
|
-
mm_results.ModelMonitoringApplicationResult,
|
|
194
|
-
list[mm_results.ModelMonitoringApplicationResult],
|
|
195
|
-
]:
|
|
196
|
-
"""
|
|
197
|
-
Implement this method with your custom monitoring logic.
|
|
198
|
-
|
|
199
|
-
:param application_name: (str) the app name
|
|
200
|
-
:param sample_df_stats: (pd.DataFrame) The new sample distribution.
|
|
201
|
-
:param feature_stats: (pd.DataFrame) The train sample distribution.
|
|
202
|
-
:param sample_df: (pd.DataFrame) The new sample DataFrame.
|
|
203
|
-
:param start_infer_time: (pd.Timestamp) Start time of the monitoring schedule.
|
|
204
|
-
:param end_infer_time: (pd.Timestamp) End time of the monitoring schedule.
|
|
205
|
-
:param latest_request: (pd.Timestamp) Timestamp of the latest request on this endpoint_id.
|
|
206
|
-
:param endpoint_id: (str) ID of the monitored model endpoint
|
|
207
|
-
:param output_stream_uri: (str) URI of the output stream for results
|
|
208
|
-
|
|
209
|
-
:returns: (ModelMonitoringApplicationResult) or
|
|
210
|
-
(list[ModelMonitoringApplicationResult]) of the application results.
|
|
211
|
-
"""
|
|
212
|
-
raise NotImplementedError
|
|
213
|
-
|
|
214
|
-
@classmethod
|
|
215
|
-
def _resolve_event(
|
|
216
|
-
cls,
|
|
217
|
-
monitoring_context: mm_context.MonitoringApplicationContext,
|
|
218
|
-
) -> tuple[
|
|
219
|
-
str,
|
|
220
|
-
pd.DataFrame,
|
|
221
|
-
pd.DataFrame,
|
|
222
|
-
pd.DataFrame,
|
|
223
|
-
pd.Timestamp,
|
|
224
|
-
pd.Timestamp,
|
|
225
|
-
pd.Timestamp,
|
|
226
|
-
str,
|
|
227
|
-
str,
|
|
228
|
-
]:
|
|
229
|
-
"""
|
|
230
|
-
Converting the event into a single tuple that will be used for passing the event arguments to the running
|
|
231
|
-
application
|
|
232
|
-
|
|
233
|
-
:param monitoring_context: (MonitoringApplicationContext) The monitoring context to process.
|
|
234
|
-
|
|
235
|
-
:return: A tuple of:
|
|
236
|
-
[0] = (str) application name
|
|
237
|
-
[1] = (pd.DataFrame) current input statistics
|
|
238
|
-
[2] = (pd.DataFrame) train statistics
|
|
239
|
-
[3] = (pd.DataFrame) current input data
|
|
240
|
-
[4] = (pd.Timestamp) start time of the monitoring schedule
|
|
241
|
-
[5] = (pd.Timestamp) end time of the monitoring schedule
|
|
242
|
-
[6] = (pd.Timestamp) timestamp of the latest request
|
|
243
|
-
[7] = (str) endpoint id
|
|
244
|
-
[8] = (str) output stream uri
|
|
245
|
-
"""
|
|
246
|
-
return (
|
|
247
|
-
monitoring_context.application_name,
|
|
248
|
-
cls.dict_to_histogram(monitoring_context.sample_df_stats),
|
|
249
|
-
cls.dict_to_histogram(monitoring_context.feature_stats),
|
|
250
|
-
monitoring_context.sample_df,
|
|
251
|
-
monitoring_context.start_infer_time,
|
|
252
|
-
monitoring_context.end_infer_time,
|
|
253
|
-
monitoring_context.latest_request,
|
|
254
|
-
monitoring_context.endpoint_id,
|
|
255
|
-
monitoring_context.output_stream_uri,
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
@staticmethod
|
|
259
|
-
def dict_to_histogram(
|
|
260
|
-
histogram_dict: mlrun.common.model_monitoring.helpers.FeatureStats,
|
|
261
|
-
) -> pd.DataFrame:
|
|
262
|
-
"""
|
|
263
|
-
Convert histogram dictionary to pandas DataFrame with feature histograms as columns
|
|
264
|
-
|
|
265
|
-
:param histogram_dict: Histogram dictionary
|
|
266
|
-
|
|
267
|
-
:returns: Histogram dataframe
|
|
268
|
-
"""
|
|
269
|
-
|
|
270
|
-
# Create a dictionary with feature histograms as values
|
|
271
|
-
histograms = {}
|
|
272
|
-
for feature, stats in histogram_dict.items():
|
|
273
|
-
if "hist" in stats:
|
|
274
|
-
# Normalize to probability distribution of each feature
|
|
275
|
-
histograms[feature] = np.array(stats["hist"][0]) / stats["count"]
|
|
276
|
-
|
|
277
|
-
# Convert the dictionary to pandas DataFrame
|
|
278
|
-
histograms = pd.DataFrame(histograms)
|
|
279
|
-
|
|
280
|
-
return histograms
|