原講義本頁只有一個連到 Dorigo 蟻群最佳化網站的連結;以下說明、公式與互動示範都是補充。 蟻群最佳化 (Ant Colony Optimization,ACO)由 Marco Dorigo 在 1992 年提出,靈感來自螞蟻找食物的行為。螞蟻視力很差,卻能找出巢與食物之間的最短路徑,靠的是 費洛蒙 (pheromone):螞蟻走過會留下費洛蒙,後面的螞蟻傾向走費洛蒙濃的路,而費洛蒙會隨時間蒸發。 著名的雙橋實驗:巢與食物之間有一長一短兩條路。一開始螞蟻隨機選,但走短路的螞蟻來回比較快,單位時間內短路上經過的螞蟻多、累積的費洛蒙也多;越多螞蟻因此選短路,又留下更多費洛蒙——這種 正回饋 加上蒸發「遺忘」舊資訊,最後幾乎所有螞蟻都走短路。 Ant System:解旅行推銷員問題 Ant System for the TSP 旅行推銷員問題(TSP):給定 個城市,找一條每個城市剛好經過一次、最後回到起點的最短路線。城市一多,可能的路線數 爆炸性成長(10 個城市就有 181 440 條),無法一一比較。Ant System 讓 隻螞蟻反覆建構路線: 每隻螞蟻從隨機城市出發,在城市 時,以下列機率選擇還沒去過的城市 : 其中 是邊 上的費洛蒙, 是「能見度」(距離越近越有吸引力), 、 決定兩者的相對比重。 所有螞蟻走完一輪後,費洛蒙先蒸發,再由每隻螞蟻在自己走過的邊上留下費洛蒙,路線越短留下越多: 是蒸發率, 是第 隻螞蟻的路線總長。 重複到指定的輪數,記錄找到過的最短路線。 預設參數( = 1、 = 2、 = 0.5)重複 100 次,每次都在 60 輪內找到最短路線 3.0916( verify_pso.py 以窮舉 181 440 條路線核對)。 = 0:螞蟻完全不看距離,只靠費洛蒙,一開始等於亂走;60 輪後的平均最短路線 3.48,比 = 2 的 3.09 差很多。 = 0:完全不看費洛蒙,等於「每一步都走最近的城市」的貪婪法加上隨機性,沒有群體學習。 太小:舊的費洛蒙忘不掉,早期的壞路線影響很久;太大:好路線也很快被忘記。 和 GA 、 PSO 一樣,ACO 也是以族群為基礎、帶隨機性的最佳化方法;但它特別適合「路徑、排程、組合」這類離散問題,在控制領域可用於機器人路徑規劃、生產排程,也有人用來搜尋 PID 或模糊控制器的參數。 延伸閱讀: Ant Colony Optimization(M. Dorigo)↗ (原講義連結的舊網址已改為這個目錄)。 原頁的 Flash(CloseLoopC.swf)啟動時要先向舊站的 ASP 登入程式查詢使用者,現在已無法播放(只剩空白畫面)。本頁改以自製的互動模擬說明閉迴路控制;理論推導見 控制系統 。 試試看 Things to Try 開迴路 :轉速穩在 33⅓ rpm 後按「加負載干擾」,轉速掉到約 28.3 rpm,控制器毫無反應。 閉迴路 P :同樣加負載,轉速只掉一點點;但即使沒有負載,轉速也達不到設定值(穩態誤差 )。把 調大,誤差變小、響應變快。 閉迴路 PI :積分項會把剩下的誤差慢慢「補」起來,有無負載最後都回到 33⅓ rpm;代價是起步時會有一點超越量。 把 調到最大後切到 P:啟動瞬間電壓頂到 12 V 上限(飽和),實際響應會比線性理論慢。 模型: , = 10 rpm/V、 = 0.6 s、負載 = 0.5 V;PI 取 ,電壓限制在 0~12 V,飽和時暫停積分(anti-windup)。穩態誤差公式由 verify_cs.py 以數值模擬核對。 原講義列出的兩個運動控制硬體資源: PIC-Servo motion control (J. R. Kerr):以 PIC 微控制器做成的伺服馬達控制板,板上直接執行 PID 位置/速度回授、讀取編碼器,可多軸串接,是早期很常見的教學與自動化用控制器。原連結: http://www.jrkerr.com/ GE 工業自動化 :可程式控制器(PLC)與運動控制產品。原連結: http://www.geindustrial.com/cwc/home 2026-11 檢查:兩個原連結都已無法連線(伺服器回應 403/404),因此只保留文字說明,不再放連結。產品說明為補充。 以唱盤轉盤的調速為例,比較控制系統的兩種基本架構。(文字說明為補充) 開迴路控制 Open-Loop Control 旋鈕(可變電阻)設定一個電壓,經放大器驅動直流馬達帶動轉盤。控制器只依「設定值」輸出, 不看實際轉速 。只要負載、電池電壓或摩擦一改變,轉速就跟著偏掉,而控制器完全不知道。 閉迴路控制 Closed-Loop (Feedback) Control 在馬達軸上加一個 轉速計 (tachometer)量測實際轉速,回授到比較器和設定值相減,得到 誤差 ,放大器依誤差驅動馬達。轉速一變慢,誤差變大,馬達電壓自動升高——這就是 回授 (feedback)。方塊圖中的四個角色: 控制器 (control device):依誤差決定命令,例如放大器、PID、模糊控制器。 致動器 (actuator):把命令變成實際的力或能量,例如馬達。 受控體 (process/plant):被控制的對象,例如轉盤。 感測器 (sensor):量測輸出並回授,例如轉速計。 數位控制系統 Digital Control System 用電腦實作控制器時,連續訊號 、 先經 A/D 每隔一個取樣週期 取樣成數列 、 ,控制演算法逐步算出命令 ,再經 D/A (通常保持到下一次取樣)驅動致動器。取樣要夠快(常取閉迴路頻寬的 10 倍以上),否則數位控制器的表現會比連續的差,甚至不穩定。 回授為什麼有用 Why Feedback Works (補充)把馬達+轉盤近似成一階系統:時間常數 、穩態增益 (每伏特對應的轉速),負載干擾 換算成等效電壓、從受控體輸入端減掉: 單位回授下,輸出對設定值與干擾的響應為 設定值與干擾都是步階時,以終值定理( , )比較三種控制器的 穩態誤差 : 控制器 穩態誤差 時間常數 開迴路(事先校正) (干擾全部反映到輸出) 比例積分 PI (二階,可能超越) 回授把干擾的影響縮小成 、響應也快了同樣倍數(時間常數一欄指線性範圍;啟動時若馬達電壓頂到上限,實際會比較慢);比例增益越大越好,但只靠 P 永遠留下一點誤差,加上積分項才會歸零——這正是下一章 PID 控制 的出發點。可在 轉盤調速互動模擬 中切換三種控制器、加上負載比較。 例: = 10 rpm/V、設定 = 33⅓ rpm、負載 = 0.5 V。開迴路掉 5 rpm(15%);P 控制 = 2 時 = 21,誤差 = 1.83 rpm,其中負載造成的只有 0.24 rpm;PI 則回到 33⅓ rpm。 本章其他頁: 轉盤調速互動模擬 、 延伸資源 。 原課程的 倒傳遞網路 要先手推每一層的梯度公式,網路結構一改,公式就得重推。現在的深度學習框架用 自動微分 (automatic differentiation)解決這件事:只要寫出前向計算,梯度由程式自動算出。 計算圖 Computational Graph 任何計算都可以拆成一連串基本運算(加、乘、tanh…),畫成 計算圖 。以一個神經元與平方誤差為例: 取 、 、 、 。 前向 由左往右算出每個節點的值; 反向 從 出發,由右往左,每經過一個節點就乘上該節點的局部導數(連鎖律): 每種基本運算只要知道自己的局部導數,框架就能把任意組合的梯度算出來。這種由輸出往輸入的做法稱為 反向模式 自動微分:不論有多少參數,算出全部梯度的成本只是前向計算的幾倍。倒傳遞演算法就是它用在類神經網路上的特例。 自動微分不是數值微分(差分近似,有截斷誤差,且每個參數都要多算一次),也不是符號微分(推出公式,式子會爆炸變長)。它計算的是精確的導數值,誤差只有浮點運算本身的誤差。 在 PyTorch 中 Autograd in PyTorch 把參數設成 requires_grad=True ,PyTorch 就會在前向計算時記下計算圖;呼叫 backward() 時沿圖反向求梯度,結果放在 .grad : 與上圖手算的結果完全相同。 從 Matlab 工具箱到 PyTorch Mapping 原課程(Matlab) PyTorch 說明 newff(PR,[3,1],{'tansig','purelin'}) nn.Sequential(nn.Linear(2,3), nn.Tanh(), nn.Linear(3,1)) 每一層 = 線性運算+激活函數;purelin 就是不加激活函數 net.IW{1,1} 、 net.b{1} net[0].weight 、 net[0].bias 權值矩陣的列數=這一層的神經元個數,兩者相同 輸入矩陣每一 行 一筆資料 輸入張量每一 列 一筆資料 PyTorch 的第一個維度是批次 net.performFcn = 'mse' nn.MSELoss() 分類問題改用 nn.CrossEntropyLoss 等 net.trainFcn = 'traingd' torch.optim.SGD 也可換成 torch.optim.Adam train(net,p,t) 自己寫訓練迴圈(見下) 每一步:清梯度 → 前向 → 算損失 → backward() → step() sim(net,p) net(p) 前向計算 範例:重做原課程的倒傳遞網路 Example 倒傳遞網路 一頁的附檔,是 Matlab 手冊的 newff 範例:2 個輸入、3 個 tansig 隱藏神經元、1 個線性輸出,用四筆資料訓練。用 PyTorch 改寫,分別以 SGD(對應 traingd)與 Adam 訓練 300 回合: 兩者都把四筆資料擬合到誤差 以下。訓練迴圈的五個步驟(清梯度、前向、損失、反向、更新)是所有 PyTorch 訓練程式的共同骨架,後面的卷積網路、強化學習也一樣,只是換了網路與損失函數。 影像的像素很多,又有「空間結構」:相鄰的像素彼此相關,同一個圖案出現在畫面的哪裡都是同一個東西。 卷積神經網路 (convolutional neural network,CNN)把這兩個特性直接做進網路結構裡,是深度學習最早大獲成功的架構:1998 年的 LeNet-5 已用來辨識支票上的手寫數字,2012 年的 AlexNet 則在 ImageNet 競賽一舉突破。 為什麼不用全連接網路 Why Not Fully Connected 一張 224×224 的彩色影像有 224 × 224 × 3 = 150,528 個輸入。若第一層有 1000 個神經元並全部連接,光這一層就有約 1.5 億個權值,不但難以訓練,也極易過擬合。更根本的問題是:全連接網路把每個像素位置當成不同的輸入,在左上角學會的貓,到了右下角就要重新學一次。 卷積運算 Convolution 卷積層用一個小的 卷積核 (kernel,例如 3×3)在輸入上滑動,每個位置做「對應相乘再加總」: (深度學習框架實際計算的是互相關,不把卷積核翻轉;因為卷積核本身是學出來的,兩者沒有差別。)卷積層有兩個關鍵性質: 局部連接 :每個輸出只看輸入的一小塊(感受野)。 參數共享 :同一組權值用在所有位置,所以參數很少,而且同一個圖案不論出現在哪裡,都會在對應位置產生同樣的反應(平移等變性)。 輸出大小 :輸入 、卷積核 、四周補 圈 0(padding)、每次移動 格(stride)時, 多通道時(例如彩色影像有 RGB 3 個通道),一個卷積核的大小是 ;用 個卷積核就得到 個輸出通道(特徵圖)。例如 3×3、3 通道輸入、64 個卷積核的卷積層,參數只有 個,和輸入影像多大無關。 下面的程式用公式手算一次,再和 PyTorch 的 conv2d 比對,並檢查輸出大小公式: 互動:卷積核在做什麼 Try It 選一個圖案與卷積核,看黃框滑過輸入、逐格算出輸出。邊緣卷積核在亮暗交界處反應最強;CNN 的第一層學到的卷積核,常常就很像這些邊緣偵測器: 池化與典型架構 Pooling and Architecture 池化 (pooling)把每個小區域(例如 2×2)縮成一個值,最常用的是取最大值(max pooling)。它讓特徵圖變小、計算量減少,也讓網路對小幅位移不敏感。典型的 CNN 是「卷積+ReLU → 池化」重複幾次,逐步把影像變成越來越抽象、越來越小的特徵圖,最後接全連接層輸出類別。 堆疊卷積層時,每個輸出看到的輸入範圍( 感受野 )會擴大:兩層 3×3 卷積的感受野是 5×5,三層是 7×7。所以深層的特徵能涵蓋大範圍的形狀,參數卻比一個 7×7 的大卷積核還少。 範例:平移不變性 Example 產生三種 16×16 的圖形(空心方框、十字、斜線叉,大小與位置隨機,加上雜訊)。 訓練時只給畫在左半邊的圖形,測試時拿畫在右半邊的圖形 ,比較上圖的卷積網路與一個全連接網路: 全連接網路的參數多了 6 倍,在看過的位置也學得不錯,但到了沒看過的右半邊,準確率就掉到跟亂猜(33%)差不多。卷積網路因為參數共享,加上最後的全域最大池化不管特徵出現在哪裡,在右半邊同樣完全正確。 殘差網路 ResNet 網路疊到幾十層以上時,即使用了 ReLU 與好的初始化,訓練誤差仍會不降反升。2015 年的 殘差網路 (ResNet)加上「捷徑」,讓每個區塊學習輸入與輸出之間的差(殘差): 因為 ,梯度至少有一條「乘以 1」的路可以直接傳回前面的層,梯度消失的問題大幅減輕。ResNet 因此能訓練到上百層,殘差連接也成為後來幾乎所有深度網路(包括 Transformer)的標準元件。 在控制上的應用 In Control CNN 讓攝影機成為控制系統的感測器:從影像估計物體的位置與姿態,用於視覺伺服(visual servoing)、機器人夾取、自動駕駛的車道與障礙物偵測、產線的瑕疵檢測等。這類系統通常是「CNN 負責感知、傳統控制器(如 PID、MPC)負責控制」;也有直接從影像學出控制動作的做法,見後續的強化學習章。 控制系統處理的大多是 時間序列 :輸入與輸出隨時間變化,而且現在的輸出取決於過去的輸入(系統有記憶,也就是有「狀態」)。前饋網路每次只看一個固定大小的輸入,處理不了這種情形。 遞迴神經網路 (recurrent neural network,RNN)在網路裡加一個會一直傳下去的隱藏狀態,正好對應動態系統的狀態。 遞迴網路 Recurrent Networks 隱藏狀態 由前一刻的狀態與這一刻的輸入算出,與離散時間的狀態方程式 形式相同。每一步都用 同一組 權值,把網路沿時間展開,就像一個很深、各層權值相同的前饋網路: 訓練時在展開後的網路上做倒傳遞,稱為 沿時間倒傳遞 (backpropagation through time,BPTT)。 沿時間的梯度消失與爆炸 Vanishing and Exploding Gradients 由連鎖律,T 時刻的狀態對 k 步之前狀態的梯度,是 k 個雅可比矩陣的乘積: 這與 深層網路的梯度消失 是同一件事,只是「層」換成了「時間步」,而且每一步乘的都是同一個 。若 的譜半徑為 :線性 RNN 的梯度大小恰為 , 時指數消失、 時指數爆炸;tanh 的導數 ,又讓梯度再縮小一些。下圖是 16 個神經元的實算結果(遞迴權值為隨機正交矩陣乘上 , verify_dl2.py 核對): 線性 RNN,ρ = 1.1:往回 60 步,梯度放大 倍(爆炸)。 tanh RNN,ρ = 1:往回 60 步只剩約 :一分鐘前(以每步一秒計)的事情幾乎學不到。 tanh RNN,ρ = 1.5:放大與 tanh 導數的縮小大致抵銷,梯度在 1 附近。但這個平衡很脆弱:同樣的權值,輸入大小 0.5 時往回 60 步放大 17 倍,輸入大小 3 時隱藏狀態更飽和,只剩 (可用下方動畫的「輸入大小」滑桿試試)。 梯度爆炸可以用 梯度裁剪 (gradient clipping,梯度範數超過上限就等比例縮小)處理;梯度消失則要從網路結構下手,這就是 LSTM。 長短期記憶 LSTM Long Short-Term Memory LSTM(Hochreiter 與 Schmidhuber,1997)在隱藏狀態之外多了一條 記憶細胞 ,並用三個「閘」(輸出 0~1 的 sigmoid)控制資訊的進出: 關鍵在記憶細胞的更新只有「乘上遺忘閘、再加上新資訊」:沿著細胞往回傳的梯度是 ,沒有 tanh 導數,也沒有反覆乘上同一個權值矩陣。只要網路學會讓遺忘閘接近 1,梯度就能保留很多步:f = 0.99 時往回 60 步仍有 (上圖綠線)。這與 ResNet 的捷徑 是同樣的想法。 (補充)GRU(2014)把遺忘閘與輸入閘合併、省掉獨立的記憶細胞,參數較少,效果通常與 LSTM 相近。 範例:用 LSTM 學一個動態系統 System Identification 拿 模糊控制章 的質量–彈簧–阻尼系統(m = 1、c = 1、k = 2)當成「不知道模型的受控體」:只給網路輸入力 F(t) 與量到的位移 x(t),讓它自己學出輸入到輸出的關係。訓練資料是 40 段 20 秒、每 1~3 秒隨機換一個力的資料;測試則用訓練時沒出現過的步階與正弦輸入: 同樣 16 個隱藏神經元,LSTM 的測試誤差約為單純 RNN 的十分之一,預測曲線幾乎與真實系統重合,連訓練時沒看過的正弦輸入也預測得很準。這樣學出來的網路模型可以拿來設計控制器或做模型預測控制(MPC),詳見後續「神經網路用於控制」章。 原課程的 倒傳遞網路 只有一個隱藏層。照同樣的做法把網路疊深,會發現它「訓練不動」:誤差降得極慢,或停在很差的地方。這一頁說明問題出在哪裡,以及現在的標準解法。最後可以在網頁上直接訓練網路,比較各種做法。 梯度消失 Vanishing Gradients 第 層的淨輸入與輸出寫成 、 。倒傳遞把誤差對淨輸入的梯度(敏感度) 由後往前傳: 每往前一層,梯度就多乘一次權值與一次 。sigmoid(即 logsig)的導數 最大只有 0.25;連乘 10 層,即使權值大小約為 1,前面幾層的梯度也只剩後面的 倍。前面的層幾乎學不到東西,這就是 梯度消失 。反過來若權值太大,梯度會逐層放大而 爆炸 。 ReLU (rectified linear unit) 在 時導數恆為 1,梯度可以原封不動往前傳,而且計算很便宜。2010 年代起,它取代 S 形函數成為隱藏層的預設選擇。 初始化 Initialization 梯度不消失、不爆炸,前提是每一層的訊號大小差不多。設輸入 與權值 互相獨立、權值平均為 0,則一個神經元的淨輸入 Xavier(Glorot,2010) :tanh 在原點附近近似線性, 。取 (或兼顧反向的 ),各層變異數就維持不變。 He(2015) :ReLU 把一半的輸入砍成 0, ,所以要取 ,把減半補回來。用均勻分布時,就是在 之間取亂數。 下圖是 20 層、每層 64 個神經元的網路實算(5 組亂數的幾何平均, verify_dl.py 核對): sigmoid:第 1 層的權值梯度只有第 20 層的約 倍,前面的層完全學不動。 ReLU 配 Xavier:淨輸入每層大約縮小 ,到第 20 層只剩約 0.0009,訊號幾乎消失。 ReLU 配 He:淨輸入的均方根從 1.4 到第 20 層仍有 0.89,訊號與梯度都維持在合理範圍。 最佳化器 Optimizers 小批次隨機梯度下降(SGD) :每次只用一小批(例如 32 筆)資料估計梯度,就更新一次。比起原課程每回合用全部資料只更新一次的 traingd,更新次數多得多;梯度的隨機性還有助於跳出不好的區域。 動量(momentum) :累積過去的梯度當作「速度」,在一致的方向上越走越快,來回振盪的方向則互相抵消: Adam(2014) :同時追蹤梯度的平均 與平方的平均 ,每個參數用自己的步長。梯度一直很大的方向,步長自動縮小: 其中 、 、 是常用預設值。分母的 修正剛開始時 、 由 0 起算造成的偏小。下圖比較三者在一個狹長碗形函數上的路徑: y 方向比 x 方向陡 100 倍(條件數 κ = 100)。SGD 的學習率必須小於 ,否則在陡的方向發散;可是這麼小的步長,在平的 x 方向每步只前進 1.9%,60 步後離最小值還有 1.42。動量法( 、 )把一致的 x 方向累積成速度,60 步後只剩 0.008;Adam 為 0.14。實際的網路有上百萬個參數,各方向的陡峭程度差異更大,這正是動量與 Adam 成為標準做法的原因。 過擬合與正規化 Overfitting and Regularization 深度網路的參數很多,很容易把訓練資料「背」下來,對沒看過的資料卻表現很差,稱為 過擬合 。下圖用多項式擬合 15 個帶雜訊的點說明: 3 次多項式在沒看過的資料上誤差最小(0.24);12 次多項式幾乎穿過每個訓練點,測試誤差卻高達上千。所以一定要把資料分成 訓練集 、 驗證集 (調超參數、決定何時停止)與 測試集 (最後評估),並使用正規化方法: 提早停止 驗證誤差開始上升就停止訓練(原課程的 traingd 也有這個選項) 權值衰減(L2) 損失加上 ,讓權值保持小,函數較平滑 Dropout(2014) 訓練時每次隨機讓一部分神經元輸出 0(比例 p),逼網路不依賴少數神經元;測試時全部使用 批次正規化(2015) 把每層的淨輸入用小批次的平均與標準差正規化,再乘上可學習的 γ、加上 β: 。訓練更穩,可用較大的學習率 資料擴增 對影像做平移、翻轉、裁切等,等於免費增加訓練資料 在網頁上訓練網路 Try It 下面的動畫在瀏覽器裡直接訓練一個多層網路,把平面上兩類點分開:輸出用 sigmoid 表示屬於橘類的機率,損失為交叉熵,每次用 20 筆資料更新。可以切換資料、激活函數、最佳化器、層數與學習率: 同樣的設定訓練 150 回合的結果(網路 2–8–8–1,螺旋資料為 2–8–8–8–1; verify_dl.py 以與動畫相同的程式碼計算): 資料 ReLU+Adam(0.01) tanh+Adam(0.01) ReLU+SGD(0.1) sigmoid+SGD(0.1) 兩月形 100% 100% 100% 93.5% 螺旋 100% 94.5% 78.5% 50%(沒學到) 同心圓 100% 100% 100% 69.5% 最難的螺旋資料上,sigmoid+SGD 只有 50%,等於亂猜;換成 ReLU+Adam 就能完全分開。 PyTorch 版本 In PyTorch 同樣的實驗用 PyTorch 只要幾十行。 nn.Sequential 疊出網路, loss.backward() 自動算梯度, opt.step() 更新權值: PyTorch 的預設初始化與資料亂數和上面的動畫不同,數字不會完全一樣,但趨勢相同:ReLU+Adam 最好,sigmoid+SGD 學不起來。程式各部分的意義見下一頁 自動微分與 PyTorch 。 本課程原本的教材約完成於 2005 年。 類神經網路 一章介紹的感知器與倒傳遞網路,是 1980 年代就成熟的方法;當時的網路通常只有一兩個隱藏層,用 Matlab 的 newff 、 traingd 訓練。2006 年起,研究者開始成功訓練很多層的網路,2012 年 AlexNet 在 ImageNet 影像辨識競賽大幅領先其他方法,此後「深度學習」(deep learning)迅速成為人工智慧的主流。 為什麼深度學習在 2010 年代起飛 Why Now 資料 網路時代累積大量標註資料,例如 ImageNet 有上百萬張標註影像;資料越多,大模型越不容易過擬合 運算 GPU 擅長大量平行的矩陣乘法,訓練速度比 CPU 快數十倍以上 演算法 ReLU 激活函數、配合激活函數的初始化、Adam 等自適應最佳化器、dropout 與批次正規化、殘差連接(ResNet),解決了深層網路「訓練不動」的問題 軟體 PyTorch、TensorFlow 等框架提供 自動微分 :只要寫出前向計算,梯度自動算好,研究者不必再手推倒傳遞公式 與原課程的對照 Then and Now 項目 原課程(約 2005,Matlab) 現在(PyTorch) 網路深度 1~2 個隱藏層 數十到上百層 隱藏層激活函數 tansig、logsig ReLU 及其變形 初始化 initnw(Nguyen–Widrow) 依激活函數選 He 或 Xavier(Glorot) 訓練法 traingd(批次梯度下降)、trainlm 小批次 SGD+動量、Adam 防止過擬合 提早停止、調整網路大小 再加上 dropout、權值衰減、批次正規化、資料擴增 求梯度 工具箱內建的倒傳遞 通用的自動微分,任何可微分的運算都能訓練 網路架構 全連接的前饋網路 卷積網路(影像)、遞迴網路與 Transformer(序列、語言) 本章內容 In This Chapter 訓練深度網路的現代技巧 梯度消失、ReLU、初始化、SGD 到 Adam、過擬合與正規化;可在網頁上直接訓練網路 自動微分與 PyTorch 計算圖與反向模式自動微分;用 PyTorch 重做原課程的倒傳遞範例 卷積神經網路 CNN 卷積、池化、參數共享與平移不變性、殘差網路 遞迴神經網路 RNN 與 LSTM 沿時間倒傳遞、梯度消失、LSTM;用 LSTM 做 MCK 系統的系統鑑別 預計陸續新增:注意力機制與 Transformer,以及強化學習、神經網路用於控制、現代最佳化與調參等章節。 程式範例使用 Python 3 與 PyTorch。安裝方式見 PyTorch 官方說明 ↗ ;本章範例只需要 CPU 版本。 FIS 設計好後,可以放進 Simulink 模型當控制器(老師錄製的操作示範): 步驟 Steps 在命令視窗把 FIS 讀進工作區,例如 fismatrix = readfis('tank'); (或在 FIS Editor 用 File → Export → To Workspace)。 從 Simulink 的 Fuzzy Logic Toolbox 方塊庫(指令 fuzblock )拖出 Fuzzy Logic Controller 方塊。 雙擊方塊,把參數設成工作區中 FIS 變數的名稱。附檔 的方塊參數就是 FISMatrix 。 方塊的輸入是一個向量(各輸入依序以 Mux 合併),輸出是 FIS 的輸出;接上受控體模型即可模擬。 附檔 與 是水槽液位控制的完整例子(tank.fis 的輸入為 level 與 rate、輸出為 valve),詳見 水位控制範例 。(步驟說明為補充) FIS 也可以完全在命令視窗中讀取、檢視與計算(老師錄製的操作示範): 常用指令 Commands (補充)以原講義所用版本的寫法,配合附檔 : 上面三個 evalfis 結果以本站的推論程式核對過( verify_fz.py ),與工具箱手冊一致。新版 Matlab 的 evalfis 引數順序改為 evalfis(a, input) 。 在命令視窗輸入 fuzzy 開啟 FIS Editor(新版 Matlab 稱為 Fuzzy Logic Designer)。它顯示整個系統的概觀:左邊是輸入變數、右邊是輸出變數、中間是系統本身。以下是老師錄製的操作示範(RoboDemo 錄影,以 Ruffle 播放): 畫面各區 Layout 功能表 (File、Edit、View):開啟、儲存 FIS,新增或刪除變數,切換到其他四個工具。 系統圖 :雙擊輸入或輸出變數的圖示,開啟 Membership Function Editor ;雙擊中間的系統方塊,開啟 Rule Editor 。 FIS 名稱與型式 :名稱可用 Save as 改;型式在新建時選 Mamdani 或 Sugeno。 推論方法選單 :And method(min/prod)、Or method(max/probor)、Implication(min/prod)、Aggregation(max/sum/probor)、Defuzzification(centroid、bisector、mom、lom、som)。這些就是 Mamdani 推論五個步驟 裡每一步的做法。 目前變數 :選中某個變數後,可在這裡改名,並看到它的型態與範圍。 狀態列 :顯示最近一次的操作,例如「System tipper: 2 inputs, 1 output, and 3 rules」。 以上說明為依原講義附圖改寫的中文。 示範如何從 FIS 中刪除歸屬函數、變數與規則(老師錄製的操作示範,搭配上一頁建立的 MyTipper): 相關指令 Commands (補充,原講義所用版本的寫法) 刪除變數或歸屬函數前,先確認規則是否還用到它們(規則是以「第幾個歸屬函數」記錄的,刪掉中間的項目會讓編號改變),必要時先刪除或修改相關規則。新版 Matlab 對應的函數是 removeMF 、 removeInput 、 removeRule 。 附檔: 使用說明: 工具箱線上說明(請詳讀) 不開任何圖形工具,完全用程式建立一個 FIS(老師錄製的操作示範): 程式 CreateFIS.m rulelist 每一列代表一條規則,前面幾欄依序是各輸入、各輸出使用第幾個歸屬函數(0 表示不使用,負數表示 NOT),接著是 權重 ,最後一欄是 連接詞 (1 = AND、2 = OR)。例如第一列 1 1 1 1 2 :服務第 1 個(poor)、食物第 1 個(rancid)、小費第 1 個(cheap)、權重 1、以 OR 連接——即「若服務差或食物難吃,則小費少」。(說明為補充) 食物的兩個梯形把頭尾延伸到範圍外(−2、12),在 0~10 之內和 tipper.fis 的結果完全相同。 編註:原講義的程式與截圖把輸出的第一個歸屬函數拼成 'cheep' ,存好的 MyTipper.fis 則是 cheap ;名稱不影響計算,這裡統一寫成 cheap。下一步: 刪除歸屬函數、變數與規則 。 工具箱的函數與選項很多,遇到問題時要會查說明文件。以下是老師錄製的說明(請詳讀): 查詢方式(補充): help fuzzy :列出工具箱的所有函數。 help evalfis 、 doc evalfis :查某個函數的用法與範例。 各圖形工具視窗中的 Help 按鈕:說明該工具的操作。 完整使用手冊(原講義附檔): 。手冊中的小費問題就是本章各頁使用的例子。 Matlab 的 Fuzzy Logic Toolbox 可以用圖形介面或指令建立、檢視、模擬模糊推論系統(FIS),並把它放進 Simulink 當控制器。一個 FIS 存成 .fis 文字檔,記錄輸入、輸出變數、各自的歸屬函數與規則;本章的範例都附有 .fis 檔。(說明為補充) GUI Tools 五個圖形介面工具 Working With Simulink 把 FIS 放進 Simulink 模型 Working From the Command Window 用指令讀取與計算 FIS Building a FIS Without GUI Tools 完全用程式建立 FIS 五個圖形介面工具 GUI Tools 工具 指令 用途 FIS Editor fuzzy 設定輸入、輸出變數的個數與名稱,選 Mamdani/Sugeno 與各種運算方法 Membership Function Editor mfedit 設定每個變數的範圍與歸屬函數形狀、參數 Rule Editor ruleedit 以「若…則…」的形式編輯規則 Rule Viewer ruleview 輸入一組值,逐條看推論過程與輸出(即 Mamdani 頁 的推論圖) Surface Viewer surfview 畫出輸出對一或兩個輸入的曲面 常用指令 Command-Line Functions 以上是原講義所用 Matlab 版本的寫法。新版 Matlab(R2018b 起)改用 mamfis 、 sugfis 、 addInput 、 addMF 、 addRule 等函數建立 FIS, evalfis 的引數順序也改成 evalfis(fis, input) ;舊的 .fis 檔仍可用 readfis 讀入。 工具箱使用手冊(原講義附檔): 以小費問題為例,從頭到尾操作一次 Fuzzy Logic Toolbox 的圖形介面(老師錄製的 RoboDemo 錄影,以 Ruffle 播放): 建議的操作順序(補充): FIS Editor :新建 Mamdani 系統,加入兩個輸入(service、food)與一個輸出(tip)。 Membership Function Editor :設定三個變數的範圍與歸屬函數。 Rule Editor :輸入三條規則。 Rule Viewer 、 Surface Viewer :檢查推論結果與輸出曲面。 File → Export → To Disk 存成 .fis 檔;To Workspace 則存成工作區變數,供指令或 Simulink 使用。 附檔: 推論原理見 Mamdani 型模糊推論 。 1965 年,美國加州大學柏克萊分校的 L. A. Zadeh 教授在《Information and Control》期刊發表〈模糊集合〉(Fuzzy Sets)一文,提出了模糊的概念。理論真正受到注目是在後來數十年的應用發展,例如日本仙台市地下鐵列車的自動駕駛(模糊控制的實用化)、模糊電腦的開發等,使「Fuzzy」一度成為新科技的代名詞。 模糊理論最主要的特色是「近似」:講究近似推理,不以精確計算為手段。乍聽之下似乎不符合科學實事求是的要求,但模糊理論允許不清晰的資訊輸入,經過模糊(近似)的推論,仍能得到正確的結果——這和人腦「過程模糊、結論清晰」的運作很像。實驗也顯示,模糊控制在精密定位上可達次微米級的誤差,並不只是「差不多」而已。 模糊集合 Fuzzy Sets 傳統(亞里斯多德式)的 明確集合 (crisp set)清楚界定「是否屬於」:自然數、三角形、汽車……一個物件不是屬於、就是不屬於,沒有「部分屬於」的中間地帶。這正好符合二元邏輯,但也帶來很大的限制——人類語言描述的事物往往是「大概的」「含糊的」:「很會打籃球」的「很會」難以用精確數字表達;看到喜歡的商品,是非常喜歡、有點喜歡,還是普通喜歡? 以「中年人」為例:假設中年人的中心是 45 歲、範圍在 35~55 歲。 明確集合的 歸屬函數 (membership function)只有 1(屬於)與 0(不屬於)兩個值,外形像方波:35~55 歲的人全都是「同等的中年」。 模糊集合的歸屬函數可以是三角形、鐘形等各種曲線:45 歲的「中年程度」為 1,其餘 35~55 歲的人介於 0 與 1 之間。依年齡就能從歸屬函數讀出「歸屬度」,知道這個年齡「有多中年」。 常用的歸屬函數形狀(補充,括號內為 Matlab 工具箱的函數名與參數): 上圖的模糊集合採 Matlab 的 pimf(x,[35 45 45 55]) (補充);原講義只畫出鐘形示意,未指定函數。 模糊系統的三個步驟 Three Steps 以規則為基礎 (rule-based)是模糊系統的骨幹:運用人的經驗,透過主觀制訂的規則把收到的資料轉成知識。精確的輸入如何變成模稜兩可的訊號、如何推論、最後又如何變回能夠執行的精確控制量,都靠人所建立的規則庫: 模糊化 (fuzzification):用歸屬函數把明確的輸入轉成各模糊集合的歸屬度。 推論 (inference):把歸屬度交給規則庫,依每條規則算出模糊的輸出。 解模糊化 (defuzzification):把模糊的輸出還原成可以執行控制的明確數值。 推論與解模糊化的做法有兩大類: Mamdani 型 與 Sugeno 型 。 本頁文字摘自盧書豪同學的專題報告,僅做段落與用詞整理。投影片: Mamdani 型是最常見的模糊推論方式(E. H. Mamdani,1975),規則的結論也是模糊集合。以 Matlab 工具箱的「小費問題」說明:依 服務 品質(0~10)與 食物 品質(0~10)決定 小費 比例(0~30%)。三條規則: 若服務差,或食物難吃,則小費少。 若服務好,則小費普通。 若服務優,或食物好吃,則小費多。 推論的五個步驟 Five Steps 以課程附檔 實際計算服務 = 3、食物 = 8 的情形: 模糊化輸入 :讀出每個輸入在各歸屬函數的歸屬度。服務 3 屬於「差」0.135、「好」0.411、「優」幾乎 0;食物 8 屬於「難吃」0、「好吃」0.5。 模糊運算 :前件有兩個條件時用 AND 或 OR 合成一個值,即規則的 激發強度 。本例規則 1、3 是 OR,取最大值(max): 、 ;規則 2 只有一個條件, 。 蘊含 (implication):用 截掉結論模糊集合的頂端(min),規則越不成立,結論越「矮」。 聚合 (aggregation):三條規則的結論取聯集(max),得到一個輸出模糊集合。 解模糊化 :取聚合後圖形的 面積重心 當作輸出: 本例 = 18.0%。 編註:原講義的兩張流程圖取自 Matlab 工具箱手冊,同樣以服務 3、食物 8 為例,得到小費 16.7%。但手冊圖的輸出軸只到 25%,是示意圖;用課程附檔 tipper.fis(輸出 0~30%)實算,結果是 18.0%( verify_fz.py 核對:本站的推論程式對手冊中 evalfis([1 2]) 的例子算出同樣的 5.5586,可見程式與工具箱一致)。 互動推論 Try It (補充)拖動服務與食物,觀察三條規則的激發強度、截頂後的結論與重心怎麼變;右上是整個輸入範圍的輸出曲面——模糊推論把幾條簡單規則「內插」成一個平滑的非線性對應: 工具箱中的操作見 小費問題(Mamdani)範例 ;Sugeno 型的做法見 下一頁 。 把模糊控制器接到質量–彈簧–阻尼(MCK)系統上,讓質量塊的位移追上單位步階。這一頁是最簡單的版本:控制器只看 誤差 一個輸入。下方是老師錄製的建模與模擬示範,附檔為 (含 FuzzyMCK1.mdl、MCK 方塊所在的 mylib.mdl 與 MCKFZ.fis)。 系統與控制器 Plant and Controller (以下依附檔整理並模擬,為補充)受控體是 Simulink 的 MCK_System 方塊(m = 1、c = 1、k = 2): MCKFZ.fis 是 Mamdani 型(min/max/重心),只有三條規則: 誤差 e(範圍 −1.2~1.2) → 力 F(範圍 −10~10) 負 trapmf [−2.06 −1.3 −1.1 0] 負力 NF trimf [−18 −10 0] 零 trimf [−0.1 0 0.1] 零 ZF trimf [−2 0 2] 正 trapmf [0 1.1 1.3 2.06] 正力 PF trimf [0 10 18] 模擬結果 Simulation 單輸入的模糊控制器其實就是一條非線性曲線 F(e),作用像一個「非線性的比例控制」。左圖畫出這條曲線;右圖是步階(t = 1 s 起)的響應: 位移衝到 1.33(超越量 33%),振盪後停在 0.933,不會回到 1。 靜止時彈簧要 kx = 2x 的力才撐得住,這個力只能來自誤差:F(e) = 2(1 − e)。兩條線的交點就是穩態,e = 0.067。和線性比例控制一樣, 沒有積分作用就有穩態誤差 。 「零」的三角形很窄(±0.1),誤差稍大一點,控制力就從 0 跳到「正力」,所以曲線在原點附近很陡,像增益很大的比例控制,因此超越量大。 下一頁加入誤差的微分,相當於加上阻尼,可以壓低超越量: MCK 模糊控制 II 。 模擬時的錯誤與排除 Boolean Logic Signals 原講義的截圖顯示,在當時的 Simulink 版本執行這個模型,會出現錯誤 “MinMax blocks do not accept 'boolean' signals”。原因是 FIS 方塊內部解模糊化子系統裡的「Zero Strength」比較器輸出 boolean,接到 Max 方塊。排除方法: Simulation → Simulation parameters… 切到 Advanced 頁籤,把 Optimizations 中的 Boolean logic signals 由 On 改為 Off ,按 Apply。 這樣比較器改輸出 double,就能執行。新版 Matlab 已沒有這個問題。比較器等關係運算方塊的說明見附檔 。 同一個 MCK 系統(m = 1、c = 1、k = 2),控制器改用兩個輸入: 誤差 e 與 誤差的變化率 ė (Simulink 以 Derivative 方塊求得,再用 Mux 和 e 合併送入 FIS)。下方是老師錄製的示範,附檔為 (FuzzyMCK2.mdl、MCKFZ2.fis)。 規則的想法 Rule Design 原講義用下圖說明規則:質量塊從左邊出發往右走向目標,此時誤差為正、誤差在減小(ė 為負);衝過目標後誤差變負,往回走時 ė 為正。 (以下表格與說明為補充)依誤差與 ė 的組合排成規則表(N 負、Z 零、P 正;輸出 NL 負大、NM 負中、NS 負小、ZR 零、PS 正小、PM 正中、PL 正大): ė \ e N(超過目標) Z(在目標附近) P(還沒到) N(e 在變小) NL NS PM Z — ZR — P(e 在變大) NM PS PL 還沒到目標、而且正在接近(e 正、ė 負):給「正中」而非「正大」,提早收力,避免衝過頭。 還沒到、卻在遠離(e 正、ė 正):給「正大」把它拉回來。 在目標附近時只依 ė 小幅修正(正小/負小),作用像阻尼。 MCKFZ2.fis Membership Functions Mamdani 型(AND = min、蘊含 min、聚合 max、重心)。e 與 ė 各三個歸屬函數,輸出七個: 編註:規則表有 7 條,但附檔 MCKFZ2.fis 只有 6 條,少了紅字的「e 零 且 ė 零 → ZR」。 ė 的「零」雖然有定義,卻沒有任何規則用到 。另外 ė 的「正」左端在 −0.199,而不是像 e 的「正」從 0 開始。這兩點合起來決定了穩態(以 verify_fzex.py 模擬確認): 若把「正」改回從 0 開始,起步瞬間 e = 1、ė = 0,ė 既不屬於「負」也不屬於「正」,6 條規則全部不觸發,控制器輸出範圍中點 0,系統完全不動。 照附檔(左端 −0.199):ė = 0 時「正」仍有 0.17。靜止在目標時,「e 零 且 ė 正 → PS」觸發,輸出 PS 的重心 3。這個力大於彈簧在 x = 1 時需要的 2,結果穩態停在 x = 1.016 ,誤差僅 1.6%。 若照規則表補上第 7 條,靜止時 ZR 與 PS 一起觸發,力被拉低到約 0.6,穩態反而退到 x = 0.881 (見下方比較圖的紅色點線)。 附檔的 6 條規則加上偏移的歸屬函數,等於給了一個接近「抵消彈簧力」的偏壓。對這個 k = 2 的系統剛好有效;換一個彈簧常數,偏差就會出現。根本的解法是加入積分作用,例如把誤差的積分也當成輸入。 模擬結果 Simulation 四種控制器的步階響應(t = 0 起步)比較如下。右圖是 ė = 0 時控制力隨誤差的變化,和彈簧力 2(1 − e) 的交點就是穩態: 加入 ė 後,峰值由 1.33 降到 1.13,穩態誤差由 6.7% 縮小到 1.6%。同一問題改用 Sugeno 型: 零階 、 一階 。 把 MCK 模糊控制 II 的雙輸入控制器改成 零階 Sugeno 型:規則的結論不再是模糊集合,而是常數。下方是老師錄製的示範,附檔為 (FuzzyMCK2su.mdl、MCKFZ2sugeno.fis)。規則表與 Mamdani 版相同: MCKFZ2sugeno.fis What Changed (以下依附檔整理並模擬,為補充) 項目 Mamdani 版(MCKFZ2.fis) Sugeno 零階版(MCKFZ2sugeno.fis) 結論 7 個模糊集合(三角形、梯形) 7 個常數:PL 10、PM 6、PS 3、ZR 0、NS −3、NM −6、NL −10 解模糊化 聚合後取重心 加權平均 wtaver :F = Σ wᵢ zᵢ / Σ wᵢ AND min prod(相乘) e 的負/零/正 [−1.45 −1.05 −0.8 0]、[−0.5 0 0.5]、[0 0.8 1.05 1.45] [−1.72 −1.08 −0.8 0]、[−0.8 0 0.8]、[0 0.8 1.08 1.72] ė 的「正」 [−0.199 0.95 1.05 1.45] [−0.177 0.8 1.01 1.7] 規則 同樣 6 條(沒有「e 零 且 ė 零」) 常數 ±3、±6、0 正好是 Mamdani 版三角形歸屬函數的頂點,±10 是輸出範圍的端點。Sugeno 型不必對輸出範圍取樣求重心,計算量小得多,適合即時控制。檔案中輸出範圍寫成 [0 1],但 Sugeno 的結論是常數,範圍不影響計算。 模擬結果 Simulation 與其他版本的比較見 MCK 模糊控制 II 的比較圖 (橘線): 位移單調爬升、沒有振盪:0.87 s 越過 1,之後緩慢爬升(3 s 時 1.041、5 s 時 1.057),最後停在 1.075 ,比目標高 7.5%。 原因和 Mamdani 版相同:在目標靜止時只有「e 零 且 ė 正 → 3」觸發,3 大於彈簧需要的 2,質量塊會被繼續推。等 e 變負,「e 負 且 ė 正 → −6」才開始分攤權重。平衡點 e = −0.075 時,e 屬於「負」0.094、「零」0.906(兩條規則的 ė 歸屬度相同,約掉),加權平均 0.906 × 3 + 0.094 × (−6) = 2.15,正好等於彈簧力 2 × 1.075。 下一頁再把常數結論改成 e 與 ė 的線性函數: MCK 模糊控制 IV(一階 Sugeno) 。 再把 零階 Sugeno 版 的常數結論改成 一階 :每條規則的結論是輸入的線性函數。下方是老師錄製的示範,附檔為 (FuzzyMCK2suL.mdl、MCKFZ2sugenoL.fis)。 一階結論 Linear Consequents (以下依附檔整理並模擬,為補充)一階 Sugeno 的第 i 條規則寫成 輸出仍是加權平均 。MCKFZ2sugenoL.fis 的歸屬函數與規則都和零階版相同,只把結論改成: 結論 PL PM PS NS NM NL [p q r] [1 1 10] [1 1 6] [1 1 3] [1 1 −3] [1 1 −6] [1 1 −10] 也就是 :每條規則在零階的常數之外,再加上一項相同的 。加權平均時,這一項可以整個提出來: 所以一階版等於「零階模糊控制器」再並聯一個增益 1 的線性 PD 控制器。(ZR 定義為 [0 0 0],但 6 條規則都沒有用到它。) 模擬結果 Simulation 比較圖見 MCK 模糊控制 II (紫色虛線): 起步瞬間 e = 1、ė = 0,F = 1 + 10 = 11(零階版為 10)。 多出的 ė 項像阻尼一樣拖慢接近的速度:到 2.5 s 才越過 1(零階版 0.87 s),同樣沒有振盪。 穩態 x = 1.070 ,比零階版的 1.075 略好。在平衡點 e = −0.070、ė = 0 時,F = e + (0.912 × 3 + 0.088 × (−6)) = 2.14,等於彈簧力。 三個雙輸入版本都靠「e 零 且 ė 正」這條規則的偏壓撐住彈簧,都沒有積分作用,因此穩態都不會剛好是 1。 Membership Function Editor(指令 mfedit )用來設定每個變數的範圍與歸屬函數。以下是老師錄製的操作示範: 畫面各區 Layout 變數區 (FIS Variables):點選要編輯的變數,它的歸屬函數就顯示在右邊的圖上。 歸屬函數圖 :顯示目前變數的所有歸屬函數。點選一條曲線即可修改它;用滑鼠拖曳可以移動曲線或改變形狀。 目前變數 :名稱、型態(輸入或輸出)、 範圍 (Range)與 顯示範圍 (Display Range)。 目前歸屬函數 :名稱、型態(trimf、gaussmf……,見 常用歸屬函數 )與數值參數(Params)。例如小費問題的「good」是 gaussmf ,參數 [1.5 5] (σ = 1.5、中心 5)。 新增歸屬函數:Edit → Add MFs,可一次加入數個同型態、平均分布的函數再調整。 以上說明為依原講義附圖改寫的中文。 Fuzzy Logic Toolbox 有五個圖形介面工具,都作用在同一個模糊推論系統(FIS)上:三個 編輯器 用來建立、修改 FIS,兩個 檢視器 只用來觀察,不會改動 FIS。任何一個工具改了 FIS,其他已開啟的工具會同步更新。點下圖的方塊可開啟各工具的說明頁(說明為補充): FIS Editor :決定有幾個輸入、輸出,選 Mamdani 或 Sugeno 與各種運算方法。 Membership Function Editor :設定每個變數的範圍與歸屬函數。 Rule Editor :建立「若…則…」規則。 Rule Viewer :輸入一組值,逐條看推論過程。 Surface Viewer :看輸出對輸入的整個曲面。 依這個順序做一次「小費問題」,就能從無到有建立一個 FIS;整體操作示範見 Fuzzy Logic Toolbox 操作示範 。 Rule Editor(指令 ruleedit )用選單組出「若…則…」規則,不必手寫語法。以下是老師錄製的操作示範: 畫面各區 Layout 規則清單 :上方大框列出所有規則,例如「1. If (service is poor) or (food is rancid) then (tip is cheap) (1)」,括號內的 1 是規則權重。 輸入、輸出選單 :每個輸入一欄(If … is),輸出一欄(Then … is),選 none 表示這條規則不使用該變數。 not 勾選框:把條件或結論取否定。 Connection :多個條件之間用 or 或 and 連接。 Weight :規則的權重(0~1),會乘上這條規則的激發強度。 Add rule/Delete rule/Change rule :新增、刪除、修改規則。 練習 Practice 開啟練習檔 ——它已經有輸入、輸出與歸屬函數,但還沒有任何規則。用 Rule Editor 輸入 小費問題的三條規則 ,完成後和驗證檔 比較:在 Rule Viewer 輸入 [3 8],應得到小費 18.0。(練習說明為補充) Rule Viewer(指令 ruleview )把整個推論過程畫成一張圖,就是 Mamdani 頁的推論圖 。以下是老師錄製的操作示範: 畫面各區 Layout 每一列是一條規則 ,每一欄是一個變數:前面幾欄(黃色)是輸入在該規則用到的歸屬函數,最後一欄(藍色)是輸出。點某一列,狀態列會顯示那條規則的內容。 紅色直線 :目前的輸入值,可以直接拖曳改變;上方會顯示目前的輸入值,例如 service = 5、food = 5。 右下角的圖 :所有規則的輸出聚合後的結果,粗紅線是解模糊化後的輸出(例如 tip = 15)。 Input 欄位 :也可以直接輸入數值,例如 [3 8] 。 Plot points :畫圖與計算用的取樣點數(預設 101),也是解模糊化時積分的點數。 Move 按鈕:變數很多時,左右上下捲動畫面。 本站的 互動推論 就是 Rule Viewer 的簡化版,可以直接在瀏覽器中拖動輸入。 老師錄製的操作示範:檢視台車倒單擺的 Sugeno 型 FIS(附檔 )。模型與動畫程式在 (slcp.mdl、slcp.fis、initcp.m、animcp.m),台車模型的說明見舊站 Cart and Pole Model ↗ 。 原講義此處的連結寫成 ../matlab/InvertCart.htm ,在原站會指到不存在的位置,已改為上方的正確網址。 台車與擺桿 Cart and Pole (以下依附檔整理並模擬,為補充)台車在水平軌道上,頂端以鉸鏈接一根擺桿。控制器對台車施加水平力 F,要讓擺桿保持直立,同時把台車移到目標位置。目標是 ±1.5 m 的方波,每 5 s 換邊一次。 依 slcp.mdl 的參數(initcp.m 只負責讀入 slcp.fis):擺桿長 1 m(質心到鉸鏈 l = 0.5 m)、質量 m = 0.1 kg,台車 M = 1 kg,g = 9.8 m/s²。θ 以直立為 0、向 +x 傾為正,運動方程式為 slcp.fis A Sugeno FIS with Four Inputs 輸入 意義 範圍 歸屬函數 in1 擺角 θ(rad) ±0.3 各兩個 gbellmf,中心在範圍兩端(例如 in1:a = 0.3、b = 2、c = ∓0.3),在範圍內大致是「偏負」與「偏正」兩個寬廣的鐘形 in2 擺角速度 θ˙(rad/s) ±1 in3 台車位置 − 目標(m) ±3 in4 台車速度 ẋ(m/s) ±3 四個輸入各兩個歸屬函數,排列組合共 2⁴ = 16 條規則,每條規則的結論都是一階線性函數 四個輸入的意義與 in3 的正負號,是以模擬確認的:照上表接線,台車能追上目標;若 in3 改成「目標 − x」,擺桿雖然還扶得住,台車卻一路往同一方向跑掉(60 s 跑了 130 m)。 編註:16 條規則的係數幾乎一樣:a = 37.6~41.4、b = 10.03~10.18、c 全部等於 √10 = 3.1623、d 全部等於 4.2875,常數 e 在 ±0.34 以內( verify_fzex.py 核對)。所以這個 FIS 在整個範圍內都接近一個線性狀態回授 係數的樣子(位置增益恰為 √10)很像以最佳控制(LQR)求得的回授增益,再讓模糊規則在不同擺角、角速度下做小幅修正。歸屬函數與規則名稱都是工具箱的自動命名(in1mf1、outmf1…),應是由程式產生,而非逐條手寫。 注意位置項的正負號:台車在目標右邊(x − x d > 0)時,F 反而先往右推。這是倒單擺的特性:要讓台車往左走,得先把台車往右推一下,讓擺桿向左傾,接著才能一邊扶住擺桿、一邊帶著台車往左移。直接往左推的話,擺桿會往右倒。 模擬結果 Simulation 每次目標換邊,台車先往反方向退約 11 cm,再加速追過去,約 2.4 s 進入目標 ±5 cm。每段結束時與目標的距離小於 2 cm。 擺角最大約 0.27 rad(16°),出現在換邊後 0.5 s,也就是擺桿被「甩」向新方向的時候。 互動模擬 Try It 下方動畫用同一個 slcp.fis 即時控制。可以換目標、推擺桿一下看它恢復,或關掉控制器看擺桿倒下: Sugeno 型(Takagi–Sugeno–Kang,1985)的規則前件和 Mamdani 一樣,但 結論是輸入的函數 ,而不是模糊集合。典型的規則是 零階 Sugeno: ,輸出 是常數。 一階 Sugeno: 是輸入的線性函數。 每條規則的輸出 以它的激發強度 加權。例如 AND 規則(輸入 1 = 、輸入 2 = )的激發強度為 , 為兩個輸入的歸屬函數。整個系統的輸出是所有規則輸出的 加權平均 : 不必像 Mamdani 那樣對整個輸出集合積分求重心,計算量小得多,也容易用最佳化或學習的方法調整參數(例如類神經模糊系統 ANFIS),所以常用於控制器與建模。 小費問題的 Sugeno 版 Sugeno Tipper 以課程附檔 (零階:小費少 = 5、普通 = 15、多 = 25)實際計算服務 = 3、食物 = 8: 編註:原講義的 Sugeno 流程圖取自 Matlab 工具箱手冊,得到 16.3%;依課程附檔 tippersg.fis 實算為 18.48%。兩者差異同 Mamdani 頁的編註 :手冊圖是示意圖,不是用這個檔案算出來的。 在 Mamdani 頁的互動推論 切到「Sugeno」可以比較兩者:同樣的規則與激發強度,只差在結論與解模糊化的方式。原講義附檔: (倒單擺台車的 Sugeno 模糊控制,見 倒單擺 FIS )。 Surface Viewer(指令 surfview )畫出輸出對一個或兩個輸入的關係,可以一眼看出整個 FIS 的行為是否合理(例如輸出是否隨服務變好而增加、有沒有不該出現的凹凸)。以下是老師錄製的操作示範: 畫面各區 Layout 曲面圖 :輸出對所選輸入的曲面,可以用滑鼠拖曳旋轉。 X、Y(input)選單 :選要畫在兩個軸上的輸入; Z(output) 選要畫的輸出。 X grids、Y grids :每個軸的取樣點數,點數越多曲面越細緻、計算越久。 Ref. Input :輸入超過兩個時,沒畫在軸上的輸入要固定在某個值,在這裡指定。 Evaluate :設定完成後按下才開始計算並畫圖。 指令 gensurf(fis) 也能畫出同樣的曲面。小費問題的曲面可在 互動推論 右上角看到。 老師錄製的操作示範:開啟並檢視水槽液位控制的 FIS(附檔 )。問題說明見 水槽液位控制 。 歸屬函數 Membership Functions (以下整理自附檔,為補充)Mamdani 型;AND = prod、OR = probor、蘊含 prod(縮放而非截頂)、聚合 max、解模糊化 centroid。 變數 範圍 歸屬函數 輸入 level(水位誤差) −1~1 m high 過高、okay 剛好、low 過低:gaussmf σ = 0.3,中心 −1、0、1 輸入 rate(水位變化率) −0.1~0.1 m/s negative 下降、none 不變、positive 上升:gaussmf σ = 0.03,中心 −0.1、0、0.1 輸出 valve(閥速度) −1~1 close_fast 快關 [−1 −0.9 −0.8]、close_slow 慢關 [−0.6 −0.5 −0.4]、no_change 不動 [−0.1 0 0.1]、open_slow 慢開 [0.2 0.3 0.4]、open_fast 快開 [0.8 0.9 1](trimf) level 是「期望 − 實際」,所以誤差為負(中心 −1)代表水位 過高 ,為正代表 過低 。 規則 Rules If level is okay, then valve is no_change.(水位剛好就不動) If level is low, then valve is open_fast.(水位過低就快開) If level is high, then valve is close_fast.(水位過高就快關) If level is okay and rate is positive, then valve is close_slow.(水位剛好但還在上升,就慢慢關) If level is okay and rate is negative, then valve is open_slow.(水位剛好但還在下降,就慢慢開) 前三條只看誤差,像一個比例控制;後兩條在接近目標時依變化率提前修正,避免衝過頭。五條規則就涵蓋了整個操作範圍,這正是模糊控制的優點:規則直接寫出操作員的經驗。 互動模擬 Try It 下方動畫使用同一個 tank.fis,可以看到每條規則的激發強度隨水位變化(推論程式與本站 Python 模組逐點比對一致,見 verify_fzex.py ): 這是 Fuzzy Logic Toolbox 手冊的經典範例(原講義節錄英文說明,以下改寫為中文)。 想像一個水槽,一條管子進水、一條管子出水。進水管上有閥可以調整;出水量則由出水管的口徑(固定)和槽內壓力(隨水位變化)決定。要控制水位,控制器必須知道目前的水位,也必須能調整進水閥。 控制器的 輸入是水位誤差 (期望水位減實際水位), 輸出是閥開啟或關閉的速度 。注意輸出不是閥的開度本身,而是開度的變化率;開度是這個速度的積分。 出水量 Outflow 槽頂與出水口都通大氣(壓力 ),由白努利方程式,水位 H 處的水以速度 V 流出: (以下依附檔 sltank.mdl 的參數整理,為補充)出水量 = 出水口面積 × V。設槽的底面積為 A、進水量為 ,水位的變化為 模型中 A = 1 m²、出水口 a = 0.05 m²、槽高 2 m,進水量 = 0.5 × 閥開度(開度 0~1)。水位 1.5 m 時出水 0.271 m³/s,閥要開 54% 才能維持;水位 0.5 m 時出水 0.157 m³/s,閥開 31%。出水量隨 變化,所以這是一個非線性系統。 除了誤差,tank.fis 還把 水位的變化率 當作第二個輸入,用來在水位接近目標時提早減速,作用像 PD 控制的微分項。規則與歸屬函數見 水槽液位控制的 FIS ,Simulink 模型與模擬結果見 水槽液位的 Simulink 模型 。 老師錄製的操作示範:水槽液位控制的 Simulink 模型(附檔 ,控制器讀入 )。 模型內容 Model (以下依附檔整理並模擬,為補充) 設定值 :1 m 加上振幅 0.5 m 的方波(0.1 rad/s,每 31.4 s 在 1.5 m 與 0.5 m 間切換)。 控制器輸入 :水位誤差(設定 − 實際),以及水位變化率(經飽和方塊限制在 ±0.1 m/s)。 閥 :FIS 的輸出是閥的開關速度,經限幅積分器(0~1,初值 0 即全關)得到開度;進水量 = 0.5 × 開度。 水槽 :底面積 1 m²、槽高 2 m、出水口 0.05 m²、初始水位 0.5 m;出水量 = 0.05·√(2 × 9.8 × h)。 模型裡另有 PID Controller 方塊(P = 2、I = 0、D = 1)、切換開關與名為 Comparison 的示波器,看來是用來和 PID 控制比較;本站沒有重建這部分。 模擬結果 Simulation 以相同參數模擬 130 s: 每次設定值切換後,約 6 s 回到 ±5 cm 以內。升到 1.5 m 時超越約 1 cm;降到 0.5 m 時下衝約 3 cm,之後慢慢回升。 穩態誤差約 1 cm:每段結束時,水位 1.5 m 段停在 1.490,0.5 m 段在 0.488(仍在緩慢收斂)。閥開度分別穩定在 0.54 與 0.31,正是出水量 ÷ 0.5(解析值見 水槽液位控制 )。 編註:控制器輸出的是閥的 速度 ,閥開度是它的積分,迴路內含積分作用,照理穩態誤差應為 0。實際停在誤差 +1.0 cm,原因在輸出歸屬函數不對稱:慢關的中心在 −0.5,慢開卻在 +0.3。 水位剛好、變化率為 0 時,「上升」「下降」兩個高斯函數的尾巴仍各有 0.004 的歸屬度,規則 4、5 同時微弱觸發。 慢關離原點較遠,重心被拉向負值,輸出 −0.00076,閥會慢慢關。 水位因此略低於設定,等「過低 → 快開」的尾巴補回來才平衡。平衡點是誤差 +0.010 m,與設定值無關。 把慢關改成對稱的 [−0.4 −0.3 −0.2],平衡誤差就變成 0( verify_fzex.py 核對)。 互動模擬 Try It 可以改成手動設定值,觀察水位變化與五條規則的激發強度(畫面時間加速 6 倍): 老師錄製的操作示範:用工具箱的圖形介面,從頭建立小費問題的 Mamdani 型 FIS。完成的檔案即附檔 。 tipper.fis 的內容 What Is in the FIS (以下整理自附檔,為補充) 變數 範圍 歸屬函數 輸入 service(服務) 0~10 poor 差 gaussmf [1.5 0] 、good 好 gaussmf [1.5 5] 、excellent 優 gaussmf [1.5 10] 輸入 food(食物) 0~10 rancid 難吃 trapmf [0 0 1 3] 、delicious 好吃 trapmf [7 9 10 10] 輸出 tip(小費 %) 0~30 cheap 少 trimf [0 5 10] 、average 普通 trimf [10 15 20] 、generous 多 trimf [20 25 30] If service is poor or food is rancid, then tip is cheap. If service is good, then tip is average. If service is excellent or food is delicious, then tip is generous. 運算方法:AND = min、OR = max、蘊含 min(截頂)、聚合 max、解模糊化 centroid(重心)。檔案中規則 1、3 的連接詞代碼是 2(OR),規則 2 的食物欄為 0(不使用)。 推論過程的逐步圖解與互動版本見 Mamdani 型模糊推論 。服務 3、食物 8 時,小費為 18.0%。 老師錄製的操作示範:把同一個小費問題改成 Sugeno 型 FIS。附檔為 。 tippersg.fis 的內容 What Is in the FIS (以下整理自附檔,為補充)規則與 Mamdani 版相同(3 條,OR = max),差別在結論與解模糊化: 變數 範圍 歸屬函數 輸入 service 0~10 poor 差 [1.5 0] 、average 好 [1.5 5] 、good 優 [1.5 10] (皆 gaussmf) 輸入 food 0~10 rancid 難吃 trapmf [−5 0 1 3] 、delicious 好吃 trapmf [7 9 10 15] 輸出 tip −30~30 cheap linear [0 0 5] 、average linear [0 0 15] 、generous linear [0 0 25] 輸出宣告為一階(linear),但兩個輸入的係數都是 0,所以實際上是零階:三條規則的結論分別固定為 5、15、25。 解模糊化為 wtaver (加權平均):小費 = Σ wᵢ zᵢ / Σ wᵢ。服務 3、食物 8 時為 18.48%。 服務的歸屬函數名稱和 tipper.fis 不同(這裡的 good 是「優」),但位置相同。食物的梯形延伸到範圍外,在 0~10 之內的形狀與 tipper.fis 完全一樣。 兩型的比較與互動推論見 Sugeno 型模糊推論 ,下方動畫也可以切換到 Sugeno。 模糊控制 (fuzzy control)把人的經驗寫成「若…則…」的語意規則,例如「誤差大而且還在變大 → 用力推」,再用模糊集合與模糊推論算出明確的控制量。它不需要精確的數學模型,適合難以建模、但有操作經驗可循的系統。(說明為補充) 理論 Theory 簡介 Introduction:模糊集合、歸屬函數、三個步驟 Mamdani 型 Mamdani Type:截頂、聯集、重心 Sugeno 型 Sugeno Type:加權平均 Fuzzy Logic Toolbox Matlab 工具箱的使用 範例 Examples 範例 型式 說明 小費問題 / Sugeno 版 Mamdani/Sugeno 依服務與食物決定小費,用來熟悉推論過程 MCK 模糊控制 I Mamdani(單輸入) 質量–彈簧–阻尼(MCK)系統的模糊控制 MCK 模糊控制 II Mamdani(雙輸入) 同一系統改用兩個輸入 MCK 模糊控制 III / IV Sugeno 零階/一階 同一問題改用 Sugeno 型 水位控制模型 、 水位控制 FIS Mamdani 水槽液位控制(工具箱的經典範例) 倒單擺台車模型(舊站)↗ 、 倒單擺 FIS Sugeno 台車上的倒單擺平衡控制 延伸閱讀(原講義附檔): 、 、 。 單點交配的 Matlab 程式。族群中的染色體兩兩一組(第 1、2 個,第 3、4 個……),每組以交配率 co_rate 決定是否交配;交配點 cross_pt 取 1~(n−1) 的隨機整數(產生方法見 複製頁的隨機整數 ),交配點之後的位元互換。 交配點取 1~(n−1),是為了保證交配點之後至少有一個、之前也至少保留一個位元,交配才有意義(取 0 或 n 等於整條互換或完全不換)。因為複製時交配池的順序已經是隨機的,直接把相鄰兩個配成一對就等於隨機配對。(說明為補充) 交配 是從交配池中隨機取出兩個母代字串,彼此交換部分位元,組成兩個新的物種;藉著把前代優秀的位元片段組合起來,期望產生更好的子代。並不是所有物種都會交配,每一對是否交配由 交配率 控制。交配有三種常見型式: 單點交配 Single-Point Crossover 隨機選一個交配點,交配點之後的所有位元互換: 雙點交配 Two-Point Crossover 隨機選兩個交配點,兩點之間的位元互換: 字罩交配 Uniform (Mask) Crossover 隨機產生一個與字串等長的「字罩」(0 與 1),字罩為 1 的位置就互換位元: 互動示範 Try It (補充)每一輪隨機產生新的母代、交配點或字罩;下方顯示解碼後的值,可以看出交配後子代的值落在哪裡: 本課程的程式使用單點交配,見 Matlab 程式 。 染色體是二進位字串,計算適應值之前要先 解碼 成實際的參數值:二進位 → 十進位 → 線性對應到參數的範圍。 二進位轉十進位 Binary to Decimal Matlab 的 polyval(p, x) 計算多項式 的值。例如 , = polyval([2 3 5],4) = 49。把二進位的各位元當成多項式係數、代入 ,正好就是轉成十進位: 對應到參數範圍 Mapping to the Range 若每個參數用 個位元,可表示的十進位值為 0~ , 。要讓全 0 對應到下限 、全 1 對應到上限 ,由相似三角形 例:4 位元、範圍 = 2~ = 10,[1 0 1 0] 的十進位 = 10,所以 = 7.333。位元數決定解析度:相鄰兩個可表示值相差 ,4 位元時是 0.533,10 位元時只有 0.0078。(例題為補充) 多變數 Multi-Variable 多個參數時,把各參數的位元依序串接成一條染色體。每個參數 個位元時,第 個參數佔第 到第 個位元: 原講義附檔: (程式註解譯為中文) 最佳染色體經過複製、交配之後不一定留在子代中,也可能因突變而消失。若把上一代的最佳染色體直接保留到下一代,就能確保「目前找到的最佳解」不會變差,收斂也比較快。這稱為 菁英政策 (elitism)。 編註:原講義寫「最佳染色體一定會被送入交配池」,用競賽法時並不成立——最佳者只要被抽到就一定贏,但它也可能一次都沒被抽到。每次競賽抽兩個(不重複),最佳者沒被抽到的機率是 ;做 次競賽都沒抽到的機率為 ,族群 20 個時約 0.12( 很大時趨近 ≈ 0.135)。所以即使在複製這一步,最佳解也有約一成的機會直接消失,這正是需要菁英政策的另一個理由。 用 sort 找出最佳者 Finding the Best with sort Matlab 的 [fs, ind] = sort(f) 把 f 由小到大排序, ind 記錄排序後每個元素原本的位置。例如 f = [10 50 30 20] : 所以適應值最大的前 名,就是 ind(end) 、 ind(end-1) 、……、 ind(end-k+1) 對應的染色體。 保留與放回 Keep and Restore 先在複製之前記下最佳的 elit_no 個染色體: 交配、突變完成後,再把它們放回新族群(取代前面幾個): 附註:原講義截圖中的 GA51_Elit2 寫成三個引數 (popu,bestxy,elit_no) ,附檔程式則由 bestxy 的列數自動取得 elit_no ,只需兩個引數;以上採附檔版本。菁英數不宜太多,否則族群很快被少數幾個染色體佔滿,失去多樣性。 原講義附檔: 、 適應函數 (fitness function)衡量每個染色體「有多好」,GA 會讓適應值大的染色體有更多機會留下後代。適應函數直接由要解的問題決定。 若要求 的 最大值 ,適應函數就取 本身。 若要求 最小值 ,適應函數改為 其中 是一個常數,要大到讓 在整個搜尋範圍內都是正值。(適應值要為正,是因為 輪盤法 以適應值的比例當作被選中的機率;若改用競賽法,只比大小,負值也沒關係。) 適應函數的地形 Fitness Landscape (補充)把搜尋範圍取為 ,畫出 的分布: 這個函數有許多分開的峰,從隨機起點出發的梯度法很可能爬上附近的小峰就停住;GA 一次在整個範圍內放很多點,又靠突變不斷探索,比較容易找到全域最大值。用 GA 演化互動模擬 可以實際觀察。 在控制上的適應函數 Fitness for Controller Tuning 用 GA 調控制器時,染色體是控制器參數,每個染色體都要跑一次閉迴路模擬,再依響應算出適應值。本章範例 的主程式 MCKPIDGA_Main.m 是這樣做的:染色體為 (各 8 位元、範圍 0.1~10),以 Simulink 模擬 MCK 系統 10 秒的步階響應,然後 也就是「誤差平方和+超越量懲罰」當成本 ,再用上面 的方法轉成要最大化的適應值。只計算 1.2 秒以後的誤差,是不在乎起步那段、只要求盡快穩定在設定值。(說明為補充,數值取自原講義附檔程式) 附註:主程式呼叫 GA1_Initpopu(PopuSize,var_n,bit_n) 時後兩個引數的順序和函式定義 (popusize,bit_n,var_n) 相反,但函式只用到兩者的乘積,結果不受影響。 GA 的第一步是隨機產生一群染色體。以二進位編碼時,每個染色體是一串 0 與 1;若有 var_n 個參數、每個參數用 bit_n 個位元,染色體長度就是 bit_n × var_n 。(說明為補充) rand(m,n) 產生 0~1 之間均勻分布的亂數矩陣,和 0.5 比較後得到 0/1 各半機率的邏輯矩陣:每一列就是一個染色體。例如 GA1_InitPopu(4,5,2) 會得到 4 列、10 行的 0/1 矩陣,每列前 5 個位元代表第一個參數、後 5 個代表第二個參數(見 參數解碼 )。 下面是老師錄製的 Matlab 操作示範(RoboDemo 錄影,以 Ruffle 播放): 突變程式 GA5_mutate.m 的 Matlab 操作示範(老師錄製的 RoboDemo 錄影,以 Ruffle 播放)。程式說明見 突變 頁。 演算法 (algorithm)是計算機的常用術語,指一套經由有規則的數學運算或邏輯判斷,完成某一特定工作的方法或流程。遺傳(基因)演算法就是依照基因遺傳的原則寫成的程式。 達爾文進化論的基本精神是「物競天擇,適者生存」,弱者會被自然淘汰。染色體具有 複製 、 交配 與 突變 的能力,所以新生代(子代)的染色體通常比母代優良,劣質的染色體則逐漸被淘汰。 最佳化方法大致分成兩類: 特定型最佳化法則 :針對某些特定的函數特性(例如可微分、凸函數)發展出來的方法,如梯度法。 廣義型最佳化法則 :不論目標函數的特性為何,都不必修改方法就能處理。遺傳演算法屬於這一類。 遺傳演算法仿效生物界優勝劣敗的進化法則:選出特性較好的母代,隨機交換彼此的基因資訊,期望產生比母代更優秀的子代;如此反覆,最後得到適應性最強的物種。三個最主要的運算子是 複製 (reproduction)、 交配 (crossover)與 突變 (mutation)。 求解的基本流程 Procedure 把所有要搜尋的參數 編碼 成染色體(二進位字串),例如以 4 位元字串 1011 代表一個參數值;隨機產生 個 初始物種 。 依求解的條件設計 適應函數 (fitness function),計算每個物種的適應值;適應能力較高的物種才有資格被挑選進入下一步,這就是 複製 的過程。 接著進行 交配 與 突變 ,完成一次演化;如此重複,直到滿足終止條件(例如演化到指定代數,或最佳解不再改進)。 流程圖依原講義重繪;文字為原講義內容,僅做段落與用詞整理。 突變 是隨機選取某些物種與突變點,把該位元的值反轉(0 變 1、1 變 0)。同樣不是每個物種都會突變(否則就失去了「突變」的意義),發生的機率由 突變率 控制。 突變的目的,是希望最後求出的解是 全域極大值 。如果沒有突變,隨機產生的初始族群經過複製與交配之後,可能只在某個區域內反覆搜尋,最後得到的只是 局部極大值 :交配只能重新組合族群中已有的位元,若某個位置上所有染色體都是 0,再怎麼交配也不會出現 1。突變能把這種「已經消失的基因」再找回來。 以 XOR 實作 Mutation by XOR 字罩 XOR 字罩 XOR 的性質: 、 。所以只要產生一個與族群同大小的字罩,每個位置以突變率為 1,再和族群做 XOR,字罩為 1 的位元就反轉、其餘不變: 注意這裡的突變率是「每個位元」的機率:染色體長度 20、突變率 0.01 時,平均每個染色體有 0.2 個位元突變。可在 交配頁的互動示範 切到「突變」觀察,操作錄影見 Matlab 程式 。(說明為補充) 複製 (reproduction,也稱選擇 selection)比較每個物種的適應程度,決定它在產生下一代時應被淘汰或保留,被選中的放進 交配池 。常見兩種方式: 輪盤法 (roulette wheel selection):依各物種的適應值在輪盤上分配面積,適應值越大、面積越大。就像對輪盤射飛鏢,飛鏢落點對應的物種就被選進交配池;重複到選出與原族群相同的數量。每個物種被選中的機率為 。 競賽法 (tournament selection):每次隨機挑兩個(或多個)物種,其中適應值最大的進交配池,重複到數量足夠。計算量少、程式好寫,而且可以用每次挑選的個數控制競爭的激烈程度(挑越多個,弱者越難被選中),本課程的程式採用這種方法。 產生隨機整數 Random Integers 挑選物種時需要 1~ 的隨機整數。Matlab 的 rand 產生 0~1 的亂數, fix 無條件捨去小數( fix(0.8) = 0、 fix(1.1) = 1)。若 為正整數: 要產生 1~7 的隨機整數,取 = 1、 = 7,即 = 7: fix(7*rand)+1 。 競賽法程式 Tournament Selection Code 原講義附檔: 。輪盤圖與機率例為補充(四個物種的適應值 10、50、30、20 取自 保留最佳染色體 頁的 sort 範例)。 遺傳演算法(Genetic Algorithm,GA)模仿生物「物競天擇、適者生存」的演化:把要找的參數編碼成染色體,一次維持一整群候選解,經由 複製、交配、突變 一代代演化,讓適應值高的解越來越多。它不需要目標函數可微分,也不怕有很多局部極值,常用來搜尋控制器的參數(例如 PID 增益、模糊規則)。 本章內容 Contents 簡介 Introduction 隨機產生初始族群 Initial Population 參數解碼 Decoding 計算適應函數值 Fitness Function 複製 Selection 交配 Crossover 突變 Mutation 保留最佳染色體 Elitism 看 GA 怎麼找到最大值 Watch GA Evolve (補充)以 適應函數頁 的 為例,在 內找最大值。做法和本章的 Matlab 程式相同:每個變數 10 位元、競賽法複製、單點交配、逐位元突變、菁英保留。可以調族群數、交配率、突變率,觀察族群(白點)怎麼從隨機分布聚集到最高峰: 突變率調成 0:族群很快「全部長得一樣」,常卡在某個局部高峰——這就是突變存在的理由。 突變率調到 0.1:族群一直被打散,最佳值上上下下,平均適應值也很低。 關閉菁英保留:最佳值可能在某一代突然變差(最好的染色體在交配或突變中被破壞)。 GA 是機率性的方法:用預設參數重複 100 次,約有三分之二能在 60 代內爬上最高峰,其餘停在別的高峰( verify_ga.py 統計)。實務上會多跑幾次、或加大族群數。 參數建議 Parameter Settings 二進位編碼 GA 的參數,文獻中常見的建議值: 來源 族群數 交配率 突變率 De Jong(1975) 50~100 0.6 0.001 Grefenstette(1986) 30 0.95 0.01 Schaffer 等(1989) 20~30 0.75~0.95 0.005~0.01 編註:原講義寫作「Grenfenstette」,應為 J. J. Grefenstette。 範例與附檔 Examples 範例一 :以 8 位元編碼、20 個染色體,在 內求 的最大值(答案 ,即染色體全為 1)。整個流程寫在同一個檔案,適合對照本章各頁逐段閱讀。 範例二 、範例三 。 與 Simulink 模型結合:以 GA 搜尋 MCK 系統的 PID 增益 、 ;搜尋模糊控制器的三個比例因子(誤差、誤差變化、輸出) 。適應值的算法見 適應函數頁 。 投影片 、 ; 。 延伸閱讀: 、 ; Introduction to Genetic Algorithms(M. Obitko)↗ (原講義連結的捷克理工大學舊址已搬到這裡;另一個美國海軍研究實驗室的 GA Archive 已停止服務)。 本頁說明為補充:原講義的簡介頁只有三張控制系統方塊圖(已移到 控制系統 )和四個主題名稱。 控制 的目標是讓系統的輸出(轉速、位置、溫度、水位……)照我們的意思變化。傳統做法先建立受控體的 數學模型 ,再依模型設計控制器,例如 PID。當系統太複雜、模型不準或根本寫不出來時,可以改用模仿人類或生物的方法,統稱為 智慧型控制 (intelligent control): 方法 靈感來源 在控制上的用途 傳統 PID 控制 回授理論 比較的基準:比例、積分、微分三項回授 模糊控制 人類用語意規則推理 把「誤差大且在變大 → 用力推」這類經驗寫成規則,不需精確模型 遺傳演算法 生物演化:選擇、交配、突變 搜尋最佳的控制參數(例如 PID 增益、模糊規則) 粒子群最佳化 、 蟻群最佳化 鳥群、蟻群的群體行為 同上,另一類全域最佳化方法 類神經網路 神經元與學習 由資料學習輸入輸出關係,用來建模、預測或直接當控制器 本課程依這個順序進行:先複習 控制系統 的基本架構(開迴路、閉迴路、數位控制)與 PID 控制 ,再介紹各種智慧型方法,並以 Matlab/Simulink 實作。動力學與建立數學模型的部分見左側「相關課程」。 現代的控制器幾乎都是數位的:感測訊號經 A/D 取樣進電腦,控制演算法(不論是 PID、模糊規則還是類神經網路)算出命令,再經 D/A 驅動致動器。智慧型控制改變的只是中間那一塊「控制演算法」。 (原講義為 Matlab 手冊的英文截圖,以下改寫為中文,程式碼改成可複製的文字。) 用 newp 建立感知器 newp PR R × 2 矩陣,每一列是一個輸入元素的最小值與最大值(共 R 個輸入元素) 感知器通常使用 hardlim 轉移函數,所以它是預設值。下面建立一個感知器:單一個只有一個元素的輸入向量、一個神經元,輸入範圍為 [0 2]: 檢視建立的網路 Inspecting the Network 預設的學習函數是 learnp ,見 感知器的學習規則 。 權值函數 dotprod 計算權值矩陣與輸入向量的乘積 。 預設的初始化函數 initzero 把權值的初值設為 0。 可見偏權值的預設初值也是 0。 編註:原文說 dotprod「產生輸入向量與權值矩陣的乘積, 並加上偏權值 ,得到淨輸入」。其實 dotprod 只負責 ;加上偏權值的是層的淨輸入函數 netsum 。課程附檔可以佐證:NnetAnd.mat 裡 newp 建立的網路,層的 netInputFcn 是 'netsum' ;由它產生的 Simulink 模型 NetAndmdl.mdl 中,dotprod 方塊之後另有一個名為 netsum 的加法方塊,才把 b 加進來(見 NN2Simulink )。 (補充)以上是原講義所用工具箱版本的寫法。新版 Matlab(Deep Learning Toolbox)改用 perceptron 函數建立感知器, newp 已不建議使用。 (原講義為 Matlab 手冊的英文截圖,以下改寫為中文。)函數 init 可以把網路的權值與偏權值重設為初始值。 重設為初值 init 從下面的網路開始,檢查權值與偏權值,果然都是 0: 把權值設成 3 與 4、偏權值設成 5,再檢查一次,確實已經改變: 用 init 把權值與偏權值重設回原來的值,再檢查: 改用亂數初始化 rands 感知器的初始化方式可以更改。例如把輸入權值與偏權值的 initFcn 改成 rands ,再執行 init : 檢查權值與偏權值,可以看到它們被設成亂數: (補充) rands 產生 −1 到 1 之間的均勻亂數,所以每次執行的數字都不同,上面只是原講義那一次的結果。感知器用 0 當初值就能訓練;後面的 倒傳遞網路 則必須用亂數初值:若所有隱藏神經元的權值都相同,它們的梯度也相同,訓練後仍會一模一樣,等於只有一個神經元。 老師錄製的操作示範:建立並訓練一個倒傳遞(backpropagation)網路,再轉成 Simulink 模型。附檔為訓練好的網路 與產生的模型 。 為什麼要多層 Why Multiple Layers (以下為補充)單層感知器只能解線性可分的問題,連 XOR 都解不了。多層網路加上非線性的隱藏層就沒有這個限制,但感知器規則只能訓練輸出層:隱藏層的神經元沒有「目標值」可以比較。 倒傳遞演算法 用微積分的連鎖律,把輸出端的誤差一層層往回分攤到隱藏層,再用梯度下降調整所有權值。為了能微分,隱藏層改用 S 形轉移函數(tansig、logsig),而不是 hardlim。 附檔的網路 The Network in NetBp.mat (以下讀取附檔整理)NetBp.mat 是 2 個輸入、3 個 tansig 隱藏神經元、1 個 purelin 輸出神經元的網路: 輸入範圍 p₁:−1~2、p₂:0~5 轉移函數 第 1 層 tansig(3 個)、第 2 層 purelin(1 個) 初始化 initnw(Nguyen–Widrow) 訓練函數 trainFcn traingd(梯度下降);檔中參數 lr = 0.01、epochs = 100、goal = 0 誤差函數 performFcn mse(均方誤差) 輸入範圍、層數與轉移函數,都和 Matlab 手冊的 newff 範例 newff([-1 2; 0 5],[3,1],{'tansig','purelin'},'traingd') 相同。手冊範例的訓練資料是 、 ;把附檔的權值代入這四點,輸出為 −0.91、−0.95、1.05、0.96,mse = 0.0035( verify_nn2.py 核對),這個網路應該就是用這組資料訓練的。 NetBpmdl.mdl 的結構和 AND 感知器的模型 相同,只是有兩層:Layer 1 用 tansig、Layer 2 用 purelin,層間以 Goto/From 方塊傳遞 a{1}。模型的常數輸入是 [0.62; 0.79],輸出 y = 0.694。 倒傳遞演算法 The Algorithm (補充)以這個兩層網路為例,前向計算: 對 Q 筆訓練資料的均方誤差 。定義各層的 敏感度 ,由輸出層往回算(tansig 的導數為 ,purelin 的導數為 1): 再以梯度下降更新(批次訓練時把各筆資料的梯度加總後更新一次,α 為學習率 lr): 互動訓練 Try It 下方動畫用同樣的 2–3–1 結構與上面的公式,從亂數初值開始做批次梯度下降。左圖是網路輸出隨輸入的變化,右圖是 mse 隨回合數下降的情形: 手冊範例 :學習率 0.05 時,幾十到一百多回合 mse 就降到 0.01 以下(依初值而定)。 XOR :2 個以上的隱藏神經元就能解;只有 1 個時,mse 卡在 0.17 左右,因為輸出只能沿單一方向單調變化。 學習率 :手冊範例把學習率調到 1 左右,幾步之內權值就爆掉(發散)。學習率太小則收斂很慢。 換一組初值 :不同的亂數初值,收斂的速度與最後的曲面形狀都不同。 單一純量輸入的神經元 Simple Neuron (原圖取自 Matlab 手冊,以下說明為補充)最簡單的神經元只有一個純量輸入 。輸入先乘上 權值 ,再送進 轉移函數 得到輸出 。加上 偏權值 (bias) 的神經元,則把 與 相加成 淨輸入 ,再經轉移函數: 偏權值可以看成一個輸入恆為 1 的權值。它讓 平移:即使 ,淨輸入仍是 。 與 都是可調整的參數,類神經網路的「學習」就是依某種規則調整它們,讓網路表現出想要的行為。 轉移函數 Transfer Functions 原講義列出三種最常用的轉移函數: 函數 定義 值域 常用於 hardlim 硬限制 ( ); ( ) {0, 1} 感知器:把輸入分成兩類 purelin 線性 任意實數 線性網路、多層網路的輸出層 logsig 對數 S 形 (0, 1) 倒傳遞網路的隱藏層(可微分) tansig 正切 S 形(補充) (−1, 1) 倒傳遞網路的隱藏層 每個函數右邊的小方塊圖示,就是網路圖中代表該轉移函數的符號。注意 hardlim 在 時輸出 1。 互動神經元 Try It 原講義建議執行 Matlab 示範程式 nnd2n1 (一個輸入)與 nnd2n2 (兩個輸入)。下方動畫提供相同的實驗:調整輸入、權值、偏權值與轉移函數,觀察淨輸入 與輸出 。切到「兩個輸入」時,右圖顯示整個輸入平面的輸出,黃線 就是之後 感知器的決策邊界 : 向量輸入的神經元 Neuron With Vector Input (原講義為 Matlab 手冊的英文截圖,以下改寫為中文。)一個神經元可以有 R 個輸入 ,各乘上權值 後送到加總點。它們的和就是單列矩陣 與向量 的內積 : 在 Matlab 中可以直接寫成 n = W*p + b 。不過實際使用時很少寫到這麼底層,因為建立與模擬整個網路的函數已經內含這些計算。兩個輸入的情形可以在 簡單神經元頁的互動動畫 中實驗(原講義建議的 Matlab 示範為 nnd2n2 )。 權值 的兩個下標:第一個 是 接收的神經元 ,第二個 是 來源的輸入 。所以 是第 2 個輸入到第 1 個神經元的連結強度。 一層神經元 A Layer of Neurons R 個輸入、S 個神經元的單層網路如下。每個輸入都接到每個神經元,所以權值排成 S × R 的矩陣 : 神經元很多時,改用 簡化畫法 :輸入向量 畫成一條粗線,權值、偏權值各畫成一個方塊,方塊下方標出矩陣大小(列 × 行)。 多層網路 Multiple Layers of Neurons 網路可以有好幾層,每一層有自己的權值矩陣、偏權值向量與輸出向量,以上標表示層號。第 1 層接收外部輸入,它的權值稱為 輸入權值 ;其他層接收前一層的輸出,權值稱為 層間權值 ,例如第 1 層到第 2 層為 。三層網路的簡化畫法: 輸出就是網路結果的那一層稱為 輸出層 ,其餘稱為 隱藏層 。(補充)只要隱藏層使用 S 形這類非線性轉移函數,兩層網路在隱藏層神經元夠多時,就能以任意精度近似有界區域上的連續函數;若每一層都是線性的,多層相乘後仍等於一層線性網路,多加層數沒有好處。 老師錄製的操作示範:用 Neural Network Toolbox 的圖形介面 NNtool 建立並訓練一個感知器,學會 AND 閘。結果存在附檔 。 附檔內容 What Is in NnetAnd.mat (以下讀取附檔整理,為補充)NnetAnd.mat 有三個變數:訓練資料 p 、 t 與訓練好的網路 NetAnd 。 輸入範圍 [0 1; 0 1] 兩個輸入,各為 0~1 層 1 個神經元,hardlim,淨輸入函數 netsum 單一感知器 權值、偏權值初始化 initzero 初值 0 學習函數 learnp 感知器學習規則 訓練函數 trainFcn trainc(epochs 100、goal 0) 逐筆循環訓練 誤差函數 performFcn mae 平均絕對誤差 訓練後的權值 、 決策邊界 這組權值正是 感知器學習規則互動示範 中,AND 從 0 開始、依序輸入學到的結果(第 6 回合收斂)。檢查四個輸入:(0,0)、(0,1)、(1,0) 的淨輸入分別為 −3、−2、−1,輸出 0;(1,1) 的淨輸入為 0,因為 hardlim(0) = 1,輸出 1,四個都對。分界線剛好通過 (1,1),對雜訊沒有任何餘裕;若要更穩健,可以再把 b 往負方向調一點(例如 b = −2.5),但感知器規則一旦全部分對就停止,不會自己去找這種「中間」的邊界。 訓練好的網路可以轉成 Simulink 模型,見下一頁 NN2Simulink 。 類神經網路的應用可分為以下四大類: 類別 訓練範例 學到什麼、怎麼用 例子 一、監督式應用 有輸入值,也有對應的輸出值 學習輸出變數與輸入變數的關係,再依新案例的輸入推論其輸出 疾病診斷、股價預測、系統鑑別 二、非監督式應用 只有輸入值 學習輸入變數的聚類規則,再判斷新案例屬於哪一類;可作為監督式應用的前端處理 資料分群(補充) 三、聯想式應用 完整的範例 學習範例的內在記憶規則,對只有不完整狀態變數的新案例,推論出完整的狀態變數 以小時候的照片訓練、用長大後的照片辨識;資料擷取、雜訊過濾 四、最佳化應用 — 決定問題的設計變數,在滿足設計限制下使設計目標達到最佳 排程、路徑規劃(補充) (補充)本課程接下來介紹的 感知器 與 倒傳遞網路 都屬於監督式:訓練時必須提供目標輸出。在控制上,監督式網路常用來做 系統鑑別 (用量測的輸入輸出資料學出受控體模型)與學習既有控制器的行為。 老師錄製的操作示範:把 上一頁 訓練好的 AND 感知器( )轉成 Simulink 模型,結果為 。 產生的模型 Generated Model (以下讀取附檔整理,為補充)Neural Network Toolbox 的 gensim 函數可以把網路物件產生成 Simulink 方塊。NetAndmdl.mdl 的方塊都引用工具箱的 neural 方塊庫,結構一層層展開如下: 層次 方塊 內容 最上層 p{1}(常數)→ Neural Network → y{1}(Scope) 輸入是常數 [0.95; 0.23] Neural Network p{1} → Layer 1 → y{1} 只有一層 Layer 1 Delays 1、IW{1,1}、b{1}、netsum、hardlim 輸入權值方塊 IW{1,1} 內是常數 [2; 1] 與 dotprod 方塊;b{1} 是常數 −3;netsum 把兩者相加後送進 hardlim 模型的輸入 的淨輸入為 ,所以 Scope 上的輸出是 0:點 (0.95, 0.23) 雖然很接近 (1, 0),但它不是 (1, 1),AND 的結果為 0(見上一頁圖中的橘色叉號)。 模型的方塊對應到網路的運算:dotprod 方塊算 ,netsum 方塊再加上 b,hardlim 方塊輸出 。這也說明了 建立感知器 頁的編註:加偏權值的是 netsum,不是 dotprod。 網路轉成 Simulink 方塊後,就能和受控體模型接在一起模擬,例如把訓練好的網路當成控制器或系統模型。 倒傳遞網路 的附檔 NetBpmdl.mdl 也是同樣的做法。 (原講義節錄 Matlab Neural Network Toolbox 手冊的英文說明,以下改寫為中文。) 類神經網路由許多平行運作的簡單元件組成,這些元件的構想來自生物的神經系統。和自然界一樣,網路的功能大致由元件之間的連結決定。調整元件間連結的數值( 權值 ,weights),就能訓練網路執行特定的功能。 監督式學習 Supervised Learning 最常見的做法是調整(訓練)網路,使某個輸入得到指定的目標輸出:比較網路的輸出與目標,依差異調整網路,直到輸出符合目標。通常要用很多組「輸入/目標」配對來訓練,這就是監督式學習。 非監督式學習與直接設計 Unsupervised Training 監督式訓練雖然常用,也有網路是以非監督式訓練或直接設計得到的。非監督式網路可用來找出資料中的群組(分群);某些線性網路與 Hopfield 網路則是直接計算出權值,不需要反覆訓練。設計與學習的方法有很多種,使用者可依問題選擇。 批次訓練與逐筆訓練 Batch and Incremental Training 方式 何時更新權值與偏權值 批次訓練(batch) 把整組(一批)輸入向量都看過一遍,依全部結果更新一次 逐筆訓練(incremental) 每看一個輸入向量就更新一次;又稱線上(on-line)或適應式(adaptive)訓練 (補充) 感知器學習規則 的互動示範就是逐筆訓練:每輸入一個樣本,分錯就立刻修正權值。 感知器神經元 Perceptron Neuron 感知器(perceptron)是最早的類神經網路之一。每個外部輸入乘上對應的權值 ,加總後送進 硬限制轉移函數 (hardlim);硬限制函數另外還經由偏權值接收一個恆為 1 的輸入: (補充)因為輸出只有 0 與 1,感知器把輸入空間切成兩半: 的一側輸出 1,另一側輸出 0。分界 在兩個輸入時是一條直線、三個輸入時是一個平面。也因此,單層感知器只能分類 線性可分 的問題,詳見 學習規則頁的決策邊界 。 感知器網路 Perceptron Architecture (原講義為手冊英文截圖,以下改寫為中文。)感知器網路是一層 S 個感知器神經元,經由權值 連到 R 個輸入。和前面一樣,下標 、 表示 是第 個輸入到第 個神經元的連結強度。完整畫法與 一般的單層網路 相同,只是轉移函數都換成 hardlim;簡化畫法如下: R 為輸入的元素個數, 為第 1 層的神經元個數。(補充)S 個神經元各自畫一條分界線,輸出是 S 個 0/1 組成的向量,最多可表示 種類別;但實際能切出幾個區域還受輸入維度限制,例如兩個輸入時,3 條直線最多只能把平面切成 7 區。 (原講義節錄 Matlab 手冊的英文說明,以下改寫為中文;公式改以 LaTeX 重排。) 感知器是用「期望行為的例子」來訓練的。期望行為可以整理成一組輸入/輸出配對 其中 是網路的輸入, 是對應的正確輸出(目標)。訓練的目的是縮小誤差 ,也就是神經元輸出 與目標 的差。 Matlab 的感知器學習函數 learnp 根據輸入向量 與誤差 ,算出權值與偏權值該改多少。因為硬限制函數只能輸出 0 或 1,目標 也只能是 0 或 1。每執行一次 learnp ,感知器就更有機會輸出正確結果。 只要解存在(問題線性可分),感知器學習規則保證在有限次迭代內收斂 。 三種情況 Three Cases 輸入一個向量 、算出輸出 後,單一神經元會遇到三種情況: 情況 輸出 誤差 e 權值的變化 Δw 效果 1. 分對 a = t 0 0(不改) — 2. 應為 1 卻輸出 0 a = 0、t = 1 +1 w 轉向 p,下次較可能把 p 判為 1 3. 應為 0 卻輸出 1 a = 1、t = 0 −1 w 轉離 p,下次較可能把 p 判為 0 若沒有偏權值, learnp 只改變權值向量 w,讓它指向該判為 1 的輸入、遠離該判為 0 的輸入,最後得到一條與 w 垂直、能正確分類的決策邊界。 學習規則 The Rule 三種情況可以合寫成一個式子。偏權值就是輸入恆為 1 的權值,所以同樣處理: 一層有多個神經元時,誤差與偏權值都是向量: 總結感知器學習規則: 決策邊界 Decision Boundary 決策邊界 L 是 的直線,把輸入平面分成兩個區域。L 與權值向量 垂直,並依偏權值 平移。下圖中 、 、 :L 左上方的輸入使淨輸入大於 0,硬限制神經元輸出 1;右下方的輸入輸出 0。 選擇不同的權值與偏權值,就能把分界線轉到任何方向、移到任何位置。沒有偏權值時,分界線一定通過原點(圖中灰色虛線);加上偏權值後,感知器才能分開不在原點兩側的兩組輸入。 原圖把線上的點標為「Wp + b = 0,a = 0」。但 hardlim 的定義是 時輸出 1,所以線上的點應該是 a = 1 ,本站的圖已改正。這一點有實際影響:下方 AND 的例子學到的分界線剛好通過 (1, 1),正是靠 hardlim(0) = 1 才分對( verify_nn.py 核對)。 原講義多神經元的偏權值公式寫成 ,大寫 E 應與前後一致寫作小寫粗體 。 互動示範 Try It (補充)從 、 開始,依序輸入各樣本並套用學習規則。逐筆看每一步屬於哪種情況、權值怎麼變、分界線怎麼移動: 資料 結果(依序輸入、初值為 0) AND 修正 11 次,第 6 回合全部分對: 、 OR 修正 5 次,第 4 回合全部分對: 、 六點 修正 3 次,第 2 回合全部分對 XOR 永遠不收斂:找不到一條直線能把 (0,1)、(1,0) 與 (0,0)、(1,1) 分開 XOR 說明了單層感知器的極限:只能分類線性可分的問題。要解 XOR 這類問題,需要多層網路,以及能訓練隱藏層的方法,也就是後面的 倒傳遞網路 。 (原講義為 Matlab 手冊的英文截圖,以下改寫為中文。)用一個簡單的問題說明 sim 怎麼運作。 建立網路並設定權值 Setting Up 取一個兩元素輸入向量的感知器,和 決策邊界圖 中的一樣: 如 前一頁 所述,這樣建立的權值與偏權值都是 0。若要特定的值,必須自己設定。把兩個權值與一個偏權值設成決策邊界圖中的 −1、1 與 1: 看網路對分界線兩側的兩個訊號各有什麼反應: 果然,感知器把兩個輸入分類正確。(補充)手算淨輸入: 1(分界線左上方) 0(分界線右下方) 也可以把兩個輸入排成序列(cell 陣列)一次送進去,得到的輸出也是序列: 可以在 簡單神經元頁的互動動畫 中設定 w₁ = −1、w₂ = 1、b = 1、hardlim,再把輸入拉到這兩點驗證。 (原講義為 Matlab 手冊的英文截圖,以下改寫為中文,公式改以 LaTeX 重排。) 反覆用 sim 把輸入送進感知器、再用 learnp 依誤差修改權值與偏權值,只要問題是感知器 解得了 的,最後一定能找到解。把全部訓練輸入與目標走過一遍,稱為一個 pass (回合)。 函數 train 就是執行這個迴圈:每個回合依序處理指定的輸入,對每個輸入向量計算輸出、誤差,並調整網路。 注意 train 並不保證訓練出來的網路一定有效。得到新的 與 後,必須對每個輸入向量計算網路輸出,確認是否都達到目標。若沒有成功,可以再呼叫 train 多訓練幾個回合;或是分析問題,看它是否適合用感知器解(感知器解不了的問題,例如 XOR )。 手算一遍 Working Through a Simple Problem 以一個神經元、兩元素輸入向量的感知器為例(同 感知器神經元 ,R = 2),要解下面的分類問題: 用預設的初值 、 ;括號中的數字表示第幾步。第一個輸入: 輸出不等於目標 ,用感知器規則依誤差修正: 這次目標是 1,誤差為 0,權值與偏權值不變: 、 。照這樣繼續下去,全部步驟如下(補充:以 verify_nn2.py 逐步核對): 步 回合 輸入 n a t e 之後的 W 之後的 b 1 1 p₁ 0 1 0 −1 [−2 −2] −1 2 1 p₂ 1 1 1 0 [−2 −2] −1 3 1 p₃ −1 0 0 0 [−2 −2] −1 4 1 p₄ −1 0 1 +1 [−3 −1] 0 5 2 p₁ −8 0 0 0 [−3 −1] 0 6 2 p₂ −1 0 1 +1 [−2 −3] 1 7 2 p₃ −1 0 0 0 [−2 −3] 1 8 2 p₄ 0 1 1 0 [−2 −3] 1 9~12 3 p₁~p₄ −9、5、−1、0 0、1、0、1 0、1、0、1 全為 0 [−2 −3] 1 走完第一回合得到 、 。再用全部輸入檢查一次,第 4 個以外並不都對;演算法在第 6 次輸入時收斂,最後的值為 、 。 原講義第二步的式子把 寫成 ,應為 。巧的是用錯的向量算出 ,結果同樣是 hardlim = 1,所以後面的數字都對。上面已改正。 第 1 步與最後的 都剛好 n = 0,依 hardlim(0) = 1 才得到表中的結果:最後的邊界正好通過 。 用 train 訓練 Using train 下面的程式建立如上的感知器,初值為 0。先只看一個輸入 、目標 ,並把 epochs 設為 1,讓 train 只走過輸入一次: 只用第一個向量訓練後,權值與偏權值為 [−2 −2] 與 −1,和手算一樣。接著輸入 ,輸出是 1;目標也是 1,誤差為 0,不會改變。可以這樣一個接一個輸入,但用 train 可以自動完成。用全部四個輸入訓練一個回合: 與手算第一回合的結果相同。模擬訓練後的網路: 輸出還不等於目標,需要訓練更多回合。試試四個回合: 網路在第三回合輸入時已經訓練完成。手算時網路在第 6 次輸入(第二回合中間)就收斂了,但要到第三回合把每個輸入都檢查過才能確認。最後的權值與偏權值為 w = [−2 −3]、b = 1,模擬輸出 a = [0 1 0 1],誤差全為 0,訓練成功。 原文說 newp 建立的網路預設訓練函數是 trains (可用 net.trainFcn 查看):它以最純粹的形式套用感知器規則,依序逐一輸入,每輸入一個向量就修正一次。因此除非問題是單層感知器解不了的,用 train 訓練感知器一定會在有限步內收斂。 編註:同一頁裡訓練過程顯示的是 TRAINC ,結尾卻說預設訓練函數是 trains 。課程附檔 NnetAnd.mat(以 newp 建立)可以釐清: net.trainFcn = 'trainc' 、 net.adaptFcn = 'trains' 。所以用 train 時跑的是 trainc(逐筆的循環訓練),trains 是 adapt 用的。另外原文的誤差寫成 a(i) − t(i),與前面 e = t − a 的定義正負相反;這裡全為 0,不影響結論。還有,原文說「試試四個回合」,輸出卻顯示 Epoch 0/20(上限 20 回合),兩者不一致;原文沒有附這一段的程式碼。 類神經網路 (neural network)由許多簡單的運算單元(神經元)平行連結而成,靈感來自生物的神經系統。網路的功能主要由神經元之間的連結強度(權值)決定;給網路看大量的例子並調整權值,就能讓它學會分類、預測、近似非線性函數等工作。(說明為補充) 基本觀念 Fundamentals 簡介 Introduction:監督式與非監督式學習、批次與逐筆訓練 應用 Application:四大類應用 簡單神經元 Simple Neuron:權值、偏權值、轉移函數 向量輸入與多層網路 Neuron With Vector Input:矩陣寫法、單層與多層 感知器 Perceptron 感知器神經元模型 Perceptron Neuron Model 建立感知器 Creating Perceptron(newp) 模擬 Perceptron Simulation(sim) 初始化 Perceptron Initialization(init) 學習規則 Perceptron Learning Rule(learnp) 訓練 Training(train) 工具與範例 Tools and Examples NNtool 用圖形介面訓練 AND 閘感知器 NN2Simulink 把網路轉成 Simulink 方塊 倒傳遞網路 Backpropagation:2–3–1 網路與梯度下降 參考書籍 References Bart Kosko, Neural Networks for Signal Processing . 羅華強,《類神經網路——Matlab 的應用》。 周鵬程,《類神經網路入門——活用 Matlab》。 葉怡成,《類神經網路模式應用與實作》。 The MathWorks, Neural Network Toolbox for Use with Matlab: User's Guide . 原講義第 1 本的作者寫作「Bark Kosko」,應為 Bart Kosko。 在微控制器普及以前,PID 是用 運算放大器 (OP Amp)做的:每個運算放大器配上電阻、電容,就能實現加減、放大、積分、微分。(電路依原講義重繪,分析為補充;運算放大器的基本電路見 運算放大器(舊站)↗ ) 類比 PID 控制器 Analog PID Controller 理想運算放大器的兩個輸入端電壓相等、不吃電流,反相放大器的增益就是「回授阻抗/輸入阻抗」再加一個負號。逐級來看: U₁ 差動放大 :四個電阻相等時輸出 ,即誤差。 U₂ 比例 :反相放大, 。 U₃ 積分 :回授是電容, 。原圖在電容旁並聯一個大電阻 ,限制極低頻的增益,避免偏移電壓把積分器慢慢推到飽和。 U₄ 微分 :輸入是電容, 。原圖在電容後串一個小電阻,限制高頻增益,否則雜訊會被無限放大。 U₅ 加總 :反相加法器(輸入電阻都為 ),三項加起來再反相回正: 原圖中 、 、 都畫成可變電阻:轉 調積分強弱、轉 調微分、轉 同時改變三項的整體增益——這就是類比控制器面板上那幾顆旋鈕。 例:流量的比例控制 Proportional Flow Control 管路流量(約 6 gal/min)由流量感測器轉成 0~5 V,控制器比較設定值後輸出 0~5 V 驅動流量閥(見原圖 (a))。控制器只用比例控制: U₁:四個 10 kΩ 的差動放大,輸出誤差 。設定值由 1 kΩ 可變電阻從 +5 V 分壓得到(圖中設為 3 V)。 U₂:反相加法器,輸入是誤差與偏壓 (各經 10 kΩ),回授 20 kΩ,輸出 。 U₃:增益 1 的反相器,把符號轉回正: 比例增益 = 20 kΩ/10 kΩ = 2;偏壓決定誤差為零時閥的開度。例如希望流量剛好在設定值時閥門電壓為 2.5 V,就把 調到 1.25 V;若流量變小使感測器降到 2.5 V,誤差 0.5 V,閥門電壓升到 = 3.5 V,閥開大、流量回升。只有比例控制時,負載一變就會留下穩態誤差,要靠手動調整偏壓(早期稱為 manual reset),這正是後來加入積分項(automatic reset)的原因。 用電腦或微控制器實作 PID 時,誤差每隔一個 取樣週期 才讀一次,連續的積分與微分要改成用取樣值近似。(說明依原講義改寫) 三項的離散化 Discretizing P, I, D 記第 次取樣的誤差為 。 比例 :直接相乘, 。 積分 :把每一段的面積累加起來。用矩形近似: 用梯形近似(取前後兩點的平均)更準,SP 或 PV 突然改變時積分項也比較平順: 微分 :用相鄰兩點的斜率, 。設定值突然改變時 會非常大,造成輸出瞬間暴衝( 微分踢 ,derivative kick)。因為設定值通常是常數, ,所以實務上改成只對量測值微分: 輸出 : ,再限制在致動器能接受的範圍:超過 就取 ,低於 就取 。輸出被限制時要同時停止積分(anti-windup),否則積分項會一直累積,誤差反向後要很久才「退得回來」。 編註:原講義梯形積分一段的文字提到「誤差越大、積分作用越小」的非線性調整,但所列公式只是單純的梯形積分,並沒有包含這個非線性因子。原講義的流程圖則採理想式:先算 ,最後才乘上 ,其中的 、 相當於 、 。下面的流程與程式採並聯式(三項各有增益),兩者只差增益的定義。 每個取樣週期的計算流程 Algorithm Flow 取樣週期要遠小於受控體的時間常數(常取閉迴路上升時間的 1/10 以下);太慢的話,上面的近似失真,閉迴路會變差甚至不穩定。 原講義附檔: 、 。 這段是老師錄製的 Simulink 操作示範(RoboDemo 錄影,以 Ruffle 播放):在 Simulink 中建立 PID 控制迴路並觀察響應。 練習 Practice 開啟 :以訊號產生器驅動自建的 PID 子系統(三個增益 P、I、D,分別接比例、積分器、微分器後加總),對照示範操作一次。 練習自行製作 中的 MCK_System :質量–彈簧–阻尼器( )的子系統。 以 為 MCK 系統加上 PID 位置控制,比較不同 、 、 的響應; 把 PID 展開在最上層,比例、積分、微分三項各接一個示波器,可以分別看每一項的輸出。 提示:MCK 系統的轉移函數是 ,和 直流馬達例題 一樣是二階系統,P、I、D 三項的影響趨勢相同(見 PID 主頁的表格 )。(提示為補充) Matlab 的 SISO Design Tool ( sisotool )可以在根軌跡圖與波德圖上用滑鼠拖拉極點、零點與增益,即時看到閉迴路響應,很適合用來設計 PID。以下是原講義的操作步驟(截圖為 Matlab 6.5 版,新版 Control System Designer 的操作邏輯相同;步驟說明為改寫的中文)。範例受控體: 操作步驟 Steps 在命令視窗建立受控體並開啟工具: sys = tf([1],[1 1 2]) ,再輸入 sisotool 。 File → Import ,從 Workspace 選 sys ,按 G 欄位旁的箭頭匯入成受控體 G(H、F、C 先維持 1)。 匯入後左邊是根軌跡、右邊是開迴路波德圖。受控體的兩個極點在 ,目前控制器 。 PID 可以寫成「一個積分器+兩個零點」: 所以先在原點加一個極點(積分器):在根軌跡上按右鍵加實數極點,再到 Edit Compensator 把位置改成 0。 再加一對共軛複數零點(約在 ),把根軌跡往左拉,讓閉迴路極點遠離虛軸。 拖動根軌跡上的粉紅色方塊(閉迴路極點)調整增益,這裡增益調到 5.42,相位邊限 51.7°。 Analysis → Response to Step Command 看閉迴路步階響應:超越量約 13%、約 1.1 s 到峰值、穩態誤差為零。 換算成 PID 增益 Converting to PID Gains 最後的控制器是 。對照第 4 步的形式(補充): 以 verify_pid.py 用這組增益重新模擬,步階響應的峰值與峰值時間和第 7 步的截圖一致。 PID 控制器是工業上用得最多的回授控制器:溫度、流量、壓力、馬達轉速與位置,大多數迴路都是 PID。它由三項組成,所以也叫「三項控制器」(three-term controller)。(本頁文字為依原講義改寫的中文說明) 項 名稱 看的是 主要作用 P 比例 Proportional 現在的誤差 誤差越大推得越用力,決定反應快慢;單獨使用會留下穩態誤差 I 積分 Integral 過去累積的誤差 只要還有誤差就持續累加,消除穩態誤差,提供精確度 D 微分 Derivative 誤差變化的趨勢 提早踩煞車,增加阻尼、減少超越量;但會放大量測雜訊 PID 演算法 Algorithms 控制器比較設定值(SP)與量測值(PV)得到誤差 ,輸出 有兩種常見寫法。 理想式 (ISA 標準式)以一個增益 乘上整個括號,積分、微分強弱用時間常數表示: 把括號展開就是 並聯式 ,三項各有獨立的增益: 取拉氏轉換,控制器的轉移函數為 編註:原講義第二式寫成 ,第二個等號不成立—— 應等於 。原意是控制器 的轉移函數,如上式。 實作細節(取樣、梯形積分、微分只作用在量測值、輸出限制)見 數位實作 ;用運算放大器做的版本見 類比實作 。 例題:直流馬達轉速控制 DC Motor Speed Control 以原講義 DCMotorData.m 的參數建模:電感 = 0.5 H、電阻 = 1 Ω、轉子慣量 = 0.01 kg·m²、黏滯摩擦 = 0.1 N·m·s、反電動勢常數 = 轉矩常數 = 0.01。 穩態增益 = 0.01/0.1001 ≈ 0.0999 (rad/s)/V。 輸入 0.6 V,轉速只到 0.6 × 0.0999 ≈ 0.060 rad/s;在 = 4 s 加上負載(原模型以 = 3 rad/s² 加在角加速度上,即 = 0.03 N·m),轉速再掉 ≈ 0.30 rad/s,變成 −0.24 rad/s——馬達被負載拖著反轉,開迴路完全無法應付。 加上 PID 回授 設定值 1 rad/s 的步階響應(三組增益與原講義相同): =100、 =1、 =1:反應很快,但積分太弱,3 s 時只到 0.91,誤差要很久才消失。 加到 200:穩態誤差很快歸零,但超越量變成約 25%。 再把 加到 10:超越量降到約 12%,約 1.3 s 安定。 編註(補充):原講義的 Simulink 圖其實相當於「微分只作用在量測值」。Simulink 的步階方塊設在 = 0 時,一開始就輸出 1,微分方塊看不到那一下跳動,所以沒有「微分踢」。以 verify_pid.py 重算,三條曲線的峰值(1.08、1.25、1.12)與峰值時間都和原圖吻合。若微分真的作用在誤差上(虛線),設定值的步階會在 產生一個電壓脈衝,曲線反而幾乎沒有超越量,但實際的驅動器根本送不出這種脈衝。這也是工業 PID 多半讓 微分只作用在量測值 的原因。 下面的模擬可以自己調整三個增益、切換微分的作用對象,並在 1.5 s 加上負載: 設定 0.6 rad/s、 = 4 s 加上同樣的負載:PID 迴路只讓轉速短暫下降,積分項很快就把它拉回 0.6 rad/s。原講義附檔: 、 、 、 、 。 P、I、D 各自的影響 Effects of Kp, Ki, Kd 增益加大 上升時間 超越量 安定時間 穩態誤差 縮短 增加 小幅變化 減小(不會歸零) 縮短 增加 增加 消除 小幅變化 減少 縮短 小幅變化 這張表只是趨勢:三個增益會互相影響,調了其中一個,另外兩個的效果也跟著變,只能當調整時的參考。設計 PID 的一般步驟: 先看開迴路響應,找出要改善什麼。 加比例控制縮短上升時間。 加微分控制壓低超越量。 加積分控制消除穩態誤差。 反覆微調 、 、 到滿意為止。 三項不一定都要用:PI 夠好就不必加 D,控制器越簡單越好。另見 Simulink 示範 、 用 SISOTool 設計 、 PID 與根軌跡(舊站)↗ 。 Ziegler–Nichols 調整法 Ziegler–Nichols Tuning Ziegler 與 Nichols 在 1942 年提出的經驗法則,至今仍是現場調 PID 的起點。以理想式 表示,分成兩種方法。為了說明,以下用範例受控體 示範(補充)。 反應曲線法(開迴路) 對受控體做步階測試,在響應的反曲點畫切線,量出延遲時間 與時間常數 ,並算出製程增益 ,令 : 範例: 的步階響應反曲點在 = 2 s,切線得 = 0.805 s、 = 3.695 s、 = 1, = 4.59,所以 PID 取 = 5.50、 = 1.61 s、 = 0.40 s。 臨界增益法(閉迴路) 只用比例控制(關掉積分、微分)。 逐步加大比例增益,直到輸出出現持續的等幅振盪,此時的增益稱為臨界增益 (原講義記為 )。 量出振盪週期 (原講義記為 )。 範例:閉迴路特徵方程式 ,以羅斯判別法得臨界 = 8,此時極點在 ,週期 = 3.63 s;PID 取 = 4.8、 = 1.81 s、 = 0.45 s。 Ziegler–Nichols 的目標是「每週期衰減到四分之一」,參數偏積極,通常會有不小的超越量,只能當起點,還要再微調。 延伸閱讀(原講義附檔): 、 、 、 ; Automation.com ↗ (原講義另列 ExperTune 公司網站,已無法連線)。 粒子群最佳化 (Particle Swarm Optimization,PSO)是 Eberhart 與 Kennedy 在 1995 年提出、以族群為基礎的隨機最佳化方法,靈感來自鳥群覓食與魚群游動的社會行為。(說明依原講義改寫) 想像一群鳥在某個區域隨機找食物,區域裡只有一處食物,沒有一隻鳥知道食物在哪裡,但每隻鳥都知道自己離食物有多遠。最有效的策略是什麼?跟著離食物最近的那隻鳥飛過去,同時也記得自己曾經到過最近的地方。 PSO 就把這個情境用在最佳化上:每一個候選解是搜尋空間中的一隻「鳥」,稱為 粒子 (particle)。每個粒子有一個由適應函數算出的適應值,以及決定飛行方向的速度。一開始隨機產生一群粒子,之後每一代(iteration)每個粒子都依兩個「最佳值」調整速度: pBest :這個粒子到目前為止找到的最佳位置(自己的經驗)。 gBest :整個族群到目前為止找到的最佳位置(群體的經驗)。 速度與位置的更新 Update Rules 記第 代第 個粒子的位置為 ( 個變數),速度為 ,則 :每次重新產生的 0~1 亂數(原講義寫作 rand()),讓粒子的行為帶點隨機性。 :加速常數,決定被 gBest、pBest 拉過去的強弱,常取 2。 : 慣性權重 ,保留多少上一代的速度。原講義投影片的公式沒有 (等於 ,即 1995 年的原始版本);附檔 Matlab 範例則用 Shi 與 Eberhart 在 1998 年提出的慣性權重,從 0.9 線性遞減到 0.4——前期大、到處探索,後期小、在好區域內細找。(補充) 另外通常限制速度不超過 (範例取搜尋範圍的一半),並把飛出範圍的粒子拉回邊界。 把 個粒子排成矩陣,就能一次更新整個族群: ,第 列是第 個粒子。 編註:原講義 gBest/pBest 定義圖中, 的第二個分量寫成 ,應為 。另外常見教科書多把 配 pBest、 配 gBest,原講義與附檔程式剛好相反;兩者通常取相同的值,結果沒有差別。 演算法流程 Pseudo Code 互動示範 Watch the Swarm (補充)照原講義 Matlab 範例二的設定:求 在 的 最小值 (程式以適應值 轉成最大化): 和 GA 比較:PSO 沒有交配、突變,只靠「記憶+跟隨」,粒子沿著連續的軌跡飛,直接在實數空間搜尋,不需要二進位編碼。 = 1:沒有慣性衰減,粒子一直在 之間來回衝,很難穩定下來;遞減的 讓粒子先探索、後收斂。模擬 200 次,40 代內找到最小值的比例: 遞減 94%, = 1 只有 3%。 編註:附檔程式限制速度時只檢查 if V(j,i) > Vmax(i) ,沒有處理負方向,往左、往下飛的粒子速度可以遠超過 (模擬中出現 −22,而 = 5)。應改為 V(j,i) = max(-Vmax(i), min(Vmax(i), V(j,i))) 。 verify_pso.py 以兩種寫法各跑 200 次:40 代內找到最小值的比例,雙向限制為 94%,原程式為 87%。上面的互動示範採雙向限制。 附檔與延伸閱讀 Files :範例一求 ( )的最小值,答案在 (4, 4)、 = −19.9667;範例二即上面的函數,答案 (9.039, 8.668)、 = −18.5547。每個範例都有等高線圖與 3D 曲面兩種動畫版本。 swarmintelligence.org ↗ (PSO 教學網站)。原講義另列 Eberhart《Swarm Intelligence》一書的網頁,該網域已不存在。