1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【機(jī)器學(xué)習(xí)基礎(chǔ)】使用python實(shí)現(xiàn)BP算法

        共 4601字,需瀏覽 10分鐘

         ·

        2020-08-24 12:13

        用pytorch跟tensorflow實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)固然爽。但是想要深入學(xué)習(xí)神經(jīng)網(wǎng)絡(luò),光學(xué)會(huì)調(diào)包是不夠的,還是得親自動(dòng)手去實(shí)現(xiàn)一個(gè)神經(jīng)網(wǎng)絡(luò),才能更好去理解。

        一、問題介紹

        傳說中線性分類器無(wú)法解決的異或分類問題。我們就拿它來(lái)作為我們神經(jīng)網(wǎng)絡(luò)的迷你訓(xùn)練數(shù)據(jù)。把輸入數(shù)據(jù)拼成一個(gè)矩陣X:

        import?numpy?as?np

        #訓(xùn)練數(shù)據(jù):經(jīng)典的異或分類問題
        train_X?=?np.array([[0,0],[0,1],[1,0],[1,1]])
        train_y?=?np.array([0,1,1,0])

        我們定義一個(gè)簡(jiǎn)單的2層神經(jīng)網(wǎng)絡(luò):對(duì)應(yīng)的代碼

        linear1?=?LinearLayer(2,3)
        relu1?=?Relu()
        linear2?=?LinearLayer(3,1)

        我們還需要定義一個(gè)損失函數(shù)Loss,用來(lái)衡量我們的輸出結(jié)果與實(shí)際結(jié)果的誤差。這里用的是均方誤差MSE,表達(dá)式如下

        二、BP算法 和 計(jì)算圖(Computing Graph)模型

        里面的線性層,Relu層也得自己動(dòng)手實(shí)現(xiàn)。

        實(shí)現(xiàn)這些,我們首先需要知道計(jì)算圖模型。計(jì)算圖模型是所有神經(jīng)網(wǎng)絡(luò)框架的核心理論基礎(chǔ)。

        我們依然還是對(duì)著這個(gè)例子來(lái)講解。

        上面的神經(jīng)網(wǎng)絡(luò),用純數(shù)學(xué)公式表達(dá)可以表達(dá)。計(jì)算圖模型把一個(gè)復(fù)合運(yùn)算拆分成為多個(gè)子運(yùn)算,因此,我們需要引入很多中間變量。

        定義:根據(jù)這些公式,我們就可以用計(jì)算圖模型來(lái)表示我們的神經(jīng)網(wǎng)絡(luò),如下:這個(gè)計(jì)算圖就是上面那一堆公式的可視化表示。

        在圖里面,公式里每個(gè)出現(xiàn)過的變量都被視為一個(gè)節(jié)點(diǎn),變量之間的連線描述了變量之間存在直接的計(jì)算的關(guān)系。計(jì)算圖的表示方法,有什么好處呢?

        下面我們基于這個(gè)計(jì)算圖來(lái)用BP算法進(jìn)行模型的訓(xùn)練。

        對(duì)模型進(jìn)行訓(xùn)練,就是找到一組模型的參數(shù),使得我們的網(wǎng)絡(luò)模型能夠準(zhǔn)確預(yù)測(cè)我們的訓(xùn)練數(shù)據(jù)。在我們這個(gè)例子里面,需要訓(xùn)練參數(shù)其實(shí)只有線性層的矩陣跟bias項(xiàng):[公式] 。

        訓(xùn)練采用的是BP算法,采用梯度下降法來(lái)逐漸迭代去更新參數(shù)。

        梯度下降法的原理很簡(jiǎn)單,每次迭代中,用損失函數(shù)關(guān)于參數(shù)的梯度乘以學(xué)習(xí)率,來(lái)更新參數(shù)。關(guān)于梯度我要多說幾句。梯度表示Y關(guān)于x的變化率,可以理解成x的速度。由于W1是一個(gè)2X3的矩陣,那么loss關(guān)于W1的梯度可以理解W1的每個(gè)元素的瞬時(shí)速度。W1的梯度的形狀,必然是嚴(yán)格跟參數(shù)本身的形狀是一樣的(每個(gè)點(diǎn)都有對(duì)應(yīng)的速度)。也就是說損失函數(shù)關(guān)于W1的梯度也必然是一個(gè)2X3的矩陣(不然更新公式里面無(wú)法做加減)。

        下面開始訓(xùn)練過程,

        首先給定輸入X,初始化 [公式] (記住不能初始為全0)。

        正向傳播(forward pass)

        BP算法首先在計(jì)算圖上面進(jìn)行正向傳播(forward pass),即從左到右計(jì)算所有未知量:嚴(yán)格按照順序計(jì)算,所有的 [公式] 都能先后求出,右邊的y就是網(wǎng)絡(luò)的當(dāng)前預(yù)測(cè)結(jié)果。

        反向傳播(backward pass)

        既然我們想要用參數(shù)的梯度來(lái)更新參數(shù),那么我們需要求出最后的節(jié)點(diǎn)輸出loss關(guān)于每個(gè)參數(shù)的梯度,求梯度的方法是反向傳播。

        由于我們已經(jīng)進(jìn)行過一次正向傳播,因此圖里面所有的節(jié)點(diǎn)的值都變成了已知量。我們現(xiàn)在要求的是圖里面標(biāo)為紅色的這4個(gè)梯度,它們距離loss有點(diǎn)兒遠(yuǎn)。

        但是不急,有了這個(gè)計(jì)算圖,我們可以慢慢從右往左推出這4個(gè)值。

        先從最右邊開始,觀察到Loss節(jié)點(diǎn)只有一條邊跟y連著,計(jì)算loss關(guān)于y的導(dǎo)數(shù)(這個(gè)求導(dǎo)只有一個(gè)變量y,怎么求不用我解釋了吧):我們就求得了損失函數(shù)關(guān)于輸出y的導(dǎo)數(shù),然后繼續(xù)往左邊計(jì)算。

        (已經(jīng)求出的梯度我們用橙色來(lái)標(biāo)記)y是通過O2計(jì)算出來(lái)的,我們可以計(jì)算y關(guān)于O2的梯度:但是我們想要的是loss關(guān)于O2的梯度,這里應(yīng)用到了鏈?zhǔn)角髮?dǎo)法則:loss關(guān)于y的梯度在之前已經(jīng)求出來(lái)過了,然后就可以求出loss關(guān)于O2的梯度。

        繼續(xù)往左計(jì)算梯度:上圖中 a2 關(guān)于它每個(gè)變量的梯度,可以直接根據(jù) a2 與它左邊3個(gè)變量的表達(dá)式來(lái)算出,如下:這一步我們算出了兩個(gè)需要計(jì)算的梯度,似乎并沒有遇到困難,繼續(xù)往左傳播。

        在計(jì)算 a1梯度的時(shí)候,我們遇到了relu激活函數(shù),relu函數(shù)的梯度也很好求:它的梯度就是在輸入X的基礎(chǔ)上,所有大于0的位置導(dǎo)數(shù)都是1,其他位置導(dǎo)數(shù)都是0,比如:(這括號(hào)里的看不懂不要緊,當(dāng)N維向量對(duì)M維向量求導(dǎo)應(yīng)用鏈?zhǔn)椒▌t時(shí),通用一點(diǎn)兒的結(jié)果是一個(gè)NM的jacobian矩陣再乘M1向量,但是這里由于1. N=M。2. jacobian矩陣是一個(gè)對(duì)角方陣。所以可以簡(jiǎn)化成兩個(gè)向量相乘)

        再往左繼續(xù)傳,我就不寫每個(gè)步驟了。總之可以一直傳到所有梯度都求出來(lái)為止。接下來(lái)一步就是愉快地進(jìn)行隨機(jī)梯度下降法的更新操作了。

        三、模塊化各種Layer

        觀察我們的網(wǎng)絡(luò),發(fā)現(xiàn)里面的幾個(gè)模塊之間其實(shí)大部分干的事情都是相似的,無(wú)非就是層數(shù)不一樣。那么我們就可以復(fù)用,我們完全可以把它們抽象成不同的Layer:于是,我們可以把這些類似模塊看成一個(gè)小黑盒子,我們的模型等價(jià)于下面這個(gè):于是上面那個(gè)復(fù)雜的網(wǎng)狀結(jié)構(gòu),被我們簡(jiǎn)化成了線性結(jié)構(gòu)。

        下面我對(duì)照代碼實(shí)現(xiàn)每個(gè)小黑盒子吧,實(shí)現(xiàn)代碼在這個(gè)文件里面:Layers.py首先介紹線性全連接層,先看代碼吧:

        class?LinearLayer:
        ????def?__init__(self,?input_D,?output_D):
        ????????self._W?=?np.random.normal(0,?0.1,?(input_D,?output_D))?#初始化不能為全0
        ????????self._b?=?np.random.normal(0,?0.1,?(1,?output_D))
        ????????self._grad_W?=?np.zeros((input_D,?output_D))
        ????????self._grad_b?=?np.zeros((1,?output_D))

        ????def?forward(self,?X):
        ????????return?np.matmul(X,?self._W)?+?self._b

        ????def?backward(self,?X,?grad):?
        ????????self._grad_W?=?np.matmul(?X.T,?grad)
        ????????self._grad_b?=?np.matmul(grad.T,?np.ones(X.shape[0]))?
        ????????return?np.matmul(grad,?self._W.T)

        ????def?update(self,?learn_rate):
        ????????self._W?=?self._W?-?self._grad_W?*?learn_rate
        ????????self._b?=?self._b?-?self._grad_b?*?learn_rate

        forward太簡(jiǎn)單了,就不講了,看一下backward。

        backward里面其實(shí)要計(jì)算3個(gè)值,W, b的梯度算完以后要存起來(lái),前一層的梯度算完以后直接作為返回值傳出去,推導(dǎo)的公式如下:注意矩陣求導(dǎo)應(yīng)用鏈?zhǔn)椒▌t的時(shí)候,順序非常重要。要嚴(yán)格按照指定順序來(lái)乘,不然形狀對(duì)不上。具體什么順序,可以自己想辦法慢慢拼湊出來(lái)。

        還有一個(gè)update函數(shù),調(diào)用此函數(shù)這一層會(huì)按照梯度下降法來(lái)更新它的W跟b的值,這個(gè)實(shí)現(xiàn)也很簡(jiǎn)單直接看代碼就明白了。

        然后實(shí)現(xiàn)Relu層:

        class?Relu:
        ????def?__init__(self):
        ????????pass

        ????def?forward(self,?X):
        ????????return?np.where(X?0,?0,?X)

        ????def?backward(self,?X,?grad):
        ????????return?np.where(X?>?0,?X,?0)?*?gr

        由于這一層沒有需要保存參數(shù),只需要實(shí)現(xiàn)以下forward跟backward方法就行了,非常簡(jiǎn)單。

        接下來(lái)開始實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)訓(xùn)練。

        四、搭建神經(jīng)網(wǎng)絡(luò)

        訓(xùn)練部分的代碼在 nn.py 里面,里面的代碼哪里看不懂可以翻回去看之前的解釋,命名都是跟上面說的一樣的。

        #訓(xùn)練數(shù)據(jù):經(jīng)典的異或分類問題
        train_X?=?np.array([[0,0],[0,1],[1,0],[1,1]])
        train_y?=?np.array([0,1,1,0])

        #初始化網(wǎng)絡(luò),總共2層,輸入數(shù)據(jù)是2維,第一層3個(gè)節(jié)點(diǎn),第二層1個(gè)節(jié)點(diǎn)作為輸出層,激活函數(shù)使用Relu
        linear1?=?LinearLayer(2,3)
        relu1?=?Relu()
        linear2?=?LinearLayer(3,1)

        #訓(xùn)練網(wǎng)絡(luò)
        for?i?in?range(10000):

        ????#前向傳播Forward,獲取網(wǎng)絡(luò)輸出
        ????o0?=?train_X
        ????a1?=?linear1.forward(o0)
        ????o1?=?relu1.forward(a1)
        ????a2?=?linear2.forward(o1)
        ????o2?=?a2

        ????#獲得網(wǎng)絡(luò)當(dāng)前輸出,計(jì)算損失loss
        ????y?=?o2.reshape(o2.shape[0])
        ????loss?=?MSELoss(train_y,?y)?#?MSE損失函數(shù)


        ????#反向傳播,獲取梯度
        ????grad?=?(y?-?train_y).reshape(result.shape[0],1)
        ????grad?=?linear2.backward(o1,?grad)
        ????grad?=?relu1.backward(a1,?grad)
        ????grad?=?linear1.backward(o0,?grad)


        ????learn_rate?=?0.01??#學(xué)習(xí)率

        ????#更新網(wǎng)絡(luò)中線性層的參數(shù)
        ????linear1.update(learn_rate)
        ????linear2.update(learn_rate)

        ????#判斷學(xué)習(xí)是否完成
        ????if?i?%?200?==?0:
        ????????print(loss)
        ????if?loss?0.001:
        ????????print("訓(xùn)練完成!?第%d次迭代"?%(i))
        ????????break

        我覺得沒啥好講的,就直接對(duì)著我們的計(jì)算圖,一步一步來(lái)。

        注意一下中間過程幾個(gè)向量的形狀。列向量跟行向量是不一樣的,一不小心把列向量跟行向量做運(yùn)算,numpy不會(huì)報(bào)錯(cuò),而是會(huì)廣播成一個(gè)矩陣。所以運(yùn)算的之前,記得該轉(zhuǎn)置得轉(zhuǎn)置。

        #將訓(xùn)練好的層打包成一個(gè)model
        model?=?[linear1,?relu1,?linear2]

        #用訓(xùn)練好的模型去預(yù)測(cè)
        def?predict(model,?X):
        ????tmp?=?X
        ????for?layer?in?model:
        ????????tmp?=?layer.forward(tmp)
        ????return?np.where(tmp?>?0.5,?1,?0)

        把模型打包然后用上面的predict函數(shù)來(lái)預(yù)測(cè)。也沒啥好說的,就直接往后一直forward就完兒事。

        #開始預(yù)測(cè)
        print("-----")
        X?=?np.array([[0,0],[0,1],[1,0],[1,1]])
        result?=?predict(model,?X)
        print("預(yù)測(cè)數(shù)據(jù)1")
        print(X)
        print("預(yù)測(cè)結(jié)果1")
        print(result)

        預(yù)測(cè)訓(xùn)練完的網(wǎng)絡(luò)就能拿去搞預(yù)測(cè)了,我這里設(shè)置學(xué)習(xí)率為0.01的情況下,在第3315次迭代時(shí)候完成訓(xùn)練。

        最后我們成功地預(yù)測(cè)了訓(xùn)練數(shù)據(jù)。


        往期精彩回顧





        獲取一折本站知識(shí)星球優(yōu)惠券,復(fù)制鏈接直接打開:

        https://t.zsxq.com/662nyZF

        本站qq群1003271085。

        加入微信群請(qǐng)掃碼進(jìn)群(如果是博士或者準(zhǔn)備讀博士請(qǐng)說明):

        瀏覽 78
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            大香蕉操逼视频 | 日日噜噜噜夜夜爽爽狠狠视频, | www.av天天 | 成人永久免费视频在线观看网站 | 国模裸体全部大尺度 | 第一色在线 | 我操综合网 | 青娱乐国产视频 | 免费亲子乱婬一级A片 | 偷拍视频免费在线观看 |