Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

114 roadmap create a UI for whitebox server using streamlit or similar tools #131

Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
85ddea2
Initialised streamlit dev
stavrostheocharis Feb 8, 2023
0ea94c0
Added Drift parts
stavrostheocharis Feb 10, 2023
a625120
App formating
stavrostheocharis Feb 11, 2023
fd9badc
Formated overview and drifting tabs
stavrostheocharis Feb 11, 2023
2e65d34
Additions
stavrostheocharis Feb 12, 2023
6bfc696
Added markdowns in functions
stavrostheocharis Feb 12, 2023
3cd5203
Removed unused imports from app
stavrostheocharis Feb 12, 2023
0031cb4
Inferences tab addition
stavrostheocharis Feb 15, 2023
3f9e3c8
Added style to inferences dataframe
stavrostheocharis Feb 15, 2023
4925535
Adjustments and additions
stavrostheocharis Feb 15, 2023
c97f2ff
Added monitors tab
stavrostheocharis Feb 16, 2023
99b2fab
Added alerts tab
stavrostheocharis Feb 16, 2023
e94d818
Added filters in alerts
stavrostheocharis Feb 17, 2023
3e69aad
Formating
stavrostheocharis Feb 17, 2023
8ce1019
Additions for different model types
stavrostheocharis Feb 17, 2023
035c478
Fixed types and name conventions
stavrostheocharis Feb 17, 2023
15e2e10
SDK connection to streamlit
stavrostheocharis Feb 20, 2023
2e6483a
Added sdk functionality in Streamlit
stavrostheocharis Feb 20, 2023
384a5ed
Added connection functionality in sidebar
stavrostheocharis Feb 20, 2023
424ee25
Additions
stavrostheocharis Feb 21, 2023
b086ad0
Adjusted expanders descriptions
stavrostheocharis Feb 21, 2023
10d0de3
Added cache functionality
stavrostheocharis Feb 21, 2023
7235d1a
Minor fixes
stavrostheocharis Feb 21, 2023
bb7e4f5
Additions/adjustments
stavrostheocharis Feb 23, 2023
65d513f
Deleted prints
stavrostheocharis Feb 23, 2023
0648112
Added error catch in inference log
stavrostheocharis Feb 23, 2023
4a1746e
Merge branch 'main' into 114-roadmap-create-a-ui-for-whitebox-server-…
stavrostheocharis Feb 23, 2023
9494457
Black formating
stavrostheocharis Feb 23, 2023
7b9f093
Black formating
stavrostheocharis Feb 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .streamlit/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[theme]
base="dark"
primaryColor="#21babe"
backgroundColor="#1e2025"
secondaryBackgroundColor="#252a33"

4 changes: 2 additions & 2 deletions docs/mkdocs/docs/sdk-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ This is the documentation for Whitebox's SDK. For an interactive experience, you

## Models

**_create_model_**_(name, type, prediction, labels=None, description="")_
**_create_model_**_(name, type, target_column, labels=None, description="")_

Creates a model in the database. This model works as placeholder for all the actual model's metadata.

| Parameter | Type | Description |
| --------------- | ---------------- | ------------------------------------------------------------------------- |
| **name** | `str` | The name of the model. |
| **type** | `str` | The model's type. Possible values: `binary`, `multi_class`, `regression`. |
| **prediction** | `str` | The prediction of the model. |
| **target_column** | `str` | The name of the target column (y). |
| **labels** | `Dict[str, int]` | The model's labels. Defaults to `None`. |
| **description** | `str` | The model's description. Defaults to an empty string `""`. |

Expand Down
2 changes: 1 addition & 1 deletion docs/mkdocs/docs/tutorial/sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ wb.create_model(
'additionalProp1': 0,
'additionalProp2': 1
},
prediction="target"
target_column="target"
)
```

Expand Down
8 changes: 4 additions & 4 deletions examples/notebooks/sdk-example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
}
],
"source": [
"wb.create_model(name=\"Model 1\", type=\"binary\", labels={'additionalProp1': 0, 'additionalProp2': 1}, prediction=\"y_prediction_multi\")"
"wb.create_model(name=\"Model 1\", type=\"binary\", labels={'additionalProp1': 0, 'additionalProp2': 1}, target_column=\"y_prediction_multi\")"
]
},
{
Expand Down Expand Up @@ -248,7 +248,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"display_name": ".venv",
"language": "python",
"name": "python3"
},
Expand All @@ -262,12 +262,12 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.8"
"version": "3.8.6"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "32a5a47fe20cdfbd609287887f2e78a4d5f2f7afeda3da775d5794970f9a3f8e"
"hash": "6b8a8ae524dcca06b04542d2d49be160be0f11dd19d43d7b2673d555344c6092"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ SQLAlchemy==1.4.41
stack-data==0.5.1
starlette==0.20.4
statsmodels==0.13.2
streamlit==1.18.1
tenacity==8.1.0
threadpoolctl==3.1.0
tomli==2.0.1
Expand All @@ -156,4 +157,4 @@ wheel==0.37.1
wrapt==1.14.1
xgboost==1.6.2
yarl==1.8.1
zipp==3.10.0
zipp==3.10.0
6 changes: 6 additions & 0 deletions whitebox/.streamlit/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[theme]
base="dark"
primaryColor="#21babe"
backgroundColor="#1e2025"
secondaryBackgroundColor="#252a33"

14 changes: 7 additions & 7 deletions whitebox/api/v1/dataset_rows.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ async def create_dataset_rows(
model = crud.models.get(db=db, _id=dict(body[0])["model_id"])
if model:
for row in body:
if not model.prediction in row.processed:
if not model.target_column in row.processed:
return errors.bad_request(
f'Column "{model.prediction}" was not found in some or any of the rows in provided training dataset. Please try again!'
f'Column "{model.target_column}" was not found in some or any of the rows in provided training dataset. Please try again!'
)

predictions = list(set(vars(x)["processed"][model.prediction] for x in body))
predictions = list(set(vars(x)["processed"][model.target_column] for x in body))
if len(predictions) <= 1:
return errors.bad_request(
f'Training dataset\'s "{model.prediction}" columns must have at least 2 different values!'
f'Training dataset\'s "{model.target_column}" columns must have at least 2 different values!'
)

new_dataset_rows = crud.dataset_rows.create_many(db=db, obj_list=body)
Expand All @@ -66,21 +66,21 @@ async def create_dataset_rows(
background_tasks.add_task(
create_binary_classification_training_model_pipeline,
processed_dataset_rows_pd,
model.prediction,
model.target_column,
model.id,
)
elif model.type == ModelType.multi_class:
background_tasks.add_task(
create_multiclass_classification_training_model_pipeline,
processed_dataset_rows_pd,
model.prediction,
model.target_column,
model.id,
)
elif model.type == ModelType.regression:
background_tasks.add_task(
create_regression_training_model_pipeline,
processed_dataset_rows_pd,
model.prediction,
model.target_column,
model.id,
)
return new_dataset_rows
Expand Down
16 changes: 13 additions & 3 deletions whitebox/api/v1/inference_rows.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,18 @@ async def create_many_inference_rows(
) -> List[InferenceRow]:
"""Inserts a set of inference rows into the database."""

new_inference_rows = crud.inference_rows.create_many(db=db, obj_list=body)
return new_inference_rows
model = crud.models.get(db=db, _id=dict(body[0])["model_id"])
if model:
for row in body:
if not model.target_column in row.processed:
return errors.bad_request(
f'Column "{model.target_column}" was not found in some or any of the rows in provided inference dataset. Please try again!'
)

new_inference_rows = crud.inference_rows.create_many(db=db, obj_list=body)
return new_inference_rows
else:
return errors.not_found(f"Model with id: {dict(body[0])['model_id']} not found")


@inference_rows_router.get(
Expand Down Expand Up @@ -138,7 +148,7 @@ async def create_inference_row_xai_report(

xai_report = create_xai_pipeline_per_inference_row(
training_set=pd.DataFrame(dataset_rows_processed),
target=model.prediction,
target=model.target_column,
inference_row=inference_row_series,
type_of_task=model.type,
model_id=model.id,
Expand Down
12 changes: 6 additions & 6 deletions whitebox/cron_tasks/monitoring_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ async def run_calculate_drifting_metrics_pipeline(

# We need to drop the target column from the data to calculate drifting metrics
processed_inference_dropped_target_df = inference_processed_df.drop(
[model.prediction], axis=1
[model.target_column], axis=1
)
processed_training_dropped_target_df = training_processed_df.drop(
[model.prediction], axis=1
[model.target_column], axis=1
)

data_drift_report = run_data_drift_pipeline(
Expand All @@ -63,7 +63,7 @@ async def run_calculate_drifting_metrics_pipeline(
concept_drift_report = run_concept_drift_pipeline(
training_processed_df,
inference_processed_df,
model.prediction,
model.target_column,
)

new_drifting_metric = entities.DriftingMetric(
Expand Down Expand Up @@ -115,7 +115,7 @@ async def run_calculate_performance_metrics_pipeline(
if model.type == ModelType.binary:
binary_classification_metrics_report = (
create_binary_classification_evaluation_metrics_pipeline(
cleaned_actuals_df, inference_processed_df[model.prediction], labels
cleaned_actuals_df, inference_processed_df[model.target_column], labels
)
)

Expand All @@ -130,7 +130,7 @@ async def run_calculate_performance_metrics_pipeline(
elif model.type == ModelType.multi_class:
multiclass_classification_metrics_report = (
create_multiple_classification_evaluation_metrics_pipeline(
cleaned_actuals_df, inference_processed_df[model.prediction], labels
cleaned_actuals_df, inference_processed_df[model.target_column], labels
)
)

Expand All @@ -144,7 +144,7 @@ async def run_calculate_performance_metrics_pipeline(

elif model.type == ModelType.regression:
regression_metrics_report = create_regression_evaluation_metrics_pipeline(
cleaned_actuals_df, inference_processed_df[model.prediction]
cleaned_actuals_df, inference_processed_df[model.target_column]
)

new_performance_metric = entities.RegressionMetrics(
Expand Down
2 changes: 1 addition & 1 deletion whitebox/entities/Model.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Model(Base):
description = Column(String)
type = Column("type", Enum(ModelType))
labels = Column(JSON, nullable=True)
prediction = Column(String)
target_column = Column(String)
created_at = Column(DateTime)
updated_at = Column(DateTime)

Expand Down
2 changes: 1 addition & 1 deletion whitebox/schemas/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ModelBase(BaseModel):
name: str
description: str
type: ModelType
prediction: str
target_column: str
labels: Optional[Dict[str, int]]


Expand Down
69 changes: 63 additions & 6 deletions whitebox/sdk/whitebox.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from enum import Enum
import numpy as np
import pandas as pd
from whitebox.schemas.model import ModelCreateDto, ModelType
from typing import Dict, Optional
from whitebox.schemas.model import ModelCreateDto, ModelType, ModelUpdateDto
from typing import Dict, Optional, Union
import requests
import logging
from fastapi import status
from fastapi.encoders import jsonable_encoder

from whitebox.schemas.modelMonitor import (
AlertSeverity,
Expand Down Expand Up @@ -39,7 +40,7 @@ def create_model(
self,
name: str,
type: ModelType,
prediction: str,
target_column: str,
labels: Dict[str, int] = None,
description: str = "",
) -> dict:
Expand All @@ -51,11 +52,12 @@ def create_model(
description=description,
type=type,
labels=labels,
prediction=prediction,
target_column=target_column,
)

result = requests.post(
url=f"{self.host}/{self.api_version}/models",
json=new_model.dict(),
json=jsonable_encoder(new_model),
headers={"api-key": self.api_key},
)

Expand All @@ -75,6 +77,35 @@ def get_model(self, model_id: str) -> dict:

return result.json()

def get_models(self) -> Union[dict, None]:
"""
Returns all the models. If no models exist, returns None.
"""
result = requests.get(
url=f"{self.host}/{self.api_version}/models",
headers={"api-key": self.api_key},
)
if result.status_code == status.HTTP_404_NOT_FOUND:
return None

return result.json()

def update_model(self, model_id: str, body: ModelUpdateDto):
"""
Updates a model by its id. If any error occurs, returns False.
"""
result = requests.put(
url=f"{self.host}/{self.api_version}/models/{model_id}",
json=body,
headers={"api-key": self.api_key},
)
logger.info(result.json())

if result.status_code == status.HTTP_200_OK:
return True

return False

def delete_model(self, model_id: str):
"""
Deletes a model by its id. If any error occurs, returns False.
Expand Down Expand Up @@ -169,6 +200,20 @@ def log_inferences(

return False

def get_inferences(self, model_id: str):
"""
Given a specific model id, this endpoint fetches all the inferences.
If some of the required data isn't found, returns None.
"""
result = requests.get(
url=f"{self.host}/{self.api_version}/inference-rows?model_id={model_id}",
headers={"api-key": self.api_key},
)
if result.status_code == status.HTTP_404_NOT_FOUND:
return None

return result.json()

def get_xai_row(self, inference_row_id: str):
"""
Given a specific inference row id, this endpoint produces an explainability report for this inference.
Expand All @@ -191,8 +236,8 @@ def create_model_monitor(
metric: MonitorMetrics,
severity: AlertSeverity,
email: str,
feature: Optional[str],
lower_threshold: Optional[float],
feature: Optional[Union[str, None]] = None,
) -> dict:
"""
Creates a monitor for a model.
Expand All @@ -218,6 +263,18 @@ def create_model_monitor(
logger.info(result.json())
return result.json()

def get_monitors(self, model_id: str) -> dict:
"""
Returns all monitors for a model.
"""
result = requests.get(
url=f"{self.host}/{self.api_version}/model-monitors?modelId={model_id}",
headers={"api-key": self.api_key},
)

logger.info(result.json())
return result.json()

def get_alerts(self, model_id: str = "") -> dict:
"""
Returns all alerts for a model.
Expand Down
Loading