@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.
@@ -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 |