【Python学习打卡-Day13】从优雅语法糖到自然启示:遗传、粒子群与退火算法初探
本文介绍了Python编程中的列表推导式技巧和三种启发式优化算法。首先讲解了列表推导式的基础用法、条件过滤、嵌套循环和函数调用等简洁写法。然后概述了遗传算法(GA)、粒子群优化(PSO)和模拟退火(SA)三种启发式算法的核心思想:GA模拟生物进化过程,PSO借鉴鸟群觅食行为,SA受金属退火工艺启发。文章强调在AI时代应注重算法思想而非具体实现,并展示了这些算法在优化随机森林模型超参数时的应用效果。
前言
各位伙伴们,Day 13 的学习旅程带我们进入了一个全新的高度!我们不再仅仅是使用现成的库函数,而是开始探索那些源于自然智慧、用于解决复杂优化问题的“启发式算法”。这些算法(如遗传算法、粒子群优化)听起来高深,但它们的思想却美妙而直观。
今天,我们将兵分两路:
- 练好内功:学习一个能让你的 Python 代码瞬间变得优雅简洁的“语法糖”——列表推导式。
- 拓展视野:概览式地了解遗传算法 (GA)、粒子群优化 (PSO) 和模拟退火 (SA) 的核心思想,为未来的科研和工程应用(甚至是老师说的“写论文”)埋下伏笔。
最重要的是,今天我们将实践一种新的学习心法:在 AI 时代,理解思想、关注输入输出,比死记硬背复杂实现更重要!
一、列表推导式:Pythonic 编程的“语法糖” 🍬
在深入算法世界前,我们先来品尝一颗让代码更甜美的“语法糖”——列表推导式。它能用一行代码替代传统的多行循环,让代码更简洁、可读性更强。
1.1 基础用法:一行顶三行
需求:生成 [1, 4, 9, 16, 25]。
传统写法:
squares = []
for x in range(1, 6):
squares.append(x**2)
print(squares)
# [1, 4, 9, 16, 25]
列表推导式写法:
# 语法:[expression for item in iterable]
squares = [x**2 for x in range(1, 6)]
print(squares)
# [1, 4, 9, 16, 25]
是不是瞬间清爽了很多?
1.2 带 if 条件过滤
需求:生成 1到10 之间的所有偶数。
传统写法:
evens = []
for x in range(1, 11):
if x % 2 == 0:
evens.append(x)
print(evens)
# [2, 4, 6, 8, 10]
列表推导式写法:
# 语法:[expression for item in iterable if condition]
evens = [x for x in range(1, 11) if x % 2 == 0]
print(evens)
# [2, 4, 6, 8, 10]
1.3 嵌套循环:生成笛卡尔积
需求:生成 [1, 2] 和 [3, 4] 的所有元素组合。
传统写法:
combinations = []
for x in [1, 2]:
for y in [3, 4]:
combinations.append((x, y))
print(combinations)
# [(1, 3), (1, 4), (2, 3), (2, 4)]
列表推导式写法:
# 语法:[expression for item1 in iterable1 for item2 in iterable2]
combinations = [(x, y) for x in [1, 2] for y in [3, 4]]
print(combinations)
# [(1, 3), (1, 4), (2, 3), (2, 4)]
1.4 结合函数调用
需求:将列表中的所有单词转为大写。
words = ["apple", "banana", "cherry"]
# 列表推导式写法
upper_words = [word.upper() for word in words]
print(upper_words)
# ['APPLE', 'BANANA', 'CHERRY']
掌握列表推导式,是迈向 Python 高手的重要一步!
二、启发式算法:当调参遇上“大自然” 🌍
我们已经知道,模型调参就像在一个复杂的地形上寻找最高峰(最佳性能)。启发式算法就是一群受自然启发的“智能探险家”,它们用各自独特的策略来寻找这个最高点。
核心思想:这些算法都是优化器。我们的目标是找到一组超参数,让机器学习模型的评估指标(如准确率)最高。
在开始之前,我们先准备好数据和基准模型,以便衡量这些高级优化算法的效果。
点击展开:查看数据预处理与基准模型代码# --- 0. 数据预处理与数据集划分 (与之前一致) ---
# 此处省略了详细的数据读取、编码、填充、划分代码
# 假设 X_train, X_test, y_train, y_test 已经准备就绪
# --- 1. 默认参数的随机森林 (作为基准) ---
import time
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
print("--- 1. 默认参数随机森林 (基准模型) ---")
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("\n默认随机森林在测试集上的分类报告:")
print(classification_report(y_test, rf_pred))
# 输出结果:
# 训练与预测耗时: 0.8383 秒
# 默认随机森林在测试集上的分类报告:
# precision recall f1-score support
# 0 0.77 0.97 0.86 1059
# 1 0.79 0.30 0.43 441
# accuracy 0.77 1500
接下来,让我们认识三位“探险家”。
2.1 遗传算法 (GA):物竞天择,适者生存 🧬
- 灵感来源:达尔文的生物进化论。
- 核心思想:
- 种群 (Population):随机生成一堆超参数组合,每个组合都是一个“个体”。
- 适应度 (Fitness):用模型性能(如准确率)来评估每个“个体”的生存能力。
- 选择 (Selection):表现好的个体(高分)更容易被选中,进入下一代。
- 交叉 (Crossover):被选中的“父母”个体交换部分参数(基因),产生新的“后代”。
- 变异 (Mutation):“后代”的参数有小概率发生随机改变。
- 循环:新一代种群重复上述过程,一代代进化,最终找到适应环境的“超级个体”(最优参数)。
- 感觉:像是在大范围“撒网”搜索,通过优胜劣汰和随机变动,逐步逼近最优解。
# --- 2. 遗传算法优化随机森林 ---
print("\n--- 2. 遗传算法优化随机森林 ---")
from deap import base, creator, tools, algorithms
import random
# (此处省略了DEAP框架的详细设置,包括定义个体、种群、评估函数、遗传操作等)
# 核心是定义了一个 evaluate 函数,输入一个体(参数组合),输出模型准确率
# 运行遗传算法...
# (此处省略了运行算法的循环代码)
# 结果展示
print(f"遗传算法优化耗时: 205.8067 秒")
print("最佳参数: {'n_estimators': 179, 'max_depth': 26, 'min_samples_split': 8, 'min_samples_leaf': 2}")
print("\n遗传算法优化后的分类报告:")
# ... (输出分类报告)
# precision recall f1-score support
# 0 0.77 0.98 0.86 1059
# 1 0.84 0.29 0.43 441
2.2 粒子群优化 (PSO):鸟群觅食,信息共享 🐦
- 灵感来源:鸟群或鱼群的集体觅食行为。
- 核心思想:
- 粒子 (Particle):每个超参数组合是一个在参数空间中“飞行”的粒子。
- 速度 (Velocity):每个粒子有自己的移动方向和速度。
- 个体最优 (pbest):每个粒子会记住自己飞过的历史最佳位置。
- 全局最优 (gbest):整个粒子群共享信息,所有粒子都知道当前整个群体发现的最佳位置。
- 更新:每个粒子根据自己的“个体经验 (pbest)”和“群体智慧 (gbest)”,并带一点惯性,来更新自己的速度和位置,飞向更有希望的区域。
- 感觉:像是一群互相协作的探险家,既有自己的记忆,又听取团队的建议,集体高效地寻找目标。通常比遗传算法收敛更快。
# --- 3. 粒子群优化随机森林 ---
print("\n--- 3. 粒子群优化随机森林 ---")
# (此处省略了PSO函数的具体实现,核心是 fitness_function 和 pso 循环)
# fitness_function 同样是输入参数组合,输出模型准确率
# 运行粒子群优化...
# (此处省略了调用 pso 函数的代码)
# 结果展示
print(f"粒子群优化耗时: 104.8761 秒")
print("最佳参数: {'n_estimators': 51, 'max_depth': 20, 'min_samples_split': 7, 'min_samples_leaf': 2}")
print("\n粒子群优化后的分类报告:")
# ... (输出分类报告)
# precision recall f1-score support
# 0 0.77 0.97 0.86 1059
# 1 0.83 0.31 0.45 441
2.3 模拟退火 (SA):高温探索,低温收敛 🔥
- 灵感来源:金属冶炼中的退火过程(加热后缓慢冷却,使分子排列到能量最低的稳定状态)。
- 核心思想:
- 初始状态:从一个随机的参数组合开始,设置一个很高的“温度”。
- 邻域搜索:在当前解附近随机找一个“新解”。
- 接受准则:
- 如果“新解”更好,直接接受。
- 如果“新解”更差,以一定概率接受它。这个概率与“温度”和“差的程度”有关。在“高温”时,接受差解的概率更高,允许算法跳出局部最优的小山谷。
- 降温:随着迭代,“温度”逐渐降低,算法接受差解的意愿也越来越低,变得“保守”,开始在已找到的优质区域内精细搜索。
- 感觉:像一个初期“胆大冲动”、后期“沉稳保守”的探险家,擅长避免被小山丘(局部最优)迷惑,有更大机会找到真正的最高峰(全局最优)。
# --- 4. 模拟退火优化随机森林 ---
print("\n--- 4. 模拟退火优化随机森林 ---")
# (此处省略了模拟退火函数的具体实现,核心是 fitness_function 和 simulated_annealing 循环)
# fitness_function 同样是输入参数组合,输出模型准确率
# 运行模拟退火...
# (此处省略了调用 simulated_annealing 函数的代码)
# 结果展示
print(f"模拟退火算法优化耗时: 123.9574 秒")
print("最佳参数: {'n_estimators': 144, 'max_depth': 16, 'min_samples_split': 7, 'min_samples_leaf': 1}")
print("\n模拟退火优化后的分类报告:")
# ... (输出分类报告)
# precision recall f1-score support
# 0 0.76 0.98 0.86 1059
# 1 0.84 0.28 0.42 441
三、总结与心得:拥抱 AI 时代的学习新范式
今天的学习带给我最大的震撼,不仅仅是这些算法本身,更是老师传递的一种学习思维的转变。
-
思想 > 实现:面对遗传算法、PSO 等复杂的代码,很容易陷入每个参数、每个循环的细节中。但老师一针见血地指出:这些代码不具备复用性,搞懂了对学习其他方法帮助也有限。这让我意识到,在 AI 时代,我们的角色不是成为算法的“人肉编译器”,而是要成为算法的“指挥家”。
-
关注“接口”,而非“内脏”:我们应该重点关注:
- 输入 (Input):这个算法需要我提供什么?(比如,参数范围
bounds,评估函数fitness_function) - 输出 (Output):它能给我返回什么?(比如,最佳参数
best_params,最佳得分best_fitness) - 适用场景 (Context):它擅长解决什么问题?(比如,参数空间巨大、不可导的优化问题)
至于内部复杂的循环、交叉、变异逻辑,交给可靠的库或者 AI 去实现。我们只需理解其工作原理,然后正确地使用它。
- 输入 (Input):这个算法需要我提供什么?(比如,参数范围
-
启发式算法的价值:虽然我们的实验中,这些算法相比默认参数在
accuracy上提升不明显(主要是因为recallfor class 1 依然很低,这是数据不平衡的锅),但它们在precisionfor class 1 上都有所提升,并且找到了与默认参数完全不同的参数组合。这证明了它们强大的全局搜索能力,是贝叶斯优化、网格搜索之外,我们武器库中的又一利器。 -
从代码到思想的飞跃:今天的学习完美诠释了从“术”到“道”的过程。列表推导式是“术”,是具体的编码技巧;而启发式算法的学习方法,则是“道”,是更高维度的学习哲学。
感谢 @浙大疏锦行 老师带来的深刻一课,这不仅是关于算法的,更是关于如何在这个快速发展的时代高效学习的。
更多推荐


所有评论(0)