|
44 | 44 | # --------------------------------------------------------- |
45 | 45 | # A. LAYER 3 VALIDATION (CAUSAL LAYER) |
46 | 46 | # --------------------------------------------------------- |
47 | | -print('\n--- SECTION A: CAUSAL LAYER ---') |
| 47 | +print('\n--- SECTION A: LAYER 3A (CAUSAL LAYER) ---') |
| 48 | +print('Note: Faults in benchmark datasets are experimentally assigned (compositional).') |
| 49 | +print( |
| 50 | + 'This section serves as a mathematical demonstration of the SCM capability, not as discovered causal effects.' |
| 51 | +) |
48 | 52 | datasets_dict = {'CWRU': data_cwru, 'PU': data_pu, 'XJTU': data_xjtu} |
49 | 53 |
|
50 | 54 | causal_results = [] |
|
54 | 58 | causal_results.append( |
55 | 59 | { |
56 | 60 | 'Dataset': name, |
57 | | - 'Causal Effect': f'{res["ate"]:.4f}', |
| 61 | + 'ATE (Demo)': f'{res["ate"]:.4f}', |
58 | 62 | 'CI': f'[{res["ci"][0]:.4f}, {res["ci"][1]:.4f}]', |
59 | 63 | 'Significance': f'p={res["p_value"]:.4f}', |
60 | 64 | } |
61 | 65 | ) |
62 | 66 |
|
63 | | -print(f'{"Dataset":<10} | {"Causal Effect":<15} | {"95% CI":<20} | {"Significance"}') |
| 67 | +print(f'{"Dataset":<10} | {"ATE (Demo)":<15} | {"95% CI":<20} | {"Significance"}') |
64 | 68 | print('-' * 65) |
65 | 69 | for r in causal_results: |
66 | | - print(f'{r["Dataset"]:<10} | {r["Causal Effect"]:<15} | {r["CI"]:<20} | {r["Significance"]}') |
| 70 | + print(f'{r["Dataset"]:<10} | {r["ATE (Demo)"]:<15} | {r["CI"]:<20} | {r["Significance"]}') |
67 | 71 |
|
68 | 72 | # Generate Visual Causal Graph |
69 | 73 | G = nx.DiGraph() |
|
114 | 118 | # Pick an example: A severe fault in XJTU target data |
115 | 119 | fault_idx = np.where(data_xjtu.y > 0)[0][10] |
116 | 120 | actual_cond = data_xjtu.cond[fault_idx] |
117 | | -cf_cond = 35.0 if actual_cond == 37.5 else 37.5 |
| 121 | +cf_cond = 15.0 # Intervene with a genuinely different speed (900 RPM) to see real risk delta |
118 | 122 |
|
119 | 123 | cf_res = model_xjtu.what_if(data_xjtu, fault_idx, cf_cond) |
120 | 124 |
|
|
161 | 165 | ) |
162 | 166 | report = model_xjtu.diagnose(subset_data) |
163 | 167 |
|
164 | | -print(f'{"Machine":<10} | {"CNSD Decision":<20} | {"Expert Decision":<15} | {"Match"}') |
165 | | -print('-' * 60) |
| 168 | +print(f'{"Machine":<10} | {"CNSD Recommended Action":<40}') |
| 169 | +print('-' * 55) |
166 | 170 |
|
167 | 171 | maintenance_priority = [] |
168 | 172 |
|
169 | 173 | for i, rec in enumerate(report.records[:10]): |
170 | 174 | machine_id = f'M-{i + 1:03d}' |
171 | 175 | cnsd_dec = rec['action'] |
172 | | - expert_dec = 'Immediate Shutdown' if subset_data.y[i] > 0 else 'Normal Operation' |
173 | | - |
174 | | - # Simple semantic match logic |
175 | | - match = ( |
176 | | - 'YES' |
177 | | - if ( |
178 | | - subset_data.y[i] > 0 |
179 | | - and ( |
180 | | - 'Shutdown' in cnsd_dec or 'Maintenance' in cnsd_dec or 'inspect' in cnsd_dec.lower() |
181 | | - ) |
182 | | - ) |
183 | | - or (subset_data.y[i] == 0 and 'monitor' in cnsd_dec.lower()) |
184 | | - else 'NO' |
185 | | - ) |
186 | 176 |
|
187 | | - print(f'{machine_id:<10} | {cnsd_dec[:18]:<20} | {expert_dec:<15} | {match}') |
| 177 | + print(f'{machine_id:<10} | {cnsd_dec:<40}') |
188 | 178 |
|
189 | 179 | # Priority ranking score = CNN confidence + severity if confirmed |
190 | 180 | score = rec['cnn_confidence'] * (1 if rec['status'] == 'CONFIRMED_FAULT' else 0) |
|
195 | 185 | for rank, (m_id, score, verdict) in enumerate(maintenance_priority[:5]): |
196 | 186 | print(f'Rank {rank + 1}: {m_id} (Priority Score: {score:.2f}, Verdict: {verdict})') |
197 | 187 |
|
198 | | -# --------------------------------------------------------- |
199 | | -# F. OPERATIONAL UTILITY STUDY |
200 | | -# --------------------------------------------------------- |
201 | | -print('\n--- SECTION F: OPERATIONAL UTILITY ---') |
202 | | -# Costs |
203 | | -COST_FALSE_ALARM = -500 |
204 | | -COST_MISSED_FAULT = -5000 |
205 | | -COST_HIT = 2000 |
206 | | - |
207 | | -# Evaluate on the 50 subset |
208 | | -baseline_preds = model_xjtu.cnn.predict(subset_data.X, verbose=0).argmax(1) |
209 | | -cnsd_statuses = [r['status'] for r in report.records] |
210 | | -gt = subset_data.y |
211 | | - |
212 | | - |
213 | | -def calc_utility(preds, is_cnsd=False): |
214 | | - tp = fp = fn = tn = 0 |
215 | | - for i in range(len(gt)): |
216 | | - actual_fault = gt[i] > 0 |
217 | | - if is_cnsd: |
218 | | - cnsd_dec = report.records[i]['action'] |
219 | | - pred_fault = ( |
220 | | - 'Shutdown' in cnsd_dec or 'Maintenance' in cnsd_dec or 'inspect' in cnsd_dec.lower() |
221 | | - ) |
222 | | - else: |
223 | | - pred_fault = preds[i] > 0 |
224 | | - |
225 | | - if pred_fault and actual_fault: |
226 | | - tp += 1 |
227 | | - elif pred_fault and not actual_fault: |
228 | | - fp += 1 |
229 | | - elif not pred_fault and actual_fault: |
230 | | - fn += 1 |
231 | | - else: |
232 | | - tn += 1 |
233 | | - |
234 | | - cost = (tp * COST_HIT) + (fp * COST_FALSE_ALARM) + (fn * COST_MISSED_FAULT) |
235 | | - return tp, fp, cost |
236 | | - |
237 | | - |
238 | | -base_tp, base_fp, base_cost = calc_utility(baseline_preds, False) |
239 | | -cnsd_tp, cnsd_fp, cnsd_cost = calc_utility(None, True) |
240 | | - |
241 | | -print(f'{"Method":<10} | {"Early Warning (TP)":<20} | {"False Alarms":<15} | {"Maintenance Value"}') |
242 | | -print('-' * 75) |
243 | | -print(f'{"Baseline":<10} | {base_tp:<20} | {base_fp:<15} | ${base_cost:,}') |
244 | | -print(f'{"CNSD":<10} | {cnsd_tp:<20} | {cnsd_fp:<15} | ${cnsd_cost:,}') |
| 188 | +# SECTION F OPERATIONAL UTILITY REMOVED (As requested by Abhi, performance was identical to baseline and weakened the paper) |
0 commit comments