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),回归已满足 |
六、代码整体逻辑总结
-
回归:用场均评分(rating)预测场均得分(point),高斯过程给出均值曲线 + 95% 置信带。
-
分类:用传球成功率、射门成功率两个特征,把样本分成两类,画出决策边界。
-
展示:左右两图并列,直观对比高斯过程在回归与分类任务上的表现。
核心价值在于演示 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()