使用kpca和贝叶斯优化进行特征降维
任务
在量化投资策略中给特征值进行降维时,类似PCA、ICA、LDA的线性降维算法比较常见。线性降维可保持特征值的全局结构,但不好处理局部关系和时间序列的非线性特点。此时非线性降维或许是更好的选择。
常用的非线性降维算法包括LLE、LE、TSNE、KPCA、SOM等。首先,算法需要能够处理无监督学习任务;其次,需要能够应用于测试集,即允许变换新数据,而并非只可用于训练;此外, 在处理时间序列任务时,数据是单条目滚动产生的,因此算法需要能够对单条目或者批量的新数据进行变换;最后,转换结果需要稳定,便于比较和校验。
根据上述目标,可以排除例如TSNE、SOM等降维方式。KPCA是较合适的方法之一。
贝叶斯优化不仅适用于有监督机器学习或深度学习,同样适用于无监督学习的聚类或降维。利用贝叶斯优化对KPCA中的超参数进行选择,提升降维表现和稳定。
一、数据加载
以sklearn中的红酒数据集作为案例进行说明。数据集包含178条样本,13个特征值,3个标签类。
from sklearn.datasets import load_wine
wine_dataset=load_wine()
二、数据预处理
数据集分割、标准化处理。
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test=train_test_split(
wine_dataset['data'],wine_dataset['target'],random_state=0)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train) #标准化处理
X_test = scaler.transform(X_test) #标准化处理
三、核主成分分析KPCA
简单介绍一下KPCA原理。以下内容摘自基于sklearn的核主成分分析(KPCA)原理及其实现-CSDN博客
KPCA是PCA的一扩展形式,通过非线性映射将数据转换到高维空间中,然后在高维空间中进行PCA分析。KPCA可以在非线性数据上提取主成分,是一种有效的非线性降维方法。
KPCA的实现过程如下:
选择一个合适的核函数(如高斯核函数、多项式核函数等),将原始数据映射到高维空间中。
在高维空间中计算数据的协方差矩阵或者Gram矩阵,这个矩阵的维度通常很高。
对协方差矩阵或Gram矩阵进行特征值分解,得到每个特征向量和对应的特征值。
选取前k个特征向量,将原始数据映射到低维空间中。这些特征向量通常与原始数据的维度相同。
————————————————
以上为博主「清纯世纪」的原创文章,https://blog.csdn.net/qq_45100200/article/details/130020683
调用sklearn中的KernelPCA即可。利用fit_transform可直接对数据进行转换。做量化策略时,建议分开此过程。先用训练集fit数据,再在测试集上用transfomr转换。示例:
from sklearn.decomposition import KernelPCA
#建立kpca模型
model = KernelPCA(kernel='rbf',
n_components=2,
fit_inverse_transform=True,
random_state=0)
#训练集拟合
model.fit(X_train)
#测试集转换
model.transform(X_test)
四、贝叶斯优化——hyperopt
四、1 调优
首先尝试常用的贝叶斯优化算法库之一hyperopt。通过pip install hyperopt安装即可。hyperopt在计算效率、支持工具种类和可搜索空间具有优势。
需要注意的几点:
1.目标是最小化误差函数,可选择常用的几个之一或进行自定义。
2.n_components的最大值可选择为全部特征值数量。
3.保证结果可复现时,random_state传入‘rstate’参数进行设置。需要传入generator而非seed。
from sklearn.metrics import mean_squared_error
from sklearn.decomposition import KernelPCA
from hyperopt import fmin, tpe, hp
# 超参搜索空间
space = {
'kernel': hp.choice('kernel', ['rbf', 'poly', 'cosine']),
'gamma': hp.uniform('gamma', 0.01, 0.9),
'n_components':hp.uniform('n_components', 1, 5)
}
# 目标误差函数
def kpca_error(params):
kpca = KernelPCA(kernel=params['kernel'], gamma=params['gamma'],
n_components=int(params['n_components']), #整数化
fit_inverse_transform=True,
random_state=0)
transed_x = kpca.fit_transform(X_train) #训练数据
inversed_x = kpca.inverse_transform(transed_x) #将训练结果逆变换
return mean_squared_error(X_train,inversed_x) #计算与真值的误差
# 贝叶斯优化
rstate = np.random.default_rng(0) #设定随机种子,使结果可复现
best = fmin(kpca_error, space, algo=tpe.suggest, rstate=rstate, max_evals=30)
100%|██████████| 30/30 [00:00<00:00, 76.93trial/s, best loss: 0.1872761007179496]
查看超参数优化的结果。注意若搜索空间中若有非数值型超参数,best的字典值中返回的是最优超参数在列表中的位置,而非原值。
best
{'gamma': 0.2365862168982712, 'kernel': 1, 'n_components': 4.54080206019777}
打印结果。
kernel_list = [ 'rbf', 'poly', 'cosine']
best_params = {'kernel':kernel_list[best['kernel']], #按位置索引
'n_components': int(best['n_components']), #整数化
'gamma': best['gamma']}
best_error = kpca_error(best_params)
print("最佳参数:", best_params)
print("最小误差:", best_error)
最佳参数: {'kernel': 'poly', 'n_components': 4, 'gamma': 0.2365862168982712}
最小误差: 0.1872761007179496
四、2 可视化
根据最优超参建立KPCA模型,再对测试集数据进行转换。转换时需要逐条进行,而非一次喂入全部测试集数据。
#在最优参数中加入inverse_transform和random_state条件
best_params.update({'fit_inverse_transform':True,'random_state':0})
#建立模型并训练
hypo = KernelPCA(**best_params).fit(X_train)
检验下对单个条目或多个条目进行降维时,结果是否一致。(对多个条目进行降维的场景,适用于例如卷积或循环神经网络的模型。)
#测试集第一条数据
print(hypo.transform(X_test[0].reshape(1,-1)))
#测试集前10条数据
print(hypo.transform(X_test[0:10]))
[[ 2.37259716 -0.10926884 0.15115008 -0.14961992]]
[[ 2.37259716 -0.10926884 0.15115008 -0.14961992]
[-3.64675841 -2.05933382 1.2071096 0.66484288]
[ 0.73687104 1.80920838 -1.37414963 -1.59350468]
[ 3.07627164 -0.30732876 0.82768742 -0.35732031]
[-0.49235334 1.22296917 -0.69379776 -1.75557317]
[ 0.8285484 1.15747042 2.59311594 -8.92095461]
[ 2.62482353 -0.49293994 -0.00970395 -0.64279828]
[-3.90039994 -2.15347714 0.73048196 0.89411751]
[-0.32589994 1.80970648 -1.65273348 -1.01566239]
[-0.70053125 1.68987254 -1.65613019 -1.21100263]]
以逐条进行转换为例。
#空列表
test_transed = []
#循环
for i in range(len(X_test)):
test_transed.append(hypo.transform(X_test[i].reshape(1,-1)))
#去掉一层嵌套,三维变为二维
test_transed = [i[0] for i in test_transed]
降维后的特征数量为4个,以第一个特征为例,对其与其他特征之间关系进行可视化。
import matplotlib.pyplot as plt
colors = ['r', 'g', 'b'] #3个标签类,3种颜色
fig, axes = plt.subplots(2,2,figsize=(10,10)) #4个子图
#建立子图
axes_list = []
for i in range(axes.shape[0]):
for j in range(axes.shape[1]):
axes_list.append(axes[i, j])
for h in range(len(axes_list)): #在子图列表中循环
for i in range(3): #在标签中循环
points = np.array([test_transed[j] for j in range(len(test_transed))
if y_test[j] == i]) #找到每类标签对应的转换后的特征值
axes_list[h].scatter(points[:, 0], points[:, h],
s=7, c=colors[i]) #对不同标签类上色
axes_list[h].set_xlabel("transed_feature_"+str(0)) #横坐标均为第一个特征
axes_list[h].set_ylabel("transed_feature_"+str(h)) #纵坐标为其余特征
plt.show()
五、贝叶斯优化——Bayes_opt
五、1 调优
其次尝试Bayes_opt库。可通过pip install bayesian-optimization命令安装。Bayes_opt库在参数空间由大量连续型参数构成时表现较好。需要注意该库的超参搜索空间仅适用于数值型超参,对非数值型不适用。
需要注意的几点:
1.算法是最大化目标函数,因此需对误差函数取负。
2.需设置好核函数。根据之前的优化结果,设置为多项式核。
from bayes_opt import BayesianOptimization
from sklearn.metrics import mean_squared_error
from sklearn.decomposition import KernelPCA
def kpca_func(gamma,n_components):
kpca = KernelPCA(kernel='poly', gamma=gamma,
n_components=int(n_components),
fit_inverse_transform=True,
random_state=0)
transed_x = kpca.fit_transform(X_train)
inversed_x = kpca.inverse_transform(transed_x)
return -mean_squared_error(X_train,inversed_x) #x_data
params = {'gamma':(0.01, 0.9), #搜索范围与之前相同
'n_components':(1, 5)} #搜索范围与之前相同
optimizer = BayesianOptimization(kpca_func,pbounds=params,random_state=0)
optimizer.maximize(init_points=1, #执行随机搜索的步数
n_iter=30, #执行贝叶斯优化的步数)
optimizer.max
| iter | target | gamma | n_comp... |
-------------------------------------------------
| 1 | -0.2997 | 0.4984 | 3.861 |
| 2 | -0.7741 | 0.01 | 1.0 |
| 3 | -0.2994 | 0.4932 | 3.857 |
| 4 | -0.6402 | 0.01 | 5.0 |
| 5 | -0.4416 | 0.9 | 2.969 |
| 6 | -0.6675 | 0.01 | 3.028 |
| 7 | -0.3133 | 0.9 | 3.588 |
| 8 | -0.207 | 0.9 | 4.678 |
| 9 | -0.207 | 0.896 | 4.989 |
| 10 | -0.6183 | 0.9 | 1.842 |
| 11 | -0.6512 | 0.01 | 4.054 |
| 12 | -0.207 | 0.9 | 4.246 |
| 13 | -0.2042 | 0.6525 | 4.876 |
| 14 | -0.2041 | 0.6463 | 4.474 |
| 15 | -0.6183 | 0.9 | 1.0 |
| 16 | -0.302 | 0.5385 | 3.455 |
| 17 | -0.2065 | 0.8406 | 4.452 |
| 18 | -0.205 | 0.7051 | 4.686 |
| 19 | -0.2051 | 0.7161 | 4.99 |
| 20 | -0.2051 | 0.7187 | 4.277 |
| 21 | -0.2062 | 0.8074 | 4.857 |
| 22 | -0.207 | 0.9 | 4.028 |
| 23 | -0.2063 | 0.8192 | 4.137 |
| 24 | -0.2018 | 0.5415 | 4.68 |
| 25 | -0.1403 | 0.5409 | 5.0 |
| 26 | -0.2063 | 0.8188 | 4.136 |
| 27 | -0.6969 | 0.01 | 2.069 |
| 28 | -0.1976 | 0.4197 | 4.982 |
| 29 | -0.2014 | 0.5259 | 4.979 |
| 30 | -0.437 | 0.6476 | 2.482 |
| 31 | -0.193 | 0.3369 | 4.458 |
=================================================
{'target': -0.14030320680092284,
'params': {'gamma': 0.5409405966633758, 'n_components': 5.0}}
优化过程和最优结果如下。在相同的核函数下,两种算法对其他两个超参数的优化结果有所区别。Bayes_opt优化后的特征数量为5,特征转换后的误差比hyperopt的结果更小一些(去掉负号)。
五、2 可视化
采取相同的方式,对优化后的结果进行可视化。
params = optimizer.max['params']
params['n_components'] = params['n_components'].astype(int)
params.update({'fit_inverse_transform':True,'random_state':0})
byo = KernelPCA(**params).fit(X_train)
test_transed = []
for i in range(len(X_test)):
test_transed.append(byo.transform(X_test[i].reshape(1,-1)))
#去掉一层嵌套,三维变为二维
test_transed = [i[0] for i in test_transed]
import matplotlib.pyplot as plt
colors = ['r', 'g', 'b'] #3个标签类,3种颜色
fig, axes = plt.subplots(2,3,figsize=(12,12)) #多一个特征,多一个子图
#建立子图
axes_list = []
for i in range(axes.shape[0]):
for j in range(axes.shape[1]):
axes_list.append(axes[i, j])
for h in range(len(axes_list)): #在子图列表中循环
for i in range(3): #在标签中循环
points = np.array([test_transed[j] for j in range(len(test_transed))
if y_test[j] == i]) #找到每类标签对应的转换后的特征值
axes_list[h].scatter(points[:, 0], points[:, h],
s=7, c=colors[i]) #对不同标签类上色
axes_list[h].set_xlabel("transed_feature_"+str(0)) #横坐标均为第一个特征
axes_list[h].set_ylabel("transed_feature_"+str(h)) #纵坐标为其余特征
plt.show()
总结
任务完成。Bayes_opt库优化后的误差要更小,但不支持非数值型超参的调优。hyperopt库会更便捷和高效一些。实际做量化策略时,特征数量要比案例中大很多,建议考虑模型计算降维后数据的效率选择目标特征数量的最大值。