44. Auto Tuning Framework | 自动调优框架
难度: Medium | 环境: CPU-first | 标签: 显存优化, 自动调优, 基准测试 | 目标人群: 显存优化学习者
🚀 云端运行环境
本章节的实战代码可以点击以下链接在免费 GPU 算力平台上直接运行:
本节导读
自动调优框架的核心不是“把参数全都扫一遍”,而是先明确目标、约束和搜索空间,再用统一评价函数筛掉不值得继续跑的配置。前面的 profiling 和显存分析页更多是在回答“瓶颈在哪里”,而 44 进一步回答“既然已经知道瓶颈了,接下来该怎样把配置搜索变成一个可复用的决策流程”。
这一节不实现复杂的工业级 tuner,也不追求最优搜索算法,而是先用一个最小教学框架把三件事固定下来:先筛掉明显违反约束的配置,再用统一分数比较收益与代价,最后输出下一轮最值得验证的候选。它在 42-45 这条显存/性能补链里承担的是“从诊断走向决策”这一步:42/43 更偏机制与预算边界,44 把这些边界收成搜索流程,45 再把结果落到具体裁剪顺序。学完后,你应该能看清“目标/约束 -> feasible set -> score -> recommendation”这条调优闭环,而不是把 profiling 结果停留在观察层面。
关键词: search space, constraint, score, early stop
前置阅读
导语: 这一节承接 profiling、调度和显存分析三条线:先知道瓶颈长什么样,再回来看哪些配置值得继续试,哪些应该尽早淘汰。
- 13. Profiling and Bottleneck Analysis | 性能分析与瓶颈定位
- 36. Decode Scheduling | Decode 调度
- 43. Unified Memory Management | 统一内存管理
相关阅读
导语: 学完最小自动调优框架后,下一步重点是看搜索结果怎样收束成具体资源决策,并进入端到端优化验证。
- 45. Memory Cut Planning | 显存裁剪规划
- 49. Parallelism Strategy Selection | 并行策略选型
- 74. Profiling Driven End-to-End Optimization | profiling 驱动优化项目
Step 1: 先写清目标和约束
- 目标可能是更快、更省显存,或固定精度下的综合最优。
- 约束则可能是最大显存、最大延迟或最小精度阈值。
- 只有目标和约束都明确,搜索空间才有意义。
Step 2: 把搜索空间和评价函数写清楚
- 搜索空间至少包括配置名、关键超参和预估成本。
- 评价函数要同时考虑收益和约束违反情况。
- 先做最小版本,再决定是否扩展到更复杂策略。
Step 3: 用早停和排序减少无效尝试
- 若某个配置明显违反约束,应尽早停止。
- 剩余配置再按统一分数排序,选出下一轮候选。
Step 4: 动手实战
- 补全
filter_feasible_configs,筛出满足约束的配置。 - 补全
score_tuning_config,给单个配置打分。 - 补全
recommend_tuning_config,输出推荐配置。
提示
TODO 1先按显存和延迟约束过滤,再返回满足条件的配置列表。TODO 2先写一个最小打分公式,把吞吐、质量、显存和延迟合成一个统一分数。TODO 3先筛 feasible,再在其中选最高分配置,最后返回推荐名和可行配置数。
from typing import Dict, Listdef filter_feasible_configs(configs: List[Dict[str, float]], max_memory_gb: float, max_latency_ms: float) -> List[Dict[str, float]]:
"""
TODO 1: 筛出满足约束的配置。
"""
# 提示:只保留 memory_gb 和 latency_ms 都不超过约束的配置。
# feasible = ???
raise NotImplementedError
def score_tuning_config(config: Dict[str, float]) -> Dict[str, float]:
"""
TODO 2: 给单个配置打分。
"""
# 提示:先算 score,再返回 {'name': ..., 'score': ...}。
# score = ???
raise NotImplementedError
def recommend_tuning_config(configs: List[Dict[str, float]], max_memory_gb: float, max_latency_ms: float) -> Dict[str, object]:
"""
TODO 3: 输出推荐配置。
"""
# 提示:先拿到 feasible;如果为空直接返回 None 和 0;否则选分数最高的配置。
# feasible = ???
# best = ???
raise NotImplementedErrordef test_auto_tuning_template():
try:
configs = [
{'name': 'cfg_a', 'throughput': 110, 'memory_gb': 14.0, 'latency_ms': 85, 'quality': 0.94},
{'name': 'cfg_b', 'throughput': 120, 'memory_gb': 16.0, 'latency_ms': 82, 'quality': 0.93},
{'name': 'cfg_c', 'throughput': 108, 'memory_gb': 13.0, 'latency_ms': 95, 'quality': 0.96},
]
feasible = filter_feasible_configs(configs, max_memory_gb=15.0, max_latency_ms=90.0)
assert [cfg['name'] for cfg in feasible] == ['cfg_a']
assert 'score' in score_tuning_config(configs[0])
recommendation = recommend_tuning_config(configs, max_memory_gb=15.0, max_latency_ms=90.0)
assert recommendation['recommended_name'] == 'cfg_a'
assert recommendation['feasible_count'] == 1
print('测试通过:自动调优框架模板可以工作。')
except NotImplementedError:
raise
except (AttributeError, NameError, TypeError, ValueError, AssertionError) as e:
raise NotImplementedError('请先完成 TODO 代码!') from e
test_auto_tuning_template()🛑 STOP HERE 🛑
请先尝试自己完成代码并跑通测试。
如果你正在 Colab 中运行,并且遇到困难没有思路,可以向下滚动查看参考答案。
参考代码与解析
代码
def filter_feasible_configs(configs: List[Dict[str, float]], max_memory_gb: float, max_latency_ms: float) -> List[Dict[str, float]]:
"""
TODO 1: 筛出满足约束的配置。
"""
# 提示:只保留 memory_gb 和 latency_ms 都不超过约束的配置。
# feasible = ???
return [
config
for config in configs
if config.get('memory_gb', 10**9) <= max_memory_gb and config.get('latency_ms', 10**9) <= max_latency_ms
]
def score_tuning_config(config: Dict[str, float]) -> Dict[str, float]:
"""
TODO 2: 给单个配置打分。
"""
# 提示:先算 score,再返回 {'name': ..., 'score': ...}。
# score = ???
score = config.get('throughput', 0.0) + config.get('quality', 0.0) * 100 - config.get('memory_gb', 0.0) * 2 - config.get('latency_ms', 0.0) * 0.5
return {'name': config.get('name', 'config'), 'score': score}
def recommend_tuning_config(configs: List[Dict[str, float]], max_memory_gb: float, max_latency_ms: float) -> Dict[str, object]:
"""
TODO 3: 输出推荐配置。
"""
# 提示:先拿到 feasible;如果为空直接返回 None 和 0;否则选分数最高的配置。
# feasible = ???
# best = ???
feasible = filter_feasible_configs(configs, max_memory_gb=max_memory_gb, max_latency_ms=max_latency_ms)
if not feasible:
return {'recommended_name': None, 'feasible_count': 0}
best = max(feasible, key=lambda config: score_tuning_config(config)['score'])
return {'recommended_name': best.get('name', 'config'), 'feasible_count': len(feasible)}解析
1. TODO 1:筛出满足约束的配置
- 先按
memory_gb和latency_ms过滤,再保留满足约束的配置列表。 - 这一步的意义是先把明显不可能上线的配置挡掉,避免后续评分浪费在无效候选上。
2. TODO 2:给单个配置打分
- 打分函数把吞吐、质量、显存和延迟合成一个统一分数,用最小方式表达“收益和成本”的综合权衡。
- 这里的公式不是唯一答案,重点是先建立一个可比较、可排序的评价口径。
3. TODO 3:输出推荐配置
- 先拿到 feasible 配置;如果为空直接返回空推荐;否则从中选出分数最高的配置。
- 自动调优的核心不是“全扫一遍”,而是先过滤、再评分、最后推荐。
4. 这页的定位
- 自动调优框架最重要的是先过滤,再评分,最后推荐。
- 没有约束过滤的搜索,通常只是在扩大无效实验成本。
