validmind 1.11.4__tar.gz → 1.11.6__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.
Files changed (64) hide show
  1. {validmind-1.11.4 → validmind-1.11.6}/PKG-INFO +2 -1
  2. {validmind-1.11.4 → validmind-1.11.6}/pyproject.toml +2 -1
  3. {validmind-1.11.4 → validmind-1.11.6}/validmind/client.py +1 -3
  4. {validmind-1.11.4 → validmind-1.11.6}/validmind/model_validation/model_metadata.py +15 -0
  5. {validmind-1.11.4 → validmind-1.11.6}/validmind/model_validation/sklearn/metrics.py +9 -3
  6. {validmind-1.11.4 → validmind-1.11.6}/validmind/model_validation/sklearn/threshold_tests.py +78 -33
  7. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/model.py +1 -0
  8. {validmind-1.11.4 → validmind-1.11.6}/LICENSE +0 -0
  9. {validmind-1.11.4 → validmind-1.11.6}/validmind/__init__.py +0 -0
  10. {validmind-1.11.4 → validmind-1.11.6}/validmind/api_client.py +0 -0
  11. {validmind-1.11.4 → validmind-1.11.6}/validmind/data_validation/__init__.py +0 -0
  12. {validmind-1.11.4 → validmind-1.11.6}/validmind/data_validation/metrics.py +0 -0
  13. {validmind-1.11.4 → validmind-1.11.6}/validmind/data_validation/threshold_tests.py +0 -0
  14. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/__init__.py +0 -0
  15. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/classification/__init__.py +0 -0
  16. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/classification/customer_churn.py +0 -0
  17. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/classification/datasets/bank_customer_churn.csv +0 -0
  18. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/classification/datasets/taiwan_credit.csv +0 -0
  19. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/classification/taiwan_credit.py +0 -0
  20. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/__init__.py +0 -0
  21. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/datasets/fred_loan_rates.csv +0 -0
  22. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/datasets/fred_loan_rates_test_1.csv +0 -0
  23. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/datasets/fred_loan_rates_test_2.csv +0 -0
  24. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/datasets/fred_loan_rates_test_3.csv +0 -0
  25. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/datasets/fred_loan_rates_test_4.csv +0 -0
  26. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/datasets/fred_loan_rates_test_5.csv +0 -0
  27. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/datasets/lending_club_loan_rates.csv +0 -0
  28. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/fred.py +0 -0
  29. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/lending_club.py +0 -0
  30. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/models/fred_loan_rates_model_1.pkl +0 -0
  31. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/models/fred_loan_rates_model_2.pkl +0 -0
  32. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/models/fred_loan_rates_model_3.pkl +0 -0
  33. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/models/fred_loan_rates_model_4.pkl +0 -0
  34. {validmind-1.11.4 → validmind-1.11.6}/validmind/datasets/regression/models/fred_loan_rates_model_5.pkl +0 -0
  35. {validmind-1.11.4 → validmind-1.11.6}/validmind/model_utils.py +0 -0
  36. {validmind-1.11.4 → validmind-1.11.6}/validmind/model_validation/__init__.py +0 -0
  37. {validmind-1.11.4 → validmind-1.11.6}/validmind/model_validation/sklearn/__init__.py +0 -0
  38. {validmind-1.11.4 → validmind-1.11.6}/validmind/model_validation/statsmodels/__init__.py +0 -0
  39. {validmind-1.11.4 → validmind-1.11.6}/validmind/model_validation/statsmodels/metrics.py +0 -0
  40. {validmind-1.11.4 → validmind-1.11.6}/validmind/model_validation/statsmodels/threshold_tests.py +0 -0
  41. {validmind-1.11.4 → validmind-1.11.6}/validmind/model_validation/utils.py +0 -0
  42. {validmind-1.11.4 → validmind-1.11.6}/validmind/statsutils.py +0 -0
  43. {validmind-1.11.4 → validmind-1.11.6}/validmind/test_plans/__init__.py +0 -0
  44. {validmind-1.11.4 → validmind-1.11.6}/validmind/test_plans/binary_classifier.py +0 -0
  45. {validmind-1.11.4 → validmind-1.11.6}/validmind/test_plans/statsmodels_timeseries.py +0 -0
  46. {validmind-1.11.4 → validmind-1.11.6}/validmind/test_plans/tabular_datasets.py +0 -0
  47. {validmind-1.11.4 → validmind-1.11.6}/validmind/test_plans/time_series.py +0 -0
  48. {validmind-1.11.4 → validmind-1.11.6}/validmind/test_suites/__init__.py +0 -0
  49. {validmind-1.11.4 → validmind-1.11.6}/validmind/test_suites/test_suites.py +0 -0
  50. {validmind-1.11.4 → validmind-1.11.6}/validmind/utils.py +0 -0
  51. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/__init__.py +0 -0
  52. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/dataset.py +0 -0
  53. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/dataset_utils.py +0 -0
  54. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/figure.py +0 -0
  55. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/metric.py +0 -0
  56. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/metric_result.py +0 -0
  57. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/plot_utils.py +0 -0
  58. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/result_summary.py +0 -0
  59. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/test_context.py +0 -0
  60. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/test_plan.py +0 -0
  61. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/test_plan_result.py +0 -0
  62. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/test_result.py +0 -0
  63. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/test_suite.py +0 -0
  64. {validmind-1.11.4 → validmind-1.11.6}/validmind/vm_models/threshold_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: validmind
3
- Version: 1.11.4
3
+ Version: 1.11.6
4
4
  Summary: ValidMind Developer Framework
5
5
  Author: Andres Rodriguez
6
6
  Author-email: andres@validmind.ai
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3.9
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Provides-Extra: r-support
13
13
  Requires-Dist: arch (>=5.4.0,<6.0.0)
14
+ Requires-Dist: catboost (>=1.2,<2.0)
14
15
  Requires-Dist: click (>=8.0.4,<9.0.0)
15
16
  Requires-Dist: dython (>=0.7.1,<0.8.0)
16
17
  Requires-Dist: ipython (==7.34.0)
@@ -13,7 +13,7 @@ description = "ValidMind Developer Framework"
13
13
  # "validmind/**/*.so",
14
14
  # ]
15
15
  name = "validmind"
16
- version = "1.11.4"
16
+ version = "1.11.6"
17
17
 
18
18
  [tool.poetry.dependencies]
19
19
  arch = "^5.4.0"
@@ -41,6 +41,7 @@ tabulate = "^0.8.9"
41
41
  tqdm = "^4.64.0"
42
42
  xgboost = "^1.5.2"
43
43
  markdown = "^3.4.3"
44
+ catboost = "^1.2"
44
45
 
45
46
  [tool.poetry.group.dev.dependencies]
46
47
  black = "^22.1.0"
@@ -91,9 +91,7 @@ def init_model(
91
91
 
92
92
  if not Model.is_supported_model(model):
93
93
  raise ValueError(
94
- "Model type {} is not supported at the moment.".format(
95
- Model.model_class(model)
96
- )
94
+ f"Model type {Model.model_library(model)}.{Model.model_class(model)} is not supported at the moment."
97
95
  )
98
96
 
99
97
  return Model.init_vm_model(
@@ -18,6 +18,13 @@ SUPPORTED_STATSMODELS_LINK_FUNCTIONS = {
18
18
  }
19
19
 
20
20
 
21
+ def get_catboost_version():
22
+ if "catboost" in sys.modules:
23
+ return sys.modules["catboost"].__version__
24
+
25
+ return "n/a"
26
+
27
+
21
28
  def get_pytorch_version():
22
29
  if "torch" in sys.modules:
23
30
  return sys.modules["torch"].__version__
@@ -113,6 +120,12 @@ def get_info_from_model_instance(model):
113
120
  subtask = "binary"
114
121
  framework = "PyTorch"
115
122
  framework_version = get_pytorch_version()
123
+ elif model_class == "CatBoostClassifier":
124
+ architecture = "Gradient Boosting"
125
+ task = "classification"
126
+ subtask = "binary"
127
+ framework = "CatBoost"
128
+ framework_version = get_catboost_version()
116
129
  else:
117
130
  raise ValueError(f"Model class {model_class} is not supported by this test")
118
131
 
@@ -162,6 +175,8 @@ def get_params_from_model_instance(model):
162
175
  params = model.get_params()
163
176
  elif model_library == "pytorch":
164
177
  params = {}
178
+ elif model_library == "catboost":
179
+ params = model.get_all_params()
165
180
  else:
166
181
  raise ValueError(f"Model library {model_library} is not supported by this test")
167
182
 
@@ -444,8 +444,12 @@ class SHAPGlobalImportance(Metric):
444
444
  # the shap library generates a bunch of annoying warnings that we don't care about
445
445
  warnings.filterwarnings("ignore", category=UserWarning)
446
446
 
447
- # RandomForestClassifier applies here too
448
- if model_class == "XGBClassifier" or model_class == "RandomForestClassifier":
447
+ # Any tree based model can go here
448
+ if (
449
+ model_class == "XGBClassifier"
450
+ or model_class == "RandomForestClassifier"
451
+ or model_class == "CatBoostClassifier"
452
+ ):
449
453
  explainer = shap.TreeExplainer(trained_model)
450
454
  elif (
451
455
  model_class == "LogisticRegression"
@@ -485,6 +489,8 @@ class PopulationStabilityIndex(Metric):
485
489
  print(f"Skiping PSI for {model_library} models")
486
490
  return
487
491
 
488
- psi_df = _get_psi(self.model.y_train_predict, self.model.y_test_predict)
492
+ psi_df = _get_psi(
493
+ self.model.y_train_predict.copy(), self.model.y_test_predict.copy()
494
+ )
489
495
 
490
496
  return self.cache_results(metric_value=psi_df)
@@ -313,13 +313,19 @@ class OverfitDiagnosis(ThresholdTest):
313
313
  raise ValueError("model must of provided to run this test")
314
314
 
315
315
  if self.params["features_columns"] is None:
316
- features_list = [
317
- field_dict["id"] for field_dict in self.model.train_ds.fields
318
- ]
319
- features_list.remove(self.model.train_ds.target_column)
316
+ features_list = self.model.train_ds.get_features_columns()
320
317
  else:
321
318
  features_list = self.params["features_columns"]
322
319
 
320
+ # Check if all elements from features_list are present in the feature columns
321
+ all_present = all(
322
+ elem in self.model.train_ds.get_features_columns() for elem in features_list
323
+ )
324
+ if not all_present:
325
+ raise ValueError(
326
+ "The list of feature columns provided do not match with training dataset feature columns"
327
+ )
328
+
323
329
  if not isinstance(features_list, list):
324
330
  raise ValueError(
325
331
  "features_columns must be a list of features you would like to test"
@@ -344,7 +350,11 @@ class OverfitDiagnosis(ThresholdTest):
344
350
  results_headers.extend(self.default_metrics.keys())
345
351
 
346
352
  for feature_column in features_list:
347
- train_df["bin"] = pd.cut(train_df[feature_column], bins=10)
353
+ bins = 10
354
+ if feature_column in self.model.train_ds.get_categorical_features_columns():
355
+ bins = len(train_df[feature_column].unique())
356
+ train_df["bin"] = pd.cut(train_df[feature_column], bins=bins)
357
+
348
358
  results_train = {k: [] for k in results_headers}
349
359
  results_test = {k: [] for k in results_headers}
350
360
 
@@ -583,17 +593,21 @@ class WeakspotsDiagnosis(ThresholdTest):
583
593
  if self.model is None:
584
594
  raise ValueError("model must of provided to run this test")
585
595
 
586
- if "features_columns" not in self.params:
587
- raise ValueError("features_columns must be provided in params")
588
-
589
596
  if self.params["features_columns"] is None:
590
- features_list = [
591
- field_dict["id"] for field_dict in self.model.train_ds.fields
592
- ]
593
- features_list.remove(self.model.train_ds.target_column)
597
+ features_list = self.model.train_ds.get_features_columns()
594
598
  else:
595
599
  features_list = self.params["features_columns"]
596
600
 
601
+ # Check if all elements from features_list are present in the feature columns
602
+ all_present = all(
603
+ elem in self.model.train_ds.get_features_columns() for elem in features_list
604
+ )
605
+ if not all_present:
606
+ raise ValueError(
607
+ "The list of feature columns provided do not match with "
608
+ + "training dataset feature columns"
609
+ )
610
+
597
611
  target_column = self.model.train_ds.target_column
598
612
  prediction_column = f"{target_column}_pred"
599
613
 
@@ -610,7 +624,11 @@ class WeakspotsDiagnosis(ThresholdTest):
610
624
  results_headers = ["slice", "shape"]
611
625
  results_headers.extend(self.default_metrics.keys())
612
626
  for feature in features_list:
613
- train_df["bin"] = pd.cut(train_df[feature], bins=10)
627
+ bins = 10
628
+ if feature in self.model.train_ds.get_categorical_features_columns():
629
+ bins = len(train_df[feature].unique())
630
+ train_df["bin"] = pd.cut(train_df[feature], bins=bins)
631
+
614
632
  results_train = {k: [] for k in results_headers}
615
633
  results_test = {k: [] for k in results_headers}
616
634
 
@@ -811,6 +829,7 @@ class RobustnessDiagnosis(ThresholdTest):
811
829
  default_params = {
812
830
  "features_columns": None,
813
831
  "scaling_factor_std_dev_list": [0.01, 0.02],
832
+ "accuracy_decay_threshold": 3,
814
833
  }
815
834
  default_metrics = {
816
835
  "accuracy": metrics.accuracy_score,
@@ -839,6 +858,10 @@ class RobustnessDiagnosis(ThresholdTest):
839
858
  raise ValueError("scaling_factor_std_dev_list must be provided in params")
840
859
  x_std_dev_list = self.params["scaling_factor_std_dev_list"]
841
860
 
861
+ if self.params["accuracy_decay_threshold"] is None:
862
+ raise ValueError("accuracy_decay_threshold must be provided in params")
863
+ accuracy_threshold = self.params["accuracy_decay_threshold"]
864
+
842
865
  if self.model is None:
843
866
  raise ValueError("model must of provided to run this test")
844
867
 
@@ -846,20 +869,25 @@ class RobustnessDiagnosis(ThresholdTest):
846
869
  if "features_columns" not in self.params:
847
870
  raise ValueError("features_columns must be provided in params")
848
871
 
849
- # Identify numeric features
850
- numeric_features_columns = [
851
- field_dic["id"]
852
- for field_dic in self.model.train_ds.fields
853
- if field_dic["type"] == "Numeric"
854
- ]
855
- if self.params["features_columns"] is None:
856
- features_list = numeric_features_columns
857
- else:
858
- features_list = self.params["features_columns"]
872
+ features_list = self.params["features_columns"]
873
+ if features_list is None:
874
+ features_list = self.model.train_ds.get_numeric_features_columns()
875
+
876
+ # Check if all elements from features_list are present in the numerical feature columns
877
+ all_present = all(
878
+ elem in self.model.train_ds.get_numeric_features_columns()
879
+ for elem in features_list
880
+ )
881
+ if not all_present:
882
+ raise ValueError(
883
+ "The list of feature columns provided do not match with training "
884
+ + "dataset numerical feature columns"
885
+ )
859
886
 
860
887
  # Remove target column if it exist in the list
861
- if self.model.train_ds.target_column in features_list:
862
- features_list.remove(self.model.train_ds.target_column)
888
+ features_list = [
889
+ col for col in features_list if col != self.model.train_ds.target_column
890
+ ]
863
891
 
864
892
  train_df = self.model.train_ds.x.copy()
865
893
  train_y_true = self.model.train_ds.y
@@ -870,8 +898,9 @@ class RobustnessDiagnosis(ThresholdTest):
870
898
  test_results = []
871
899
  test_figures = []
872
900
 
873
- results_headers = ["Perturbation Size", "Dataset Type", "Records"]
874
- results_headers.extend(self.default_metrics.keys())
901
+ results_headers = ["Perturbation Size", "Dataset Type", "Records"] + list(
902
+ self.default_metrics.keys()
903
+ )
875
904
  results = {k: [] for k in results_headers}
876
905
 
877
906
  # Iterate scaling factor for the standard deviation list
@@ -881,10 +910,10 @@ class RobustnessDiagnosis(ThresholdTest):
881
910
 
882
911
  # Add noise to numeric features columns provided by user
883
912
  for feature in features_list:
884
- temp_train_df[feature] = self.add_noise_std_dev(
913
+ temp_train_df[feature] = self._add_noise_std_dev(
885
914
  temp_train_df[feature].to_list(), x_std_dev
886
915
  )
887
- temp_test_df[feature] = self.add_noise_std_dev(
916
+ temp_test_df[feature] = self._add_noise_std_dev(
888
917
  temp_test_df[feature].to_list(), x_std_dev
889
918
  )
890
919
 
@@ -907,15 +936,31 @@ class RobustnessDiagnosis(ThresholdTest):
907
936
  )
908
937
  )
909
938
 
939
+ train_acc = df.loc[(df["Dataset Type"] == "Training"), "accuracy"].values[0]
940
+ test_acc = df.loc[(df["Dataset Type"] == "Test"), "accuracy"].values[0]
941
+
942
+ df["Passed"] = np.where(
943
+ (df["Dataset Type"] == "Training")
944
+ & (df["accuracy"] >= (train_acc - accuracy_threshold)),
945
+ True,
946
+ np.where(
947
+ (df["Dataset Type"] == "Test")
948
+ & (df["accuracy"] >= (test_acc - accuracy_threshold)),
949
+ True,
950
+ False,
951
+ ),
952
+ )
910
953
  test_results.append(
911
954
  TestResult(
912
955
  test_name="accuracy",
913
- column=features_list[0],
956
+ column=features_list,
914
957
  passed=True,
915
- values=df.to_dict(orient="list"),
958
+ values=df.to_dict(),
916
959
  )
917
960
  )
918
- return self.cache_results(test_results, passed=True, figures=test_figures)
961
+ return self.cache_results(
962
+ test_results, passed=df["Passed"].all(), figures=test_figures
963
+ )
919
964
 
920
965
  def _compute_metrics(
921
966
  self,
@@ -946,7 +991,7 @@ class RobustnessDiagnosis(ThresholdTest):
946
991
  for metric, metric_fn in self.default_metrics.items():
947
992
  results[metric].append(metric_fn(y_true.values, y_prediction) * 100)
948
993
 
949
- def add_noise_std_dev(
994
+ def _add_noise_std_dev(
950
995
  self, values: List[float], x_std_dev: float
951
996
  ) -> Tuple[List[float], float]:
952
997
  """
@@ -9,6 +9,7 @@ from .dataset import Dataset
9
9
  # import torch.nn as nn
10
10
 
11
11
  SUPPORTED_MODEL_TYPES = [
12
+ "catboost.CatBoostClassifier",
12
13
  "pytorch.PyTorchModel",
13
14
  "sklearn.LogisticRegression",
14
15
  "sklearn.LinearRegression",
File without changes