# 解析強化學習 ###### tags: `學習筆記` ## 前言 本文為個人閱讀 R.S Sutton - Reinforcement Learning: An Introduction 的筆記。如果覺得這個筆記幫助你理解強化學習,那是書寫得好,請買書(?)支持大神,讓大神繼續寫書。 ## 適合閱讀之對象 * 不了解強化學習,但具機器學習相關背景 * 想透過 **數學** 與 **程式碼** 了解強化學習演算法 --- ## 馬可夫鏈 (Markov chains) 在談強化學習之前,我們先討論讓強化學習可以收斂的關鍵 ─ 馬可夫矩陣。[Andrey Markow](https://en.wikipedia.org/wiki/Andrey_Markov) 是俄羅斯的一名數學家,主要研究興趣是[隨機程序](https://en.wikipedia.org/wiki/Stochastic_process)。特別由一連串相關的事件所組成的系統,會怎麼隨著時間變化的問題。 > Stochastic Process/隨機程序/隨機過程/隨機處理:討論[隨機變數](https://en.wikipedia.org/wiki/Random_variable)隨時間如何變化。 > [name=TCW][color=#00B2FF] 在 1906 年時,馬可夫定義一種稱為 **鏈 (chain)** 的觀念,指的是離散狀態集的(變化)過程,後世稱之為「馬可夫鏈」。以下我們分項說明馬可夫鏈中的一些專有名詞,往後出現粗體字,指的就是這些專有名詞。若非粗體字,則只是一般性的描述。 1. **state (狀態)**:一種攏統的稱呼,可以指很多東西。 ex: 人的動作 (站著、坐著)、電腦的狀態 (開機、關機) 2. **states set (狀態集)**:以集合表示所有狀態,記作 $S = \{s_0, s_1, s_2, ..., s_m\}$,初始狀態為 $s_0$。 3. **transition model (轉換模型)**:狀態之間有可能改變,改變規則依據狀態模型訂定,以機率表示。 4. **Markov property (馬可夫性)**:狀態的轉變,只與現在有關,與過去無關。 5. **steps (階段)**:狀態穩定(未改變)時,即為一個階段,狀態轉換發生於兩階段之間。 6. **process (過程)**:描述「狀態連續改變」一事。 > [Comment] > 1:數學家用簡單符號,表達複雜概念的一個例子... > 4:有關無關,指的是機率上的獨立 (independent) > 5:成立於時間離散型,這個定義在時間連續型馬可夫鏈不成立。 > [color=#00B2FF] 上述的專有名詞可能過於複雜,下列舉個例子說明: * 假設小明是一個很懶惰的人,平時只會有兩個**狀態**,躺著 ($s_0$) 跟坐著 ($s_1$)。觀察其**過程**,躺著的時候,有 $90\%$ 的機率繼續躺著,有 $10\%$ 會變成坐著;坐著的時候,有 $50\%$ 會變成躺著,有 $50\%$ 繼續坐著。 根據前面**過程**的描述,我們用矩陣表達小明的**轉換模型**,稱為 **transition matrix (轉換矩陣)**。 $$ T= \begin{bmatrix} 0.90 & 0.10 \\ 0.50 & 0.50 \end{bmatrix} $$ **轉換矩陣**一定是方陣 (square matrix),每個橫列表示某一原始**狀態**,轉換到其他**狀態**的機率,總和必定為 $1$。可以用條件機率表示成: $$ T= \begin{bmatrix} P(s_0|s_0) & P(s_1|s_0) \\ P(s_0|s_1) & P(s_1|s_1) \end{bmatrix} $$ 如果不喜歡數學,覺得「列式不能算懂……列式!……數學家的事,能算懂麼?」。那可以改用有向圖,表達小明的**過程**。用點表示狀態,用邊表示轉換機率,邊的粗細表示對應機率大小。 ![](https://i.imgur.com/KQGlwtZ.png) 接著我們討論時間的部分,前面提到的**階段**,即是馬可夫鏈計算時間的方式。隨著不斷前進到下個**階段** ,**狀態**也不斷地一直改變。在計算上,我們可以計算**轉換矩陣**的次方,表示現在是第幾**階段**,以下用程式碼舉例: ```python import numpy as np Matrix_trans = np.array([[0.9,0.1],[0.5,0.5]]) # after k steps Matrix_trans_step3 = np.linalg.matrix_power(Matrix_trans, 3) Matrix_trans_step5 = np.linalg.matrix_power(Matrix_trans, 5) Matrix_trans_step10 = np.linalg.matrix_power(Matrix_trans, 10) Matrix_trans_step50 = np.linalg.matrix_power(Matrix_trans, 50) Matrix_trans_step100 = np.linalg.matrix_power(Matrix_trans, 100) # print the result print('After 3 steps: \n', Matrix_trans_step3, '\n') print('After 5 steps: \n', Matrix_trans_step5, '\n') print('After 10 steps: \n', Matrix_trans_step10, '\n') print('After 50 steps: \n', Matrix_trans_step50, '\n') print('After 100 steps: \n', Matrix_trans_step100, '\n') ``` ``` After 3 steps: [[0.844 0.156] [0.78 0.22 ]] After 5 steps: [[0.83504 0.16496] [0.8248 0.1752 ]] After 10 steps: [[0.83335081 0.16664919] [0.83324595 0.16675405]] After 50 steps: [[0.83333333 0.16666667] [0.83333333 0.16666667]] After 100 steps: [[0.83333333 0.16666667] [0.83333333 0.16666667]] ``` 從運算的結果,我們發現隨著時間改變,轉移矩陣最終呈現一個定值。那麼如果我們現在給定起始**狀態** $s_{0} = [1, 0]$ 或是使用 numpy 給定任意**狀態**,計算結果會如何? ```python # succeeding the above scripts s0 = np.array([1.0, 0.0]) # print the result print('Initial state: \n', s0, '\n') print('After 3 steps: \n', np.dot(s0, Matrix_trans_step3), '\n') print('After 5 steps: \n', np.dot(s0, Matrix_trans_step5), '\n') print('After 10 steps: \n', np.dot(s0, Matrix_trans_step10), '\n') print('After 50 steps: \n', np.dot(s0, Matrix_trans_step50), '\n') print('After 100 steps: \n', np.dot(s0, Matrix_trans_step100), '\n') ``` ``` Initial state: [1. 0.] After 3 steps: [0.844 0.156] After 5 steps: [0.83504 0.16496] After 10 steps: [0.83335081 0.16664919] After 50 steps: [0.83333333 0.16666667] After 100 steps: [0.83333333 0.16666667] ``` ```python # succeeding the above scripts # random initial state rand_num = np.random.rand(1) random_s0 = np.array([rand_num, 1-rand_num]).reshape(2) # print the result print('Initial state: \n', random_s0, '\n') print('After 3 steps: \n', np.dot(random_s0, Matrix_trans_step3), '\n') print('After 5 steps: \n', np.dot(random_s0, Matrix_trans_step5), '\n') print('After 10 steps: \n', np.dot(random_s0, Matrix_trans_step10), '\n') print('After 50 steps: \n', np.dot(random_s0, Matrix_trans_step50), '\n') print('After 100 steps: \n', np.dot(random_s0, Matrix_trans_step100), '\n') ``` ``` Initial state: [0.6913491 0.3086509] After 3 steps: [0.82424634 0.17575366] After 5 steps: [0.83187941 0.16812059] After 10 steps: [0.83331845 0.16668155] After 50 steps: [0.83333333 0.16666667] After 100 steps: [0.83333333 0.16666667] ``` 由上述的計算,我們可以發現在符合某些情況 $^{[1]}$ 下,隨著時間的改變,**狀態**最終會收斂於定值。然而,強化學習是一個能夠與環境互動的方法,並不是給定一個**轉換矩陣**後,讓結果自動收斂。因此接著介紹由馬可夫鏈延伸出的概念 - 馬可夫決策過程 (Markov Decision Process) > $^{[1]}$以有向圖來說,必須滿足 > * 任一點可以抵達其他所有點 [[example]()] > * 路徑是非週期性 (不可以在固定的路徑上移動) [[example](https://hackmd.io/s/ry0EssCw7)] > > 違反範例:[[違反1:不完全收斂](https://hackmd.io/s/BJmEx20v7)], [[違反2:震盪](https://hackmd.io/s/ry0EssCw7)], [[違反1+2](https://hackmd.io/s/r1Kh6jRDm)] > [[reference](https://www.quora.com/How-do-I-tell-whether-a-Markov-chain-can-converge-to-equilibrium)] > [[math proof](http://www.tcs.hut.fi/Studies/T-79.250/tekstit/lecnotes_02.pdf)] > [name=TCW][color=#00B2FF] ## 馬可夫決策過程 (Markov Decision Process) 在強化學習中,我們考慮各種 **狀態** 後,再決定採用的動作,這個過程與上述的 Markov Chain 並不完全相同。不一樣的地方在於, Markov Chain 是依據 **轉換矩陣** 改變狀態,而在 Markov Decision Process 中,**轉移矩陣** 並不是固定的,受到 **狀態** 與 **動作** 的影響。組成 MDP 的部件如下: 1. **狀態集** $S = \{s_{0}, s_{1}, s_{2}, ..., s_{m}\}$ 2. **初始狀態** $s_0$ 3. **動作集** $A = \{a_{0}, a_{1}, a_{2}, ..., a_{n}\}$ 4. **轉移矩陣** $T(s,a,s')$ 5. **獎勵函數** $r(s,a,s')$ > 雖然這邊這樣定義獎勵函數,但它可能並不只與狀態和動作有關 > [name=TCW][color=#00B2FF] 跟上述的馬可夫鏈比起來,主要差異在於第 3,4,5 項。首先我們解釋 3 與 4,這邊導入了原本沒有的 **動作** ,而這個 **動作** 同時也作為 **轉移矩陣** 的輸入內容,並由計算產生移動到下一個狀態的機率。也就是說,「處於的狀態、採取的動作,都會影響我們移動到某個狀態的機率」。 * 回想小明的例子,當小明在躺著 ($s_{0}$) 與坐著 ($s_{1}$) 的同時,他會採取兩種動作,看漫畫 ($a_{0}$) 跟讀書 ($a_{1}$)。我們可以把上述的行為,表示成條件機率的形式。例如 * $P(a_{0}|s_{0})$ 表示小明躺著時,看漫畫的機率。 * $P(s_{0}|s_{0},a_{0})$ 表示原本小明躺著看漫畫,下一時刻還是躺著的機率。 * 關於前面提的兩個例子,我們需要額外補充一些說明 * 動作是由狀態決定的,這個決定是由 **agent**(無合適的翻譯,暫譯**代理人**) 決策。 * 條件機率只與現在的狀態、動作有關,與過去無關,仍然保持馬可夫性質。 這邊所提到的 **代理人**,是指在馬可夫決策過程中,我們不可能一直跟環境互動,所以需要 **代理人** 來代替我們跟環境互動。但是 **代理人** 不知道我們的偏好,所以每次行動後都需要由給予回饋 ( r ),告訴 **代理人** 這個動作是好的,要多多出現;或是這個動作是壞的,要減少出現。 而這個回饋,就由 **獎勵函數** 代勞了。透過 **獎勵函數** 在每次 **動作** 後給予正回饋或負回饋,進而讓 **代理人** 學習每個 **狀態** 的最佳 **動作**,產生最佳的決策 **策略**。用數學式來說,可以這個表達獎勵函數: * $R \propto r(s,a,s')$ 表示回饋與「現在狀態 $(s)$、動作 $(a)$、動作後狀態 $(s')$」三者是有關連的。 ### 整體來說 * 任務:我們要讓 **代理人** 在任何 **狀態**,進行最佳的 **動作**,獲得最高的正回饋。 * 解法:評估可以得到的正回饋,最高者即為最佳策略。 因此在做決定之前,我們需要先評估這個狀態,要採用哪個動作,可以得到的最好的回饋。我們稱這個評估得到回饋的函數為「**價值函數(Value funciton)**」 ## 價值函數 在講解價值函數之前,我們需要先定義幾項東西。 * 策略 $(\pi)$:將狀態與動作對應到機率的函數,表示某狀態下,採取某動作的機率,記做 $\pi(a|s)$ * 整體回饋 $(G_{t})$:我們將所有可以得到的回饋累加起來,以數學式表達: $$ G_{t} = R_{t+1} + R_{t+2} + R_{t+3} +...+ R_{T},其中\ T\ 表示最終動作。 $$ 然而,這樣定義遇到 $T \rightarrow \infty$ 的情況時,需稍微調整上式: $$ G_{t} = R_{t+1} + \gamma R_{t+2} + \gamma^{2} R_{t+3} +... = \sum^{T-t-1}_{k=0} \gamma^{k} R_{t+k+1} $$ 其中 $0 \leq \gamma \leq 1^{[1]}$ ,在 $T \rightarrow \infty ^{[1]}$ 的情況下, $\gamma^{k} \rightarrow 0$,可以理解 $\gamma$ 為對未來獎懲的重視程度。 * 當 $\gamma \rightarrow 0$,$G_{t} \rightarrow R_{t+1}$,只在乎現在的獎懲。 * 當 $\gamma \rightarrow 1$,$G_{t} \rightarrow R_{t+1} + R_{t+2} + R_{t+3} +...+ R_{T}$,在乎每一次的表現。 > [1] $\gamma=1$ 與 $T \rightarrow \infty$ 只會擇一成立。 > [name=TCW][color=#00B2FF] ### 價值函數定義 分成兩種 * $v_{\pi}(s)$:狀態價值函數 (state-value function)。用來估計在某個狀態下,可以得到多少回饋的函數。定義如下: $$ v_{\pi}(s) = \mathbb{E}_{\pi}[G_{t}|S_{t} = s] $$ 將 $G_{t} = \sum^{\infty}_{k=0} \gamma^{k} R_{t+k+1}$ 代入 $$ v_{\pi}(s) = \mathbb{E}_{\pi} [\sum^{\infty}_{k=0} \gamma^{k} R_{t+k+1} | S_{t} = s] $$ * $q_{\pi}(s,a)$:動作價值函數 (action-value function)。類似 $v_{\pi}(s)$,用於評估某狀態下,某動作可以得到多少回饋的函數。 $$ q(s, a) = \mathbb{E}_{\pi}[G_{t}|S_{t} = s, A_{t} = a] $$ 一樣將 $G_{t}$ 代入 $$ q(s, a) = \mathbb{E}_{\pi}[\sum^{\infty}_{k=0} \gamma^{k} R_{t+k+1} | S_{t} = s, A_{t} = a] $$ ### 計算價值函數 #### 動態規劃 - 貝爾曼方程 如此一來,我們有了評估狀態、動作的函式,此外使用上述的定義,我們可以將價值函數寫成遞迴的形式。例如: $$ v_{\pi}(s) = \mathbb{E}_{\pi}[G_{t} | S_{t} = s] $$ $$ = \mathbb{E}_{\pi}[\sum^{\infty}_{k=0} \gamma^{k} R_{t+k+1} | S_{t} = s] $$ $$ = \mathbb{E}_{\pi}[R_{t+1} + \gamma \cdot \sum^{\infty}_{k=0} \gamma^{k} R_{t+k+2} | S_{t} = s] $$ 將期望值展開 ($\mathbb{E}[X] = \sum p_{i} \cdot x_{i}$)、並代入上述數值 $$ = \sum_{a} \pi(a|s) \sum_{s'}p(s'|s,a)[r(s,a,s')+\gamma \cdot \mathbb{E}_{\pi} [\sum^{\infty}_{k=0} \gamma^{k} R_{t+k+2} | S_{t+1} = s']] $$ > 處理完 $S_{t}$ 後,下一個狀態的期望值,要考慮 $S_{t+1}$ ,所以要加上這個條件。 > [name=TCW][color=#00B2FF] $$ = \sum_{a} \pi(a|s) \sum_{s'}p(s'|s,a)[r(s,a,s')+\gamma \cdot v_{\pi}(s')] $$ 這條式子可以遞迴運算,又稱為 $v_{\pi}$ 的 Bellman equation (貝爾曼方程) 同理,我們也可以推論 $q(s,a)$ 的貝爾曼方程。 $$ q(s,a) = \mathbb{E}[R_{t+1}+ \gamma \cdot v(S_{t+1})|S_{t}=s,A_{t}=a]\\ = \sum_{s'}p(s'|s,a)[r(s,a,s')+ \gamma \cdot v(s')] $$ --- 好的,現在我們手上有動態規劃這個方法了,接著要開始真的評狀態價值了。這邊有兩種方法,一個是策略迭代,另一個是價值迭代,我們先介紹策略迭代。 #### 動態規劃 - 策略評估 (Policy Evaluation) 想像我們要從隨機的策略下,決定狀態的價值,這個過程稱作 **策略評估 (Policy Evaluation)** 。實際上,我們可以透過迭代,一步一步的更新狀態價值。 根據狀態價值函數定義,結合迭代,可以得到 $$ v_{k+1}= \sum_{a} \pi(a|s) \sum_{s'}p(s'|s,a)[r(s,a,s')+\gamma \cdot v_{k}(s')] $$ 可以透過這條式子逼近狀態價值。 演算法: ![](https://i.imgur.com/FLT2CIe.png) > 1. 以策略為單位進行更新,也就是說我們經過一次迭代,所有的狀態價值都會更新,因此稱為策略迭代。 > 2. 所有新的價值函數,都是由舊的價值函數計算的,沒有先計算先更新的問題。(ex: 現在有狀態 A, B, C。更新 A 的價值後,若計算 B 時需要 A 的價值,仍使用舊的價值進行計算。) > [name=TCW][color=#00B2FF] * 範例 - Example 4.1 ![](https://i.imgur.com/67OYLWW.png) * 目標:評估各位置的狀態價值 * 狀態:1-14 為非終點、左上與右下為終點 * 動作:上、左、下、右 (假設移動各方向機率相同) * 獎勵函數:非終點 $= -1$ * 其他規定: * 一次走一格 * 抵達終點後,不會離開終點 * 撞牆回原位 (ex: 在位置 1 時,往上還是移動到位置 1) * 轉移矩陣:[[Link](https://github.com/ChampDBG/LearnRL/blob/master/gridworld/T.npy)] * 實作:[[Link](https://github.com/ChampDBG/LearnRL/blob/master/Iterative_Policy_Evaluation.py)] * 結果 ``` ============================================================ [Parameters] Gamma = 0.99 Thershold = 0.05 [Variables] No.50 iteration Delta = 0.04759090371885932 [State-Value] [[ 0. -10.62647251 -15.48618269 -17.07138964] [-10.62647251 -13.90097573 -15.50704743 -15.48618269] [-15.48618269 -15.50704743 -13.90097573 -10.62647251] [-17.07138964 -15.48618269 -10.62647251 0. ]] ============================================================ ``` #### 動態規劃 - 策略增進 (Policy Improvement) 不過我們剛剛的結果,是隨機亂走得情況,假如我們不要隨便亂走呢?假如我們根據 **動作價值** 選擇我們的動作,結果會不會不一樣? 這邊我們回頭看動作價值函數: $$ q_{\pi}(s,a) = \mathbb{E}[R_{t+1} + \gamma \cdot v_{\pi}(S_{t+1}) | S_{t} = s, A_{t} = a] \\ =\sum_{s'}p(s'|s,a)[r(s,a,s')+ \gamma \cdot v_{\pi}(s')] $$ 透過動作價值函數,我們可以使用狀態價值,評估出每個狀態下,最適合的動作。為了簡單表示,我們把在狀態 $s$ 下,最適合的動作記為 $\pi '(s)$。這種,每次都選擇最好的動作的策略,我們稱為 greedy policy,數學定義如下: $$ \pi '(s) = \arg \max_{a}q_{\pi}(s,a) \\ = \arg \max_{a} \mathbb{E}[R_{t+1} + \gamma \cdot v_{\pi}(S_{t+1}) | S_{t} = s, A_{t} = a] \\ = \arg \max_{a} \sum_{s'}p(s'|s,a)[r(s,a,s') + \gamma \cdot v_{\pi}(s')] $$ 用這個動作策略,我們可以再回去更新我們的價值函數: $$ v_{\pi '} = \max_{a} \mathbb{E}[R_{t+1} + \gamma \cdot v_{\pi '} (S_{t+1}) | S_{t} = s, A_{t} = a] \\ =\max_{a} \sum_{s'} p(s'|s,a)[r(s,a,s')+ \gamma \cdot v_{\pi '}(s')] $$ #### 動態規劃 - 策略迭代 (Policy Iteration) 透過重複前兩個過程 - 「策略評估」與「策略增進」,我們可以不斷逼近真實的狀態價值,以及得到最好的狀態動作。演算法如下: ![](https://i.imgur.com/bqAonqI.png) * 實作 - Example 4.1 - [GitHub](https://github.com/ChampDBG/LearnRL/blob/master/Policy_Iteration.py) * 結果: ``` ============================================================ Final Result ============================================================ [State-value] [[ 0. 0. -1. -1.99] [ 0. -1. -1.99 -1. ] [-1. -1.99 -1. 0. ] [-1.99 -1. 0. 0. ]] ============================================================ [Policy] [['*' '<' '<' '<'] ['^' '^' '^' 'v'] ['^' '^' 'v' 'v'] ['^' '>' '>' '*']] ============================================================ ``` #### 動態規劃 - 價值迭代 (Value Iteration) 上述的策略迭代,有一個很明顯的缺點 - 我們需要花兩個步驟,且不斷來回計算,才能夠逼近狀態價值以及得到最佳動作。那麼,我們有沒有辦法,把這兩個步驟再更簡化一些呢? 答案是可以的,我們能將「策略增進」容納到「策略評估」的過程中,如下式: $$ v_{k+1}(s) = \max_{a} \mathbb{E}[R_{t+1} + \gamma \cdot v_{k}(S_{t+1}) | S_{t}=s, A_{t}=a] \\ = \max_{a} \sum_{s'} p(s'|s,a) [r(s,a,s') + \gamma \cdot v_{k}(s')] $$ 其中我們這邊的計算,並非從 **策略增進** 得到最適動作,而是直接計算各結果產生的價值,並以最高價值更新現有價值。 ![](https://i.imgur.com/kFOB6XN.png) * 實作內容:[GitHub](https://github.com/ChampDBG/LearnRL/blob/master/Value_Iteration.py) * 實作結果: ``` ============================================================ Final Result ============================================================ [State-value] [[ 0. 0. -1. -1.99] [ 0. -1. -1.99 -1. ] [-1. -1.99 -1. 0. ] [-1.99 -1. 0. 0. ]] ============================================================ [Policy] [['*' '<' '<' '<'] ['^' '^' '^' 'v'] ['^' '^' 'v' 'v'] ['^' '>' '>' '*']] ============================================================ ``` #### 動態規劃 - 特色討論 * 更新所有狀態價值 * 優點:更新所有狀態價值,是由價值函數的定義,衍伸出來的解決方案。因此滿足這個條件時,我們可以確保得到的狀態估計值,可以代表狀態價值,賦予這個數值意義。 * 缺點:更新所有狀態價值,計算量會隨著狀態數量而改變。舉個例子來說,我們前面提到的 GridWorld 是一個 4x4 的世界,但如果我今天的世界大一點,是個 40x40 的世界。光是要更新狀態價值,我們就給花上 100 倍的時間,更何況我們不知需要更新狀態價值一次。另外,我們在處理的問題,也未必只有小維度 (ex: 二維、三維) * 小結:更新所有狀態價值,確保我們得到數值的意義,但也限制我們問題的大小。過於複雜的問題,可能不適合使用這個方法。事實上,這個限制來自於動態規劃本身,有興趣請見 [Curse of dimensionality](https://en.wikipedia.org/wiki/Curse_of_dimensionality)。 * 其他討論:那如果我們不更新狀態價值,會發生什麼事? 從數學上來說,不更新所有狀態價值時,將不滿足價值函數定義。所以計算出來的出來的數值不一定代表真實的狀態價值,造成動作決策錯誤。 * 迭代輪流更新狀態價值與策略 * 優點:我們可以確保評估價值與策略,是往同一個方向、有相同目標。如下圖所示: ![](https://i.imgur.com/qaJmuL0.png) * 缺點:透過迭代更新,代表我們必須不斷與環境互動,以更新價值狀態與策略。想像以下情境: > 針對同一個問題,現在有三個人同時處理,我們可以得到三人的狀態價值與策略。 雖然我們獲得其他三人的資訊,但我們卻沒法統整這些資訊 (ex: 評估誰的資訊比較好),產生一個更準確的狀態價值或策略。因為你不知道那個人與環境互動到什麼程度了。說簡單一點,這個方法沒辦法分工。 * 小結:迭代輪流更新狀態價值與策略,確保我們評估狀態價值和策略有相同的目標,這個過程讓我們限制沒辦法分工處理問題。 #### 動態規劃 - 小結 以上,說明一些這個方法的特色。整體看來,動態規劃方法比較適合少量狀態的問題。除了動態規劃會遇到的 Curse of Dimensionality 外,沒辦法分工在處理複雜問題時會很麻煩。 [[Next Page](https://hackmd.io/s/HJ4N-p9o7)]