前言
- 最近在学习
scikit-opt
包,尝试使用启发式算法对机器学习模型参数进行优化。 - 因为时间关系,挑了一个简单的机器学习模型进行尝试,即K邻近分类模型。
- 推荐读scikit-opt官方文档,了解各启发式算法输入参数与输出。
安装scitit-opt包
pip install scikit-opt
导入必要包
#加载包
import numpy as np
import pandas as pd
from plotnine import*
import seaborn as sns
from scipy import stats
import matplotlib as mpl
import matplotlib.pyplot as plt
#中文显示问题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# notebook嵌入图片
%matplotlib inline
# 提高分辨率
%config InlineBackend.figure_format='retina'
# 切分数据
from sklearn.model_selection import train_test_split
# 评价指标
from sklearn.metrics import mean_squared_error
# 忽略警告
import warnings
warnings.filterwarnings('ignore')
# 导入粒子群算法
from sko.PSO import PSO
# 交叉检验
from sklearn.model_selection import cross_val_score
from sklearn.metrics import accuracy_score
from sklearn.metrics import f1_score
粒子群算法
- 首先展示一个官方小例子理解一下算法构造。
定义目标函数
def demo_func(x):
x1, x2 = x
return -20 * np.exp(-0.2 * np.sqrt(0.5 * (x1 ** 2 + x2 ** 2))) - np.exp(
0.5 * (np.cos(2 * np.pi * x1) + np.cos(2 * np.pi * x2))) + 20 + np.e
不等式约束
- 末尾的逗号
,
一定要加,否则会报错
constraint_ueq = (lambda x: (x[0] - 1) ** 2 + (x[1] - 0) ** 2 - 0.5 ** 2,)
优化求解
- 输入参数请参考PSO参数说明
# 迭代次数
max_iter = 50
pso = PSO(func=demo_func,
n_dim=2,pop=40,
max_iter=max_iter,
lb=[-2, -2],
ub=[2, 2],
constraint_ueq=constraint_ueq)
pso.record_mode = True
pso.run()
print('best_x is ', pso.gbest_x, 'best_y is', pso.gbest_y)
输出:
best_x is [9.52188498e-01 4.39396655e-05] best_y is [2.57992762]
迭代曲线
plt.figure(dpi = 300)
plt.plot(pso.gbest_y_hist)
plt.show()
利用粒子群算法调参
生成分类数据
# 生成分类数据
from sklearn.datasets import make_classification
x, y = make_classification(n_samples=1000, # 样本个数
n_features=3, # 特征个数
n_informative=2, # 有效特征个数
n_redundant=1, # 冗余特征个数(有效特征的随机组合)
n_repeated=0, # 重复特征个数(有效特征和冗余特征的随机组合)
n_classes=3, # 样本类别
n_clusters_per_class=1, # 簇的个数
random_state=42)
# 可视化分类数据集,只选择前两个特征
plt.figure(dpi = 600,figsize = (6,4))
plt.scatter(x[:,0],x[:,1],c = y)
切分数据集
# 切分数据集
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=45, test_size=0.2)
K邻近算法
- 使用未调参K邻近算法,并检验模型效果
# K邻近算法
from sklearn import neighbors
knn_clf_old = neighbors.KNeighborsClassifier()
knn_clf_old.fit(x_train,y_train)
print('训练集准确率:{:.2f}'.format(accuracy_score(y_train, knn_clf_old.predict(x_train))))
print('测试集准确率:{:.2f}'.format(accuracy_score(y_test, knn_clf_old.predict(x_test))))
输出:
训练集准确率:0.95
测试集准确率:0.92
定义目标函数
- 目标函数为10折交叉检验
f1_macro
平均值 - 因为算法以最小化为目标,所以这里对
f1_macro
值取负
def knn_score(x):
knn_score = cross_val_score(neighbors.KNeighborsClassifier(n_neighbors = int(x)),
x_train,y_train,scoring='f1_macro',cv=10).mean()
return -knn_score
优化
max_iter = 50
knn_pso = PSO(func=knn_score,
n_dim=1,pop=40,
max_iter=max_iter,
lb=[1],ub=[100])
knn_pso.record_mode = True
knn_pso.run()
print('best_k is ', int(knn_pso.gbest_x))
输出:
best_k is [12.5256584]
迭代曲线
plt.figure(dpi = 300)
plt.plot(knn_pso.gbest_y_hist)
plt.show()
可以看到当k为12时,算法收敛。
使用最优参
knn_clf_new = neighbors.KNeighborsClassifier(n_neighbors = 12)
knn_clf_new.fit(x_train,y_train)
print('训练集准确率:{:.2f}'.format(accuracy_score(y_train, knn_clf_new.predict(x_train))))
print('测试集准确率:{:.2f}'.format(accuracy_score(y_test, knn_clf_new.predict(x_test))))
输出:
训练集准确率:0.93
测试集准确率:0.90