ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

组合优化与凸优化实验:从建模到可复现的求解全流程

组合优化与凸优化实验:从建模到可复现的求解全流程 简介哈工大组合优化与凸优化研究生课程实验资料包面向计算机、数学等方向的研究生与进阶学习者旨在通过动手实验理解优化理论中的核心算法并顺利完成课程任务。包内共262个文件整体约47.1MB以Python脚本.py/.pyc和大量实验截图.bmp/.png为主同时包含XML/IML工程配置、all-data数据及MAT文件方便复现运行环境与核对中间结果。已有294人浏览学习。内容覆盖组合优化经典策略动态规划、贪心、回溯与凸优化主流算法梯度下降、牛顿、拟牛顿并拆分为Lab1无约束优化、Lab2有约束优化两大实验模块涉及线性规划、惩罚函数、内点法等求解思路。配套实验报告与说明书细致记录问题背景、方法步骤、结果分析和预期输出读者可对照源码理解参数调整逻辑借助截图检查可视化效果节省环境搭建与排错时间适合作为研读优化理论、准备同类研究生课程实验的参考。1. 组合优化与凸优化实验包先跑通再讲理论拿到一个“组合优化与凸优化研究生课程实验”的zip包里面通常是一组实验报告、说明书和代码骨架。这类课程实验真正考验的不是背公式而是能否把问题描述快速变成可执行的求解流程组合优化要求你在离散解空间里搜出最优或近似最优凸优化则要求你把连续问题写成标准形式并让求解器稳定收敛。对刚入门的研究生和转算法的工程师来说最大的障碍往往不是数学而是环境适配、数据对比和报告呈现——测试集一换结果就完全不可复现。下面按我自己拿到这类工程包时的做法从解压、建模、调参到写报告给出一条能直接照做的路线。2. 组合优化实验的建模与求解——用Python把模型变成代码组合优化的实验通常会围绕0-1背包、旅行商、最小生成树或最大流这类经典问题展开。实验报告里真正要解释的是为什么选这个算法它在不同规模上的行为如何。所以动手写代码之前先要判断这个实验要求的是精确解还是近似解。2.1 先分出种类精确算法、近似算法、元启发式我一般先把问题按理论框架分类。如果问题是P类且规模小直接用动态规划或专用网络流算法如果是NP-hard但实例规模可控用分支定界或整数规划求解器规模大到无法保证最优时才引入近似比或元启发式。组合优化实验的评分点不只是“跑出了结果”还要看你有没有给出复杂度分析和不同规模下的行为。因此报告里至少要有三组实验小规模验证正确性中规模看趋势大规模看稳定性。2.2 最小可复现实验用OR-Tools解0-1背包0-1背包是组合优化最经典的入门问题。我常用Google OR-Tools的CP-SAT求解器建模直观能精确求解中小规模实例也方便后续和贪心、动态规划等算法做gap对比。下面是最小完整示例from ortools.sat.python import cp_model # 物品重量和价值 weights [3, 2, 5, 4, 1] values [10, 5, 12, 8, 3] capacity 8 n len(weights) model cp_model.CpModel() x [model.NewBoolVar(fitem_{i}) for i in range(n)] model.Add(sum(weights[i] * x[i] for i in range(n)) capacity) model.Maximize(sum(values[i] * x[i] for i in range(n))) solver cp_model.CpSolver() status solver.Solve(model) if status in (cp_model.OPTIMAL, cp_model.FEASIBLE): total_v solver.ObjectiveValue() picked [i for i in range(n) if solver.Value(x[i]) 0] print(目标值:, total_v) print(选择的物品:, picked) else: print(无解)这段代码里NewBoolVar为每个物品生成0-1决策变量容量约束通过model.Add写入优化目标用Maximize声明问题以整数线性规划的形式交给CP-SAT。参数上要注意capacity必须是整数物品数量变大时求解时间可能指数增长实验时建议给solver.parameters.max_time_in_seconds设定时限避免单个实例卡死整个批量实验。2.3 实验报告里最有说服力的对比表无论是背包还是TSP最终都要靠数据说话。我习惯在实验报告里固定一张表格同一份测试数据下列出算法名称、解质量、运行时间和与最优解的gap。算法最优解运行时间(ms)与最优gap时间复杂度CP-SAT280.80%指数实际快一维动态规划281.20%O(nC)贪心260.27.1%O(n log n)这个表的核心价值在于gap列它保证了算法之间可比。只要规模在几万以内精确解都能求出来规模上涨后动态规划的空间复杂度会先失控这时再去看CP-SAT或元启发式结论就很明显。表下方一定要写清楚数据集的生成规则比如“重量均匀分布在U(1,100)容量为总重量的60%”否则数值没有任何意义。3. 凸优化实验的落地——从目标函数到求解器凸优化实验不像组合优化那样强调离散状态它更考验建模与调参能力。课程里经常把组合优化和凸优化并列本质上一个是离散搜索一个是连续优化但都遵循“建模—求解—验证”这条链路。3.1 凸优化的标准形式与实验命题套路凸优化实验里最常出现的是最小二乘、岭回归、LASSO、逻辑回归和SVM对偶。它们都可以写成或近似写成minimize f0(x) subject to fi(x) 0 (i1,...,m) Ax b判断目标函数是否凸只需看Hessian是否半正定。实验里给的损失函数大多是凸的比如最小二乘的平方损失。你要做的是把数据、初值、终止条件设计好。最常见的翻车点是把非凸损失硬套凸优化框架结果收敛到局部极小值报告却还在按凸问题写结论。3.2 用梯度下降实现线性最小二乘并调参为了理解求解器内部发生了什么我会先用纯NumPy实现一版梯度下降作为全过程的“对照组”。import numpy as np # 生成线性回归实验数据 np.random.seed(42) m, n 1000, 5 X np.random.randn(m, n) true_w np.array([1.0, -2.0, 0.5, 3.0, -1.0]) y X true_w 0.1 * np.random.randn(m) # 梯度下降求解 min ||Xw - y||^2 def gradient_descent(X, y, lr0.01, epochs500, tol1e-6): w np.zeros(X.shape[1]) for i in range(epochs): grad 2 * X.T (X w - y) / len(y) w - lr * grad if np.linalg.norm(grad) tol: break return w w_fit gradient_descent(X, y) print(拟合权重:, w_fit) print(真实权重:, true_w)这里的核心参数是学习率lr、迭代轮数epochs和停止阈值tol。学习率太大会发散太小收敛很慢。工程上我会先取lr0.1观察前100轮目标函数下降曲线再按十倍步长上下调整。3.3 凸优化的三个数值坑学习率、条件数、停止条件第一个坑是学习率不需要从极小值试起。第二个坑是特征尺度差异如果X的条件数很大梯度下降会很慢实验前应当对特征做标准化。第三个坑是停止条件不要只用目标函数差值因为接近最优时目标差值会小于浮点误差。我更习惯同时看梯度范数。下面是个检查学习过程的片段loss_history [] for i in range(epochs): grad 2 * X.T (X w - y) / len(y) w - lr * grad if i % 50 0: obj np.mean((X w - y) ** 2) loss_history.append(obj) print(fepoch {i:4d}, obj {obj:.8f})看到目标值持续下降进入平台基本可以判断收敛如果出现无规律跳动先检查学习率而不是目标函数。3.4 用cvxpy快速验证实验结果实验报告里应当有一个权威对照。我通常用cvxpy在同一组数据上求一次参考解用来和自写梯度下降结果对比。最小二乘的cvxpy写法如下import cvxpy as cp w_var cp.Variable(X.shape[1]) objective cp.Minimize(cp.sum_squares(X w_var - y)) prob cp.Problem(objective) prob.solve(solvercp.OSQP) print(cvxpy解:, w_var.value)OSQP适合中等规模稀疏问题速度比SDP快如果你想验证SVM对偶可以用cp.quad_form或cp.kl_div。最终报告里可以放一张自写梯度下降与cvxpy结果差异的对比表方法目标值与cvxpy差距注梯度下降 lr0.10.009831.2e-6500轮收敛梯度下降 lr0.010.009852.0e-4未完全收敛cvxpy OSQP0.009820权威解这张表能直接显示自写算法的数值精度和收敛代价。4. 从zip到能跑的实验环境——解压、完整性与一键配置实验包以zip格式分发第一步往往不是打开报告而是确认文件完整性。很多人卡在“could not find eocd”或“bad CRC”这类压缩包错误问题往往不在算法而在下载或解压环节。为了尽早排错我会先把环境整治干净再跑代码。4.1 解压之前先验证zip完整性处理zip压缩包我固定用7-Zip的“测试压缩包”功能检查CRC错误或者用命令行校验。Windows下7-Zip最顺手Linux下直接跑zip -T student_experiments.zipzip -T会把压缩包内每个文件解压到内存做CRC校验输出“OK”说明没有损坏。如果出现“bad CRC”或找不到EOCD结束标记说明文件截断或头部损坏。这时不要立刻重下先检查网络工具是否把文件名改成非ASCII大多数情况下需要重新下载。如果只是本机拷贝造成的小损坏可以用zip -F尝试修复但源压缩包本身损坏时修复不回来。整个过程中不需要额外工具。提示unzip -t file.zip在Windows和Linux下都可以用输出比7-Zip更详细但速度稍慢。另一个经常出现的情况是IDE或资源管理器解压时提示“导入资源包失败”实际原因是文件路径过长或包含中文字符。遇到这种问题我习惯用7-Zip的命令行模式解压到纯英文目录例如7z x student_experiments.zip -o./experiments这样可以绕开Windows自带解压的路径解析限制。解压后第一时间检查目录结构确认实验说明书和代码目录在同一层级避免相对路径失效。4.2 一次性搭好组合优化与凸优化的运行环境实验代码通常依赖numpy、scipy、ortools、cvxpy。我会把环境依赖写进requirements.txt而不是手动一条条装。python 3.10 numpy 1.26 scipy 1.11 cvxpy 1.4 ortools 9.8 matplotlib 3.8用conda创建干净环境并安装conda create -n opt_course python3.10 -y conda activate opt_course pip install -r requirements.txt选择Python 3.10是为了兼容OR-Tools和cvxpy的二进制轮子。安装完成后跑一个冒烟测试python -c import cvxpy, ortools, numpy; print(cvxpy.__version__)如果这一行能通过环境基本可用。若导入时报错多数是依赖冲突优先用pip check定位。4.3 用Makefile和固定随机种子保证复现实验报告里最容易缺失的是如何让别人复现。我会把解压、装依赖、跑实验三个入口统一到一个Makefilevenv: python -m venv .venv install: venv .venv/bin/pip install -r requirements.txt run: install .venv/bin/python experiments/run_all.py之后只需要make run就能一键跑完。代码里涉及随机性的部分都固定numpy.random.seed(42)输出文件命名带上数据集规模和运行时间。评审者不需要看说明书也能从头复现结果。5. 组合优化与凸优化实验结果怎么对比才不踩坑这一章处理“出数据不等于结论正确”的问题。实验报告里真正拉分的部分是对照实验的设计。无论是组合优化里的贪心与精确解对比还是凸优化里的自写梯度下降与cvxpy解析解对比都要在同一数据、同一指标上衡量。5.1 收敛曲线怎么画才不误导画收敛曲线最容易犯的错误是纵轴用线性尺度。凸优化接近最优解时下降幅度往往是指数级的线性尺度会让曲线“看起来已经平了”实际还没收敛。我会让横轴是epoch纵轴是对数目标值import matplotlib.pyplot as plt plt.figure(figsize(6, 4)) plt.plot(range(len(loss_history)), loss_history) plt.yscale(log) plt.xlabel(epoch) plt.ylabel(objective (log)) plt.grid(True, whichboth, alpha0.3) plt.savefig(convergence.pdf, bbox_inchestight)这样能清晰展示拐点。更进一步把不同学习率下的收敛曲线画在同一张图里就是一组很有说服力的消融实验。5.2 对比表必须带数据集和参数列很多人写对比表只放算法名和时间却漏掉数据集规模、迭代次数、超参数。我会在表格下方加一段说明列出测试实例生成方式n的范围、稠密度、噪声方差。以组合优化为例用“物品重量均匀分布”还是“重量偏斜分布”生成的数据贪心算法的gap会差别很大。所以对比表要在显著位置标数据集ID并把生成器代码放进附录才能保证可复现。数据集ID规模 n生成方式超参数运行环境DS-011000U(1,100)lr0.1, epochs500Python 3.10DS-021000幂律分布lr0.01, epochs2000Python 3.105.3 稳定性验证换随机种子不只看单次运行实验报告里不能只跑一次。常规做法是至少跑10组随机种子或10个不同规模实例报告均值和方差。组合优化里报告最优gap的均值凸优化里报告目标值的标准差。原因是启发式算法和梯度下降都对初值和数据敏感单次成功说明不了鲁棒性。稳定性结果最好用箱线图展示。对同一算法跑10个随机种子把每个种子的最终目标值或gap收集成列表用plt.boxplot画出来。如果箱体很宽说明算法对初值敏感报告讨论部分就要说明原因的来源。6. 用热启动和敏感性分析把实验报告再做深一层这一章讲一个我在处理这类课程实验时常用的进阶技巧当求解器已经能跑出结果后不要停在“跑通”而是做热启动和敏感性分析。这个技巧只花很少的代码但能把组合优化和凸优化两个主题串联起来。6.1 CP-SAT里的热启动在OR-Tools的CP-SAT中可以用model.AddHint把上一轮可行解作为初值置入新实例。多组对比实验时下一组实例往往和上一组相似热启动能显著减少搜索时间。示例if last_solution is not None: for i in range(n): if last_solution[i] is not None: model.AddHint(x[i], last_solution[i])这里last_solution是上一实例的选择结果AddHint不是硬约束但求解器会优先朝这个方向搜索收敛明显加快。6.2 凸优化里的敏感性分析凸优化部分我会对目标函数中的某个约束项系数做扰动比如把损失函数里的正则系数乘上1ε观察最优解变化。具体做法是先用cvxpy参数化模型eps cp.Parameter(nonnegTrue) eps.value 0.0 coefficient 1.0 eps然后循环求解并记录目标值。用这段代码把扰动幅度和最优目标值画成曲线。如果曲线平缓说明模型对数据误差不敏感如果曲线在某一点出现突变这个点就是模型的瓶颈约束。需要注意的是敏感性分析不能只改变一个参数后看完数值就结束还要记录求解状态。如果prob.status是infeasible或infeasible_or_unbounded说明扰动越过了可行域边界此时不能把目标值拿来比较而要在报告中注明约束集被破坏。这个记录本身也是实验结论。敏感性曲线会直接暴露哪些约束是模型真正的短板。本文还有配套的精品资源点击获取
返回列表