戰(zhàn):從調(diào)參翻車到多目標(biāo)優(yōu)化與避坑指南)
簡(jiǎn)介這份MATLAB遺傳算法工具箱資源面向從事科學(xué)計(jì)算、優(yōu)化算法學(xué)習(xí)與工程設(shè)計(jì)的MATLAB用戶尤其適合需要復(fù)現(xiàn)謝菲爾德大學(xué)GATBX工具箱、解決“未定義函數(shù)或變量 crtbp”報(bào)錯(cuò)的人群。資源包共74個(gè)文件以59個(gè)m函數(shù)腳本為核心涵蓋crtbp、mutate、recombin、select、ranking等遺傳算法關(guān)鍵算子另含4個(gè)ps與3個(gè)pdf說(shuō)明文檔、4個(gè)txt問(wèn)答與readme、3個(gè)log運(yùn)行日志及1個(gè)mht網(wǎng)頁(yè)存檔壓縮包約414KB結(jié)構(gòu)緊湊便于按模塊查閱。目前已有1016人學(xué)習(xí)下載。通過(guò)該資源讀者可獲得謝菲爾德遺傳算法工具箱的完整函數(shù)框架理解編碼、適應(yīng)度函數(shù)、選擇、交叉變異與停止條件等核心概念并借助示例腳本與文檔排查路徑配置問(wèn)題為函數(shù)優(yōu)化、參數(shù)估計(jì)與組合優(yōu)化等任務(wù)提供可運(yùn)行的實(shí)踐參考。1. 從一次調(diào)參翻車說(shuō)起MATLAB 遺傳算法工具箱到底能干什么去年幫一個(gè)做機(jī)械結(jié)構(gòu)優(yōu)化的朋友看代碼他用 MATLAB 手寫遺傳算法種群規(guī)模設(shè) 200、迭代 500 代跑了一整夜結(jié)果還不如初始解。我讓他把crossover和mutation兩段貼出來(lái)一看就明白了——交叉概率 0.9 配變異概率 0.001前期搜索猛如虎后期基本原地踏步典型的參數(shù)配比失衡。后來(lái)?yè)Q成 MATLAB 自帶的遺傳算法工具箱同樣的目標(biāo)函數(shù)二十分鐘出結(jié)果而且收斂曲線干凈得多。這就是今天要聊的東西MATLAB 遺傳算法工具箱Genetic Algorithm Toolbox。它不是某個(gè)第三方插件而是 MATLAB 全局優(yōu)化工具箱Global Optimization Toolbox里的核心組件提供ga、gamultiobj、gaoptimset新版為optimoptions等函數(shù)把選擇、交叉、變異、精英保留這一整套流程封裝成可配置的求解器。你只需要寫目標(biāo)函數(shù)和約束剩下的種群演化交給它。適合誰(shuí)用三類人最劃算一是做參數(shù)尋優(yōu)但不想手寫進(jìn)化框架的工程師二是需要多目標(biāo) Pareto 前沿的科研人員三是教學(xué)場(chǎng)景下要快速演示遺傳算法行為的學(xué)生。如果你追求極致性能、要自定義特殊算子那手寫反而更靈活——工具箱的價(jià)值在于“標(biāo)準(zhǔn)化流程 可視化 約束處理”不是萬(wàn)能鑰匙。2. 工具箱核心機(jī)制與最小可運(yùn)行示例2.1 它內(nèi)部到底怎么跑編碼、適應(yīng)度與算子調(diào)度遺傳算法工具箱的默認(rèn)流程是實(shí)值編碼不是二進(jìn)制這一點(diǎn)很多人第一次用會(huì)懵。它直接對(duì)實(shí)數(shù)變量操作交叉用“算術(shù)交叉”或“啟發(fā)式交叉”變異用高斯變異省去了編解碼環(huán)節(jié)。整個(gè)循環(huán)大致是初始化種群 → 計(jì)算適應(yīng)度 → 按排名或隨機(jī)均勻選擇父代 → 交叉生成子代 → 變異 → 精英保留 → 判斷終止條件。關(guān)鍵參數(shù)集中在optimoptions(ga, ...)里。我列幾個(gè)最常調(diào)的參數(shù)名默認(rèn)值作用調(diào)參建議PopulationSize50種群規(guī)模變量數(shù) 5 以下用 5010 以上加到 200MaxGenerations100×變量數(shù)最大迭代代數(shù)先設(shè)小跑通再放大CrossoverFraction0.8交叉產(chǎn)生的子代比例0.6~0.9 之間太低搜索慢MutationFcnmutationgaussian變異函數(shù)高維問(wèn)題換 mutationadaptfeasibleEliteCountceil(0.05×種群)精英保留數(shù)別超過(guò)種群 10%否則早熟StallGenLimit50停滯代數(shù)連續(xù) 50 代無(wú)提升就停這些默認(rèn)值不是拍腦袋來(lái)的是大量基準(zhǔn)測(cè)試后的折中。但你的問(wèn)題不是基準(zhǔn)測(cè)試所以該改就得改。2.2 第一個(gè)能跑的腳本從目標(biāo)函數(shù)到 ga 調(diào)用下面這段代碼是我常用的最小模板求解一個(gè)帶邊界約束的二元函數(shù)最小值% 目標(biāo)函數(shù)Rastrigin 函數(shù)經(jīng)典多峰測(cè)試函數(shù) fun (x) 20 x(1)^2 x(2)^2 - 10*(cos(2*pi*x(1)) cos(2*pi*x(2))); % 變量上下界 lb [-5.12, -5.12]; ub [ 5.12, 5.12]; % 配置遺傳算法選項(xiàng) options optimoptions(ga, ... PopulationSize, 80, ... % 種群規(guī)模 MaxGenerations, 300, ... % 最大代數(shù) CrossoverFraction, 0.75, ... % 交叉比例 EliteCount, 4, ... % 精英保留個(gè)體數(shù) StallGenLimit, 60, ... % 停滯多少代后停止 Display, iter, ... % 每代打印信息 PlotFcn, gaplotbestf); % 繪制最優(yōu)適應(yīng)度曲線 % 調(diào)用 ga 求解 [x_best, f_best, exitflag, output] ga(fun, 2, [], [], [], [], lb, ub, [], options); fprintf(最優(yōu)解: x1%.4f, x2%.4f\n, x_best(1), x_best(2)); fprintf(最優(yōu)值: %.6f\n, f_best); fprintf(退出原因: %s\n, output.message);邏輯說(shuō)明fun是匿名函數(shù)形式的適應(yīng)度函數(shù)輸入行向量返回標(biāo)量。ga第二個(gè)參數(shù)2是變量個(gè)數(shù)必須和目標(biāo)函數(shù)輸入維度一致。中間四個(gè)空矩陣[]分別對(duì)應(yīng)線性不等式約束 A、b、線性等式約束 Aeq、beq這里沒(méi)有就留空。lb和ub是下界和上界維度必須和變量數(shù)相同。參數(shù)說(shuō)明Display設(shè)為iter會(huì)每代輸出一行調(diào)試時(shí)非常有用正式跑可以改final減少 I/O。PlotFcn支持多個(gè)繪圖函數(shù)用元胞數(shù)組包起來(lái)即可比如{gaplotbestf, gaplotdistance}。exitflag返回正數(shù)表示正常收斂0 表示達(dá)到最大代數(shù)負(fù)數(shù)說(shuō)明無(wú)可行解——這個(gè)一定要檢查很多人只看結(jié)果不看退出標(biāo)志拿到一個(gè)違反約束的“最優(yōu)解”還渾然不知。跑完你會(huì)看到命令行滾動(dòng)輸出每代的最優(yōu)值和均值最后彈出適應(yīng)度曲線。如果曲線在前期快速下降然后長(zhǎng)期平直說(shuō)明種群多樣性丟了該加大變異或提高種群規(guī)模。3. 約束處理與多目標(biāo)擴(kuò)展從單目標(biāo)到 Pareto 前沿3.1 非線性約束怎么寫把不等式整理成標(biāo)準(zhǔn)形式實(shí)際工程問(wèn)題很少是無(wú)約束的。遺傳算法工具箱支持非線性約束通過(guò)nonlcon函數(shù)句柄傳入。關(guān)鍵點(diǎn)MATLAB 要求不等式約束寫成c(x) 0等式約束寫成ceq(x) 0。如果你的原始約束是g(x) 5得改寫成5 - g(x) 0。% 非線性約束函數(shù) function [c, ceq] myConstraint(x) % 不等式約束x1^2 x2^2 4 → x1^2 x2^2 - 4 0 c x(1)^2 x(2)^2 - 4; % 等式約束x1 x2 1 → x1 x2 - 1 0 ceq x(1) x(2) - 1; end % 主腳本中調(diào)用 fun (x) (x(1)-1)^2 (x(2)-2)^2; options optimoptions(ga, PopulationSize, 100, Display, iter); [x_best, f_best] ga(fun, 2, [], [], [], [], [], [], myConstraint, options);邏輯說(shuō)明myConstraint返回兩個(gè)輸出c是不等式約束向量每個(gè)元素都要 ≤ 0ceq是等式約束向量每個(gè)元素都要 0。如果某個(gè)約束不存在返回空矩陣[]。注意等式約束會(huì)顯著增加求解難度工具箱內(nèi)部用增廣拉格朗日方法處理代數(shù)不夠往往收斂不到可行域。參數(shù)說(shuō)明當(dāng)問(wèn)題同時(shí)有邊界約束和非線性約束時(shí)ga的搜索空間被切割得很碎建議把PopulationSize提高到變量數(shù)的 20 倍以上同時(shí)把MutationFcn換成mutationadaptfeasible這個(gè)函數(shù)會(huì)根據(jù)約束可行性自適應(yīng)調(diào)整變異步長(zhǎng)。另外MaxGenerations要留足帶等式約束的問(wèn)題通常需要 500 代以上。3.2 多目標(biāo)優(yōu)化gamultiobj 的用法與 Pareto 圖解讀單目標(biāo)只有一個(gè)最優(yōu)值多目標(biāo)則是一組互不支配的解叫 Pareto 前沿。工具箱里對(duì)應(yīng)的是gamultiobj調(diào)用方式和ga幾乎一樣但返回的是一個(gè)矩陣每行一個(gè) Pareto 解。% 雙目標(biāo)函數(shù)同時(shí)最小化 f1 和 f2 fun (x) [x(1)^2 x(2)^2, (x(1)-2)^2 (x(2)-1)^2]; lb [-3, -3]; ub [ 3, 3]; options optimoptions(gamultiobj, ... PopulationSize, 150, ... % 多目標(biāo)需要更大種群 MaxGenerations, 400, ... ParetoFraction, 0.35, ... % Pareto 解在前沿上的比例 Display, iter, ... PlotFcn, gaplotpareto); % 繪制 Pareto 前沿 [x_pareto, f_pareto] gamultiobj(fun, 2, [], [], [], [], lb, ub, options); fprintf(Pareto 解個(gè)數(shù): %d\n, size(x_pareto, 1)); % 繪制前沿 figure; plot(f_pareto(:,1), f_pareto(:,2), o); xlabel(f1); ylabel(f2); title(Pareto 前沿); grid on;邏輯說(shuō)明目標(biāo)函數(shù)返回一個(gè)行向量每個(gè)元素對(duì)應(yīng)一個(gè)目標(biāo)。gamultiobj內(nèi)部用 NSGA-II 的變體通過(guò)非支配排序和擁擠距離維持解的多樣性。ParetoFraction控制種群中保留在前沿上的比例默認(rèn) 0.35調(diào)大能讓前沿更均勻但收斂變慢。參數(shù)說(shuō)明多目標(biāo)問(wèn)題的種群規(guī)模要比單目標(biāo)大一般 100~200。PlotFcn用gaplotpareto可以實(shí)時(shí)看前沿形狀如果前沿是一條斷斷續(xù)續(xù)的散點(diǎn)說(shuō)明種群多樣性不足加大PopulationSize或提高變異概率。另外gamultiobj不支持非線性等式約束有等式約束得先想辦法消元。拿到 Pareto 解集后怎么選看你的偏好。如果兩個(gè)目標(biāo)同等重要可以歸一化后取到原點(diǎn)距離最小的那個(gè)如果某個(gè)目標(biāo)有硬性上限就篩掉超限的解再選。這一步?jīng)]有標(biāo)準(zhǔn)答案取決于業(yè)務(wù)場(chǎng)景。4. 避坑與排查那些讓我熬夜的報(bào)錯(cuò)和異常4.1 目標(biāo)函數(shù)維度不匹配最常見(jiàn)的翻車現(xiàn)場(chǎng)現(xiàn)象報(bào)錯(cuò)The number of variables (N) must match the length of the input vector to FUN。原因ga第二個(gè)參數(shù)寫的變量數(shù)和目標(biāo)函數(shù)實(shí)際接受的元素個(gè)數(shù)不一致。比如目標(biāo)函數(shù)里用了x(3)但調(diào)用時(shí)寫ga(fun, 2, ...)。解決數(shù)一遍目標(biāo)函數(shù)里用到的最大索引或者用size(x,2)動(dòng)態(tài)判斷。我一般會(huì)在目標(biāo)函數(shù)開(kāi)頭加一句assert(numel(x)2, 變量數(shù)不對(duì))讓報(bào)錯(cuò)更早更明確。4.2 早熟收斂曲線早早躺平結(jié)果不是全局最優(yōu)現(xiàn)象適應(yīng)度曲線在前 20 代快速下降然后一條直線到結(jié)束最終解明顯是局部最優(yōu)。原因種群多樣性丟失精英保留太多或變異概率太低。解決先把EliteCount降到種群規(guī)模的 5% 以下再把CrossoverFraction從 0.8 降到 0.6~0.7同時(shí)把MutationFcn換成mutationadaptfeasible。如果還不行把PopulationSize翻倍。血淚經(jīng)驗(yàn)別指望一套參數(shù)打天下多峰函數(shù)就是要靠大種群和適度變異來(lái)“翻山”。4.3 非線性約束下無(wú)可行解exitflag 為負(fù)現(xiàn)象exitflag返回 -2output.message提示找不到可行點(diǎn)。原因約束太緊初始種群隨機(jī)生成的點(diǎn)全部違反約束算法沒(méi)有可行起點(diǎn)。解決先用fmincon找一個(gè)可行解通過(guò)InitialPopulationMatrix選項(xiàng)塞給ga作為初始種群的一部分。或者放寬約束先跑無(wú)約束版本看最優(yōu)解大概在哪再逐步收緊約束。注意等式約束尤其容易導(dǎo)致無(wú)可行解能轉(zhuǎn)化成不等式就轉(zhuǎn)化。4.4 整數(shù)變量被忽略設(shè)了整數(shù)約束但結(jié)果帶小數(shù)現(xiàn)象用IntCon指定了整數(shù)變量但輸出解里那些變量還是小數(shù)。原因IntCon的位置寫錯(cuò)了或者目標(biāo)函數(shù)里對(duì)整數(shù)變量做了連續(xù)運(yùn)算導(dǎo)致工具箱內(nèi)部松弛。解決IntCon傳的是變量索引向量比如第 1 和第 3 個(gè)變量是整數(shù)就寫[1, 3]。另外整數(shù)規(guī)劃下MutationFcn要換成mutationadaptfeasible否則變異可能產(chǎn)生非整數(shù)。檢查方法跑完后用any(abs(x_best(IntCon) - round(x_best(IntCon))) 1e-6)驗(yàn)證。4.5 并行加速?zèng)]生效開(kāi)了 UseParallel 但 CPU 占用沒(méi)變化現(xiàn)象設(shè)置UseParallel, true后求解時(shí)間沒(méi)縮短任務(wù)管理器里 MATLAB 還是單核跑。原因沒(méi)開(kāi)并行池或者目標(biāo)函數(shù)里有不支持并行的操作比如寫文件、改全局變量。解決在命令窗口先執(zhí)行parpool啟動(dòng)并行池確認(rèn)池子起來(lái)了再跑ga。另外目標(biāo)函數(shù)必須是純函數(shù)——同樣輸入返回同樣輸出不能有副作用。如果目標(biāo)函數(shù)調(diào)用了外部程序并行反而可能因?yàn)橘Y源競(jìng)爭(zhēng)變慢這時(shí)候關(guān)掉并行更明智。5. 進(jìn)階技巧自定義變異函數(shù)與結(jié)果復(fù)現(xiàn)5.1 寫一個(gè)自己的變異算子工具箱自帶的變異函數(shù)夠用但遇到特殊編碼或先驗(yàn)知識(shí)時(shí)自定義更靈活。下面是一個(gè)“有界多項(xiàng)式變異”的簡(jiǎn)化實(shí)現(xiàn)function mutationChildren myMutation(parents, options, nvars, FitnessFcn, ... state, thisScore, thisPopulation, rate, shrink) % parents: 父代索引 % thisPopulation: 當(dāng)前種群矩陣 % rate: 變異概率 % shrink: 收縮因子控制變異幅度 mutationChildren zeros(length(parents), nvars); for i 1:length(parents) child thisPopulation(parents(i), :); for j 1:nvars if rand rate delta (rand - 0.5) * 2 * shrink; child(j) child(j) delta; end end mutationChildren(i, :) child; end end邏輯說(shuō)明輸入?yún)?shù)是工具箱規(guī)定的固定簽名不能改順序。parents是父代在種群中的行索引thisPopulation是當(dāng)前整個(gè)種群矩陣。函數(shù)返回變異后的子代矩陣行數(shù)等于父代個(gè)數(shù)。rate和shrink是額外參數(shù)通過(guò)optimoptions的MutationFcn用元胞數(shù)組傳入MutationFcn, {myMutation, 0.05, 0.2}。參數(shù)說(shuō)明rate控制每個(gè)變量發(fā)生變異的概率一般 0.01~0.1shrink控制變異幅度太大會(huì)變成隨機(jī)搜索太小起不到探索作用。這個(gè)實(shí)現(xiàn)沒(méi)有處理邊界實(shí)際用的時(shí)候要在變異后做一次截?cái)喟殉鰈b和ub的值拉回來(lái)。5.2 讓結(jié)果可復(fù)現(xiàn)隨機(jī)種子與輸出存檔遺傳算法是隨機(jī)算法同樣的代碼跑兩次結(jié)果可能不同。要復(fù)現(xiàn)必須在調(diào)用ga之前固定隨機(jī)種子rng(42, twister); % 固定種子42 是任意選的換成你喜歡的數(shù)字 [x_best, f_best, exitflag, output, population, scores] ga(fun, 2, ... [], [], [], [], lb, ub, [], options); % 存檔關(guān)鍵結(jié)果 save(ga_result.mat, x_best, f_best, exitflag, output, population, scores);邏輯說(shuō)明rng設(shè)置全局隨機(jī)流twister是梅森旋轉(zhuǎn)算法MATLAB 默認(rèn)就是它。固定種子后只要代碼和工具箱版本不變每次運(yùn)行結(jié)果完全一致。ga的完整輸出還包括最終種群population和對(duì)應(yīng)適應(yīng)度scores存下來(lái)方便后續(xù)分析種群分布。參數(shù)說(shuō)明種子數(shù)字本身沒(méi)有特殊含義選個(gè)你記得住的就行。注意并行計(jì)算時(shí)每個(gè) worker 的隨機(jī)流是獨(dú)立的固定主進(jìn)程種子不一定能完全復(fù)現(xiàn)并行結(jié)果所以對(duì)復(fù)現(xiàn)要求高的場(chǎng)景建議關(guān)掉并行。另外工具箱版本升級(jí)可能改變內(nèi)部隨機(jī)數(shù)調(diào)用順序跨版本復(fù)現(xiàn)基本做不到重要實(shí)驗(yàn)記得連同 MATLAB 版本號(hào)一起記錄。從那以后我每次跑ga都強(qiáng)制走一遍“固定種子 → 檢查 exitflag → 看收斂曲線 → 存檔”這四步再也沒(méi)出現(xiàn)過(guò)“上次那個(gè)好結(jié)果怎么都跑不出來(lái)”的尷尬。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取