From 3c7e73534209a93053110ecad5482cda654d4a3a Mon Sep 17 00:00:00 2001 From: duxin Date: Tue, 28 Jul 2026 16:38:07 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=A2=84=E6=B5=8B=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E7=99=BE=E5=88=86=E4=BD=8D=E8=A3=81=E5=89=AA=20=E2=80=94=20?= =?UTF-8?q?=E6=B6=88=E9=99=A4=E6=9E=81=E7=AB=AF=E5=BC=82=E5=B8=B8=E5=80=BC?= =?UTF-8?q?=E6=8B=89=E7=88=86=E4=B8=93=E9=A2=98=E5=9B=BE=E8=89=B2=E9=98=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 _clip_outliers(P1-P99) 方法,预测完成后自动裁剪 - 水体边界零值区域导致模型外推极端值(-86~7026), 裁剪后克里金插值色阶不再被拉爆,正常空间细节可见 - 无异常值时不触发裁剪(零开销) --- src/core/prediction/inference_batch.py | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/core/prediction/inference_batch.py b/src/core/prediction/inference_batch.py index 3536187..d4001cd 100644 --- a/src/core/prediction/inference_batch.py +++ b/src/core/prediction/inference_batch.py @@ -740,12 +740,52 @@ class WaterQualityInference: print(f"预测值范围: [{np.min(predictions):.4f}, {np.max(predictions):.4f}]") print(f"预测值统计: 均值={np.mean(predictions):.4f}, 标准差={np.std(predictions):.4f}") + # ★ 百分位裁剪:去除极端异常值,避免专题图色阶被拉爆 + predictions = self._clip_outliers(predictions) + return predictions except Exception as e: print(f"预测失败: {e}") raise + @staticmethod + def _clip_outliers(predictions: np.ndarray, lower_pct: float = 1.0, + upper_pct: float = 99.0) -> np.ndarray: + """百分位裁剪:将极端异常值裁剪到合理范围。 + + 水体边界/零值区域的光谱异常会导致模型外推到极端值 + (如 BGA 预测 -86 ~ 7026),若不处理,专题图的克里金 + 插值色阶会被拉爆,正常空间变化完全不可见。 + + Parameters + ---------- + predictions : np.ndarray + 原始预测值 + lower_pct : float + 下百分位(默认 1%,低于此分位数的值被裁剪) + upper_pct : float + 上百分位(默认 99%,高于此分位数的值被裁剪) + + Returns + ------- + np.ndarray + 裁剪后的预测值(副本) + """ + lo = np.percentile(predictions, lower_pct) + hi = np.percentile(predictions, upper_pct) + # 只在实际有异常值时才裁剪 + if lo >= hi: + return predictions + n_clipped_lo = int(np.sum(predictions < lo)) + n_clipped_hi = int(np.sum(predictions > hi)) + if n_clipped_lo == 0 and n_clipped_hi == 0: + return predictions + print(f"[异常值裁剪] P{lower_pct:.0f}={lo:.4f}, P{upper_pct:.0f}={hi:.4f}, " + f"裁剪低端 {n_clipped_lo} 个, 高端 {n_clipped_hi} 个 " + f"({(n_clipped_lo + n_clipped_hi) / len(predictions) * 100:.1f}%)") + return np.clip(predictions, lo, hi) + def save_predictions(self, coords: pd.DataFrame, predictions: np.ndarray, output_path: str, prediction_column: str = 'prediction', wqi_columns: Optional[pd.DataFrame] = None):