剖析強化學習 - 第八部分

作者:Massimiliano Patacchiola

上一篇文章中,我介紹了函數逼近作爲在強化學習設置中表示效用函數的方法。我們使用的簡單逼近器基於特徵的線性組合,並且它非常有限,因爲它無法模擬複雜的狀態空間(如XOR網格世界)。在這篇文章中,我將介紹人工神經網絡作爲非線性函數逼近器,向您展示如何使用神經網絡來模擬效用函數。我將從名爲Perceptron 的基本架構開始,然後轉向稱爲多層感知機(MLP的標準前饋模型。此外,將介紹(大多數時候)基於神經網絡策略的策略梯度方法。我將使用純Numpy來實現網絡和更新規則,這樣你就可以有一個淺顯的代碼來學習。這篇文章很重要,因爲它可以使你理解深度強化學習中使用的深層模型(例如卷積神經網絡),我將在下一篇文章中介紹。

這篇文章的參考文獻是SuttonBarto的書 “Generalization and Function Approximation”的第8章,最近該書的第二版已經發布,你可以在DuckDuckGo上輕鬆找到免費預印版。此外,一個很好的資源是David Silver課程的視頻課程7。在GoodfellowBengioCourville撰寫的深度學習一書中,對神經網絡進行了更廣泛的介紹。

我想以神經網絡簡史開始這篇文章,向您展示第一個神經網絡如何具有前一篇文章中講述的線性模型的相同限制,然後將介紹稱爲多層感知機(MLP的標準神經網絡,我將向您展示如何使用它來模擬任何效用函數。這個帖子與之前的帖子相比可能很難懂,但我建議你仔細研究,因爲使用神經網絡作爲函數逼近器是支持現代RL方法的基礎。

感知機歷史

1957年,美國心理學家弗蘭克·羅森布拉特Frank Rosenblatt在紐約康奈爾航空實驗室會議上發表了題爲感知機:感知和自動識別的報告。Perceptron不是一個軟件,它是在一個定製的硬件(像衣櫃一樣大)中實現的,它可以區分兩種類型的標記卡,第一類卡片標記在左側,而第二類卡片標記在右側,400個光電管的網格能夠定位標記並激活機械繼電器,調整Perceptron的參數並不像今天那麼容易。在pyTorch/Tensorflow中,我們只是定義了一堆變量,然後我們搜索最佳組合。在硬件實現中,要調整的參數是通過電動機調節的物理手柄(電位計)來實現的。

下面爲您奉上我找到的紐約時報的原始文章,它登載了Rosenblatt及其工作人員對Perceptron的官方介紹:

正如你所讀到的那樣,Perceptron的力量有點被高估了,特別是談論到會意識到它們的存在人造大腦(我們可以從這裏開始討論意識究竟是什麼,但它需要另一個博客系列文章)。但是,Rosenblatt做出的另外一些預測是正確的:

後來的感知機將能夠識別人並叫出他們的名字,並且立即將演講從一種語言翻譯成另一種語言

經過了數十年的時間這終於成爲了現實。從技術角度來看,Perceptron是什麼?您可能會驚訝地發現Perceptron是一個線性函數逼近器,就像上一篇文章中描述的那樣。讓我們試着將這一點正式化。輸入到Rosenblat的感知機是400個二進制光電管,我們可以建模爲矢量x =(x1,x2 ,,..., x400),輸出是電壓,我們稱之爲y。輸入和輸出之間的連接是一系列電線和電位計,我們可以通過另一個包含400個實數值(電位器的電阻)的矢量w來建模。我說Perceptron是一個線性系統,和上一篇文章中描述的線性模型完全一樣,我們可以將它定義爲兩個向量之間的點積\large x^{T}w 。但是,有一箇中間步驟:使用稱爲\phi激活函數將加權和的結果作爲輸入並生成輸出y

我們還可以使用一個名爲x_{b}的附加偏置單元和它的權重w_{b},可以將偏置附加到向量x,同時將偏置權重附加到向量w。偏置單元已經在以前的帖子中被廣泛討論,如果你不記得我們爲什麼需要它,我建議你再回去看看。最後一個等式是Perceptron的最終形式,以圖形方式將Perceptron表示爲向圖可以幫助理解,其中每個節點是單元(或神經元),每個邊是權重(或突觸)。關於名稱慣例,社區中沒有明確的規則,我個人更喜歡使用術語單元(unit權重(weight而不是神經元突觸,以避免與生物學術語相混淆。

下圖代表Rosenblatt 的感知機。只有在卡片上標識了標記時,纔會觸發400個光電管,每個光電管都可以被認爲是Perceptron的輸入。在這裏,我使用綠色表示神經元的活動單元(狀態=1),紅色表示非活動單元(狀態= 0),橙色表示具有未定義狀態的單元。當我說未定義狀態時,我的意思是該單元的輸出尚未計算,例如,這是在計算加權和及應用轉換函數之前輸出單元y的狀態。在圖像中你必須注意到偏置單元始終處於活動狀態,在上一篇文章中,我們使用這個技巧用於增加模型中的偏置。

正如我上面所說,線性逼近器和感知機之間的唯一區別是激活函數\phi。原始感知機使用符號函數sign function)生成類型爲01的二進制輸出,符號函數引入了一個問題,使用符號函數不可能應用梯度下降,因爲此函數不可微,即你應該知道的一個重要細節是基於梯度的技術(例如反向傳播)此時是未知的。

Rosenblatt 是如何訓練Perceptron的?這是個很有意思的部分,Rosenblatt使用了一種Hebbian學習形式。心理學家Donald Hebb在幾年前(1949年)發表了題爲行爲的組織的著作,他從神經連接模式的角度解釋了認知過程,原理很容易掌握:當兩個神經元同時激活時,它們的連接得到加強Ronsenblat在定義Perceptron的更新規則時受到了這項工作的啓發,更新規則直接管理訓練階段的三種可能結果,並重復一定數量的epoch

  1. 如果輸出y是正確的,保留權重w不變
  2. 如果輸出y不正確(0而不是1),增加輸入向量x到權重向量w
  3. 如果輸出y不正確(1而不是0),從權重向量w減去輸入向量x

更新規則的幾何解釋可以幫助您瞭解正在發生的事情。w是一個向量,意味着我們可以在三維空間中想象這個向量,向量的起點位於軸的原點,而向量的終點位於其值指定的座標處,所有輸入向量的集合也可以被認爲是佔據相同三維空間的一部分的一組向量。我們在更新期間正在做的是移動權重向量來改變其終點座標,矢量的最佳位置在錐體內(其頂點在原點處),其中始終滿足學習規則的第一標準。換句話說,當權重向量達到該錐體時,不再觸發更新規則的第二和第三個條件。

Python實現Perceptron及其更新規則非常簡單,下面是一個例子:

def perceptron(x_array, w_array):
    '''Perceptron model

    Given an input array and a weight array
    it returns the output of the model.
    @param: x_array a binary input array
    @param: w_array numpy array of weights
    @return: the output of the model
    '''
    import numpy as np
    #Dot product of input and weights (weighted sum)
    z = np.dot(x_array, w_array)
    #Applying the sign function
    if(z>0): y = 1
    else: y = 0
    #Returning the output
    return y

def update_rule(x_array, w_array, y_output, y_target)
    '''Perceptron update rule

    Given input, weights, output and target
    it returns the updated set of weights.
    @param: x_array a binary input array
    @param: w_array numpy array of weights
    @param: y_output the Perceptron output
    @param: y_target the target value for the input
    @return: the updated set of weights
    '''
    if(y_output == y_target):
        return w_array #first condition
    elif(y_output == 0 and y_target == 1):
        return x_array + w_array #second condition
    elif(y_output == 1 and y_target == 0)
        return x_array - w_array #third condition

一篇文章中,我提到線性逼近器是有限的,因爲它們只能用於線性可分的問題,同樣的考慮適用於Perceptron。從歷史上看,研究界並未意識到這個問題,Perceptron的工作持續了數年最終取得了成功。WidrowHoff1960年在斯坦福大學獲得的結果引起了人們的興趣,當時Perceptron名爲ADALINEADALINE網絡有多個輸入和輸出,輸出上使用的激活函數是線性函數,更新規則是Delta規則Delta規則是基於梯度下降過程的反向傳播的特例,當時這是一個重要的成功,但主要問題仍然存在:ADALINE仍然是線性模型,Delta規則不適用於非線性設置。

問題的出現: 1969年出版了一本名爲“Perceptrons: an introduction to computational geometry” 的書,在書中,作者Marvin MinskySeymour Papert數學上證明了類似Perceptron的模型只能解決線性可分的問題,並且它們不能應用於非線性數據集,如XOR數據集。在一篇文章中,我仔細選擇了XOR網格世界作爲非線性問題的一個例子,向您展示線性逼近器無論如何無法描述這個世界的效用函數,這就是MinskyPapert在他們的書中所證明的。所謂的XOR事件標誌了Perceptron時代的結束,人工神經網絡項目的資金逐漸消失,只有少數研究人員繼續研究這些模型。

復興(多層感知機)

冬天過後,春天回來了!直到1985年我們纔看到神經網絡的復甦,RumelhartHintonWilliams發表了一篇題爲通過誤差傳播學習內部表徵的文章該文章使用廣義delta規則來訓練具有多層的感知機。多層感知機(MLP是具有一個或多箇中間層的經典感知機的擴展,作者通過實驗證實,使用附加層(稱爲隱藏層)和新的更新規則,網絡能夠解決XOR問題,這一結果再次點燃了人們對神經網絡的研究熱情。作者說:

簡而言之,我們相信我們已經回答了MinskyPapert的挑戰,並且發現了一個足夠強大的學習成果,以證明他們對多層機器學習的悲觀態度是錯誤的。

MLP的經典形式基於輸入層、隱藏層和輸出層,層之間使用的轉換函數通常是sigmoid誤差函數可以被定義爲輸出和標籤之間的均方誤差(MSEMLP的每一層可以表示爲輸入向量x和權重矩陣w的乘積,其結果加上偏置後傳遞給激活函數,生成輸出向量y,這些操作等效於用於線性逼近器中的輸入值的加權和。與Perceptron類似,我們可以將MLP表示爲向圖,在下圖中(我建議您仔細查看)您可以看到MLP是如何組裝的。與Perceptron相關的主要區別是隱藏層h是通過輸入向量x和權重矩陣w1的乘積得到的,權重求和後的數組z傳遞給sigmoid激活函數,您應該注意偏置項是如何嵌入到輸入和隱藏向量中的。再次重複相同的過程,隱藏層的輸出與權重矩陣w2相乘,傳遞給一個sigmoid函數,最終輸出向量y。在神經網絡術語中,從輸入x開始到輸出y的過程被稱爲前向傳播(forward pass

爲什麼MLP不是線性模型?這是至關重要的一點。只有當我們使用非線性激活函數(例如,sigmoidtanhReLU等)時,才能很容易地證明MLP是非線性逼近器。如果我們用線性激活函數替換sigmoid,那麼我們的MLP就會退化成Perceptron。關於MLP的另一個有趣的事實是,給定足夠數量的隱藏神經元,它可以逼近任何連續函數,這被稱爲通用逼近定理universal approximation theorem,它由George Cybenko1989年被證明。

反向傳播(概述)

MLP背後的主要創新是基於反向傳播更新規則。反向傳播的想法當時並不是全新的,但經過一段時間後才發現可以將其應用於前饋網絡。正如就像上一篇文章中描述的那樣,每當我們想要訓練函數逼近器時,我們需要兩件事:

  1. 一個是誤差度量,用於給出評估器的輸出與目標之間的距離。
  2. 一個是更新規則,用於設置評估器的參數以減少誤差。

由於神經網絡是函數逼近器,因此它們也需要這兩個部分,所述誤差度量仍然由網絡輸出和目標值之間的均方誤差(MSE給定。由於我們在每個步驟之後使用TD(0)進行自助(bootstrapping)和在線更新,因此該誤差度量將減少爲:

使用自助目標效用函數U^{\sim ~}MLP本身在t + 1估算,並通過折扣因子γ加權後加上獎勵,形成以下表達式:

更新規則仍是一個梯度下降過程的應用,在神經網絡的情況下被稱爲反向傳播(backpropagation。有必要說明,自助忽略了對目標的影響,只考慮了 s_{t}處針對權重w1 w2估計的偏導數(半梯度法)。如果你不想進入反向傳播的數學細節,這就足夠了,你可以跳過下一節。

反向傳播(一些數學公式)

可以將反向傳播視爲鏈式法則(chain rule在網絡的不同層上的迭代應用。如果你想理解反向傳播背後的數學,你應該複習下微積分。在這裏,我只能根據我在本科生入門課程中使用的材料給你一個快速回顧。首先,必須知道什麼是導數以及如何計算最重要函數的導數(例如sincos、指數、多項式)。鏈式法則是一個簡單的迭代過程,允許計算函數組合的導數,迭代過程包括在組合中移動並計算過程中遇到的所有函數的導數。在這裏,我將向你展示一個應用於具有單個變量x的等式的鏈式法則的示例

正如您所看到的,這個想法是(1)將初始方程式分解爲不同的部分,然後(2)計算每個部分的導數,最後(3)乘以每個部分。直觀地,您可以看到反向傳播是打開一組黑色中國盒子Chinese boxes)直到出現紅色盒子的過程。在我們的例子中,紅色盒子是變量x並且要打開的黑色盒子是我們必須得到的最終需要計算的各個式子。

鏈式法則如何應用到神經網絡?這是很多人都在一直努力的事情。將神經網絡視爲一個神奇的盒子,你可以在其中推送一個數組,並獲得另一個數組,推送輸入,獲得輸出,輸入和輸出可以有不同的大小。例如在我們的Perceptron中,輸入數組由400個值組成,輸出是單個值,這在技術上是一個標量場scalar field。在我們的MLP示例中,提供一個表示世界狀態的數組,我們將得到一個輸出,表示該狀態的效用(作爲單個值),或每個可能的操作(作爲數組)的效用。現在,神經網絡不是一個神奇的盒子,輸出y是通過一系列的操作獲得的。如果你仔細看看上一節的MLP方案,你可以反向從yh再從hx,最後,y通過將sigmoid應用於h的加權和及一組權重來獲得,而h使用x的加權和及第一個權重矩陣以類似的方式獲得。這個長鏈操作是多個函數組合,根據定義,我們可以將鏈式法則應用於它。

哪些是神經網絡中的變量?在神經網絡中,我們需要估計的不只一個變量,我們網絡的變量(或未知數)是連接單元的權重,我們的目標是找到更好地代表實際效用函數的權重集。與單個變量微積分(如上面的鏈式法則示例中使用的微積分)不同,神經網絡是多變量微積分(multivariable equation。例如,您可以將RosenblattPerceptron視爲具有400個未知數的多變量微積分。在多變量微積分中,我們不考慮導數,而是考慮梯度gradient,我在上一篇文章中已經寫過關於梯度的內容。處理多變量微積分不應該嚇到你,因爲我們的鏈式法則仍然有效,但我們現在需要使用偏導數partial derivatives而不是標準導數。給出誤差函數E我們必須尋找變量集w1 w2,這意味着我們一直計算導數(打開中國盒子,或者俄羅斯套娃),直到解開它們爲止。在下圖中,您可以找到MLP過程的數學描述,其中以向量x作爲輸入,向量y作爲輸出,兩組權重w1(在輸入層和隱藏層之間)和w2(在隱藏層和輸出層之間)。這些分量的加權和產生兩個數組z1z2,並傳遞給兩個激活函數h =φ(z1)y =φ(z2)。請記住,鏈的最後一個元素是誤差函數E,出於這個原因E是鏈式法則的起點。

您可以注意到反向傳播的使用非常有效,因爲它允許我們重用在後面的層中計算的部分偏導數,以便計算先前層的導數。此外,所有這些操作都可以使用矩陣向量的形式,並且可以在GPU上輕鬆並行化計算。

反向傳播返回的是什麼?反向傳播找到未知的梯度向量。在我們的MLP中,反向傳播找到兩個向量,一個是權重w1另一個是權重w2。很好,但我們應該如何使用這些向量?如果您記得上一篇文章中的梯度向量是向上指向誤差曲面的向量,這意味着它向我們顯示了我們應該移動到哪裏以便到達函數的頂部。然而,在這裏我們感興趣的是向下,因爲我們想要最小化誤差(最終我們做梯度下降),改變梯度前面的符號可以很容易地實現這種方向的變化。

現在,知道移動權重的方向是不夠的,我們需要知道我們必須向這個方向移動多少。您必須記住,特定點處函數的陡度由梯度向量大小給出。在線性代數中,您應該知道將矢量乘以標量會改變矢量的大小,這個標量稱爲學習率。找到最佳學習率並不容易,研究人員通常使用線性衰減來降低學習率。今天有一些技術(例如AdagradAdamRMSProp等)可以根據誤差表面的斜率動態調整學習率。如果您想了解更多有關自適應梯度方法的信息,可以看看這篇寫得很好的博文

Python實現

如果我們根據矩陣和向量進行思考,在Python中實現多層感知機非常容易。如果輸入是向量x,第一層的權重是矩陣w1,第二層的權重爲矩陣w2,正向傳播可以表示爲x矩陣向量和w1之間的乘積,該乘積在隱藏層中生成一個名爲h的激活,然後使用該向量與第二層中的權重矩陣w2乘積後產生輸出y。我會將所有內容嵌入到一個叫做MLP的類中,以後我們將會使用這個類。在這裏我說明了該類的方法,你可以在GitHub存儲庫中找到完整的代碼,並可在腳本mlp.py中找到MLP類,讓我們從__init__()方法開始:

def __init__(self, tot_inputs, tot_hidden, tot_outputs):
    '''Init an MLP object

    Defines the matrices associated with the MLP.
    @param: tot_inputs
    @param: tot_hidden
    @param: tot_outputs
    '''
    import numpy as np
    if(activation!="sigmoid" and  activation!="tanh"):
        raise ValueError("[ERROR] The activation function " 
                         + str(activation) + " does not exist!")
    else:
        self.activation = activation
    self.tot_inputs = tot_inputs
    self.W1 = np.random.normal(0.0, 0.1, (tot_inputs+1, tot_hidden))
    self.W2 = np.random.normal(0.0, 0.1, (tot_hidden+1, tot_outputs))
    self.tot_outputs = tot_outputs

你應該注意到tot_inputs+1tot_hidden+1,就可以明白我是如何在權重矩陣增加附加行作爲偏置項的。如本文和前面所述,偏置項是輸入向量x和隱藏的激活h中的一個附加單元。矩陣的權重使用高斯分佈隨機初始化,平均值爲0.0,標準差爲0.1。好的,我們現在可以看一下該類的其他方法:激活函數和前向傳播。

def _sigmoid(self, z):
    return 1.0 / (1.0 + np.exp(-z))

def _tanh(self, z):
    return np.tanh(z)

def forward(self, x):
    '''Forward pass in the neural network

    Forward pass via dot product
    @param: x the input vector (must have shape==tot_inputs)
    @return: the output of the network
    '''
    self.x = np.hstack([x, np.array([1.0])]) #add the bias unit
    self.z1 = np.dot(self.x, self.W1)
    if(self.activation=="sigmoid"):
        self.h = self._sigmoid(self.z1)
    elif(self.activation=="tanh"):
        self.h = self._tanh(self.z1)
    self.h = np.hstack([self.h, np.array([1.0])]) #add the bias unit
    self.z2 = np.dot(self.h, self.W2) #shape [tot_outputs]
    if(self.activation=="sigmoid"):
        self.y = self._sigmoid(self.z2)
    elif(self.activation=="tanh"):
        self.y = self._tanh(self.z2)
    return self.y

有幾點需要注意,我使用Numpy在方法hstack()添加了偏置單元,它同時對輸入和隱藏激活函數進行了處理。我添加了兩個激活函數:sigmoid雙曲正切tanh),兩者之間的區別是在於輸出,sigmoid的輸出範圍是[0,1]tanh的輸出範圍是[−1,1],您可以通過增加其他方法來擴展激活函數的功能。下面是反向傳播方法:

def _sigmoid_derivative(self, z):
    return self._sigmoid(z) * (1.0 - self._sigmoid(z))

def _tanh_derivative(self, z):
    return 1 - np.square(np.tanh(z))
               
def backward(self, y, target):
    '''Backward pass in the network

    @param y: the output of the network in the forward pass
    @param target: the value of taget vector (same shape of output)
    '''
    if(y.shape!=target.shape): 
        raise ValueError("[ERROR] The size of target is wrong!")
    #gathering all the partial derivatives
    dE_dy = -(target - y)
    if(self.activation=="sigmoid"):
        dy_dz2 = self._sigmoid_derivative(self.z2)
    elif(self.activation=="tanh"):
        dy_dz2 = self._tanh_derivative(self.z2)
    dz2_dW2 = self.h        
    dz2_dh = self.W2
    if(self.activation=="sigmoid"):
        dh_dz1 = self._sigmoid_derivative(self.z1)
    elif(self.activation=="tanh"):
        dh_dz1 = self._tanh_derivative(self.z1)     
    dz1_dW1 = self.x
    #gathering the gradient of W2
    dE_dW2 = np.dot(np.expand_dims(dE_dy * dy_dz2, axis=1), 
                    np.expand_dims(dz2_dW2, axis=0)).T
    #gathering the gradient of W1
    dE_dW1 = (dE_dy * dy_dz2)
    dE_dW1 = np.dot(dE_dW1, dz2_dh.T)[0:-1] * dh_dz1
    dE_dW1 = np.dot(np.expand_dims(dE_dW1,axis=1), 
                    np.expand_dims(dz1_dW1,axis=0)).T
    return dE_dW1, dE_dW2

backward方法返回更新網絡所需的梯度向量。在這裏,我使用了與該帖子前面部分中使用的名稱一致的偏導數的一致約定,這樣就可以匹配代碼和數學公式。最後,我們必須使用前向和後向傳播來進行訓練:

def train(self, x, target, learning_rate=0.1):
    '''train the network

    @param x: the input vector
    @param target: the target value vector
    @param learning_rate: the learning rate (default 0.01)
    @return: the error RMSE
    '''
    y = self.forward(x)
    dE_dW1, dE_dW2 = self.backward(y, target)
    #update the weights
    self.W2 = self.W2 - (learning_rate * dE_dW2) 
    self.W1 = self.W1 - (learning_rate * dE_dW1)
    #estimate the error
    error = 0.5 * (target - y)**2
    return error

這是一個基本MLP的核心代碼,您可以擴展它添加更多層及誤差/激活函數。請注意,當前版本的代碼不包括mini-batch訓練,對於這篇文章的目的,單個input-target對就足夠了,但是通常隨機梯度下降需要一個mini-batch的多個樣本,它可以代表我們想要逼近的函數。擴展該類的方法可以加入mini-batch的訓練,有兩種方式可以實現:(i)第一種方法是將input-target對列表傳遞給train()方法,並迭代通過backward()方法累加這些對的梯度,然後在兩個矩陣w1w2上計算平均梯度;(ii)第二種方法是利用Numpy函數並實現基於張量的方法forward()backward()。這意味着我們必須添加另一個代表mini-batch的維度,類似於TensorflowpyTorch等框架,可以把它作爲家庭作業...

應用:多層XOR

上一篇文章中我們看到了如何使用線性函數逼近器解決XOR問題。現在我們有了一個新的方法,多層神經網絡,它是強大的非線性逼近器,能夠解決XOR問題。在這裏,我將使用類似於Rumelhartal.使用的架構解決XOR問題。

在繼續之前,讓我們快速回顧一下。神經網絡是非線性函數逼近器,與上一篇文章中介紹的線性逼近器類似,訓練神經網絡需要兩個步驟,誤差度量和更新規則:

誤差度量:通過兩個量之間的均方誤差(MSE給出了一個常見的誤差度量。

更新規則(backprop:神經網絡的更新規則基於梯度下降,並通過稱爲反向傳播的過程獲取梯度向量,反向傳播是鏈式法則的應用。

我們可以使用神經網絡來近似強化學習問題中的效用函數。我們拿清潔機器人作爲例子,即在25個狀態的網格世界中移動,使用標準表,我們需要把25個單元格的所有效用存儲在內存中,此外,我們要訪問所有25個狀態來獲取這些值。在這裏,我們使用MLP來解決這兩個問題,一個具有2個輸入、2個隱藏和1個輸出的MLP總共具有4 + 2 = 6個連接存儲在內存中(當使用偏置時9 + 3 = 12),因此我們節省了存儲空間!此外,使用MLP我們不需要訪問網格世界的所有25個單元格,因爲我們可以使用函數逼近以擴展到看不見的狀態。

在這個例子中,我使用了一個帶有2個輸入,2個隱藏單元和1個輸出的網絡,兩層中我使用tanh激活函數,這很重要,因爲我們的效用函數在 [-1, +1] 範圍內,而sigmoid無法逼近它。對於強化學習部分,我使用了前一篇文章的相同代碼和(快速)使用matplotlib的類似繪圖代碼來繪製結果。您可以在GitHub存儲庫找到名稱爲xor_mlp_td.py的完整代碼,我們可以使用MLP類來定義一個新的網絡並命名爲my_mlp,然後訓練網絡來逼近效用函數。

def main():
  env = init_env()
  my_mlp = MLP(tot_inputs=2, tot_hidden=2, tot_outputs=1, activation="tanh")
  learning_rate = 0.1
  gamma = 0.9
  tot_epoch = 10001
  #One epoch is one episode
  for epoch in range(tot_epoch):
      observation = env.reset(exploring_starts=True)
      #The episode starts here
      for step in range(1000):
          action = np.random.randint(0,4)
          new_observation, reward, done = env.step(action)
          #call the update method and then the my_mlp.train() method
          #the update() returns the new MLP and the RMSE error
          my_mlp, error = update(my_mlp, new_observation, reward, 
                                 learning_rate, gamma, done)
          observation = new_observation
          if done: break

上面的代碼類似於用於線性和雙曲線逼近器的代碼。我使用學習率0.1γ0.9訓練10000步,用x-y元組網格輸入神經網絡,我們可以得到近似效用函數的輸出,並繪製如下圖所示:

這樣一個簡單的神經網絡可以產生一個相當複雜的函數,這與我們在前一篇文章使用的雙曲拋物面非常相似。這裏打印了在上一個epoch獲得的效用矩陣

Epoch: 10001
[[ 0.81  0.23 -0.36 -0.55 -0.6 ]
 [ 0.49 -0.12 -0.42 -0.49 -0.5 ]
 [ 0.01 -0.18 -0.21 -0.16 -0.1 ]
 [-0.44 -0.02  0.28  0.45  0.54]
 [-0.87 -0.28  0.43  0.7   0.79]]

XOR世界中,矩陣的左上角和右下角具有+1的效用,而右上角和左下角具有-1的效用,我們看到網絡捕獲了這一趨勢,其值爲0.81,0.79-0.87-0.6。使用神經網絡毫不費力地得到了這個結果,我認爲對超參數進行微調可以讓我們更接近。

您的家庭作業是更改網絡結構,增加隱藏層的大小,並觀察圖是怎麼變化的。請記住,增加隱藏單元的數量會增加逼近器的自由度。爲了避免欠擬合過擬合,您必須始終考慮偏差-方差權衡

超越多層感知機(MLP

即使使用MLP可以在許多問題中提供很大的優勢,但仍然存在一些我們未提及的主要限制,在這裏,我列出其中三個:

  1. 模型的大小MLP的完全連接層具有許多參數,這些參數隨着單元的數量而快速增長。擁有共享權重會更好。
  2. 更新規則。訓練具有隨機梯度下降的神經網絡需要獨立同分布(i.i.d.)的樣本,然而,從我們的網格世界收集的樣本是密切相關的。
  3. 不穩定。在一些強化學習算法中訓練神經網絡可能非常不穩定(參見實例Q-learning及其bootstrap 機制)。

經過了多年時間才找到了解決所有這些問題的方案,直到最近纔開發了一種穩定的算法,即深度強化學習,這將是下一篇文章的主題。

策略梯度方法

作爲額外材料,我想提供一個廣泛使用神經網絡的另一種強化學習方法的簡單概述。到目前爲止,我們已經逼近了效用(或狀態-動作)函數,然而,還有另一種方法:策略的逼近。在第四篇文章的最後,我解釋了actor-onlycritic-only算法之間的區別,策略梯度是一種actor-only的方法,目的是逼近策略。在繼續學習之前,回顧效用,狀態-動作和策略函數之間的區別可能很有用:

  1. 效用(或值)函數y =U(s),接收向量s作爲輸入並返回對應狀態的效用y
  2. 狀態-動作函數y =Q(s, a),接收一個狀態s一個動作a作爲輸入,並返回對應狀態-動作對的效用y
  3. 策略函數y =π(s),接收狀態s作爲輸入並返回要在該狀態下執行的動作。

在策略梯度方法中,我們使用神經網絡來逼近策略函數π(s),正像名字所說的我們使用梯度。在這個系列中你應該學習到什麼是梯度向量以及我們如何在梯度下降方法中使用它,在這裏,我們想要估計神經網絡的梯度,但我們不想再通過將誤差最小化來得到,相反,我們希望最大化預期收益(長期累積獎勵)。正如我之前告訴你的那樣,梯度指示了最大化函數我們應該移動權重的方向,這正是我們想要在策略梯度方法中實現的。

優點:策略梯度方法的主要優點是在某些上下文中,更容易估計策略函數而不是效用函數。我們知道,由於信用分配問題,對效用函數的估計可能很困難。在策略梯度方法中,我們繞過了這個問題,我們獲得了更好的收斂性和更高的穩定性;另一個優點是使用策略梯度我們可以近似連續的動作空間,這克服了Q-Learning的重要限制,其中max運算符只允許我們近似離散的動作空間(有關詳細信息,請參閱第三篇文章)。

缺點:策略梯度方法的主要缺點是它們通常較慢並且通常會收斂到局部最優;另一個問題是方差,用大量軌跡評估策略會導致函數逼近中變化非常大。

最廣泛使用的策略梯度方法之一稱爲REINFORCE,它基於蒙特卡羅方法。REINFORCE算法在不使用任何效用函數的情況下找到梯度的無偏估計。我希望有時間在將來的帖子中講解REINFORCE,目前如果你想了解更多信息,可以閱讀相關學術文章

結論

在這篇文章中,我們學到了前饋神經網絡如何工作以及它們如何用於逼近效用函數。最近的發展已經超越了MLP,使用了卷積神經網絡和穩定的訓練技術,最令人印象深刻的結果是在諸如ATARI遊戲和古老的圍棋遊戲等挑戰性問題中獲得的。在下一篇文章中,我們將看到如何使用深度強化學習來解決其中的一些問題。

索引

  1. [第一篇]馬爾科夫決策過程,貝爾曼方程,值迭代和策略迭代算法。
  2. [第二篇]蒙特卡羅概念,蒙特卡洛方法,預測與控制,廣義策略迭代,Q函數。
  3. [第三篇]時間差分概念,動物學習,TD(0), TD(λ)和資格痕跡,SARSA,Q-learning。
  4. [第四篇] Actor-Critic方法背後的神經生物學,計算Actor-Critic方法,Actor-only和Critic-only方法。
  5. [第五篇]進化算法介紹,強化學習中的遺傳算法,遺傳算法的策略選擇。
  6. [第六篇]強化學習應用,多臂老虎機,山地車,倒立擺,無人機着陸,難題。
  7. [第七篇]函數逼近概念,線性逼近器,應用,高階逼近器。
  8. [第八篇] 非線性函數逼近,感知器,多層感知器,應用,政策梯度。

參考

Goodfellow, I., Bengio, Y., Courville, A., & Bengio, Y. (2016). Deep learning. Cambridge: MIT press.

Hebb, D. O. (2005). The first stage of perception: growth of the assembly. In The Organization of Behavior (pp. 102-120). Psychology Press.

Minsky, M., & Papert, S. A. (2017). Perceptrons: An introduction to computational geometry. MIT press.

Rumelhart, D. E., Hinton, G. E., & Williams, R. J. (1986). Learning representations by back-propagating errors. nature, 323(6088), 533.

Sutton, R. S., McAllester, D. A., Singh, S. P., & Mansour, Y. (2000). Policy gradient methods for reinforcement learning with function approximation. In Advances in neural information processing systems (pp. 1057-1063).

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章