shapeaudit 1.0.2__tar.gz → 1.0.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.
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/PKG-INFO +191 -180
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/README.md +17 -6
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/ale_detector.py +8 -1
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/ale_fast.py +10 -1
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/api.py +220 -11
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/backends.py +10 -1
- {shapeaudit-1.0.2/modelcheck/experimental → shapeaudit-1.0.4/modelcheck}/calibration.py +10 -3
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/cli.py +116 -28
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/explorer.py +36 -13
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/frechet.py +213 -42
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/importance.py +182 -12
- shapeaudit-1.0.4/modelcheck/laudo.py +1282 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/orchestrator.py +79 -21
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/pipeline.py +36 -5
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/shape.py +5 -2
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/slope.py +2 -2
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/snapshot.py +91 -16
- shapeaudit-1.0.4/modelcheck/validation.py +466 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelguard/__init__.py +3 -1
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/pyproject.toml +57 -57
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/setup.cfg +4 -4
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/shapeaudit.egg-info/PKG-INFO +191 -180
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/shapeaudit.egg-info/SOURCES.txt +7 -0
- shapeaudit-1.0.4/tests/test_caminhos.py +122 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_chaos.py +5 -2
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_classes_de_bug.py +100 -5
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_frechet.py +5 -2
- shapeaudit-1.0.4/tests/test_idempotencia.py +52 -0
- shapeaudit-1.0.4/tests/test_regressao_saudaveis.py +137 -0
- shapeaudit-1.0.4/tests/test_revisao_docs.py +177 -0
- shapeaudit-1.0.4/tests/test_revisao_laudo.py +395 -0
- shapeaudit-1.0.4/tests/test_revisao_nucleo.py +322 -0
- shapeaudit-1.0.4/tests/test_revisao_validacao.py +235 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_snapshot_cli.py +13 -2
- shapeaudit-1.0.2/modelcheck/laudo.py +0 -885
- shapeaudit-1.0.2/modelcheck/validation.py +0 -297
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/LICENSE +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/__init__.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/_validate.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/ale.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/ale_selective.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/audit.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/baselines.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/canary.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/causal.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/causal_bootstrap.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/ci.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/compare/__init__.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/compare/model_comparator.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/confidence.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/contradict.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/dashboard.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/drift.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/experimental/__init__.py +0 -0
- {shapeaudit-1.0.2/modelcheck → shapeaudit-1.0.4/modelcheck/experimental}/calibration.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/experimental/gtsf.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/experimental/llm.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/experimental/llm_audit.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/experimental/rhale.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/experimental/topology.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/experimental/vision.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/experimental/zest.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/explainers/__init__.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/explainers/shap_explainer.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/fairness.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/geometry.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/gtsf.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/guard.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/integrations/__init__.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/integrations/mlflow_integration.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/leakage.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/leakage_temporal.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/narrate.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/ood.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/paired.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/plots.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/psi_shap.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/ranking.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/report.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/rhale.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/rules/__init__.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/rules/business_rules.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/rules/rule_learner.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/score.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/shape_extensions.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/shapedist.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/silence.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/stability.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/studio.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/temporal.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/temporal_shape.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/topology.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/trending.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/universal.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/modelcheck/zest.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/shapeaudit/__init__.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/shapeaudit.egg-info/dependency_links.txt +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/shapeaudit.egg-info/entry_points.txt +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/shapeaudit.egg-info/requires.txt +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/shapeaudit.egg-info/top_level.txt +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_comparabilidade.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_consistency.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_edge_cases.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_fairness.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_golden.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_importance_core.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_laudo.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_llm_experimental.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_modelcheck.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_performance.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_pipeline.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_properties.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_public_api.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_regression.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_robustez_snapshot.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_schema_compat.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_shape_core.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_tutorial.py +0 -0
- {shapeaudit-1.0.2 → shapeaudit-1.0.4}/tests/test_vision_experimental.py +0 -0
|
@@ -1,180 +1,191 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: shapeaudit
|
|
3
|
-
Version: 1.0.
|
|
4
|
-
Summary: Descubra se o modelo novo aprendeu a mesma coisa que o antigo — auditoria entre duas versões, com laudo em HTML
|
|
5
|
-
Author-email: Riviane Maria Albuquerque Donha <riviane.donha@ufpr.br>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/Rivianee/shapeaudit
|
|
8
|
-
Keywords: machine-learning,mlops,model-monitoring,explainability,shap,model-audit,ci-cd
|
|
9
|
-
Classifier: Development Status :: 4 - Beta
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: Intended Audience :: Science/Research
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
-
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
-
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
-
Requires-Python: >=3.9
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
License-File: LICENSE
|
|
23
|
-
Requires-Dist: shap<0.53,>=0.44
|
|
24
|
-
Requires-Dist: pandas>=2.0.0
|
|
25
|
-
Requires-Dist: numpy>=1.24.0
|
|
26
|
-
Requires-Dist: pyyaml>=6.0
|
|
27
|
-
Requires-Dist: scipy>=1.10
|
|
28
|
-
Requires-Dist: matplotlib>=3.7
|
|
29
|
-
Requires-Dist: scikit-learn>=1.3
|
|
30
|
-
Provides-Extra: mlflow
|
|
31
|
-
Requires-Dist: mlflow-skinny>=2.10; extra == "mlflow"
|
|
32
|
-
Provides-Extra: xgboost
|
|
33
|
-
Requires-Dist: xgboost<3,>=1.7; extra == "xgboost"
|
|
34
|
-
Provides-Extra: lightgbm
|
|
35
|
-
Requires-Dist: lightgbm>=4.0; extra == "lightgbm"
|
|
36
|
-
Provides-Extra: evidently
|
|
37
|
-
Requires-Dist: evidently>=0.7; extra == "evidently"
|
|
38
|
-
Provides-Extra: fairlearn
|
|
39
|
-
Requires-Dist: fairlearn>=0.10; extra == "fairlearn"
|
|
40
|
-
Provides-Extra: dev
|
|
41
|
-
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
42
|
-
Dynamic: license-file
|
|
43
|
-
|
|
44
|
-
# ShapeAudit
|
|
45
|
-
|
|
46
|
-
**Seu modelo novo tem a mesma acurácia do antigo. Mas será que ele aprendeu a
|
|
47
|
-
mesma coisa?**
|
|
48
|
-
|
|
49
|
-
Toda vez que um modelo é retreinado, alguém precisa decidir se promove a versão
|
|
50
|
-
nova. Na prática, a decisão costuma ser uma só: *a acurácia caiu?* Se não caiu,
|
|
51
|
-
sobe.
|
|
52
|
-
|
|
53
|
-
O problema é que essa pergunta não cobre tudo. Duas versões podem acertar na
|
|
54
|
-
mesma proporção e **usar as variáveis de um jeito diferente** — inclusive uma
|
|
55
|
-
delas ter perdido uma coluna no caminho, sem que a métrica acuse.
|
|
56
|
-
|
|
57
|
-
Um exemplo real, com dados públicos de churn de telecom:
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
versão em produção: 0.815
|
|
61
|
-
versão candidata: 0.807
|
|
62
|
-
diferença: 0.008 ← praticamente nada
|
|
63
|
-
|
|
64
|
-
ShapeAudit: NÃO PROMOVA — a variável `Contract` deixou de
|
|
65
|
-
influenciar a previsão e chega constante no treino
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Uma coluna virou constante depois de uma mudança no pipeline. **Um gate por
|
|
69
|
-
métrica aprovaria essa versão.**
|
|
70
|
-
|
|
71
|
-
## Instalar
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
pip install shapeaudit
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
## Usar
|
|
78
|
-
|
|
79
|
-
```python
|
|
80
|
-
from shapeaudit import Guard
|
|
81
|
-
|
|
82
|
-
r = Guard().audit(modelo_antigo, modelo_novo, X_avaliacao, y_avaliacao,
|
|
83
|
-
X_train=dados_de_treino_do_modelo_novo)
|
|
84
|
-
|
|
85
|
-
print(r.verdict) # APROVADO | REVISAR | ATENCAO | BLOQUEADO
|
|
86
|
-
print(r.root_cause_features) # ['Contract']
|
|
87
|
-
open('laudo.html', 'w').write(r.to_html())
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
`X_train` é o treino do **modelo novo**. É esse argumento que separa "a coluna
|
|
91
|
-
sumiu do pipeline" de "o modelo deixou de usar a coluna". Sem ele a auditoria
|
|
92
|
-
roda, mas avisa que não verificou isso.
|
|
93
|
-
|
|
94
|
-
## O que você recebe
|
|
95
|
-
|
|
96
|
-
| veredito | o que aconteceu | o que fazer |
|
|
97
|
-
|---|---|---|
|
|
98
|
-
| **BLOQUEADO** | uma variável sumiu do modelo e está constante no treino | não promova: verifique o pipeline |
|
|
99
|
-
| **ATENÇÃO** | os dados chegam diferentes do treino, ou uma variável quase idêntica assumiu o lugar de outra | promova com monitoramento, recalibre |
|
|
100
|
-
| **REVISAR** | com os mesmos dados, o modelo novo responde de outro jeito | olhe as curvas antes de promover |
|
|
101
|
-
| **APROVADO** | nenhuma verificação encontrou mudança | pode promover |
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
O
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
Em
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shapeaudit
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: Descubra se o modelo novo aprendeu a mesma coisa que o antigo — auditoria entre duas versões, com laudo em HTML
|
|
5
|
+
Author-email: Riviane Maria Albuquerque Donha <riviane.donha@ufpr.br>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Rivianee/shapeaudit
|
|
8
|
+
Keywords: machine-learning,mlops,model-monitoring,explainability,shap,model-audit,ci-cd
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: shap<0.53,>=0.44
|
|
24
|
+
Requires-Dist: pandas>=2.0.0
|
|
25
|
+
Requires-Dist: numpy>=1.24.0
|
|
26
|
+
Requires-Dist: pyyaml>=6.0
|
|
27
|
+
Requires-Dist: scipy>=1.10
|
|
28
|
+
Requires-Dist: matplotlib>=3.7
|
|
29
|
+
Requires-Dist: scikit-learn>=1.3
|
|
30
|
+
Provides-Extra: mlflow
|
|
31
|
+
Requires-Dist: mlflow-skinny>=2.10; extra == "mlflow"
|
|
32
|
+
Provides-Extra: xgboost
|
|
33
|
+
Requires-Dist: xgboost<3,>=1.7; extra == "xgboost"
|
|
34
|
+
Provides-Extra: lightgbm
|
|
35
|
+
Requires-Dist: lightgbm>=4.0; extra == "lightgbm"
|
|
36
|
+
Provides-Extra: evidently
|
|
37
|
+
Requires-Dist: evidently>=0.7; extra == "evidently"
|
|
38
|
+
Provides-Extra: fairlearn
|
|
39
|
+
Requires-Dist: fairlearn>=0.10; extra == "fairlearn"
|
|
40
|
+
Provides-Extra: dev
|
|
41
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# ShapeAudit
|
|
45
|
+
|
|
46
|
+
**Seu modelo novo tem a mesma acurácia do antigo. Mas será que ele aprendeu a
|
|
47
|
+
mesma coisa?**
|
|
48
|
+
|
|
49
|
+
Toda vez que um modelo é retreinado, alguém precisa decidir se promove a versão
|
|
50
|
+
nova. Na prática, a decisão costuma ser uma só: *a acurácia caiu?* Se não caiu,
|
|
51
|
+
sobe.
|
|
52
|
+
|
|
53
|
+
O problema é que essa pergunta não cobre tudo. Duas versões podem acertar na
|
|
54
|
+
mesma proporção e **usar as variáveis de um jeito diferente** — inclusive uma
|
|
55
|
+
delas ter perdido uma coluna no caminho, sem que a métrica acuse.
|
|
56
|
+
|
|
57
|
+
Um exemplo real, com dados públicos de churn de telecom:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
versão em produção: 0.815
|
|
61
|
+
versão candidata: 0.807
|
|
62
|
+
diferença: 0.008 ← praticamente nada
|
|
63
|
+
|
|
64
|
+
ShapeAudit: NÃO PROMOVA — a variável `Contract` deixou de
|
|
65
|
+
influenciar a previsão e chega constante no treino
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Uma coluna virou constante depois de uma mudança no pipeline. **Um gate por
|
|
69
|
+
métrica aprovaria essa versão.**
|
|
70
|
+
|
|
71
|
+
## Instalar
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install shapeaudit
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Usar
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from shapeaudit import Guard
|
|
81
|
+
|
|
82
|
+
r = Guard().audit(modelo_antigo, modelo_novo, X_avaliacao, y_avaliacao,
|
|
83
|
+
X_train=dados_de_treino_do_modelo_novo)
|
|
84
|
+
|
|
85
|
+
print(r.verdict) # APROVADO | REVISAR | ATENCAO | BLOQUEADO | INDETERMINADO
|
|
86
|
+
print(r.root_cause_features) # ['Contract']
|
|
87
|
+
open('laudo.html', 'w', encoding='utf-8').write(r.to_html()) # laudo para abrir no navegador
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`X_train` é o treino do **modelo novo**. É esse argumento que separa "a coluna
|
|
91
|
+
sumiu do pipeline" de "o modelo deixou de usar a coluna". Sem ele a auditoria
|
|
92
|
+
roda, mas avisa que não verificou isso.
|
|
93
|
+
|
|
94
|
+
## O que você recebe
|
|
95
|
+
|
|
96
|
+
| veredito | o que aconteceu | o que fazer |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| **BLOQUEADO** | uma variável sumiu do modelo e está constante no treino | não promova: verifique o pipeline |
|
|
99
|
+
| **ATENÇÃO** | os dados chegam diferentes do treino, ou uma variável quase idêntica assumiu o lugar de outra | promova com monitoramento, recalibre |
|
|
100
|
+
| **REVISAR** | com os mesmos dados, o modelo novo responde de outro jeito | olhe as curvas antes de promover |
|
|
101
|
+
| **APROVADO** | nenhuma verificação encontrou mudança | pode promover |
|
|
102
|
+
| **INDETERMINADO** | uma verificação principal não rodou (ex.: base de avaliação pequena demais, modelo sem suporte) | não promova com base nesta auditoria: veja a lacuna declarada no laudo |
|
|
103
|
+
|
|
104
|
+
E um laudo HTML que responde, nesta ordem: o que aconteceu, o que mudou de
|
|
105
|
+
peso e de comportamento, onde exatamente mudou, se isso muda decisões, por onde
|
|
106
|
+
começar a investigar, e **o que não foi verificado**.
|
|
107
|
+
|
|
108
|
+
## Experimentar em dois minutos
|
|
109
|
+
|
|
110
|
+
Os arquivos abaixo estão no [repositório](https://github.com/Rivianee/shapeaudit),
|
|
111
|
+
não no pacote do `pip` — baixe o repositório (ou só o arquivo) antes.
|
|
112
|
+
|
|
113
|
+
- **No Colab**, sem instalar nada: abra `ShapeAudit_Colab.ipynb` e rode tudo.
|
|
114
|
+
Termina com o laudo e com um caso de pipeline quebrado.
|
|
115
|
+
- **Com uma planilha sua**: `python demo_excel.py meus_dados.xlsx nome_do_alvo`
|
|
116
|
+
(sem argumentos usa `exemplo_churn.xlsx`). Audita um retreino da sua
|
|
117
|
+
planilha e grava `laudo_shapeaudit.html`.
|
|
118
|
+
|
|
119
|
+
## Três perguntas, três camadas
|
|
120
|
+
|
|
121
|
+
1. **Alguma variável sumiu do modelo?** Compara o peso de cada variável nas
|
|
122
|
+
duas versões e distingue "a coluna sumiu" de "o modelo deixou de usar".
|
|
123
|
+
2. **Os dados chegam como no treino?** Compara o treino do modelo novo com os
|
|
124
|
+
dados que ele vai pontuar.
|
|
125
|
+
3. **O modelo passou a reagir de outro jeito?** Compara a curva de efeito de
|
|
126
|
+
cada variável entre as duas versões. Uma distância detecta a mudança e uma
|
|
127
|
+
classificação em cinco formas a nomeia.
|
|
128
|
+
|
|
129
|
+
O veredito usa só essas três. Extremos, subgrupos e impacto em grupos de
|
|
130
|
+
pessoas aparecem como verificações adicionais.
|
|
131
|
+
|
|
132
|
+
## Calibrar no seu caso
|
|
133
|
+
|
|
134
|
+
O limiar padrão veio de uma bancada sintética. Calibre com retreinos que você
|
|
135
|
+
**teria promovido** — não precisa de rótulo nem de falha conhecida:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from shapeaudit import Guard
|
|
139
|
+
from shapeaudit.frechet import calibrar_limiar
|
|
140
|
+
|
|
141
|
+
cal = calibrar_limiar([(modelo_jan, modelo_fev), ...], X_avaliacao)
|
|
142
|
+
r = Guard(frechet_threshold=cal['limiar']).audit(producao, candidata, X_avaliacao, y)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Em domínio com efeito por interação forte, calibrar não é opcional: com o
|
|
146
|
+
limiar padrão, retreinos saudáveis pedem revisão quase sempre
|
|
147
|
+
(`LIMITATIONS.md`, seção 7-quater).
|
|
148
|
+
|
|
149
|
+
Medido: de 0,12 a 0,47 em seis conjuntos de dados, e o limiar de modelos de
|
|
150
|
+
boosting é cinco a seis vezes o de florestas nos mesmos dados. Calibre por
|
|
151
|
+
domínio **e** por família de modelo.
|
|
152
|
+
|
|
153
|
+
## O que ele não faz
|
|
154
|
+
|
|
155
|
+
- **Não bloqueia degradação parcial:** com até 80% das linhas de uma variável
|
|
156
|
+
corrompidas, o modelo perde 4 pontos de R² e nada dispara.
|
|
157
|
+
- **Em bases pequenas** (menos de ~400 linhas de avaliação) pede revisão em
|
|
158
|
+
cerca de um terço dos retreinos saudáveis.
|
|
159
|
+
- **Fora de árvores** (MLP, ridge) é experimental: bloqueia falha de pipeline,
|
|
160
|
+
mas alarma mais e é cerca de dez vezes mais lento.
|
|
161
|
+
- **Não distingue sazonalidade de mudança real.**
|
|
162
|
+
- **Não diz qual das duas versões está certa** — só que elas discordam.
|
|
163
|
+
|
|
164
|
+
`LIMITATIONS.md` traz cada limite com o número medido.
|
|
165
|
+
|
|
166
|
+
## Confiança no veredito
|
|
167
|
+
|
|
168
|
+
Em 240 controles saudáveis de benchmark, 161 retreinos reais de séries públicas
|
|
169
|
+
do setor elétrico brasileiro e 12 conjuntos públicos, **nenhum retreino saudável
|
|
170
|
+
foi bloqueado**. Quando o pipeline quebrou de verdade, bloqueou 16 de 18 casos
|
|
171
|
+
nomeando a variável.
|
|
172
|
+
|
|
173
|
+
A recíproca não vale: não bloquear não garante que está tudo bem.
|
|
174
|
+
|
|
175
|
+
## Nomes
|
|
176
|
+
|
|
177
|
+
A distribuição é `shapeaudit`. Três nomes de import funcionam, e apontam para
|
|
178
|
+
a mesma implementação:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
import shapeaudit # nome atual
|
|
182
|
+
import modelguard # nome anterior (pacote `modelguard-ml`), mantido
|
|
183
|
+
import modelcheck # nome original, mantido por compatibilidade
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Quem já usava `modelguard-ml` não precisa mudar código: troque a instalação
|
|
187
|
+
para `shapeaudit` e os imports antigos continuam funcionando.
|
|
188
|
+
|
|
189
|
+
## Licença
|
|
190
|
+
|
|
191
|
+
MIT. README em português; a documentação de limites também.
|
|
@@ -39,9 +39,9 @@ from shapeaudit import Guard
|
|
|
39
39
|
r = Guard().audit(modelo_antigo, modelo_novo, X_avaliacao, y_avaliacao,
|
|
40
40
|
X_train=dados_de_treino_do_modelo_novo)
|
|
41
41
|
|
|
42
|
-
print(r.verdict) # APROVADO | REVISAR | ATENCAO | BLOQUEADO
|
|
42
|
+
print(r.verdict) # APROVADO | REVISAR | ATENCAO | BLOQUEADO | INDETERMINADO
|
|
43
43
|
print(r.root_cause_features) # ['Contract']
|
|
44
|
-
open('laudo.html', 'w').write(r.to_html())
|
|
44
|
+
open('laudo.html', 'w', encoding='utf-8').write(r.to_html()) # laudo para abrir no navegador
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
`X_train` é o treino do **modelo novo**. É esse argumento que separa "a coluna
|
|
@@ -56,6 +56,7 @@ roda, mas avisa que não verificou isso.
|
|
|
56
56
|
| **ATENÇÃO** | os dados chegam diferentes do treino, ou uma variável quase idêntica assumiu o lugar de outra | promova com monitoramento, recalibre |
|
|
57
57
|
| **REVISAR** | com os mesmos dados, o modelo novo responde de outro jeito | olhe as curvas antes de promover |
|
|
58
58
|
| **APROVADO** | nenhuma verificação encontrou mudança | pode promover |
|
|
59
|
+
| **INDETERMINADO** | uma verificação principal não rodou (ex.: base de avaliação pequena demais, modelo sem suporte) | não promova com base nesta auditoria: veja a lacuna declarada no laudo |
|
|
59
60
|
|
|
60
61
|
E um laudo HTML que responde, nesta ordem: o que aconteceu, o que mudou de
|
|
61
62
|
peso e de comportamento, onde exatamente mudou, se isso muda decisões, por onde
|
|
@@ -63,11 +64,14 @@ começar a investigar, e **o que não foi verificado**.
|
|
|
63
64
|
|
|
64
65
|
## Experimentar em dois minutos
|
|
65
66
|
|
|
67
|
+
Os arquivos abaixo estão no [repositório](https://github.com/Rivianee/shapeaudit),
|
|
68
|
+
não no pacote do `pip` — baixe o repositório (ou só o arquivo) antes.
|
|
69
|
+
|
|
66
70
|
- **No Colab**, sem instalar nada: abra `ShapeAudit_Colab.ipynb` e rode tudo.
|
|
71
|
+
Termina com o laudo e com um caso de pipeline quebrado.
|
|
67
72
|
- **Com uma planilha sua**: `python demo_excel.py meus_dados.xlsx nome_do_alvo`
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
ver os dois extremos.
|
|
73
|
+
(sem argumentos usa `exemplo_churn.xlsx`). Audita um retreino da sua
|
|
74
|
+
planilha e grava `laudo_shapeaudit.html`.
|
|
71
75
|
|
|
72
76
|
## Três perguntas, três camadas
|
|
73
77
|
|
|
@@ -88,10 +92,17 @@ O limiar padrão veio de uma bancada sintética. Calibre com retreinos que você
|
|
|
88
92
|
**teria promovido** — não precisa de rótulo nem de falha conhecida:
|
|
89
93
|
|
|
90
94
|
```python
|
|
95
|
+
from shapeaudit import Guard
|
|
91
96
|
from shapeaudit.frechet import calibrar_limiar
|
|
92
|
-
|
|
97
|
+
|
|
98
|
+
cal = calibrar_limiar([(modelo_jan, modelo_fev), ...], X_avaliacao)
|
|
99
|
+
r = Guard(frechet_threshold=cal['limiar']).audit(producao, candidata, X_avaliacao, y)
|
|
93
100
|
```
|
|
94
101
|
|
|
102
|
+
Em domínio com efeito por interação forte, calibrar não é opcional: com o
|
|
103
|
+
limiar padrão, retreinos saudáveis pedem revisão quase sempre
|
|
104
|
+
(`LIMITATIONS.md`, seção 7-quater).
|
|
105
|
+
|
|
95
106
|
Medido: de 0,12 a 0,47 em seis conjuntos de dados, e o limiar de modelos de
|
|
96
107
|
boosting é cinco a seis vezes o de florestas nos mesmos dados. Calibre por
|
|
97
108
|
domínio **e** por família de modelo.
|
|
@@ -102,7 +102,14 @@ def ale_shape_profile(model,
|
|
|
102
102
|
Xhi[feature] = bins[i + 1]
|
|
103
103
|
|
|
104
104
|
# Efeito local médio do bin
|
|
105
|
-
|
|
105
|
+
# BUG 84 (v1.0.4): chamava model.predict() direto — em classificador
|
|
106
|
+
# com 3+ classes, rótulo virava número e um par saudável saía
|
|
107
|
+
# APROVADO sem lacuna. Agora usa a mesma saída do ALE rápido, que
|
|
108
|
+
# falha de propósito com 3+ classes (vira lacuna declarada).
|
|
109
|
+
from modelcheck.ale_fast import _predict as _pred_ale
|
|
110
|
+
# .values: mantém o comportamento anterior com X sem os nomes do
|
|
111
|
+
# treino (numpy convertido) — passar o DataFrame quebrava o predict
|
|
112
|
+
delta = _pred_ale(model, Xhi.values) - _pred_ale(model, Xlo.values)
|
|
106
113
|
ale_means.append(float(delta.mean()))
|
|
107
114
|
centers.append((bins[i] + bins[i + 1]) / 2)
|
|
108
115
|
|
|
@@ -33,8 +33,17 @@ def _predict(model, X: pd.DataFrame) -> np.ndarray:
|
|
|
33
33
|
p = model.predict_proba(X)
|
|
34
34
|
if p.ndim == 2 and p.shape[1] == 2:
|
|
35
35
|
return np.asarray(p[:, 1], dtype=float)
|
|
36
|
+
if p.ndim == 2 and p.shape[1] > 2:
|
|
37
|
+
# BUG 70 (v1.0.4): com 3+ classes caía em predict() e tratava
|
|
38
|
+
# o rótulo como número (0->2 valia o dobro de 0->1). Não há
|
|
39
|
+
# curva única honesta aqui: falha explícita, e a camada vira
|
|
40
|
+
# lacuna declarada em vez de número errado.
|
|
41
|
+
raise ValueError('curva de efeito para classificador com '
|
|
42
|
+
f'{p.shape[1]} classes não é suportada')
|
|
43
|
+
except ValueError:
|
|
44
|
+
raise
|
|
36
45
|
except Exception:
|
|
37
|
-
pass
|
|
46
|
+
pass # noqa: lacuna-nao-aplicavel — sem predict_proba utilizável
|
|
38
47
|
return np.asarray(model.predict(X), dtype=float)
|
|
39
48
|
|
|
40
49
|
|