Min-Max归一化

Min-Max归一化

一、数学原理

1.1 基础公式

Min-Max归一化(最小-最大标准化)将原始数据线性映射到[0,1]区间:

\[x_{\text{norm}} = \frac{x - x_{\min}}{x_{\max} - x_{\min}} \]

1.2 扩展到任意区间[a,b]

有时需要将数据映射到任意区间[a,b]:

\[x_{\text{norm}}^{[a,b]} = a + \frac{(x - x_{\min}) \times (b - a)}{x_{\max} - x_{\min}} \]

1.3 矩阵形式表示

对于数据集 \(X \in \mathbb{R}^{m \times n}\)(m个样本,n个特征):

\[X_{\text{norm}} = \frac{X - \text{min}(X)}{\text{max}(X) - \text{min}(X)} \]

其中:

  • \(\text{min}(X)\):每列的最小值向量 \(\in \mathbb{R}^{1 \times n}\)
  • \(\text{max}(X)\):每列的最大值向量 \(\in \mathbb{R}^{1 \times n}\)
  • 运算为广播操作

二、数学性质分析

2.1 线性变换性质

\(T(x) = \frac{x - x_{\min}}{x_{\max} - x_{\min}}\),则:

  1. 保持序关系:若 \(x_1 < x_2\),则 \(T(x_1) < T(x_2)\)
  2. 线性变换\(T(ax + b) = aT(x) + b'\)(保持线性关系)
  3. 可逆性\(T^{-1}(y) = x_{\min} + y \times (x_{\max} - x_{\min})\)

2.2 数据分布影响

原始数据 \(X \sim F(x)\),归一化后:

\[X_{\text{norm}} \sim F\left(x_{\min} + x_{\text{norm}}(x_{\max} - x_{\min})\right) \]

归一化不改变数据的分布形状,只改变位置和尺度。

三、代码实现与数学验证

3.1 基础实现

import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler

class MinMaxNormalization:
    """Min-Max归一化实现"""
    
    def __init__(self, feature_range=(0, 1)):
        """
        参数:
        feature_range: 目标范围,默认为[0, 1]
        """
        self.feature_range = feature_range
        self.min_val = None
        self.max_val = None
        self.data_min = None
        self.data_max = None
        
    def fit(self, X):
        """
        计算数据的min和max
        
        参数:
        X: 原始数据,形状为(n_samples, n_features)或(n_samples,)
        """
        X = np.array(X)
        if X.ndim == 1:
            X = X.reshape(-1, 1)
            
        self.data_min = np.min(X, axis=0)
        self.data_max = np.max(X, axis=0)
        
        # 防止除零
        self.range_ = self.data_max - self.data_min
        self.range_[self.range_ == 0] = 1
        
        return self
    
    def transform(self, X):
        """
        应用Min-Max归一化
        
        公式: X_norm = (X - min) / (max - min)
        """
        if self.data_min is None or self.data_max is None:
            raise ValueError("必须先调用fit方法")
            
        X = np.array(X)
        if X.ndim == 1:
            X = X.reshape(-1, 1)
            
        # 应用公式
        X_norm = (X - self.data_min) / self.range_
        
        # 缩放到指定范围
        if self.feature_range != (0, 1):
            a, b = self.feature_range
            X_norm = X_norm * (b - a) + a
            
        return X_norm.squeeze()
    
    def fit_transform(self, X):
        """fit和transform的组合"""
        return self.fit(X).transform(X)
    
    def inverse_transform(self, X_norm):
        """
        反归一化
        
        公式: X = X_norm * (max - min) + min
        """
        if self.data_min is None or self.data_max is None:
            raise ValueError("必须先调用fit方法")
            
        X_norm = np.array(X_norm)
        if X_norm.ndim == 1:
            X_norm = X_norm.reshape(-1, 1)
            
        # 如果缩放到其他范围,先映射回[0,1]
        if self.feature_range != (0, 1):
            a, b = self.feature_range
            X_norm = (X_norm - a) / (b - a)
            
        # 应用反变换公式
        X = X_norm * self.range_ + self.data_min
        
        return X.squeeze()
    
    def get_params(self):
        """获取归一化参数"""
        return {
            'min': self.data_min,
            'max': self.data_max,
            'range': self.range_,
            'feature_range': self.feature_range
        }

3.2 数学验证

# 验证数学性质
def verify_minmax_properties():
    """验证Min-Max归一化的数学性质"""
    
    # 1. 生成测试数据
    np.random.seed(42)
    X = np.random.randn(100) * 10 + 50  # 均值为50,标准差为10
    
    # 2. 初始化归一化器
    scaler = MinMaxNormalization()
    X_norm = scaler.fit_transform(X)
    
    # 3. 验证性质1: 范围在[0,1]
    print("性质1验证 - 范围检查:")
    print(f"  原始数据范围: [{X.min():.4f}, {X.max():.4f}]")
    print(f"  归一化数据范围: [{X_norm.min():.4f}, {X_norm.max():.4f}]")
    print(f"  是否在[0,1]: {X_norm.min() >= 0 and X_norm.max() <= 1}")
    
    # 4. 验证性质2: 序关系保持
    print("\n性质2验证 - 序关系保持:")
    indices = np.random.choice(len(X), 5, replace=False)
    for i in indices:
        for j in indices:
            if i != j:
                orig_order = X[i] < X[j]
                norm_order = X_norm[i] < X_norm[j]
                if orig_order != norm_order:
                    print(f"  序关系不匹配: X[{i}]={X[i]:.2f}, X[{j}]={X[j]:.2f}")
    
    # 5. 验证性质3: 线性变换保持
    print("\n性质3验证 - 线性变换:")
    # 对原始数据做线性变换
    X_linear = 2 * X + 3
    scaler2 = MinMaxNormalization()
    X_linear_norm = scaler2.fit_transform(X_linear)
    
    # 理论上,X_norm应该与(2*X+3)_norm有线性关系
    # 计算两者的相关性
    corr = np.corrcoef(X_norm, X_linear_norm)[0, 1]
    print(f"  线性变换后数据的归一化与原始数据归一化的相关系数: {corr:.6f}")
    print(f"  是否保持线性关系: {abs(corr) > 0.999}")
    
    # 6. 验证性质4: 可逆性
    print("\n性质4验证 - 可逆性:")
    X_reconstructed = scaler.inverse_transform(X_norm)
    reconstruction_error = np.max(np.abs(X - X_reconstructed))
    print(f"  最大重构误差: {reconstruction_error:.10f}")
    print(f"  是否可逆: {reconstruction_error < 1e-10}")
    
    # 7. 验证扩展到任意区间
    print("\n扩展验证 - 任意区间[a,b]:")
    scaler_ab = MinMaxNormalization(feature_range=(-1, 1))
    X_norm_ab = scaler_ab.fit_transform(X)
    print(f"  归一化到[-1,1]范围: [{X_norm_ab.min():.4f}, {X_norm_ab.max():.4f}]")
    
    # 验证公式: x_norm = a + (x - min) * (b - a) / (max - min)
    a, b = -1, 1
    expected = a + (X - scaler_ab.data_min) * (b - a) / scaler_ab.range_
    formula_error = np.max(np.abs(X_norm_ab - expected))
    print(f"  公式计算误差: {formula_error:.10f}")

verify_minmax_properties()

四、几何解释与可视化

4.1 一维数据几何解释

def visualize_minmax_1d():
    """可视化一维数据的Min-Max归一化"""
    
    # 生成数据
    np.random.seed(42)
    X = np.array([1, 3, 5, 7, 9, 11, 13, 15, 17, 19])
    
    # 归一化
    scaler = MinMaxNormalization()
    X_norm = scaler.fit_transform(X)
    
    # 创建图形
    fig, axes = plt.subplots(2, 2, figsize=(12, 10))
    
    # 1. 原始数据分布
    axes[0, 0].scatter(X, np.zeros_like(X), color='blue', s=100, label='原始数据')
    axes[0, 0].axvline(x=scaler.data_min, color='red', linestyle='--', label=f'min={scaler.data_min}')
    axes[0, 0].axvline(x=scaler.data_max, color='green', linestyle='--', label=f'max={scaler.data_max}')
    axes[0, 0].set_xlabel('X值')
    axes[0, 0].set_title('原始数据分布')
    axes[0, 0].legend()
    axes[0, 0].grid(True, alpha=0.3)
    
    # 2. 归一化过程示意图
    axes[0, 1].plot([scaler.data_min, scaler.data_max], [0, 1], 'b-', linewidth=2)
    axes[0, 1].scatter(X, (X - scaler.data_min) / scaler.range_, 
                       color='red', s=100, zorder=5, label='归一化映射')
    
    # 添加映射线
    for i, (x, y) in enumerate(zip(X, X_norm)):
        axes[0, 1].plot([x, x], [0, y], 'r--', alpha=0.5)
        axes[0, 1].plot([scaler.data_min, x], [y, y], 'r--', alpha=0.5)
    
    axes[0, 1].set_xlabel('原始X值')
    axes[0, 1].set_ylabel('归一化X值')
    axes[0, 1].set_title('Min-Max归一化映射过程')
    axes[0, 1].grid(True, alpha=0.3)
    axes[0, 1].legend()
    
    # 3. 归一化后数据分布
    axes[1, 0].scatter(X_norm, np.zeros_like(X_norm), color='green', s=100, label='归一化数据')
    axes[1, 0].axvline(x=0, color='red', linestyle='--', label='min=0')
    axes[1, 0].axvline(x=1, color='blue', linestyle='--', label='max=1')
    axes[1, 0].set_xlabel('归一化X值')
    axes[1, 0].set_title('归一化后数据分布')
    axes[1, 0].set_xlim(-0.1, 1.1)
    axes[1, 0].legend()
    axes[1, 0].grid(True, alpha=0.3)
    
    # 4. 线性关系保持验证
    # 创建线性变换数据
    X_linear = 2*X + 3
    X_linear_norm = MinMaxNormalization().fit_transform(X_linear)
    
    axes[1, 1].scatter(X_norm, X_linear_norm, color='purple', s=100, label='数据点')
    
    # 拟合直线
    from scipy.stats import linregress
    slope, intercept, r_value, p_value, std_err = linregress(X_norm, X_linear_norm)
    x_line = np.array([0, 1])
    y_line = slope * x_line + intercept
    
    axes[1, 1].plot(x_line, y_line, 'r-', label=f'拟合直线: y={slope:.4f}x+{intercept:.4f}')
    axes[1, 1].set_xlabel('X归一化值')
    axes[1, 1].set_ylabel('(2X+3)归一化值')
    axes[1, 1].set_title('线性关系保持验证')
    axes[1, 1].legend()
    axes[1, 1].grid(True, alpha=0.3)
    axes[1, 1].text(0.05, 0.9, f'相关系数: {r_value:.6f}', 
                   transform=axes[1, 1].transAxes, fontsize=10,
                   bbox=dict(boxstyle="round,pad=0.3", facecolor="yellow", alpha=0.5))
    
    plt.tight_layout()
    plt.show()
    
    # 打印数学公式
    print("Min-Max归一化数学公式:")
    print(f"  x_norm = (x - {scaler.data_min}) / ({scaler.data_max} - {scaler.data_min})")
    print(f"  x_norm = (x - {scaler.data_min}) / {scaler.range_}")
    print("\n反归一化公式:")
    print(f"  x = x_norm * {scaler.range_} + {scaler.data_min}")

visualize_minmax_1d()

4.2 二维数据可视化

def visualize_minmax_2d():
    """可视化二维数据的Min-Max归一化"""
    
    # 生成二维数据
    np.random.seed(42)
    n_samples = 50
    
    # 创建两个相关但有不同尺度的特征
    X1 = np.random.randn(n_samples) * 10 + 50  # 均值50,标准差10
    X2 = np.random.randn(n_samples) * 100 + 200  # 均值200,标准差100
    X = np.column_stack([X1, X2])
    
    # 添加标签
    y = (X1 + 0.1*X2) > 125
    
    # 归一化
    scaler = MinMaxNormalization()
    X_norm = scaler.fit_transform(X)
    
    # 创建图形
    fig, axes = plt.subplots(1, 3, figsize=(15, 5))
    
    # 1. 原始数据散点图
    scatter1 = axes[0].scatter(X[:, 0], X[:, 1], c=y, cmap='viridis', 
                               s=50, alpha=0.7, edgecolors='k')
    axes[0].set_xlabel('特征1 (尺度: ~40-60)')
    axes[0].set_ylabel('特征2 (尺度: ~0-400)')
    axes[0].set_title('原始数据(尺度不同)')
    axes[0].grid(True, alpha=0.3)
    
    # 添加特征范围框
    rect1 = plt.Rectangle((X[:, 0].min(), X[:, 1].min()), 
                         X[:, 0].max()-X[:, 0].min(), 
                         X[:, 1].max()-X[:, 1].min(),
                         fill=False, edgecolor='red', linewidth=2, linestyle='--')
    axes[0].add_patch(rect1)
    
    # 2. 归一化过程示意图
    # 绘制原始数据点
    axes[1].scatter(X[:, 0], X[:, 1], c='blue', s=30, alpha=0.5, label='原始数据')
    
    # 绘制归一化后的数据点位置
    # 创建从原始到归一化的箭头
    for i in range(min(10, n_samples)):  # 只显示前10个样本的映射
        x1, x2 = X[i, 0], X[i, 1]
        x1_norm, x2_norm = X_norm[i, 0], X_norm[i, 1]
        
        # 将归一化坐标映射回可视化空间
        # 为了可视化,我们将归一化坐标放在[0,1]x[0,1]区域内
        vis_x1 = 0.1 + 0.8 * x1_norm
        vis_x2 = 0.1 + 0.8 * x2_norm
        
        axes[1].plot([x1, vis_x1], [x2, vis_x2], 'r--', alpha=0.3)
    
    # 绘制归一化区域
    norm_area = plt.Rectangle((0.1, 0.1), 0.8, 0.8, 
                             fill=False, edgecolor='green', 
                             linewidth=2, label='归一化区域[0,1]×[0,1]')
    axes[1].add_patch(norm_area)
    
    axes[1].set_xlabel('特征1')
    axes[1].set_ylabel('特征2')
    axes[1].set_title('归一化映射过程')
    axes[1].legend(loc='upper right')
    axes[1].grid(True, alpha=0.3)
    
    # 3. 归一化后数据散点图
    scatter3 = axes[2].scatter(X_norm[:, 0], X_norm[:, 1], c=y, 
                               cmap='viridis', s=50, alpha=0.7, edgecolors='k')
    axes[2].set_xlabel('特征1 (归一化)')
    axes[2].set_ylabel('特征2 (归一化)')
    axes[2].set_title('归一化后数据(尺度相同)')
    axes[2].set_xlim(-0.1, 1.1)
    axes[2].set_ylim(-0.1, 1.1)
    axes[2].grid(True, alpha=0.3)
    
    # 添加[0,1]边界
    rect3 = plt.Rectangle((0, 0), 1, 1, fill=False, 
                         edgecolor='red', linewidth=2, linestyle='--')
    axes[2].add_patch(rect3)
    
    plt.tight_layout()
    plt.show()
    
    # 打印统计信息
    print("原始数据统计:")
    print(f"  特征1 - 最小值: {X[:, 0].min():.2f}, 最大值: {X[:, 0].max():.2f}, 范围: {X[:, 0].max()-X[:, 0].min():.2f}")
    print(f"  特征2 - 最小值: {X[:, 1].min():.2f}, 最大值: {X[:, 1].max():.2f}, 范围: {X[:, 1].max()-X[:, 1].min():.2f}")
    print(f"  特征2范围 / 特征1范围 = {(X[:, 1].max()-X[:, 1].min()):.2f} / {(X[:, 0].max()-X[:, 0].min()):.2f} = {(X[:, 1].max()-X[:, 1].min())/(X[:, 0].max()-X[:, 0].min()):.2f}")
    
    print("\n归一化后数据统计:")
    print(f"  特征1 - 最小值: {X_norm[:, 0].min():.2f}, 最大值: {X_norm[:, 0].max():.2f}")
    print(f"  特征2 - 最小值: {X_norm[:, 1].min():.2f}, 最大值: {X_norm[:, 1].max():.2f}")
    print("  两个特征现在具有相同的尺度[0,1]")

visualize_minmax_2d()

五、数学推导与证明

5.1 公式推导

给定数据 \(x \in [x_{\min}, x_{\max}]\),我们希望将其映射到 \([0, 1]\)

设线性变换:\(x_{\text{norm}} = \alpha x + \beta\)

由边界条件:

  1. \(x = x_{\min}\) 时,\(x_{\text{norm}} = 0\)
  2. \(x = x_{\max}\) 时,\(x_{\text{norm}} = 1\)

得方程组:

\[\begin{cases} \alpha x_{\min} + \beta = 0 \\ \alpha x_{\max} + \beta = 1 \end{cases} \]

解方程组:

  1. 两式相减:\(\alpha (x_{\max} - x_{\min}) = 1 \Rightarrow \alpha = \frac{1}{x_{\max} - x_{\min}}\)
  2. 代入第一式:\(\frac{x_{\min}}{x_{\max} - x_{\min}} + \beta = 0 \Rightarrow \beta = -\frac{x_{\min}}{x_{\max} - x_{\min}}\)

因此:

\[x_{\text{norm}} = \frac{1}{x_{\max} - x_{\min}} x - \frac{x_{\min}}{x_{\max} - x_{\min}} = \frac{x - x_{\min}}{x_{\max} - x_{\min}} \]

5.2 扩展到[a,b]区间

类似地,要映射到 \([a, b]\)

边界条件:

  1. \(x = x_{\min} \Rightarrow x_{\text{norm}} = a\)
  2. \(x = x_{\max} \Rightarrow x_{\text{norm}} = b\)

\(x_{\text{norm}} = \alpha x + \beta\)

\[\begin{cases} \alpha x_{\min} + \beta = a \\ \alpha x_{\max} + \beta = b \end{cases} \]

解得:

  1. \(\alpha = \frac{b - a}{x_{\max} - x_{\min}}\)
  2. \(\beta = a - \frac{b - a}{x_{\max} - x_{\min}} x_{\min} = \frac{a x_{\max} - b x_{\min}}{x_{\max} - x_{\min}}\)

因此:

\[x_{\text{norm}}^{[a,b]} = \frac{b - a}{x_{\max} - x_{\min}} x + \frac{a x_{\max} - b x_{\min}}{x_{\max} - x_{\min}} = a + \frac{(x - x_{\min})(b - a)}{x_{\max} - x_{\min}} \]

5.3 保持序关系的证明

对于任意 \(x_1, x_2\),若 \(x_1 < x_2\)

\[x_{1,\text{norm}} - x_{2,\text{norm}} = \frac{x_1 - x_{\min}}{x_{\max} - x_{\min}} - \frac{x_2 - x_{\min}}{x_{\max} - x_{\min}} = \frac{x_1 - x_2}{x_{\max} - x_{\min}} \]

由于 \(x_{\max} - x_{\min} > 0\)(假设数据不全部相同),且 \(x_1 - x_2 < 0\),所以:

\[x_{1,\text{norm}} - x_{2,\text{norm}} < 0 \Rightarrow x_{1,\text{norm}} < x_{2,\text{norm}} \]

因此,Min-Max归一化保持数据的序关系。

六、应用实例:神经网络训练

6.1 Min-Max归一化在神经网络中的应用

def neural_network_with_minmax():
    """使用Min-Max归一化的神经网络示例"""
    
    # 1. 生成非线性数据
    np.random.seed(42)
    n_samples = 200
    
    # 生成数据:y = sin(2πx) + 噪声
    X = np.random.rand(n_samples) * 4 - 2  # [-2, 2]
    y = np.sin(2 * np.pi * X) + np.random.randn(n_samples) * 0.2
    
    # 2. 划分训练集和测试集
    from sklearn.model_selection import train_test_split
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=42
    )
    
    # 3. 分别对X和y进行Min-Max归一化
    scaler_X = MinMaxNormalization(feature_range=(-1, 1))
    scaler_y = MinMaxNormalization(feature_range=(-1, 1))
    
    X_train_norm = scaler_X.fit_transform(X_train.reshape(-1, 1))
    y_train_norm = scaler_y.fit_transform(y_train.reshape(-1, 1))
    
    X_test_norm = scaler_X.transform(X_test.reshape(-1, 1))
    
    # 4. 构建简单神经网络
    class SimpleNN:
        def __init__(self, input_dim=1, hidden_dim=10, output_dim=1):
            # 初始化权重
            np.random.seed(42)
            self.W1 = np.random.randn(input_dim, hidden_dim) * 0.1
            self.b1 = np.zeros((1, hidden_dim))
            self.W2 = np.random.randn(hidden_dim, output_dim) * 0.1
            self.b2 = np.zeros((1, output_dim))
            
        def sigmoid(self, x):
            return 1 / (1 + np.exp(-x))
        
        def sigmoid_derivative(self, x):
            return x * (1 - x)
        
        def forward(self, X):
            # 前向传播
            self.z1 = np.dot(X, self.W1) + self.b1
            self.a1 = self.sigmoid(self.z1)
            self.z2 = np.dot(self.a1, self.W2) + self.b2
            return self.z2
        
        def backward(self, X, y, y_pred, lr=0.1):
            # 反向传播
            m = X.shape[0]
            
            # 输出层误差
            d_loss = 2 * (y_pred - y) / m  # MSE损失的导数
            
            # 第二层梯度
            dW2 = np.dot(self.a1.T, d_loss)
            db2 = np.sum(d_loss, axis=0, keepdims=True)
            
            # 隐藏层误差
            d_a1 = np.dot(d_loss, self.W2.T)
            d_z1 = d_a1 * self.sigmoid_derivative(self.a1)
            
            # 第一层梯度
            dW1 = np.dot(X.T, d_z1)
            db1 = np.sum(d_z1, axis=0, keepdims=True)
            
            # 更新权重
            self.W1 -= lr * dW1
            self.b1 -= lr * db1
            self.W2 -= lr * dW2
            self.b2 -= lr * db2
    
    # 5. 训练神经网络
    nn = SimpleNN()
    epochs = 5000
    losses = []
    
    for epoch in range(epochs):
        # 前向传播
        y_pred_norm = nn.forward(X_train_norm.reshape(-1, 1))
        
        # 计算损失
        loss = np.mean((y_pred_norm - y_train_norm.reshape(-1, 1)) ** 2)
        losses.append(loss)
        
        # 反向传播
        nn.backward(X_train_norm.reshape(-1, 1), 
                    y_train_norm.reshape(-1, 1), 
                    y_pred_norm, 
                    lr=0.1)
        
        if epoch % 1000 == 0:
            print(f"Epoch {epoch}: Loss = {loss:.6f}")
    
    # 6. 预测并反归一化
    y_pred_test_norm = nn.forward(X_test_norm.reshape(-1, 1))
    y_pred_test = scaler_y.inverse_transform(y_pred_test_norm)
    
    # 7. 可视化结果
    fig, axes = plt.subplots(2, 2, figsize=(12, 10))
    
    # 原始数据与真实函数
    X_plot = np.linspace(-2, 2, 1000)
    y_true = np.sin(2 * np.pi * X_plot)
    
    axes[0, 0].scatter(X_train, y_train, alpha=0.5, label='训练数据')
    axes[0, 0].plot(X_plot, y_true, 'r-', label='真实函数: y=sin(2πx)')
    axes[0, 0].set_xlabel('X')
    axes[0, 0].set_ylabel('y')
    axes[0, 0].set_title('原始数据与真实函数')
    axes[0, 0].legend()
    axes[0, 0].grid(True, alpha=0.3)
    
    # 归一化后的数据
    X_plot_norm = scaler_X.transform(X_plot.reshape(-1, 1))
    y_true_norm = scaler_y.transform(y_true.reshape(-1, 1))
    
    axes[0, 1].scatter(X_train_norm, y_train_norm, alpha=0.5, label='归一化训练数据')
    axes[0, 1].plot(X_plot_norm, y_true_norm, 'r-', label='归一化真实函数')
    axes[0, 1].set_xlabel('X (归一化)')
    axes[0, 1].set_ylabel('y (归一化)')
    axes[0, 1].set_title('归一化数据与函数')
    axes[0, 1].legend()
    axes[0, 1].grid(True, alpha=0.3)
    
    # 损失曲线
    axes[1, 0].plot(losses)
    axes[1, 0].set_xlabel('Epoch')
    axes[1, 0].set_ylabel('Loss (MSE)')
    axes[1, 0].set_title('训练损失曲线')
    axes[1, 0].set_yscale('log')  # 对数坐标更清晰
    axes[1, 0].grid(True, alpha=0.3)
    
    # 预测结果
    axes[1, 1].scatter(X_test, y_test, alpha=0.5, label='测试数据')
    axes[1, 1].scatter(X_test, y_pred_test, alpha=0.8, 
                       color='red', s=20, label='神经网络预测')
    axes[1, 1].plot(X_plot, y_true, 'g-', label='真实函数')
    axes[1, 1].set_xlabel('X')
    axes[1, 1].set_ylabel('y')
    axes[1, 1].set_title('神经网络预测结果')
    axes[1, 1].legend()
    axes[1, 1].grid(True, alpha=0.3)
    
    # 计算R²分数
    from sklearn.metrics import r2_score
    r2 = r2_score(y_test, y_pred_test)
    axes[1, 1].text(0.05, 0.95, f'R²分数: {r2:.4f}', 
                   transform=axes[1, 1].transAxes, fontsize=12,
                   bbox=dict(boxstyle="round,pad=0.3", facecolor="yellow", alpha=0.5))
    
    plt.tight_layout()
    plt.show()
    
    # 打印归一化参数
    print("\n归一化参数总结:")
    print(f"X归一化参数: min={scaler_X.data_min[0]:.4f}, max={scaler_X.data_max[0]:.4f}")
    print(f"y归一化参数: min={scaler_y.data_min[0]:.4f}, max={scaler_y.data_max[0]:.4f}")
    
    # 数学分析
    print("\n数学分析:")
    print("1. 原始数据X范围: [-2, 2]")
    print(f"2. 归一化后X范围: [{scaler_X.feature_range[0]}, {scaler_X.feature_range[1]}]")
    print(f"3. 归一化公式: x_norm = {scaler_X.feature_range[0]} + (x - {scaler_X.data_min[0]:.4f}) * ({scaler_X.feature_range[1]}-{scaler_X.feature_range[0]}) / ({scaler_X.data_max[0]:.4f}-{scaler_X.data_min[0]:.4f})")
    print(f"4. 简化为: x_norm = -1 + (x + 2) * 2 / 4 = -1 + (x + 2) / 2")

neural_network_with_minmax()

七、Min-Max归一化的优缺点

7.1 优点

  1. 简单直观:公式简单,易于理解和实现
  2. 保持序关系:不改变数据的相对顺序
  3. 有界输出:结果严格限制在指定范围内
  4. 保持线性关系:原始数据的线性关系在归一化后保持不变
  5. 对稀疏数据友好:不会改变0值的位置

7.2 缺点

  1. 对异常值敏感:极值会压缩大部分数据的范围
    • 例:数据[1, 2, 3, 4, 100],归一化后前4个值集中在[0, 0.03]区间
  2. 仅适用于有界数据:不适合处理可能无限增长的数据
  3. 破坏分布信息:如果数据不是均匀分布,归一化可能扭曲数据
  4. 需要保存min/max:预测新数据时需要原始min/max

7.3 数学形式表示缺点

假设原始数据 \(X = [x_1, x_2, ..., x_n]\),其中 \(x_n\) 是异常值,远大于其他值:

\[x_{\max} \gg x_i \ (i < n) \Rightarrow x_{\max} - x_{\min} \approx x_n \]

那么对于正常数据点 \(x_i\)

\[x_{i,\text{norm}} = \frac{x_i - x_{\min}}{x_{\max} - x_{\min}} \approx \frac{x_i - x_{\min}}{x_n} \approx 0 \]

导致正常数据被压缩到接近0的区域。

八、 Min-Max归一化适用场景

  1. 图像处理:像素值天然在[0,255]范围内
  2. 神经网络输入:某些激活函数对输入范围敏感(如sigmoid)
  3. 需要特定范围:某些算法要求输入在特定范围
  4. 数据可视化:统一尺度便于比较
posted @ 2026-02-10 21:15  ffff5  阅读(82)  评论(0)    收藏  举报