超参数调整专题2
# 预处理
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 设置中文字体(解决中文显示问题)
plt.rcParams["font.sans-serif"]=["SimHei"] # Windows系统常用黑体字体
plt.rcParams["axes.unicode_minus"]=False # 正常显示负号
# 读取数据
data=pd.read_csv(r"data.csv")
# 先筛选字符串变量
list_discrete=data.select_dtypes(include=["object"]).columns.tolist()
# Home Ownership 标签编码
home_ownership_mapping={"Own Home":1,"Rent":2,"Have Mortgage":3,"Home Mortgage":4}
data["Home Ownership"]=data["Home Ownership"].map(home_ownership_mapping)
# Years in current job 标签编码
years_in_job_mapping={"< 1 year":1,"1 year":2,"2 years":3,"3 years":4,"4 years":5,"5 years":6,"6 years":7,"7 years":8,"8 years":9,"9 years":10,"10+ years":11}
data["Years in current job"]=data["Years in current job"].map(years_in_job_mapping)
# Purpose 独热编码
data=pd.get_dummies(data,columns=["Purpose"])
data2=pd.read_csv(r"data.csv")
list_new=[] # 独热编码后的新特征
for i in data.columns:
if i not in data2.columns:
list_new.append(i)
for i in list_new:
data[i]=data[i].astype(int) # 对新特征进行类型转换
# Term 0 - 1 映射
term_mapping={"Short Term":0,"Long Term":1}
data["Term"]=data["Term"].map(term_mapping)
data.rename(columns={"Term":"Long Term"},inplace=True) # 重命名列
# 查找连续型变量
list_continuous=data.select_dtypes(include=["int64","float64"]).columns.tolist()
# 中位数填补
for i in list_continuous:
median_value=data[i].median()
data[i]=data[i].fillna(median_value)
# 划分一次数据集
from sklearn.model_selection import train_test_split
X=data.drop(["Credit Default"],axis=1)
Y=data["Credit Default"]
# 80% 作为训练集,20% 作为测试集
X_train,X_test,Y_train,Y_test=train_test_split(X,Y,test_size=0.2,random_state=42)
from sklearn.svm import SVC # 支持向量机分类器
from sklearn.neighbors import KNeighborsClassifier # K近邻分类器
from sklearn.linear_model import LogisticRegression # 逻辑回归分类器
import xgboost as xgb # XGBoost分类器
import lightgbm as lgb # LightGBM分类器
from sklearn.ensemble import RandomForestClassifier # 随机森林分类器
from catboost import CatBoostClassifier # CatBoost分类器
from sklearn.tree import DecisionTreeClassifier # 决策树分类器
from sklearn.naive_bayes import GaussianNB # 高斯朴素贝叶斯分类器
from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score # 用于评估分类器性能的指标
from sklearn.metrics import classification_report,confusion_matrix # 用于生成分类报告和混淆矩阵
import warnings # 用于忽略警告信息
warnings.filterwarnings("ignore") # 忽略所有警告信息
print("默认参数随机森林(训练集 -> 测试集)")
import time
start_time=time.time()
rf_model=RandomForestClassifier(random_state=42)
rf_model.fit(X_train,Y_train)
rf_pred=rf_model.predict(X_test)
end_time=time.time()
print(f"训练与预测耗时:{end_time-start_time:.4f} 秒")
print("默认随机森林 在测试集上的分类报告:")
print(classification_report(Y_test,rf_pred))
print("默认随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(Y_test,rf_pred))
默认参数随机森林(训练集 -> 测试集)
训练与预测耗时:1.0711 秒
默认随机森林 在测试集上的分类报告:
precision recall f1-score support
0 0.76 0.97 0.85 1059
1 0.78 0.27 0.40 441
accuracy 0.76 1500
macro avg 0.77 0.62 0.63 1500
weighted avg 0.77 0.76 0.72 1500
默认随机森林 在测试集上的混淆矩阵:
[[1025 34]
[ 321 120]]
# 遗传算法优化随机森林
import time
import random
from deap import base,creator,tools,algorithms # DEAP是一个用于遗传算法和进化计算的Python库
print("遗传算法优化随机森林(训练集 -> 测试集)")
# 定义适应度函数和个体类型
creator.create("FitnessMax",base.Fitness,weights=(1.0,))
creator.create("Individual",list,fitness=creator.FitnessMax)
# 定义超参数范围
n_estimators_range=(50,200)
max_depth_range=(10,30)
min_samples_split_range=(2,10)
min_samples_leaf_range=(1,4)
# 初始化工具盒
toolbox=base.Toolbox()
# 定义基因生成器
toolbox.register("attr_n_estimators",random.randint,*n_estimators_range)
toolbox.register("attr_max_depth",random.randint,*max_depth_range)
toolbox.register("attr_min_samples_split",random.randint,*min_samples_split_range)
toolbox.register("attr_min_samples_leaf",random.randint,*min_samples_leaf_range)
# 定义个体生成器
toolbox.register("individual",tools.initCycle,creator.Individual,(toolbox.attr_n_estimators,toolbox.attr_max_depth,toolbox.attr_min_samples_split,toolbox.attr_min_samples_leaf),n=1)
# 定义种群生成器
toolbox.register("population",tools.initRepeat,list,toolbox.individual)
# 定义评估函数
def evaluate(individual):
n_estimators,max_depth,min_samples_split,min_samples_leaf=individual
model=RandomForestClassifier(n_estimators=n_estimators,max_depth=max_depth,min_samples_split=min_samples_split,min_samples_leaf=min_samples_leaf,random_state=42)
model.fit(X_train,Y_train)
Y_pred=model.predict(X_test)
accuracy=accuracy_score(Y_test,Y_pred)
return accuracy,
# 注册评估函数
toolbox.register("evaluate",evaluate)
# 注册遗传操作
toolbox.register("mate",tools.cxTwoPoint)
toolbox.register("mutate",tools.mutUniformInt,low=[n_estimators_range[0],max_depth_range[0],min_samples_split_range[0],min_samples_leaf_range[0]],up=[n_estimators_range[1],max_depth_range[1],min_samples_split_range[1],min_samples_leaf_range[1]],indpb=0.1)
toolbox.register("select",tools.selTournament,tournsize=3)
# 初始化种群
pop=toolbox.population(n=20)
# 遗传算法参数
NGEN=10
CXPB=0.5
MUTPB=0.2
start_time=time.time()
# 运行遗传算法
for gen in range(NGEN):
offspring=algorithms.varAnd(pop,toolbox,cxpb=CXPB,mutpb=MUTPB)
fits=toolbox.map(toolbox.evaluate,offspring)
for fit,ind in zip(fits,offspring):
ind.fitness.values=fit
pop=toolbox.select(offspring,k=len(pop))
end_time=time.time()
# 找到最优个体
best_ind=tools.selBest(pop,k=1)[0]
best_n_estimators,best_max_depth,best_min_samples_split,best_min_samples_leaf=best_ind
print(f"遗传算法优化耗时:{end_time-start_time:.4f} 秒")
print("最佳参数:",{"n_estimators":best_n_estimators,"max_depth":best_max_depth,"min_samples_split":best_min_samples_split,"min_samples_leaf":best_min_samples_leaf})
# 使用最佳参数的模型进行预测
best_model=RandomForestClassifier(n_estimators=best_n_estimators,max_depth=best_max_depth,min_samples_split=best_min_samples_split,min_samples_leaf=best_min_samples_leaf,random_state=42)
best_model.fit(X_train,Y_train)
best_pred=best_model.predict(X_test)
print("遗传算法优化后的随机森林 在测试集上的分类报告:")
print(classification_report(Y_test,best_pred))
print("遗传算法优化后的随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(Y_test,best_pred))
遗传算法优化随机森林(训练集 -> 测试集)
遗传算法优化耗时:156.6839 秒
最佳参数: {'n_estimators': 61, 'max_depth': 25, 'min_samples_split': 7, 'min_samples_leaf': 1}
遗传算法优化后的随机森林 在测试集上的分类报告:
precision recall f1-score support
0 0.77 0.97 0.86 1059
1 0.81 0.30 0.44 441
accuracy 0.77 1500
macro avg 0.79 0.64 0.65 1500
weighted avg 0.78 0.77 0.74 1500
遗传算法优化后的随机森林 在测试集上的混淆矩阵:
[[1028 31]
[ 307 134]]
# 粒子群优化算法优化随机森林
print("粒子群优化算法优化随机森林(训练集 -> 测试集)")
# 定义适应度函数,本质就是构建了一个函数实现 参数--> 评估指标的映射
def fitness_function(params):
n_estimators,max_depth,min_samples_split,min_samples_leaf=params # 序列解包,允许你将一个可迭代对象(如列表、元组、字符串等)中的元素依次赋值给多个变量。
model=RandomForestClassifier(n_estimators=int(n_estimators),max_depth=int(max_depth),min_samples_split=int(min_samples_split),min_samples_leaf=int(min_samples_leaf),random_state=42)
model.fit(X_train,Y_train)
Y_pred=model.predict(X_test)
accuracy=accuracy_score(Y_test,Y_pred)
return accuracy
# 粒子群优化算法实现
def pso(num_particles,num_iterations,c1,c2,w,bounds): # 粒子群优化算法核心函数
# num_particles:粒子的数量,即算法中用于搜索最优解的个体数量。
# num_iterations:迭代次数,算法运行的最大循环次数。
# c1:认知学习因子,用于控制粒子向自身历史最佳位置移动的程度。
# c2:社会学习因子,用于控制粒子向全局最佳位置移动的程度。
# w:惯性权重,控制粒子的惯性,影响粒子在搜索空间中的移动速度和方向。
# bounds:超参数的取值范围,是一个包含多个元组的列表,每个元组表示一个超参数的最小值和最大值。
num_params=len(bounds)
particles=np.array([[random.uniform(bounds[i][0],bounds[i][1]) for i in range(num_params)] for _ in range(num_particles)])
velocities=np.array([[0]*num_params for _ in range(num_particles)])
personal_best=particles.copy()
personal_best_fitness=np.array([fitness_function(p) for p in particles])
global_best_index=np.argmax(personal_best_fitness)
global_best=personal_best[global_best_index]
global_best_fitness=personal_best_fitness[global_best_index]
for _ in range(num_iterations):
r1=np.array([[random.random() for _ in range(num_params)] for _ in range(num_particles)])
r2=np.array([[random.random() for _ in range(num_params)] for _ in range(num_particles)])
velocities=w*velocities+c1*r1*(personal_best-particles)+c2*r2*(global_best-particles)
particles=particles+velocities
for i in range(num_particles):
for j in range(num_params):
if particles[i][j]<bounds[j][0]:
particles[i][j]=bounds[j][0]
elif particles[i][j]>bounds[j][1]:
particles[i][j]=bounds[j][1]
fitness_values=np.array([fitness_function(p) for p in particles])
improved_indices=fitness_values>personal_best_fitness
personal_best[improved_indices]=particles[improved_indices]
personal_best_fitness[improved_indices]=fitness_values[improved_indices]
current_best_index=np.argmax(personal_best_fitness)
if personal_best_fitness[current_best_index]>global_best_fitness:
global_best=personal_best[current_best_index]
global_best_fitness=personal_best_fitness[current_best_index]
return global_best,global_best_fitness
# 超参数范围
bounds=[(50,200),(10,30),(2,10),(1,4)] # n_estimators, max_depth, min_samples_split, min_samples_leaf
# 粒子群优化算法参数
num_particles=20
num_iterations=10
c1=1.5
c2=1.5
w=0.5
start_time=time.time()
best_params,best_fitness=pso(num_particles,num_iterations,c1,c2,w,bounds)
end_time=time.time()
print(f"粒子群优化算法优化耗时:{end_time-start_time:.4f} 秒")
print("最佳参数:",{"n_estimators":int(best_params[0]),"max_depth":int(best_params[1]),"min_samples_split":int(best_params[2]),"min_samples_leaf":int(best_params[3])})
# 使用最佳参数的模型进行预测
best_model=RandomForestClassifier(n_estimators=int(best_params[0]),max_depth=int(best_params[1]),min_samples_split=int(best_params[2]),min_samples_leaf=int(best_params[3]),random_state=42)
best_model.fit(X_train,Y_train)
best_pred=best_model.predict(X_test)
print("粒子群优化算法优化后的随机森林 在测试集上的分类报告:")
print(classification_report(Y_test,best_pred))
print("粒子群优化算法优化后的随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(Y_test,best_pred))
粒子群优化算法优化随机森林(训练集 -> 测试集)
粒子群优化算法优化耗时:324.3438 秒
最佳参数: {'n_estimators': 174, 'max_depth': 23, 'min_samples_split': 4, 'min_samples_leaf': 3}
粒子群优化算法优化后的随机森林 在测试集上的分类报告:
precision recall f1-score support
0 0.77 0.98 0.86 1059
1 0.87 0.29 0.43 441
accuracy 0.78 1500
macro avg 0.82 0.64 0.65 1500
weighted avg 0.80 0.78 0.74 1500
粒子群优化算法优化后的随机森林 在测试集上的混淆矩阵:
[[1040 19]
[ 314 127]]
# 模拟退火算法优化随机森林
print("模拟退火算法优化随机森林(训练集 -> 测试集)")
# 定义适应度函数
def fitness_function(params):
n_estimators,max_depth,min_samples_split,min_samples_leaf=params
model=RandomForestClassifier(n_estimators=int(n_estimators),max_depth=int(max_depth),min_samples_split=int(min_samples_split),min_samples_leaf=int(min_samples_leaf),random_state=42)
model.fit(X_train,Y_train)
Y_pred=model.predict(X_test)
accuracy=accuracy_score(Y_test,Y_pred)
return accuracy
# 模拟退火算法实现
def simulated_annealing(initial_solution,bounds,initial_temp,final_temp,alpha):
current_solution=initial_solution
current_fitness=fitness_function(current_solution)
best_solution=current_solution
best_fitness=current_fitness
temp=initial_temp
while temp>final_temp:
# 生成邻域解
neighbor_solution=[]
for i in range(len(current_solution)):
new_val=current_solution[i]+random.uniform(-1,1)*(bounds[i][1]-bounds[i][0])*0.1
new_val=max(bounds[i][0],min(bounds[i][1],new_val))
neighbor_solution.append(new_val)
neighbor_fitness=fitness_function(neighbor_solution)
delta_fitness=neighbor_fitness-current_fitness
if delta_fitness>0 or random.random()<np.exp(delta_fitness/temp):
current_solution=neighbor_solution
current_fitness=neighbor_fitness
if current_fitness>best_fitness:
best_solution=current_solution
best_fitness=current_fitness
temp*=alpha
return best_solution,best_fitness
# 超参数范围
bounds=[(50,200),(10,30),(2,10),(1,4)] # n_estimators, max_depth, min_samples_split, min_samples_leaf
# 模拟退火算法参数
initial_temp=100 # 初始温度
final_temp=0.1 # 终止温度
alpha=0.95 # 温度衰减系数
# 初始化初始解
initial_solution=[random.uniform(bounds[i][0],bounds[i][1]) for i in range(len(bounds))]
start_time=time.time()
best_params,best_fitness=simulated_annealing(initial_solution,bounds,initial_temp,final_temp,alpha)
end_time=time.time()
print(f"模拟退火算法优化耗时:{end_time-start_time:.4f} 秒")
print("最佳参数:",{"n_estimators":int(best_params[0]),"max_depth":int(best_params[1]),"min_samples_split":int(best_params[2]),"min_samples_leaf":int(best_params[3])})
# 使用最佳参数的模型进行预测
best_model=RandomForestClassifier(n_estimators=int(best_params[0]),max_depth=int(best_params[1]),min_samples_split=int(best_params[2]),min_samples_leaf=int(best_params[3]),random_state=42)
best_model.fit(X_train,Y_train)
best_pred=best_model.predict(X_test)
print("模拟退火算法优化后的随机森林 在测试集上的分类报告:")
print(classification_report(Y_test,best_pred))
print("模拟退火算法优化后的随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(Y_test,best_pred))
模拟退火算法优化随机森林(训练集 -> 测试集)
模拟退火算法优化耗时:183.0954 秒
最佳参数: {'n_estimators': 69, 'max_depth': 21, 'min_samples_split': 6, 'min_samples_leaf': 1}
模拟退火算法优化后的随机森林 在测试集上的分类报告:
precision recall f1-score support
0 0.77 0.97 0.86 1059
1 0.83 0.29 0.43 441
accuracy 0.77 1500
macro avg 0.80 0.63 0.65 1500
weighted avg 0.79 0.77 0.73 1500
模拟退火算法优化后的随机森林 在测试集上的混淆矩阵:
[[1032 27]
[ 311 130]]
遗传算法 (Genetic Algorithm, GA) 的核心思想是模拟生物进化过程来解决优化问题。它通过“物竞天择,适者生存”的原则,不断迭代地生成和筛选解,最终找到最优解或接近最优解。
核心逻辑:
伪代码:
// 遗传算法主函数
GeneticAlgorithm(problem) {
population = InitializePopulation() // 1. 初始化种群
WHILE NOT TerminationConditionMet() DO // 7. 终止条件
// 2. 评估适应度
FOR EACH individual IN population DO
individual.fitness = EvaluateFitness(individual)
END FOR
selected_parents = SelectParents(population) // 3. 选择
new_population = EMPTY_LIST
FOR i FROM 1 TO population_size STEP 2 DO // 交叉生成子代
parent1 = selected_parents[i]
parent2 = selected_parents[i+1]
// 4. 交叉
child1, child2 = Crossover(parent1, parent2)
// 5. 变异
Mutate(child1)
Mutate(child2)
Add child1 TO new_population
Add child2 TO new_population
END FOR
population = new_population // 6. 更新种群
END WHILE
RETURN BestIndividualInPopulation(population) // 返回最优解
}
// 辅助函数(简化表示,具体实现依赖于问题)
InitializePopulation() { ... }
EvaluateFitness(individual) { ... }
SelectParents(population) { ... }
Crossover(parent1, parent2) { ... }
Mutate(individual) { ... }
TerminationConditionMet() { ... }
BestIndividualInPopulation(population) { ... }
公式解释:
适应度函数 ( F ( x ) F(x) F(x)):
F ( x ) F(x) F(x) 表示个体 x x x 的适应度值。
F ( x ) ↑ ⟹ F(x) \uparrow \implies F(x)↑⟹ 个体 x x x 越好。
这个函数是问题相关的,它将一个解(个体)映射到一个数值,表示其优劣程度。例如,在旅行商问题中,适应度可能是路径长度的倒数;在函数优化中,可能是函数值的某个变换。
选择概率 ( P s e l e c t ( x ) P_{select}(x) Pselect(x)):
P s e l e c t ( x ) = F ( x ) ∑ i = 1 N F ( x i ) P_{select}(x) = \frac{F(x)}{\sum_{i=1}^{N} F(x_i)} Pselect(x)=∑i=1NF(xi)F(x)
其中, N N N 是种群大小, x i x_i xi 是种群中的第 i i i 个个体。
P s e l e c t ( x ) P_{select}(x) Pselect(x) 表示个体 x x x 被选中的概率。适应度越高的个体,被选中的概率越大,这体现了“适者生存”的原则。常见的选择方法有轮盘赌选择、锦标赛选择等。
交叉操作 ( C r o s s o v e r ( p 1 , p 2 ) Crossover(p_1, p_2) Crossover(p1,p2)):
C r o s s o v e r ( p 1 , p 2 ) → ( c 1 , c 2 ) Crossover(p_1, p_2) \to (c_1, c_2) Crossover(p1,p2)→(c1,c2)
c 1 = f c r o s s o v e r ( p 1 , p 2 ) c_1 = f_{crossover}(p_1, p_2) c1=fcrossover(p1,p2)
c 2 = g c r o s s o v e r ( p 1 , p 2 ) c_2 = g_{crossover}(p_1, p_2) c2=gcrossover(p1,p2)
其中, p 1 , p 2 p_1, p_2 p1,p2 是父代个体, c 1 , c 2 c_1, c_2 c1,c2 是子代个体。
交叉操作将父代的部分基因进行组合,产生新的子代。例如,对于二进制串编码的个体,可以随机选择一个交叉点,交换父代交叉点后的基因段。
交叉概率 ( P c P_c Pc):在进行交叉操作时,以 P c P_c Pc 的概率发生交叉。 P c ∈ [ 0 , 1 ] P_c \in [0, 1] Pc∈[0,1]。
变异操作 ( M u t a t e ( c ) Mutate(c) Mutate(c)):
M u t a t e ( c ) → c ′ Mutate(c) \to c' Mutate(c)→c′
其中, c c c 是子代个体, c ′ c' c′ 是变异后的个体。
变异操作对个体的某些基因进行随机改变。例如,对于二进制串编码,可以随机选择一个基因位,将其 0 变为 1,或 1 变为 0。
变异概率 ( P m P_m Pm):对每个基因位或每个个体,以 P m P_m Pm 的概率发生变异。 P m ∈ [ 0 , 1 ] P_m \in [0, 1] Pm∈[0,1],通常是一个非常小的数值。
@浙大疏锦行