[机器学习40讲]32 - 从有限到无限:高斯过程

复制代码
import pandas as pd
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from sklearn import gaussian_process
from sklearn.gaussian_process.kernels import RBF

kernel = 1e-2 * RBF([5e-3])

# Gaussian Process Regression with regression dataset
stats = pd.read_table('./regression.csv')
point = stats.iloc[:, 4] / 38
rating = stats.iloc[:, 5].to_numpy().reshape(-1, 1)

x_min, x_max = rating.min() - 0.05, rating.max() + 0.05
xx_regression = np.arange(x_min, x_max, 1e-3).reshape(-1, 1)

gpr = gaussian_process.GaussianProcessRegressor(kernel=kernel).fit(rating, point)
gpr_pred, sigma = gpr.predict(xx_regression, return_std=True)

# Gaussian Process Classification with linear inseparable dataset
ratio = []

rawstat = pd.read_table('./linear inseparable.csv')
category = rawstat.iloc[:, 0]
pass_ratio = rawstat.iloc[:, 1] / rawstat.iloc[:, 2]
shot_ratio = rawstat.iloc[:, 3] / rawstat.iloc[:, 4]

ratio.append(pass_ratio)
ratio.append(shot_ratio)
ratio = np.array(ratio)
ratio = ratio.astype('float')

gpc = gaussian_process.GaussianProcessClassifier(kernel=kernel).fit(ratio.T, category)

x_min, x_max = ratio[0].min() - 0.05, ratio[0].max() + 0.05
y_min, y_max = ratio[1].min() - 0.05, ratio[1].max() + 0.05
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.01), np.arange(y_min, y_max, 0.01))
xy = np.vstack([xx.ravel(), yy.ravel()]).T
Z = gpc.predict(xy).reshape(xx.shape)

# result demonstration
fig = plt.figure()

ax1 = fig.add_subplot(121)
plt.plot(xx_regression, gpr_pred, c="r", linewidth=4)
plt.scatter(rating, point, c="b", s=5)
plt.fill(np.concatenate([xx_regression, xx_regression[::-1]]),
         np.concatenate([gpr_pred - 1.96 * sigma, (gpr_pred + 1.96 * sigma)[::-1]]),
         alpha=.5, fc='b', ec='None')
plt.title("Gaussian Process Regression")
plt.xlabel("average rating")
plt.ylabel("average point per game")

ax2 = fig.add_subplot(122)
plt.scatter(pass_ratio[category == 0], shot_ratio[category == 0], c='r', marker='o')
plt.scatter(pass_ratio[category == 1], shot_ratio[category == 1], c='b', marker='^')
plt.contour(xx, yy, Z, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
plt.title("Gaussian Process Classification")
plt.xlabel('pass ratio')
plt.ylabel('shot ratio')

plt.show()

一、导入与全局配置

复制代码
import pandas as pd
import numpy as np
import matplotlib
matplotlib.use('TkAgg')          # 指定后端为 TkAgg(弹窗显示)
import matplotlib.pyplot as plt
from sklearn import gaussian_process
from sklearn.gaussian_process.kernels import RBF
  • matplotlib.use('TkAgg'):强制使用 Tkinter 后端,保证 plt.show() 能弹出窗口。必须写在 import pyplot 之前,否则无效。

  • 从 sklearn.gaussian_process 导入回归器、分类器,从 kernels 导入 RBF 核。

    kernel = 1e-2 * RBF([5e-3])

  • 定义高斯过程核函数:常数 0.01 × RBF(长度尺度=0.005)。

  • 注意 :这里 RBF([5e-3]) 传入的是列表 ,表示每个特征维度一个长度尺度(各向异性 ARD 核)。这意味着输入数据必须是多维的,否则会报错。


二、高斯过程回归(GPR)

1. 读取数据

复制代码
stats = pd.read_table('./regression.csv')
point = stats.iloc[:, 4] / 38
rating = stats.iloc[:, 5].to_numpy().reshape(-1, 1)
  • 读取 regression.csv(制表符分隔)。

  • point:第 5 列(索引 4)除以 38,得到"场均得分"的归一化值,是 Series。

  • rating:第 6 列(索引 5)转 NumPy 并 reshape 成 (n,1) 列向量------这就是你上次报错修好的那行。

2. 构造预测网格

复制代码
x_min, x_max = rating.min() - 0.05, rating.max() + 0.05
xx_regression = np.arange(x_min, x_max, 1e-3).reshape(-1, 1)
  • 在 rating 范围左右各扩 0.05,步长 0.001 生成预测点,形状 (m,1)。

3. 训练与预测

复制代码
gpr = gaussian_process.GaussianProcessRegressor(kernel=kernel).fit(rating, point)
gpr_pred, sigma = gpr.predict(xx_regression, return_std=True)
  • fit(rating, point):用 rating 预测 point。point 是 Series,sklearn 会自动转成数组,可以正常接受。

  • return_std=True:同时返回预测标准差 sigma,用于画置信区间。


三、高斯过程分类(GPC)

1. 读取数据

复制代码
rawstat = pd.read_table('./linear inseparable.csv')
category = rawstat.iloc[:, 0]
pass_ratio = rawstat.iloc[:, 1] / rawstat.iloc[:, 2]
shot_ratio = rawstat.iloc[:, 3] / rawstat.iloc[:, 4]
  • category:第 1 列,类别标签(0/1)。

  • pass_ratio:第 2 列 / 第 3 列,传球成功率。

  • shot_ratio:第 4 列 / 第 5 列,射门成功率。

2. 组装特征矩阵

复制代码
ratio.append(pass_ratio)
ratio.append(shot_ratio)
ratio = np.array(ratio)
ratio = ratio.astype('float')
  • 拼成 2×n 数组,再转 float。这里有个隐患 :pass_ratio、shot_ratio 是 Series,np.array([Series, Series]) 会按索引对齐,若两个 Series 索引不一致会产生 NaN 或错位。更稳妥的写法是 .to_numpy()。

3. 训练分类器

复制代码
gpc = gaussian_process.GaussianProcessClassifier(kernel=kernel).fit(ratio.T, category)
  • ratio.T 形状 (n,2),作为特征;category 作为标签。

  • 注意 :这里复用了同一个 kernel。但回归核是 1e-2 * RBF([5e-3]),长度尺度只有 0.005,对取值 0~1 的比率特征来说过小 ,模型会极其"敏感"、几乎过拟合,分类边界会非常碎片化。实际使用中通常应给分类任务单独设核(如 RBF(length_scale=0.5))并加 ConstantKernel。

4. 生成分类网格与预测

复制代码
x_min, x_max = ratio[0].min() - 0.05, ratio[0].max() + 0.05
y_min, y_max = ratio[1].min() - 0.05, ratio[1].max() + 0.05
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.01), np.arange(y_min, y_max, 0.01))
xy = np.vstack([xx.ravel(), yy.ravel()]).T
Z = gpc.predict(xy).reshape(xx.shape)
复制代码
  • 在二维平面生成 0.01 步长网格,展平成 (N,2) 送入 predict,再把结果 reshape 回网格形状 Z。

  • Z 是 0/1 的预测类别,用于画决策边界。


四、绘图

复制代码
fig = plt.figure()
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
复制代码
  • 创建 1×2 子图。小问题 :创建了 ax1/ax2 却用 plt.xxx 在"当前坐标轴"上画------因为 add_subplot(122) 后当前轴是 ax2,所以回归图实际画在了 ax2 上,分类图画在了 ax1 上 (顺序反了)。虽然能显示,但标题和位置对应关系是错的。正确做法是用 ax1.plot(...)、ax2.plot(...)。

回归子图

复制代码
plt.plot(xx_regression, gpr_pred, c="r", linewidth=4)          # 均值曲线
plt.scatter(rating, point, c="b", s=5)                          # 原始散点
plt.fill(np.concatenate([xx_regression, xx_regression[::-1]]),
         np.concatenate([gpr_pred - 1.96*sigma, (gpr_pred + 1.96*sigma)[::-1]]),
         alpha=.5, fc='b', ec='None')          
复制代码
                 # 95% 置信带
  • 用 fill 画 ±1.96σ 的 95% 置信区间:正向走一遍、反向走一遍拼成闭合多边形,这是标准画法。

分类子图

复制代码
plt.scatter(pass_ratio[category == 0], shot_ratio[category == 0], c='r', marker='o')
plt.scatter(pass_ratio[category == 1], shot_ratio[category == 1], c='b', marker='^')
plt.contour(xx, yy, Z, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
  • 按类别 0/1 分别画红圆、蓝三角。

  • contour 画 Z 的等值线。注意 :Z 是 predict 返回的离散标签 0/1 ,其取值范围只有 {0,1},而 levels=[-1,0,1] 里 -1 和 1 根本不存在,只有 0 可能画出线(且只在 0/1 交界处)。通常分类图应画 predict_proba 的概率值或 decision_function 的连续输出,才能得到平滑的决策边界;这里用离散标签画轮廓,效果会很粗糙、甚至只画出一条线。这是本代码最值得改进的点之一。


五、潜在问题汇总

位置 问题 建议
np.array([Series, Series]) 索引对齐可能错位 用 .to_numpy() 后再堆叠
分类复用回归 kernel 长度尺度 0.005 过小,严重过拟合 为 GPC 单独设核,如 RBF(0.5)
ax1/ax2 与 plt.xxx 混用 两张图画反 统一用 ax1.xxx / ax2.xxx
contour 用离散标签 Z 边界粗糙、levels 不匹配 改用 gpc.predict_proba(xy)[:,1]
RBF([5e-3]) 要求多维输入 若数据是 1 维会报错 确认输入形状为 (n,1),回归已满足

六、代码整体逻辑总结

  1. 回归:用场均评分(rating)预测场均得分(point),高斯过程给出均值曲线 + 95% 置信带。

  2. 分类:用传球成功率、射门成功率两个特征,把样本分成两类,画出决策边界。

  3. 展示:左右两图并列,直观对比高斯过程在回归与分类任务上的表现。

核心价值在于演示 GaussianProcessRegressor 的 return_std 置信区间画法,以及 GaussianProcessClassifier 的二维决策边界绘制流程。若要让分类图更美观、模型更合理,建议按上表调整核函数和轮廓绘制方式。

复制代码
import pandas as pd
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from sklearn import gaussian_process
from sklearn.gaussian_process.kernels import RBF, ConstantKernel

# ============ 回归任务:核函数 ============
kernel_reg = 1e-2 * RBF([5e-3])

# ============ 高斯过程回归(GPR) ============
stats = pd.read_table('./regression.csv')
point = (stats.iloc[:, 4] / 38).to_numpy()                 # 转 ndarray,避免 Series 对齐问题
rating = stats.iloc[:, 5].to_numpy().reshape(-1, 1)        # 列向量

x_min, x_max = rating.min() - 0.05, rating.max() + 0.05
xx_regression = np.arange(x_min, x_max, 1e-3).reshape(-1, 1)

gpr = gaussian_process.GaussianProcessRegressor(
    kernel=kernel_reg,
    normalize_y=True,          # 让均值/方差尺度更稳定
    n_restarts_optimizer=5     # 多次重启避免局部最优
).fit(rating, point)

gpr_pred, sigma = gpr.predict(xx_regression, return_std=True)

# ============ 高斯过程分类(GPC) ============
rawstat = pd.read_table('./linear inseparable.csv')
category = rawstat.iloc[:, 0].to_numpy()
pass_ratio = (rawstat.iloc[:, 1] / rawstat.iloc[:, 2]).to_numpy()
shot_ratio = (rawstat.iloc[:, 3] / rawstat.iloc[:, 4]).to_numpy()

# 用 np.column_stack 堆叠,避免 Series 索引对齐错位
ratio = np.column_stack([pass_ratio, shot_ratio]).astype('float')

# 分类任务单独设核:取值 0~1 的比率特征,长度尺度应更大
kernel_clf = ConstantKernel(1.0, (1e-2, 1e2)) * RBF(
    length_scale=[0.5, 0.5], length_scale_bounds=(1e-2, 1e2)
)

gpc = gaussian_process.GaussianProcessClassifier(
    kernel=kernel_clf,
    n_restarts_optimizer=5
).fit(ratio, category)

# 网格 + 连续概率输出(用于画平滑决策边界)
x_min, x_max = ratio[:, 0].min() - 0.05, ratio[:, 0].max() + 0.05
y_min, y_max = ratio[:, 1].min() - 0.05, ratio[:, 1].max() + 0.05
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.01),
                     np.arange(y_min, y_max, 0.01))
xy = np.vstack([xx.ravel(), yy.ravel()]).T
Z_prob = gpc.predict_proba(xy)[:, 1].reshape(xx.shape)    # 类别 1 的概率
Z_label = gpc.predict(xy).reshape(xx.shape)               # 离散标签(备用)

# ============ 绘图 ============
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

# ---- 左图:回归 ----
ax1.plot(xx_regression, gpr_pred, c="r", linewidth=3, label="GPR mean")
ax1.scatter(rating, point, c="b", s=8, label="data")
ax1.fill(np.concatenate([xx_regression.ravel(), xx_regression.ravel()[::-1]]),
         np.concatenate([gpr_pred - 1.96 * sigma,
                         (gpr_pred + 1.96 * sigma)[::-1]]),
         alpha=.3, fc='b', ec='None', label="95% CI")
ax1.set_title("Gaussian Process Regression")
ax1.set_xlabel("average rating")
ax1.set_ylabel("average point per game")
ax1.legend()

# ---- 右图:分类 ----
# 用概率做填充色,决策边界更平滑
cf = ax2.contourf(xx, yy, Z_prob, levels=np.linspace(0, 1, 11),
                  cmap='RdBu_r', alpha=0.6)
ax2.contour(xx, yy, Z_prob, levels=[0.5], colors='k', linewidths=2)  # 0.5 决策边界
ax2.scatter(pass_ratio[category == 0], shot_ratio[category == 0],
            c='r', marker='o', edgecolors='k', label="class 0")
ax2.scatter(pass_ratio[category == 1], shot_ratio[category == 1],
            c='b', marker='^', edgecolors='k', label="class 1")
ax2.set_title("Gaussian Process Classification")
ax2.set_xlabel("pass ratio")
ax2.set_ylabel("shot ratio")
ax2.legend()

fig.colorbar(cf, ax=ax2, label="P(class=1)")
plt.tight_layout()
plt.show()
相关推荐
热心网友俣先生1 小时前
2026年MathorCup大数据竞赛-B题数据集分享
数学建模·1024程序员节
老王IT1 小时前
Rust基础语法介绍
1024程序员节
泡海椒1 小时前
JQuick-SQL NoSQL 数据接入:先标准化文档记录,再统一 SQL 查询
1024程序员节
小贺儿开发1 小时前
《长征 · 征途》Unity 三维互动沙盘
科技·3d·unity·1024程序员节·地形·互动·沙盘
让代码飞19261 小时前
DSH 数据目录从 C 盘迁到 其他 盘完整教程
1024程序员节
benchmark_cc1 小时前
量化开发中,什么时候应该停止手动下载股票数据?
python·数据分析·pandas·量化交易·1024程序员节·股票数据·quantdash
passionSnail1 小时前
DeepSeek V4.1 Flash 高效落地实战指南
1024程序员节
泡茶喝茶写代码1 小时前
A股量化策略回测实战:从行情数据到绩效评估(第 15 篇):新高新低策略
python·1024程序员节·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口
johnsong1 小时前
AI前沿日报 — 2026年10月24日:AI的边界
1024程序员节