回归模型-4核回归

 

4 核回归

看模型的范式 核回归
问题是什么 回归问题
模型是什么 线性模型
优化指标 间隔最大化(最大化支持面之间的距离)
求解方法 (序列最小最优化算法)smo
评价模型  

核回归是一种非参数回归方法,不需要事先假设回归函数的具体形式,核回归是放弃寻找一个全局模型(比如线性回归),转而相信“物以类聚”:要预测一个点$ x$ 的值,我们应该更多地参考那些在 $x$ 附近点的值,而远离$ x$ 的点则参考价值较小。通过局部加权平均来拟合复杂的非线性关系。

核心思想:(局部拟合)

  • 通过核函数实现“局部加权平均”

  • 对每一个待预测点$x_0$,仅利用其邻近的样本点计算预测值$\hat m(x_0)$

  • 邻近点的权重由核函数决定,靠近预测点的样本点权重大,远离预测点的样本点权重小

核函数 $K(u)$ 是一个对称、非负的函数,且通常满足积分为1(即它是一个概率密度函数)。它定义了权重如何随着距离 $u$ 的变化而变化,常见的核函数有:

高斯核: 最常用的核之一,权重随距离呈指数衰减 \(K(u)=\frac{1}{\sqrt{2\pi}}\exp(-\frac{1}{2}u^2)\) Epanechnikov 核: 在均方误差意义下是最优的,计算效率高 \(K(u)=\frac{3}{4}(1-u^2)\quad\text{for}\quad|u|\leq1\) 均匀核: 在窗口内的点权重相同,窗口外的点权重为0 \(K(u)=\frac{1}{2}\quad\text{for}\quad|u|\leq1\) 核回归,特别是最常用的Nadaraya-Watson 核回归,假设我们有一组数据点 $(x_1, y_1), (x_2, y_2), …, (x_n, y_n)$。我们想要预测在任意一点 $x$ 对应的 $y$ 值(即 $m(x)$)。核回归给出的估计值是: \(\hat{m}_h(x)=\frac{\sum_{i=1}^nK_h(x-x_i)y_i}{\sum_{i=1}^nK_h(x-x_i)}\)

  • $\hat{m}_h(x)$: 在点 $x$ 处的预测值。
  • $K_h(\cdot)$: 这就是核函数,它是整个方法的灵魂。
  • $h$ 是一个超参数,叫做带宽窗宽。它控制了“邻居”的范围有多大。
  • $x - x_i$: 表示预测点 $x$ 与样本点 $x_i$ 之间的距离。

其中,带宽 $h$ 是核回归中最重要的超参数,它直接决定了模型的复杂度和性能。如果h 过大(过平滑),会忽略数据中的细节(欠拟合),偏差大。如果h 过小(欠平滑),估计的曲线会紧紧跟随训练数据,甚至穿过每一个点,导致波动非常大(过拟合),方差大。选择带宽 $h$ 的过程就是在偏差方差之间做一个权衡,通常使用交叉验证的方法来寻找最优的 $h$

参考:

[统计学基础(核回归) SIRLIS](https://sirlis.cn/posts/statistics-kernel-regression/)

Kernel Regression 核回归 详细讲解 - 编程宝典

Kernel Regression Advanced Methods for Data Analysis(36-402/36-608) Spring 2014

代码示例:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 设置随机种子以确保结果可重现
np.random.seed(42)

# 1. 生成示例数据
def generate_data(n_samples=300):
    X = np.linspace(0, 10, n_samples)
    # 创建非线性关系并添加噪声
    y = np.sin(X) + 0.2 * X + np.random.normal(0, 0.3, n_samples)
    return X, y

X, y = generate_data()

# 将数据划分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 2. 定义核函数
def gaussian_kernel(u, h):
    """高斯核函数"""
    return np.exp(-(u**2) / (2 * h**2)) / (h * np.sqrt(2 * np.pi))

def epanechnikov_kernel(u, h):
    """Epanechnikov 核函数"""
    u_normalized = np.abs(u) / h
    return np.where(u_normalized <= 1, 0.75 * (1 - u_normalized**2), 0)

# 3. 实现核回归
def kernel_regression(X_train, y_train, X_test, h, kernel='gaussian'):
    """
    核回归实现
    
    参数:
    X_train: 训练特征
    y_train: 训练目标
    X_test: 测试特征
    h: 带宽参数
    kernel: 核函数类型 ('gaussian' 或 'epanechnikov')
    
    返回:
    y_pred: 预测值
    """
    y_pred = np.zeros_like(X_test)
    
    for i, x in enumerate(X_test):
        # 计算权重
        if kernel == 'gaussian':
            weights = gaussian_kernel(X_train - x, h)
        elif kernel == 'epanechnikov':
            weights = epanechnikov_kernel(X_train - x, h)
        else:
            raise ValueError("不支持的核函数类型")
        
        # 计算加权平均值
        weighted_sum = np.sum(weights * y_train)
        sum_weights = np.sum(weights)
        
        # 避免除以零
        if sum_weights > 1e-10:
            y_pred[i] = weighted_sum / sum_weights
        else:
            y_pred[i] = np.mean(y_train)  # 回退到全局均值
    
    return y_pred

# 4. 使用不同带宽进行预测并评估
bandwidths = [0.1, 0.5, 1.0, 2.0]
mse_scores = []

plt.figure(figsize=(15, 10))

for i, h in enumerate(bandwidths):
    # 使用高斯核进行预测
    y_pred = kernel_regression(X_train, y_train, X_test, h, kernel='gaussian')
    mse = mean_squared_error(y_test, y_pred)
    mse_scores.append(mse)
    
    # 为了绘制平滑曲线,在密集点上进行预测
    X_plot = np.linspace(0, 10, 300)
    y_plot = kernel_regression(X_train, y_train, X_plot, h, kernel='gaussian')
    
    # 绘制子图
    plt.subplot(2, 2, i+1)
    plt.scatter(X_train, y_train, alpha=0.5, label='训练数据', color='lightgray')
    plt.scatter(X_test, y_test, alpha=0.7, label='测试数据', color='blue')
    plt.plot(X_plot, y_plot, color='red', linewidth=2, label=f'核回归 (h={h})')
    plt.title(f'带宽 h={h}, MSE={mse:.4f}')
    plt.xlabel('X')
    plt.ylabel('y')
    plt.legend()

plt.tight_layout()
plt.show()

# 5. 比较不同核函数
plt.figure(figsize=(12, 5))

# 使用最佳带宽
best_h = bandwidths[np.argmin(mse_scores)]

# 高斯核
X_plot = np.linspace(0, 10, 300)
y_gaussian = kernel_regression(X_train, y_train, X_plot, best_h, kernel='gaussian')

# Epanechnikov核
y_epanechnikov = kernel_regression(X_train, y_train, X_plot, best_h, kernel='epanechnikov')

plt.subplot(1, 2, 1)
plt.scatter(X_train, y_train, alpha=0.5, label='训练数据', color='lightgray')
plt.plot(X_plot, y_gaussian, color='red', linewidth=2, label='高斯核')
plt.title(f'高斯核回归 (h={best_h})')
plt.xlabel('X')
plt.ylabel('y')
plt.legend()

plt.subplot(1, 2, 2)
plt.scatter(X_train, y_train, alpha=0.5, label='训练数据', color='lightgray')
plt.plot(X_plot, y_epanechnikov, color='green', linewidth=2, label='Epanechnikov核')
plt.title(f'Epanechnikov核回归 (h={best_h})')
plt.xlabel('X')
plt.ylabel('y')
plt.legend()

plt.tight_layout()
plt.show()

# 6. 展示带宽选择对模型复杂度的影响
print("不同带宽的MSE得分:")
for h, score in zip(bandwidths, mse_scores):
    print(f"带宽 h={h}: MSE = {score:.4f}")