DAY 12 启发式算法

目录

      • DAY 12 启发式算法
        • 1.三种启发式算法的示例代码:遗传算法、粒子群算法、退火算法
          • 遗传算法
          • 粒子群算法
          • 退火算法
        • 2.学习优化算法的思路(避免浪费无效时间)
        • 作业:今天以自由探索的思路为主,尝试检索资料、视频、文档,用尽可能简短但是清晰的语言看是否能说清楚这三种算法每种算法的实现逻辑,帮助更深入的理解。

DAY 12 启发式算法

超参数调整专题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]]
1.三种启发式算法的示例代码:遗传算法、粒子群算法、退火算法
遗传算法
# 遗传算法优化随机森林
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]]
2.学习优化算法的思路(避免浪费无效时间)
作业:今天以自由探索的思路为主,尝试检索资料、视频、文档,用尽可能简短但是清晰的语言看是否能说清楚这三种算法每种算法的实现逻辑,帮助更深入的理解。

遗传算法 (Genetic Algorithm, GA) 的核心思想是模拟生物进化过程来解决优化问题。它通过“物竞天择,适者生存”的原则,不断迭代地生成和筛选解,最终找到最优解或接近最优解。

核心逻辑:

  1. 初始化种群 (Initialization): 随机生成一组初始的潜在解(称为“个体”或“染色体”)。
  2. 评估适应度 (Fitness Evaluation): 为每个个体计算一个“适应度”值,衡量其解决问题的优劣程度。适应度越高,个体越优秀。
  3. 选择 (Selection): 根据适应度值,从当前种群中选择一部分个体作为“父代”,适应度高的个体被选中的概率更大。
  4. 交叉 (Crossover/Recombination): 选中的父代个体之间进行基因“交叉”,产生新的“子代”个体。这模拟了生物的基因重组,目的是产生新的、可能更优秀的组合。
  5. 变异 (Mutation): 对新生成的子代个体进行随机的微小改变(“基因变异”)。这增加了种群的多样性,避免算法陷入局部最优。
  6. 更新种群 (Population Update): 将新生成的子代个体替换掉旧的种群,形成新的种群。
  7. 终止条件 (Termination): 重复步骤 2-6,直到满足预设的终止条件(如达到最大迭代次数、找到足够好的解等)。

伪代码:

// 遗传算法主函数
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) { ... }

公式解释:

  1. 适应度函数 ( 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 越好。
    这个函数是问题相关的,它将一个解(个体)映射到一个数值,表示其优劣程度。例如,在旅行商问题中,适应度可能是路径长度的倒数;在函数优化中,可能是函数值的某个变换。

  2. 选择概率 ( 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 被选中的概率。适应度越高的个体,被选中的概率越大,这体现了“适者生存”的原则。常见的选择方法有轮盘赌选择、锦标赛选择等。

  3. 交叉操作 ( 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]

  4. 变异操作 ( 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],通常是一个非常小的数值。

@浙大疏锦行

你可能感兴趣的:(Python训练营,python,开发语言)