ai-critic 0.1.0__py3-none-any.whl → 0.2.1__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.
- ai_critic/critic.py +67 -10
- ai_critic/evaluators/config.py +34 -5
- ai_critic/evaluators/data.py +53 -10
- ai_critic/evaluators/performance.py +40 -8
- ai_critic/evaluators/robustness.py +46 -10
- ai_critic/evaluators/summary.py +96 -0
- ai_critic-0.2.1.dist-info/METADATA +258 -0
- ai_critic-0.2.1.dist-info/RECORD +12 -0
- ai_critic-0.1.0.dist-info/METADATA +0 -64
- ai_critic-0.1.0.dist-info/RECORD +0 -11
- {ai_critic-0.1.0.dist-info → ai_critic-0.2.1.dist-info}/WHEEL +0 -0
- {ai_critic-0.1.0.dist-info → ai_critic-0.2.1.dist-info}/top_level.txt +0 -0
ai_critic/critic.py
CHANGED
|
@@ -4,10 +4,13 @@ from ai_critic.evaluators import (
|
|
|
4
4
|
data,
|
|
5
5
|
performance
|
|
6
6
|
)
|
|
7
|
+
from ai_critic.evaluators.summary import HumanSummary
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
class AICritic:
|
|
9
11
|
"""
|
|
10
|
-
|
|
12
|
+
Automated reviewer for scikit-learn models.
|
|
13
|
+
Produces a multi-layered risk assessment with visualizations.
|
|
11
14
|
"""
|
|
12
15
|
|
|
13
16
|
def __init__(self, model, X, y):
|
|
@@ -15,16 +18,70 @@ class AICritic:
|
|
|
15
18
|
self.X = X
|
|
16
19
|
self.y = y
|
|
17
20
|
|
|
18
|
-
def evaluate(self):
|
|
19
|
-
|
|
21
|
+
def evaluate(self, view="all", plot=False):
|
|
22
|
+
"""
|
|
23
|
+
view:
|
|
24
|
+
- "all"
|
|
25
|
+
- "executive"
|
|
26
|
+
- "technical"
|
|
27
|
+
- "details"
|
|
28
|
+
- list of views
|
|
29
|
+
plot:
|
|
30
|
+
- True: gera gráficos de learning curve, heatmap e robustez
|
|
31
|
+
- False: sem gráficos
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
# =========================
|
|
35
|
+
# Low-level technical details
|
|
36
|
+
# =========================
|
|
37
|
+
details = {}
|
|
38
|
+
|
|
39
|
+
# Data analysis + heatmap
|
|
40
|
+
data_report = data(self.X, self.y, plot=plot)
|
|
41
|
+
details["data"] = data_report
|
|
42
|
+
|
|
43
|
+
# Model configuration
|
|
44
|
+
details["config"] = config(
|
|
45
|
+
self.model,
|
|
46
|
+
n_samples=data_report["n_samples"],
|
|
47
|
+
n_features=data_report["n_features"]
|
|
48
|
+
)
|
|
20
49
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
self.model, self.X, self.y
|
|
50
|
+
# Performance + learning curve
|
|
51
|
+
details["performance"] = performance(
|
|
52
|
+
self.model, self.X, self.y, plot=plot
|
|
25
53
|
)
|
|
26
|
-
|
|
27
|
-
|
|
54
|
+
|
|
55
|
+
# Robustness + CV clean vs noisy
|
|
56
|
+
details["robustness"] = robustness(
|
|
57
|
+
self.model,
|
|
58
|
+
self.X,
|
|
59
|
+
self.y,
|
|
60
|
+
leakage_suspected=data_report["data_leakage"]["suspected"],
|
|
61
|
+
plot=plot
|
|
28
62
|
)
|
|
29
63
|
|
|
30
|
-
|
|
64
|
+
# =========================
|
|
65
|
+
# Human interpretation
|
|
66
|
+
# =========================
|
|
67
|
+
human = HumanSummary().generate(details)
|
|
68
|
+
|
|
69
|
+
# =========================
|
|
70
|
+
# Full payload
|
|
71
|
+
# =========================
|
|
72
|
+
payload = {
|
|
73
|
+
"executive": human["executive_summary"],
|
|
74
|
+
"technical": human["technical_summary"],
|
|
75
|
+
"details": details
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# =========================
|
|
79
|
+
# View selector
|
|
80
|
+
# =========================
|
|
81
|
+
if view == "all":
|
|
82
|
+
return payload
|
|
83
|
+
|
|
84
|
+
if isinstance(view, list):
|
|
85
|
+
return {k: payload[k] for k in view if k in payload}
|
|
86
|
+
|
|
87
|
+
return payload.get(view)
|
ai_critic/evaluators/config.py
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
def evaluate(model, n_samples=None, n_features=None):
|
|
4
|
+
params = model.get_params()
|
|
5
|
+
model_type = type(model).__name__
|
|
6
|
+
|
|
7
|
+
report = {
|
|
8
|
+
"model_type": model_type,
|
|
9
|
+
"n_params": len(params),
|
|
10
|
+
"uses_random_state": "random_state" in params
|
|
6
11
|
}
|
|
12
|
+
|
|
13
|
+
# 🧠 Structural overfitting heuristics
|
|
14
|
+
warnings = []
|
|
15
|
+
|
|
16
|
+
if n_samples and hasattr(model, "max_depth"):
|
|
17
|
+
max_depth = params.get("max_depth")
|
|
18
|
+
if max_depth is not None:
|
|
19
|
+
recommended_depth = math.log2(n_samples)
|
|
20
|
+
if max_depth > recommended_depth:
|
|
21
|
+
warnings.append({
|
|
22
|
+
"issue": "structural_overfitting_risk",
|
|
23
|
+
"max_depth": max_depth,
|
|
24
|
+
"recommended_max_depth": int(recommended_depth),
|
|
25
|
+
"message": "Tree depth may be too high for dataset size."
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
if n_samples and n_features and n_features > n_samples:
|
|
29
|
+
warnings.append({
|
|
30
|
+
"issue": "high_feature_sample_ratio",
|
|
31
|
+
"message": "More features than samples can cause instability."
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
report["structural_warnings"] = warnings
|
|
35
|
+
return report
|
ai_critic/evaluators/data.py
CHANGED
|
@@ -1,14 +1,57 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
import seaborn as sns
|
|
4
|
+
import pandas as pd
|
|
2
5
|
|
|
3
|
-
def evaluate(X, y):
|
|
4
|
-
|
|
5
|
-
"n_samples": X.shape[0],
|
|
6
|
-
"n_features": X.shape[1],
|
|
7
|
-
"has_nan": bool(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
def evaluate(X, y, plot=False):
|
|
7
|
+
report = {
|
|
8
|
+
"n_samples": int(X.shape[0]),
|
|
9
|
+
"n_features": int(X.shape[1]),
|
|
10
|
+
"has_nan": bool(np.isnan(X).any() or np.isnan(y).any())
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
# Class balance
|
|
14
|
+
if len(set(y)) < 20:
|
|
15
|
+
values, counts = np.unique(y, return_counts=True)
|
|
16
|
+
report["class_balance"] = {int(v): int(c) for v, c in zip(values, counts)}
|
|
17
|
+
else:
|
|
18
|
+
report["class_balance"] = "many_classes"
|
|
19
|
+
|
|
20
|
+
# Data leakage detection
|
|
21
|
+
suspicious_features = []
|
|
22
|
+
y_mean = np.mean(y)
|
|
23
|
+
y_centered = y - y_mean
|
|
24
|
+
for i in range(X.shape[1]):
|
|
25
|
+
feature = X[:, i]
|
|
26
|
+
if np.std(feature) == 0:
|
|
27
|
+
continue
|
|
28
|
+
corr = np.corrcoef(feature, y_centered)[0, 1]
|
|
29
|
+
if abs(corr) > 0.98:
|
|
30
|
+
suspicious_features.append({"feature_index": int(i), "correlation": float(corr)})
|
|
31
|
+
|
|
32
|
+
report["data_leakage"] = {
|
|
33
|
+
"suspected": bool(len(suspicious_features) > 0),
|
|
34
|
+
"details": suspicious_features,
|
|
35
|
+
"message": (
|
|
36
|
+
"Highly correlated features may reveal the target directly."
|
|
37
|
+
if suspicious_features else "No obvious data leakage detected."
|
|
13
38
|
)
|
|
14
39
|
}
|
|
40
|
+
|
|
41
|
+
# =========================
|
|
42
|
+
# Heatmap de correlação Features x Target
|
|
43
|
+
# =========================
|
|
44
|
+
if plot:
|
|
45
|
+
feature_names = [f"feat_{i}" for i in range(X.shape[1])]
|
|
46
|
+
df = pd.DataFrame(X, columns=feature_names)
|
|
47
|
+
df['target'] = y
|
|
48
|
+
corr_matrix = df.corr()
|
|
49
|
+
|
|
50
|
+
plt.figure(figsize=(10,8))
|
|
51
|
+
sns.heatmap(corr_matrix, annot=False, cmap="coolwarm") # <- removi os números
|
|
52
|
+
plt.title("Correlação Features x Target")
|
|
53
|
+
plt.tight_layout()
|
|
54
|
+
plt.savefig("heatmap_correlation.png", dpi=150) # Salva automaticamente
|
|
55
|
+
plt.show()
|
|
56
|
+
|
|
57
|
+
return report
|
|
@@ -1,11 +1,43 @@
|
|
|
1
|
-
from sklearn.model_selection import cross_val_score
|
|
1
|
+
from sklearn.model_selection import cross_val_score, learning_curve
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
import numpy as np
|
|
2
4
|
|
|
3
|
-
def evaluate(model, X, y):
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
)
|
|
5
|
+
def evaluate(model, X, y, plot=False):
|
|
6
|
+
# CV básico
|
|
7
|
+
scores = cross_val_score(model, X, y, cv=3)
|
|
8
|
+
mean = float(scores.mean())
|
|
9
|
+
std = float(scores.std())
|
|
10
|
+
suspicious = mean > 0.995
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
"cv_mean_score":
|
|
10
|
-
"cv_std":
|
|
12
|
+
result = {
|
|
13
|
+
"cv_mean_score": mean,
|
|
14
|
+
"cv_std": std,
|
|
15
|
+
"suspiciously_perfect": suspicious,
|
|
16
|
+
"message": (
|
|
17
|
+
"Perfect CV score detected — possible data leakage."
|
|
18
|
+
if suspicious else "CV performance within expected range."
|
|
19
|
+
)
|
|
11
20
|
}
|
|
21
|
+
|
|
22
|
+
# =========================
|
|
23
|
+
# Learning curve
|
|
24
|
+
# =========================
|
|
25
|
+
if plot:
|
|
26
|
+
train_sizes, train_scores, test_scores = learning_curve(
|
|
27
|
+
model, X, y, cv=3, train_sizes=np.linspace(0.1, 1.0, 5)
|
|
28
|
+
)
|
|
29
|
+
plt.figure(figsize=(6,4))
|
|
30
|
+
plt.plot(train_sizes, np.mean(train_scores, axis=1), label="Treino")
|
|
31
|
+
plt.plot(train_sizes, np.mean(test_scores, axis=1), label="Validação")
|
|
32
|
+
plt.fill_between(train_sizes,
|
|
33
|
+
np.mean(test_scores, axis=1)-np.std(test_scores, axis=1),
|
|
34
|
+
np.mean(test_scores, axis=1)+np.std(test_scores, axis=1),
|
|
35
|
+
alpha=0.2)
|
|
36
|
+
plt.xlabel("Amostra de treino")
|
|
37
|
+
plt.ylabel("Score")
|
|
38
|
+
plt.title("Learning Curve")
|
|
39
|
+
plt.legend()
|
|
40
|
+
plt.tight_layout()
|
|
41
|
+
plt.show()
|
|
42
|
+
|
|
43
|
+
return result
|
|
@@ -1,18 +1,54 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
from sklearn.base import clone
|
|
3
|
+
from sklearn.model_selection import cross_val_score
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
3
5
|
|
|
4
|
-
def evaluate(model, X, y):
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
def evaluate(model, X, y, leakage_suspected=False, plot=False):
|
|
7
|
+
noise_level = 0.02 # 2% relative noise
|
|
8
|
+
scale = np.std(X)
|
|
9
|
+
noise = np.random.normal(0, noise_level * scale, X.shape)
|
|
10
|
+
X_noisy = X + noise
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
model_clean = clone(model)
|
|
13
|
+
model_noisy = clone(model)
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
score_clean = cross_val_score(model_clean, X, y, cv=3, n_jobs=1).mean()
|
|
16
|
+
score_noisy = cross_val_score(model_noisy, X_noisy, y, cv=3, n_jobs=1).mean()
|
|
17
|
+
drop = score_clean - score_noisy
|
|
18
|
+
|
|
19
|
+
# =========================
|
|
20
|
+
# Verdict
|
|
21
|
+
# =========================
|
|
22
|
+
if leakage_suspected and score_clean > 0.98:
|
|
23
|
+
verdict = "misleading"
|
|
24
|
+
message = (
|
|
25
|
+
"Model appears robust to noise, but original performance is "
|
|
26
|
+
"likely inflated due to data leakage."
|
|
27
|
+
)
|
|
28
|
+
elif drop > 0.15:
|
|
29
|
+
verdict = "fragile"
|
|
30
|
+
message = "Model performance degrades significantly under noise."
|
|
31
|
+
else:
|
|
32
|
+
verdict = "stable"
|
|
33
|
+
message = "Model shows acceptable robustness to noise."
|
|
34
|
+
|
|
35
|
+
# =========================
|
|
36
|
+
# Plot CV Clean vs Noisy
|
|
37
|
+
# =========================
|
|
38
|
+
if plot:
|
|
39
|
+
plt.figure(figsize=(4,4))
|
|
40
|
+
plt.bar(["Original", "Com Ruído"], [score_clean, score_noisy], color=['green','red'])
|
|
41
|
+
plt.ylabel("CV Score")
|
|
42
|
+
plt.title("Robustez do Modelo")
|
|
43
|
+
plt.ylim(0, 1)
|
|
44
|
+
plt.tight_layout()
|
|
45
|
+
plt.savefig("robustness.png", dpi=150) # Salva automaticamente
|
|
46
|
+
plt.show()
|
|
13
47
|
|
|
14
48
|
return {
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
49
|
+
"cv_score_original": float(score_clean),
|
|
50
|
+
"cv_score_noisy": float(score_noisy),
|
|
51
|
+
"performance_drop": float(drop),
|
|
52
|
+
"verdict": verdict,
|
|
53
|
+
"message": message
|
|
18
54
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
class HumanSummary:
|
|
2
|
+
"""
|
|
3
|
+
Builds a hierarchical, human-centered interpretation
|
|
4
|
+
of the AI Critic technical report.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
def generate(self, report: dict) -> dict:
|
|
8
|
+
leakage = report["data"]["data_leakage"]["suspected"]
|
|
9
|
+
perfect_cv = report["performance"]["suspiciously_perfect"]
|
|
10
|
+
robustness_verdict = report["robustness"].get("verdict")
|
|
11
|
+
structural_warnings = report["config"]["structural_warnings"]
|
|
12
|
+
|
|
13
|
+
# =========================
|
|
14
|
+
# Executive summary
|
|
15
|
+
# =========================
|
|
16
|
+
if leakage and perfect_cv:
|
|
17
|
+
verdict = "❌ Unreliable"
|
|
18
|
+
risk_level = "high"
|
|
19
|
+
deploy = False
|
|
20
|
+
main_reason = "Strong evidence of data leakage inflating model performance."
|
|
21
|
+
elif robustness_verdict in ("fragile", "misleading") or structural_warnings:
|
|
22
|
+
verdict = "⚠️ Risky"
|
|
23
|
+
risk_level = "medium"
|
|
24
|
+
deploy = False
|
|
25
|
+
main_reason = "Structural or robustness-related risks detected."
|
|
26
|
+
else:
|
|
27
|
+
verdict = "✅ Acceptable"
|
|
28
|
+
risk_level = "low"
|
|
29
|
+
deploy = True
|
|
30
|
+
main_reason = "No critical risks detected."
|
|
31
|
+
|
|
32
|
+
executive_summary = {
|
|
33
|
+
"verdict": verdict,
|
|
34
|
+
"risk_level": risk_level,
|
|
35
|
+
"deploy_recommended": deploy,
|
|
36
|
+
"main_reason": main_reason,
|
|
37
|
+
"one_line_explanation": (
|
|
38
|
+
"Although validation accuracy is extremely high, multiple signals "
|
|
39
|
+
"indicate that the model does not generalize reliably."
|
|
40
|
+
if verdict == "❌ Unreliable"
|
|
41
|
+
else
|
|
42
|
+
"The model shows acceptable behavior under current evaluation heuristics."
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# =========================
|
|
47
|
+
# Technical summary
|
|
48
|
+
# =========================
|
|
49
|
+
key_risks = []
|
|
50
|
+
recommendations = []
|
|
51
|
+
|
|
52
|
+
if leakage:
|
|
53
|
+
key_risks.append(
|
|
54
|
+
"Data leakage suspected due to near-perfect feature–target correlation."
|
|
55
|
+
)
|
|
56
|
+
recommendations.append(
|
|
57
|
+
"Audit and remove features highly correlated with the target."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if perfect_cv:
|
|
61
|
+
key_risks.append(
|
|
62
|
+
"Perfect cross-validation score detected (statistically unlikely)."
|
|
63
|
+
)
|
|
64
|
+
recommendations.append(
|
|
65
|
+
"Re-run validation after leakage mitigation."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
for w in structural_warnings:
|
|
69
|
+
key_risks.append(w["message"])
|
|
70
|
+
recommendations.append(
|
|
71
|
+
"Reduce model complexity or adjust hyperparameters."
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if robustness_verdict == "misleading":
|
|
75
|
+
key_risks.append(
|
|
76
|
+
"Robustness metrics are misleading due to inflated baseline performance."
|
|
77
|
+
)
|
|
78
|
+
recommendations.append(
|
|
79
|
+
"Fix baseline performance issues before trusting robustness metrics."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
technical_summary = {
|
|
83
|
+
"key_risks": key_risks or ["No significant risks detected."],
|
|
84
|
+
"model_health": {
|
|
85
|
+
"data_leakage": leakage,
|
|
86
|
+
"suspicious_cv": perfect_cv,
|
|
87
|
+
"structural_risk": bool(structural_warnings),
|
|
88
|
+
"robustness_verdict": robustness_verdict
|
|
89
|
+
},
|
|
90
|
+
"recommendations": recommendations
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
"executive_summary": executive_summary,
|
|
95
|
+
"technical_summary": technical_summary
|
|
96
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ai-critic
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Fast AI evaluator for scikit-learn models
|
|
5
|
+
Author-email: Luiz Seabra <filipedemarco@yahoo.com>
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: numpy
|
|
9
|
+
Requires-Dist: scikit-learn
|
|
10
|
+
|
|
11
|
+
# ai-critic: Automated Risk Auditor for Machine Learning Models**
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 🚀 What is ai-critic?
|
|
16
|
+
|
|
17
|
+
`ai-critic` é um **auditor de risco automatizado baseado em heurísticas** para modelos de *machine learning*. Ele avalia modelos treinados antes da implantação e traduz riscos técnicos de ML em decisões claras e centradas no ser humano.
|
|
18
|
+
|
|
19
|
+
Em vez de apenas relatar métricas, o `ai-critic` responde à pergunta crítica:
|
|
20
|
+
|
|
21
|
+
> “Este modelo pode ser implantado com segurança?”
|
|
22
|
+
|
|
23
|
+
Ele faz isso analisando as principais áreas de risco:
|
|
24
|
+
|
|
25
|
+
* **Integridade dos Dados:** (*data leakage*, desequilíbrio, NaNs)
|
|
26
|
+
* **Estrutura do Modelo:** (risco de *overfitting*, complexidade)
|
|
27
|
+
* **Comportamento de Validação:** (pontuações suspeitamente perfeitas)
|
|
28
|
+
* **Robustez:** (sensibilidade a ruído)
|
|
29
|
+
|
|
30
|
+
Os resultados são organizados em três camadas semânticas para diferentes *stakeholders*:
|
|
31
|
+
|
|
32
|
+
* **Executiva:** (tomadores de decisão)
|
|
33
|
+
* **Técnica:** (engenheiros de ML)
|
|
34
|
+
* **Detalhada:** (auditores e depuração)
|
|
35
|
+
|
|
36
|
+
## 🎯 Por que o ai-critic Existe: Filosofia Central
|
|
37
|
+
|
|
38
|
+
A maioria das ferramentas de ML:
|
|
39
|
+
|
|
40
|
+
* assume que métricas = verdade
|
|
41
|
+
* confia cegamente na validação cruzada
|
|
42
|
+
* despeja números brutos sem interpretação
|
|
43
|
+
|
|
44
|
+
O `ai-critic` é cético por design.
|
|
45
|
+
|
|
46
|
+
Ele trata:
|
|
47
|
+
|
|
48
|
+
* pontuações perfeitas como **sinais**, não sucesso
|
|
49
|
+
* métricas de robustez como **dependentes do contexto**
|
|
50
|
+
* implantação como uma **decisão de risco**, não um limite de métrica
|
|
51
|
+
|
|
52
|
+
A filosofia central é: **Métricas não falham modelos — o contexto falha.**
|
|
53
|
+
|
|
54
|
+
O `ai-critic` aplica heurísticas de raciocínio humano à avaliação de ML:
|
|
55
|
+
|
|
56
|
+
* “Isso é bom demais para ser verdade?”
|
|
57
|
+
* “Isso pode estar vazando o alvo (*target*)?”
|
|
58
|
+
* “A robustez importa se a linha de base estiver errada?”
|
|
59
|
+
|
|
60
|
+
## 🛠️ Instalação
|
|
61
|
+
|
|
62
|
+
Instale o `ai-critic` via pip:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install ai-critic
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Requisitos:**
|
|
69
|
+
|
|
70
|
+
* Python ≥ 3.8
|
|
71
|
+
* `scikit-learn`
|
|
72
|
+
|
|
73
|
+
## 💡 Início Rápido
|
|
74
|
+
|
|
75
|
+
Audite seu modelo treinado em apenas algumas linhas:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from sklearn.datasets import load_breast_cancer
|
|
79
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
80
|
+
from ai_critic import AICritic
|
|
81
|
+
|
|
82
|
+
# 1. Carregar dados e treinar um modelo (exemplo)
|
|
83
|
+
X, y = load_breast_cancer(return_X_y=True)
|
|
84
|
+
model = RandomForestClassifier(max_depth=20, random_state=42)
|
|
85
|
+
model.fit(X, y) # O modelo deve estar treinado
|
|
86
|
+
|
|
87
|
+
# 2. Inicializar e avaliar com ai-critic
|
|
88
|
+
critic = AICritic(model, X, y)
|
|
89
|
+
report = critic.evaluate()
|
|
90
|
+
|
|
91
|
+
# A visualização padrão é 'all' (todas as camadas)
|
|
92
|
+
print(report)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 🧩 Saída Multi-Camadas
|
|
96
|
+
|
|
97
|
+
O `ai-critic` nunca despeja tudo de uma vez. Ele estrutura os resultados em camadas de decisão claras.
|
|
98
|
+
|
|
99
|
+
### 🔹 Visualização Executiva (`view="executive"`)
|
|
100
|
+
|
|
101
|
+
Projetado para CTOs, gerentes e *stakeholders*. Sem jargão de ML.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
critic.evaluate(view="executive")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Exemplo de Saída:**
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"verdict": "❌ Não Confiável",
|
|
112
|
+
"risk_level": "high",
|
|
113
|
+
"deploy_recommended": false,
|
|
114
|
+
"main_reason": "Forte evidência de vazamento de dados inflando o desempenho do modelo."
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 🔹 Visualização Técnica (`view="technical"`)
|
|
119
|
+
|
|
120
|
+
Projetado para engenheiros de ML. Acionável, diagnóstico e focado no que precisa ser corrigido.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
critic.evaluate(view="technical")
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Exemplo de Saída:**
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"key_risks": [
|
|
131
|
+
"Vazamento de dados suspeito devido à correlação quase perfeita entre recurso e alvo.",
|
|
132
|
+
"Pontuação de validação cruzada perfeita detectada (estatisticamente improvável).",
|
|
133
|
+
"A profundidade da árvore pode ser muito alta para o tamanho do conjunto de dados."
|
|
134
|
+
],
|
|
135
|
+
"model_health": {
|
|
136
|
+
"data_leakage": true,
|
|
137
|
+
"suspicious_cv": true,
|
|
138
|
+
"structural_risk": true,
|
|
139
|
+
"robustness_verdict": "misleading"
|
|
140
|
+
},
|
|
141
|
+
"recommendations": [
|
|
142
|
+
"Auditar e remover recursos com vazamento.",
|
|
143
|
+
"Reduzir a complexidade do modelo.",
|
|
144
|
+
"Executar novamente a validação após a mitigação do vazamento."
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 🔹 Visualização Detalhada (`view="details"`)
|
|
150
|
+
|
|
151
|
+
Projetado para auditoria, depuração e conformidade.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
critic.evaluate(view="details")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Inclui:
|
|
158
|
+
|
|
159
|
+
* Métricas brutas
|
|
160
|
+
* Correlações de recursos
|
|
161
|
+
* Pontuações de robustez
|
|
162
|
+
* Avisos estruturais
|
|
163
|
+
* Rastreabilidade completa
|
|
164
|
+
|
|
165
|
+
### 🔹 Visualização Combinada (`view="all"`)
|
|
166
|
+
|
|
167
|
+
Retorna todas as três camadas em um único dicionário.
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
critic.evaluate(view="all")
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Retorna:**
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"executive": {...},
|
|
178
|
+
"technical": {...},
|
|
179
|
+
"details": {...}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## ⚙️ API Principal
|
|
184
|
+
|
|
185
|
+
### `AICritic`
|
|
186
|
+
|
|
187
|
+
| Parâmetro | Descrição |
|
|
188
|
+
| :--- | :--- |
|
|
189
|
+
| `model` | Modelo `scikit-learn` treinado |
|
|
190
|
+
| `X` | Matriz de recursos |
|
|
191
|
+
| `y` | Vetor alvo |
|
|
192
|
+
|
|
193
|
+
**Uso:** `AICritic(model, X, y)`
|
|
194
|
+
|
|
195
|
+
### `evaluate()`
|
|
196
|
+
|
|
197
|
+
| Parâmetro | Descrição |
|
|
198
|
+
| :--- | :--- |
|
|
199
|
+
| `view` | Camada de saída desejada: `"executive"`, `"technical"`, `"details"`, ou `"all"` (padrão) |
|
|
200
|
+
|
|
201
|
+
**Uso:** `evaluate(view="all")`
|
|
202
|
+
|
|
203
|
+
## 🧠 O que o ai-critic Detecta
|
|
204
|
+
|
|
205
|
+
| Categoria | Riscos Detectados |
|
|
206
|
+
| :--- | :--- |
|
|
207
|
+
| **🔍 Riscos de Dados** | Vazamento de alvo via correlação, NaNs, desequilíbrio de classes |
|
|
208
|
+
| **🧱 Riscos Estruturais** | Árvores excessivamente complexas, altas taxas de recurso/amostra, *configuration smells* |
|
|
209
|
+
| **📈 Riscos de Validação** | Pontuações de CV suspeitosamente perfeitas, variância irreal |
|
|
210
|
+
| **🧪 Riscos de Robustez** | Sensibilidade a ruído, robustez enganosa se a linha de base estiver inflada |
|
|
211
|
+
|
|
212
|
+
## 🧪 Exemplo: Detectando Vazamento de Dados
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
import numpy as np
|
|
216
|
+
# ... (imports e código do modelo)
|
|
217
|
+
|
|
218
|
+
# Vazamento artificial: adicionando o alvo como um recurso
|
|
219
|
+
X_leaky = np.hstack([X, y.reshape(-1, 1)])
|
|
220
|
+
|
|
221
|
+
critic = AICritic(model, X_leaky, y)
|
|
222
|
+
executive_report = critic.evaluate(view="executive")
|
|
223
|
+
|
|
224
|
+
print(executive_report)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Saída (Visualização Executiva):**
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
❌ Não Confiável
|
|
231
|
+
Forte evidência de vazamento de dados inflando o desempenho do modelo.
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## 🛡️ Melhores Práticas
|
|
235
|
+
|
|
236
|
+
* Execute o `ai-critic` antes da implantação.
|
|
237
|
+
* Nunca confie cegamente em pontuações de CV perfeitas.
|
|
238
|
+
* Use a Visualização Executiva em seu *pipeline* de CI/CD como um portão de modelo.
|
|
239
|
+
* Use a Visualização Técnica durante a iteração do modelo.
|
|
240
|
+
* Use a Visualização Detalhada para auditoria e conformidade.
|
|
241
|
+
|
|
242
|
+
## 🧭 Casos de Uso Típicos
|
|
243
|
+
|
|
244
|
+
* Auditorias de modelo pré-implantação
|
|
245
|
+
* Governança e conformidade de ML
|
|
246
|
+
* Portões de modelo CI/CD
|
|
247
|
+
* Ensino de ceticismo em ML
|
|
248
|
+
* Explicação de risco de ML para *stakeholders* não técnicos
|
|
249
|
+
|
|
250
|
+
## 📄 Licença
|
|
251
|
+
|
|
252
|
+
Distribuído sob a Licença MIT.
|
|
253
|
+
|
|
254
|
+
## 🧠 Nota Final
|
|
255
|
+
|
|
256
|
+
O `ai-critic` não é uma ferramenta de *benchmarking*. É uma **ferramenta de decisão**.
|
|
257
|
+
|
|
258
|
+
Se um modelo falhar aqui, não significa que seja ruim — significa que **não deve ser confiável ainda**.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
ai_critic/__init__.py,sha256=H6DlPMmbcFUamhsNULPLk9vHx81XCiXuKKf63EJ8eM0,53
|
|
2
|
+
ai_critic/critic.py,sha256=4fKwR7LtXGlQihstZWqauBZT2BmbaSLpuO9FBV42tus,2287
|
|
3
|
+
ai_critic/evaluators/__init__.py,sha256=Jmmz9899YD__4Uj3bA6R7vYOwlH2giPc1wuCSLv7FVw,170
|
|
4
|
+
ai_critic/evaluators/config.py,sha256=gBXaS8Qxl14f40JnvMWgA0Z0SGEtbCuCHpTOPem0H90,1163
|
|
5
|
+
ai_critic/evaluators/data.py,sha256=YAK5NkwCeJOny_UueZ5ALwvEcRDIbEck404eV2oqWnc,1871
|
|
6
|
+
ai_critic/evaluators/performance.py,sha256=JpXM_7-RN_q_FvXga4TkSVBBo90Nk0AdBWbjmS-D1oI,1469
|
|
7
|
+
ai_critic/evaluators/robustness.py,sha256=UiGTpE-h2d2U19p1Ce4XpcMv4NMb2I4MmrlTrsPTIag,1808
|
|
8
|
+
ai_critic/evaluators/summary.py,sha256=drP43HrWu1WJfLiUIQVczJ5Qs8sfoywpXFdavd2ogQI,3516
|
|
9
|
+
ai_critic-0.2.1.dist-info/METADATA,sha256=TFYwZAG1AvWnvVweQpBS3p9obX11h_M_CNVFgzt6MLs,7064
|
|
10
|
+
ai_critic-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
+
ai_critic-0.2.1.dist-info/top_level.txt,sha256=TRyZkm1vyLLcFDg_80yeg5cHvPis_oW1Ti170417jkw,10
|
|
12
|
+
ai_critic-0.2.1.dist-info/RECORD,,
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: ai-critic
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Fast AI evaluator for scikit-learn models
|
|
5
|
-
Author-email: Luiz Seabra <seu-email@exemplo.com>
|
|
6
|
-
Requires-Python: >=3.9
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
|
-
Requires-Dist: numpy
|
|
9
|
-
Requires-Dist: scikit-learn
|
|
10
|
-
|
|
11
|
-
# AI Critic 🧠⚖️
|
|
12
|
-
|
|
13
|
-
**AI Critic** is a fast evaluator for scikit-learn models.
|
|
14
|
-
It analyzes configuration, robustness, data quality and performance in minutes.
|
|
15
|
-
|
|
16
|
-
## Install
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
pip install ai-critic
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Quick Example
|
|
23
|
-
|
|
24
|
-
```python
|
|
25
|
-
from sklearn.datasets import load_breast_cancer
|
|
26
|
-
from sklearn.ensemble import RandomForestClassifier
|
|
27
|
-
from ai_critic import AICritic
|
|
28
|
-
|
|
29
|
-
X, y = load_breast_cancer(return_X_y=True)
|
|
30
|
-
|
|
31
|
-
model = RandomForestClassifier(n_estimators=50, max_depth=3)
|
|
32
|
-
|
|
33
|
-
critic = AICritic(model, X, y)
|
|
34
|
-
report = critic.evaluate()
|
|
35
|
-
|
|
36
|
-
print(report)
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## What it evaluates
|
|
40
|
-
|
|
41
|
-
* Model configuration sanity
|
|
42
|
-
* Data consistency
|
|
43
|
-
* Robustness to noise
|
|
44
|
-
* Basic performance metrics
|
|
45
|
-
|
|
46
|
-
## Philosophy
|
|
47
|
-
|
|
48
|
-
Fast, modular, and brutally honest AI evaluation.
|
|
49
|
-
|
|
50
|
-
📌 README simples = mais confiança
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## Development & Testing
|
|
55
|
-
|
|
56
|
-
To test the package locally as an end-user (mandatory for development):
|
|
57
|
-
|
|
58
|
-
In the root directory:
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
pip install -e .
|
|
62
|
-
python -c "from ai_critic import AICritic; print('OK')"
|
|
63
|
-
python -m pytest
|
|
64
|
-
```
|
ai_critic-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
ai_critic/__init__.py,sha256=H6DlPMmbcFUamhsNULPLk9vHx81XCiXuKKf63EJ8eM0,53
|
|
2
|
-
ai_critic/critic.py,sha256=ulkj6A6flREQy4HMdFj8ktp-iH_-5bsGkxsYE6zCMBE,635
|
|
3
|
-
ai_critic/evaluators/__init__.py,sha256=Jmmz9899YD__4Uj3bA6R7vYOwlH2giPc1wuCSLv7FVw,170
|
|
4
|
-
ai_critic/evaluators/config.py,sha256=3q4_Wg-lbrM_I_tcZl4InL0_OijlHNtnDBnewaGDtco,195
|
|
5
|
-
ai_critic/evaluators/data.py,sha256=gbDz1NdJ3vZuXRGV3mQtpfLgmVRnowfCTCJ0RH5SlxM,359
|
|
6
|
-
ai_critic/evaluators/performance.py,sha256=25Ja6jOaSc-maSVJNABrhGrxElYQH7IDGGbYx_Rr0J8,257
|
|
7
|
-
ai_critic/evaluators/robustness.py,sha256=InBx3bmTTGBU8yQ4I-E81xOC2gEAOpwpKRSqkxn2Cw4,435
|
|
8
|
-
ai_critic-0.1.0.dist-info/METADATA,sha256=iO4Oz0mSHsm0YzpeEzmwzAwJaNvY2hzb2dfn4RbBSaE,1286
|
|
9
|
-
ai_critic-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
-
ai_critic-0.1.0.dist-info/top_level.txt,sha256=TRyZkm1vyLLcFDg_80yeg5cHvPis_oW1Ti170417jkw,10
|
|
11
|
-
ai_critic-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|