@nahisaho/satori 0.22.0 → 0.24.0
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.
- package/README.md +52 -20
- package/package.json +1 -1
- package/src/.github/skills/scientific-active-learning/SKILL.md +289 -0
- package/src/.github/skills/scientific-advanced-visualization/SKILL.md +310 -0
- package/src/.github/skills/scientific-anomaly-detection/SKILL.md +296 -0
- package/src/.github/skills/scientific-automl/SKILL.md +264 -0
- package/src/.github/skills/scientific-causal-ml/SKILL.md +240 -0
- package/src/.github/skills/scientific-data-profiling/SKILL.md +247 -0
- package/src/.github/skills/scientific-ensemble-methods/SKILL.md +263 -0
- package/src/.github/skills/scientific-geospatial-analysis/SKILL.md +274 -0
- package/src/.github/skills/scientific-interactive-dashboard/SKILL.md +346 -0
- package/src/.github/skills/scientific-missing-data-analysis/SKILL.md +312 -0
- package/src/.github/skills/scientific-model-monitoring/SKILL.md +247 -0
- package/src/.github/skills/scientific-network-visualization/SKILL.md +278 -0
- package/src/.github/skills/scientific-reproducible-reporting/SKILL.md +330 -0
- package/src/.github/skills/scientific-time-series-forecasting/SKILL.md +246 -0
- package/src/.github/skills/scientific-transfer-learning/SKILL.md +298 -0
- package/src/.github/skills/scientific-uncertainty-quantification/SKILL.md +286 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scientific-uncertainty-quantification
|
|
3
|
+
description: |
|
|
4
|
+
不確実性定量化スキル。Conformal Prediction・MC Dropout・
|
|
5
|
+
深層アンサンブル・アレアトリック / エピステミック分離・
|
|
6
|
+
Calibration Curve・予測区間推定・Expected Calibration Error。
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Scientific Uncertainty Quantification
|
|
10
|
+
|
|
11
|
+
機械学習予測の不確実性を定量化し、信頼できる予測区間と
|
|
12
|
+
校正された確率推定を提供する。
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
- モデル予測の信頼度を定量化したいとき
|
|
17
|
+
- 予測区間・信頼区間を推定するとき
|
|
18
|
+
- 校正曲線でモデルの確率推定品質を評価するとき
|
|
19
|
+
- Conformal Prediction で分布フリーの保証区間を得るとき
|
|
20
|
+
- MC Dropout でベイズ近似推論するとき
|
|
21
|
+
- アレアトリック / エピステミック不確実性を分離するとき
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
## 1. Conformal Prediction
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
import numpy as np
|
|
31
|
+
import pandas as pd
|
|
32
|
+
from sklearn.base import BaseEstimator
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def conformal_prediction(model, X_calib, y_calib, X_test,
|
|
36
|
+
alpha=0.1, method="quantile"):
|
|
37
|
+
"""
|
|
38
|
+
Conformal Prediction — 分布フリーの予測区間推定。
|
|
39
|
+
|
|
40
|
+
Parameters:
|
|
41
|
+
model: BaseEstimator — 学習済みモデル
|
|
42
|
+
X_calib: np.ndarray — 校正データ特徴量
|
|
43
|
+
y_calib: np.ndarray — 校正データ目的変数
|
|
44
|
+
X_test: np.ndarray — テストデータ特徴量
|
|
45
|
+
alpha: float — 有意水準 (1-α がカバレッジ保証)
|
|
46
|
+
method: str — "quantile" / "cqr" (Conformalized Quantile Regression)
|
|
47
|
+
"""
|
|
48
|
+
# 分類 or 回帰を自動判定
|
|
49
|
+
if hasattr(model, "predict_proba"):
|
|
50
|
+
return _conformal_classification(model, X_calib, y_calib, X_test, alpha)
|
|
51
|
+
else:
|
|
52
|
+
return _conformal_regression(model, X_calib, y_calib, X_test, alpha, method)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _conformal_regression(model, X_calib, y_calib, X_test, alpha, method):
|
|
56
|
+
preds_calib = model.predict(X_calib)
|
|
57
|
+
residuals = np.abs(y_calib - preds_calib)
|
|
58
|
+
|
|
59
|
+
# (1-α)(1 + 1/n) 分位点
|
|
60
|
+
n = len(residuals)
|
|
61
|
+
q = np.quantile(residuals, np.ceil((1 - alpha) * (n + 1)) / n)
|
|
62
|
+
|
|
63
|
+
preds_test = model.predict(X_test)
|
|
64
|
+
lower = preds_test - q
|
|
65
|
+
upper = preds_test + q
|
|
66
|
+
|
|
67
|
+
coverage = None
|
|
68
|
+
width = np.mean(upper - lower)
|
|
69
|
+
|
|
70
|
+
result_df = pd.DataFrame({
|
|
71
|
+
"prediction": preds_test,
|
|
72
|
+
"lower": lower,
|
|
73
|
+
"upper": upper,
|
|
74
|
+
"interval_width": upper - lower
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
print(f"Conformal Prediction (α={alpha}): "
|
|
78
|
+
f"mean width = {width:.4f}")
|
|
79
|
+
return result_df
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _conformal_classification(model, X_calib, y_calib, X_test, alpha):
|
|
83
|
+
proba_calib = model.predict_proba(X_calib)
|
|
84
|
+
n_classes = proba_calib.shape[1]
|
|
85
|
+
|
|
86
|
+
# Non-conformity score = 1 - 正解クラスの確率
|
|
87
|
+
scores = 1 - proba_calib[np.arange(len(y_calib)), y_calib]
|
|
88
|
+
n = len(scores)
|
|
89
|
+
q = np.quantile(scores, np.ceil((1 - alpha) * (n + 1)) / n)
|
|
90
|
+
|
|
91
|
+
proba_test = model.predict_proba(X_test)
|
|
92
|
+
prediction_sets = []
|
|
93
|
+
for i in range(len(X_test)):
|
|
94
|
+
pred_set = np.where(proba_test[i] >= 1 - q)[0].tolist()
|
|
95
|
+
prediction_sets.append(pred_set)
|
|
96
|
+
|
|
97
|
+
avg_size = np.mean([len(s) for s in prediction_sets])
|
|
98
|
+
print(f"Conformal Classification (α={alpha}): "
|
|
99
|
+
f"avg set size = {avg_size:.2f}")
|
|
100
|
+
return prediction_sets
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 2. MC Dropout
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
def mc_dropout_predict(model, X, n_forward=100, dropout_rate=0.1):
|
|
107
|
+
"""
|
|
108
|
+
MC Dropout — ベイズ近似推論。
|
|
109
|
+
|
|
110
|
+
Parameters:
|
|
111
|
+
model: nn.Module — ドロップアウト付きモデル
|
|
112
|
+
X: torch.Tensor — 入力テンソル
|
|
113
|
+
n_forward: int — フォワードパス回数
|
|
114
|
+
dropout_rate: float — ドロップアウト率
|
|
115
|
+
"""
|
|
116
|
+
import torch
|
|
117
|
+
|
|
118
|
+
model.train() # Dropout を有効にする
|
|
119
|
+
|
|
120
|
+
predictions = []
|
|
121
|
+
with torch.no_grad():
|
|
122
|
+
for _ in range(n_forward):
|
|
123
|
+
pred = model(X)
|
|
124
|
+
if pred.dim() > 1 and pred.shape[1] > 1:
|
|
125
|
+
pred = torch.softmax(pred, dim=1)
|
|
126
|
+
predictions.append(pred.cpu().numpy())
|
|
127
|
+
|
|
128
|
+
predictions = np.array(predictions) # (n_forward, n_samples, ...)
|
|
129
|
+
mean_pred = predictions.mean(axis=0)
|
|
130
|
+
std_pred = predictions.std(axis=0)
|
|
131
|
+
|
|
132
|
+
# エピステミック不確実性: 予測のばらつき
|
|
133
|
+
epistemic = std_pred
|
|
134
|
+
# 分類の場合: 予測エントロピー
|
|
135
|
+
if mean_pred.ndim == 2 and mean_pred.shape[1] > 1:
|
|
136
|
+
entropy = -np.sum(mean_pred * np.log(mean_pred + 1e-10), axis=1)
|
|
137
|
+
else:
|
|
138
|
+
entropy = None
|
|
139
|
+
|
|
140
|
+
print(f"MC Dropout: {n_forward} passes, "
|
|
141
|
+
f"mean epistemic unc = {epistemic.mean():.4f}")
|
|
142
|
+
return {"mean": mean_pred, "std": std_pred,
|
|
143
|
+
"epistemic": epistemic, "entropy": entropy}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 3. 深層アンサンブル不確実性
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
def deep_ensemble_uncertainty(models, X, task="classification"):
|
|
150
|
+
"""
|
|
151
|
+
深層アンサンブル不確実性推定 (Lakshminarayanan+ 2017)。
|
|
152
|
+
|
|
153
|
+
Parameters:
|
|
154
|
+
models: list[nn.Module] — アンサンブルメンバー
|
|
155
|
+
X: torch.Tensor — 入力テンソル
|
|
156
|
+
task: str — "classification" / "regression"
|
|
157
|
+
"""
|
|
158
|
+
import torch
|
|
159
|
+
|
|
160
|
+
all_preds = []
|
|
161
|
+
for m in models:
|
|
162
|
+
m.eval()
|
|
163
|
+
with torch.no_grad():
|
|
164
|
+
pred = m(X)
|
|
165
|
+
if task == "classification":
|
|
166
|
+
pred = torch.softmax(pred, dim=1)
|
|
167
|
+
all_preds.append(pred.cpu().numpy())
|
|
168
|
+
|
|
169
|
+
all_preds = np.array(all_preds) # (M, N, ...)
|
|
170
|
+
|
|
171
|
+
# 平均予測
|
|
172
|
+
mean_pred = all_preds.mean(axis=0)
|
|
173
|
+
|
|
174
|
+
if task == "classification":
|
|
175
|
+
# アレアトリック: 平均エントロピー
|
|
176
|
+
aleatoric = np.mean([
|
|
177
|
+
-np.sum(p * np.log(p + 1e-10), axis=1) for p in all_preds
|
|
178
|
+
], axis=0)
|
|
179
|
+
# 全体エントロピー
|
|
180
|
+
total_entropy = -np.sum(mean_pred * np.log(mean_pred + 1e-10), axis=1)
|
|
181
|
+
# エピステミック = 全体 - アレアトリック (相互情報量)
|
|
182
|
+
epistemic = total_entropy - aleatoric
|
|
183
|
+
else:
|
|
184
|
+
# 回帰: 分散分解
|
|
185
|
+
aleatoric = np.zeros(len(X)) # 各モデルの分散 (ここでは簡略)
|
|
186
|
+
epistemic = all_preds.var(axis=0).squeeze()
|
|
187
|
+
total_entropy = None
|
|
188
|
+
|
|
189
|
+
result = {
|
|
190
|
+
"mean_prediction": mean_pred,
|
|
191
|
+
"aleatoric": aleatoric,
|
|
192
|
+
"epistemic": epistemic,
|
|
193
|
+
"total_uncertainty": (aleatoric + epistemic) if task == "regression" else total_entropy
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
print(f"Deep Ensemble ({len(models)} models): "
|
|
197
|
+
f"epistemic={epistemic.mean():.4f}, "
|
|
198
|
+
f"aleatoric={aleatoric.mean():.4f}")
|
|
199
|
+
return result
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## 4. Calibration 評価
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
def calibration_analysis(y_true, y_proba, n_bins=10):
|
|
206
|
+
"""
|
|
207
|
+
校正曲線とECE (Expected Calibration Error) を計算。
|
|
208
|
+
|
|
209
|
+
Parameters:
|
|
210
|
+
y_true: np.ndarray — 真のラベル (0/1)
|
|
211
|
+
y_proba: np.ndarray — 予測確率
|
|
212
|
+
n_bins: int — ビン数
|
|
213
|
+
"""
|
|
214
|
+
import matplotlib.pyplot as plt
|
|
215
|
+
from sklearn.calibration import calibration_curve
|
|
216
|
+
|
|
217
|
+
# 校正曲線
|
|
218
|
+
frac_pos, mean_pred = calibration_curve(
|
|
219
|
+
y_true, y_proba, n_bins=n_bins, strategy="uniform")
|
|
220
|
+
|
|
221
|
+
# ECE
|
|
222
|
+
bin_edges = np.linspace(0, 1, n_bins + 1)
|
|
223
|
+
ece = 0.0
|
|
224
|
+
bin_stats = []
|
|
225
|
+
for i in range(n_bins):
|
|
226
|
+
mask = (y_proba >= bin_edges[i]) & (y_proba < bin_edges[i + 1])
|
|
227
|
+
if mask.sum() == 0:
|
|
228
|
+
continue
|
|
229
|
+
bin_acc = y_true[mask].mean()
|
|
230
|
+
bin_conf = y_proba[mask].mean()
|
|
231
|
+
bin_count = mask.sum()
|
|
232
|
+
ece += (bin_count / len(y_true)) * abs(bin_acc - bin_conf)
|
|
233
|
+
bin_stats.append({
|
|
234
|
+
"bin": f"[{bin_edges[i]:.1f}, {bin_edges[i+1]:.1f})",
|
|
235
|
+
"count": int(bin_count),
|
|
236
|
+
"accuracy": bin_acc,
|
|
237
|
+
"confidence": bin_conf,
|
|
238
|
+
"gap": abs(bin_acc - bin_conf)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
# 可視化
|
|
242
|
+
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
|
|
243
|
+
|
|
244
|
+
# 校正曲線
|
|
245
|
+
ax1.plot([0, 1], [0, 1], "k--", label="Perfect calibration")
|
|
246
|
+
ax1.plot(mean_pred, frac_pos, "o-", label=f"Model (ECE={ece:.4f})")
|
|
247
|
+
ax1.set_xlabel("Mean predicted probability")
|
|
248
|
+
ax1.set_ylabel("Fraction of positives")
|
|
249
|
+
ax1.set_title("Calibration Curve")
|
|
250
|
+
ax1.legend()
|
|
251
|
+
|
|
252
|
+
# 信頼度ヒストグラム
|
|
253
|
+
ax2.hist(y_proba, bins=n_bins, edgecolor="black", alpha=0.7)
|
|
254
|
+
ax2.set_xlabel("Predicted probability")
|
|
255
|
+
ax2.set_ylabel("Count")
|
|
256
|
+
ax2.set_title("Confidence Histogram")
|
|
257
|
+
|
|
258
|
+
plt.tight_layout()
|
|
259
|
+
plt.savefig("calibration_analysis.png", dpi=150, bbox_inches="tight")
|
|
260
|
+
plt.close()
|
|
261
|
+
|
|
262
|
+
stats_df = pd.DataFrame(bin_stats)
|
|
263
|
+
print(f"Calibration: ECE = {ece:.4f}, {n_bins} bins")
|
|
264
|
+
return {"ece": ece, "bin_stats": stats_df, "fig": "calibration_analysis.png"}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## パイプライン統合
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
ensemble-methods → uncertainty-quantification → explainable-ai
|
|
273
|
+
(アンサンブル) (不確実性定量化) (説明可能 AI)
|
|
274
|
+
│ │ ↓
|
|
275
|
+
active-learning ──────────┘ bayesian-statistics
|
|
276
|
+
(能動学習) (ベイズ統計)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## パイプライン出力
|
|
280
|
+
|
|
281
|
+
| ファイル | 説明 | 次スキル |
|
|
282
|
+
|---------|------|---------|
|
|
283
|
+
| `conformal_intervals.csv` | Conformal 予測区間 | → reporting |
|
|
284
|
+
| `mc_dropout_uncertainty.csv` | MC Dropout 不確実性 | → active-learning |
|
|
285
|
+
| `calibration_analysis.png` | 校正曲線 | → presentation |
|
|
286
|
+
| `uncertainty_decomposition.json` | 分離結果 | → explainable-ai |
|