我编写了一个Python脚本,使用scikit-learn
来拟合一些数据的高斯过程。
简而言之:我面临的问题是,虽然高斯过程似乎很好地学习了训练数据集,但对测试数据集的预测却不准确,我认为这背后可能存在一个归一化的问题。
详细来说:我的训练数据集包含1500
个时间序列。每个时间序列有50
个时间分量。高斯过程学习的是从一组三维坐标x,y,z
(代表我的模型的参数)到一个时间序列的映射。换句话说,x,y,z
与一个时间序列之间存在一对一的映射,高斯过程学习这种映射。想法是,通过给训练后的高斯过程新的坐标,它们应该能够给出与这些坐标相关联的预测时间序列。
这是我的代码:
from __future__ import divisionimport numpy as npfrom matplotlib import pyplot as pltfrom sklearn.gaussian_process import GaussianProcessRegressorfrom sklearn.gaussian_process.kernels import Materncoordinates_training = np.loadtxt(...) # 从文件中读取训练坐标 x, y, zcoordinates_testing = np.loadtxt(..) # 从文件中读取测试坐标 x, y, z# 对训练和测试数据的坐标进行z-score归一化。# 注意,我使用训练数据集的均值和标准差也来归一化测试数据集mean_coords_training = np.zeros(3)std_coords_training = np.zeros(3)for i in range(3): mean_coords_training[i] = coordinates_training[:, i].mean() std_coords_training[i] = coordinates_training[:, i].std() coordinates_training[:, i] = (coordinates_training[:, i] - mean_coords_training[i])/std_coords_training[i] coordinates_testing[:, i] = (coordinates_testing[:, i] - mean_coords_training[i])/std_coords_training[i]time_series_training = np.loadtxt(...)# 从文件中读取训练数据的时间序列number_of_time_components = np.shape(time_series_training)[1] # 100个时间分量# 对时间序列进行z-score归一化mean_time_series_training = np.zeros(number_of_time_components)std_time_series_training = np.zeros(number_of_time_components)for i in range(number_of_time_components): mean_time_series_training[i] = time_series_training[:, i].mean() std_time_series_training[i] = time_series_training[:, i].std() time_series_training[:, i] = (time_series_training[:, i] - mean_time_series_training[i])/std_time_series_training[i]time_series_testing = np.loadtxt(...)# 从文件中读取测试数据# 训练和测试数据集的时间分量数相同# 再次使用训练数据的均值和标准差对测试数据进行z-score归一化为 i in range(number_of_time_components): time_series_testing[:, i] = (time_series_testing[:, i] - mean_time_series_training[i])/std_time_series_training[i]# 高斯过程 pred_time_series_training = np.zeros((np.shape(time_series_training)))pred_time_series_testing = np.zeros((np.shape(time_series_testing)))# 实例化一个高斯过程模型kernel = 1.0 * Matern(nu=1.5)gp = GaussianProcessRegressor(kernel=kernel)for i in range(number_of_time_components): print("time component", i) # 使用参数的最大似然估计来拟合数据 gp.fit(coordinates_training, time_series_training[:,i]) # 在网格化的x轴上进行预测(同时请求MSE) y_pred_train, sigma_train = gp.predict(coordinates_train, return_std=True) y_pred_test, sigma_test = gp.predict(coordinates_test, return_std=True) pred_time_series_training[:,i] = y_pred_train*std_time_series_training[i] + mean_time_series_training[i] pred_time_series_testing[:,i] = y_pred_test*std_time_series_training[i] + mean_time_series_training[i]# 绘制训练数据的图fig, ax = plt.subplots(5, figsize=(10,20))for i in range(5): ax[i].plot(time_series_training[100*i], color='blue', label='原始训练数据') ax[i].plot(pred_time_series_training[100*i], color='black', label='高斯过程预测 - 训练数据')# 绘制测试数据的图fig, ax = plt.subplots(5, figsize=(10,20))for i in range(5): ax[i].plot(features_time_series_testing[100*i], color='blue', label='原始测试数据') ax[i].plot(pred_time_series_testing[100*i], color='black', label='高斯过程预测 - 测试数据')
回答:
首先,你应该使用sklearn的预处理工具来处理你的数据。
from sklearn.preprocessing import StandardScaler
有其他有用的工具来组织数据,但这个特定的工具是用来归一化数据的。其次,你应该使用相同的参数来归一化训练集和测试集,模型会根据数据的“几何形状”来定义参数,如果你用不同的尺度来训练模型,就像是使用了错误的单位系统。
scale = StandardScaler()training_set = scale.fit_transform(data_train)test_set = scale.transform(data_test)
这将对两组数据使用相同的转换。
最后,你需要归一化的是特征而不是目标,我是说要归一化X的输入而不是Y的输出,归一化有助于模型在优化过程中通过改变目标函数的拓扑结构更快地找到答案,输出不影响这一点。
希望这能回答你的问题。