fix: MNFTransformer 数值稳定性 — 防 inf/NaN 导致 SVR 拒绝输入
- fit: 正则化按协方差总迹的1%自适应 + 截断噪声主导特征值 - fit: 输入先做 nan_to_num 清洗 - transform: 输出 clip 到 float32 安全范围,防止极端值 - 噪声协方差正则化从固定 1e-9 改为 trace*0.01,对 113 维特征更稳健
This commit is contained in:
@ -378,37 +378,48 @@ class MNFTransformer(TransformerMixin, BaseEstimator, _ArrayAsFloat64):
|
||||
|
||||
def fit(self, X, y=None):
|
||||
X = self._to_ndarray(X)
|
||||
X = np.nan_to_num(X, nan=0.0, posinf=0.0, neginf=0.0)
|
||||
n_samples, n_features = X.shape
|
||||
|
||||
# 1) 噪声矩阵:相邻行差分(假设输入按空间/时间连续排列)
|
||||
# 1) 噪声矩阵:相邻行差分
|
||||
noise = X[1:] - X[:-1] # (n-1, p)
|
||||
Cn = np.cov(noise, rowvar=False) # (p, p)
|
||||
Cn += np.eye(n_features) * 1e-9 # 防止奇异
|
||||
# 正则化:按总方差的 1% 加对角项,强于固定 1e-9
|
||||
_reg = np.trace(Cn) / n_features * 0.01
|
||||
Cn += np.eye(n_features) * max(_reg, 1e-6)
|
||||
|
||||
# 2) 噪声白化:Cn = U diag(S) Vt — 对称矩阵用 eigh
|
||||
# 2) 噪声白化:仅对显著特征值做白化,忽略噪声主导的成分
|
||||
eigvals_n, eigvecs_n = np.linalg.eigh(Cn)
|
||||
# eigh 返回升序排列;白化矩阵 = V @ diag(1/sqrt(s)) @ V.T
|
||||
inv_sqrt = 1.0 / np.sqrt(np.maximum(eigvals_n, 1e-12))
|
||||
Wn = eigvecs_n @ np.diag(inv_sqrt) @ eigvecs_n.T # (p, p)
|
||||
# 截断:特征值 < max * 1e-4 的视为噪声,不参与白化(置单位矩阵)
|
||||
_keep_mask = eigvals_n > eigvals_n.max() * 1e-4
|
||||
if _keep_mask.sum() < 2:
|
||||
_keep_mask[:2] = True # 至少保留 2 个成分
|
||||
_kept_vals = np.where(_keep_mask, eigvals_n, 1.0)
|
||||
inv_sqrt = np.where(_keep_mask, 1.0 / np.sqrt(np.maximum(_kept_vals, 1e-12)), 1.0)
|
||||
Wn = eigvecs_n @ np.diag(inv_sqrt) @ eigvecs_n.T
|
||||
|
||||
# 3) 均值
|
||||
self.mean_ = X.mean(axis=0, keepdims=True) # (1, p)
|
||||
self.mean_ = X.mean(axis=0, keepdims=True)
|
||||
|
||||
# 4) 白化后的数据协方差 → PCA
|
||||
X_w = (X - self.mean_) @ Wn # (n, p)
|
||||
Cw = np.cov(X_w, rowvar=False) # (p, p)
|
||||
# 4) 白化后 PCA
|
||||
X_w = (X - self.mean_) @ Wn
|
||||
Cw = np.cov(X_w, rowvar=False)
|
||||
eigvals_w, eigvecs_w = np.linalg.eigh(Cw)
|
||||
# 降序排列(eigh 升序,翻转)
|
||||
Wp = eigvecs_w[:, ::-1] # (p, p)
|
||||
Wp = eigvecs_w[:, ::-1]
|
||||
|
||||
# 5) 最终变换矩阵
|
||||
self.W_mnf_ = Wn @ Wp # (p, p)
|
||||
self.W_mnf_ = Wn @ Wp
|
||||
|
||||
return self
|
||||
|
||||
def transform(self, X):
|
||||
X = self._to_ndarray(X)
|
||||
return (X - self.mean_) @ self.W_mnf_[:, :self.n_components]
|
||||
X = np.nan_to_num(X, nan=0.0, posinf=0.0, neginf=0.0)
|
||||
result = (X - self.mean_) @ self.W_mnf_[:, :self.n_components]
|
||||
# 防 SVR 拒绝 inf:clip 到 float32 安全范围
|
||||
result = np.nan_to_num(result, nan=0.0, posinf=0.0, neginf=0.0)
|
||||
result = np.clip(result, -1e15, 1e15)
|
||||
return result
|
||||
|
||||
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user