官术网_书友最值得收藏!

1.4 使用神經(jīng)網(wǎng)絡(luò)解決問題

到這里為止,我們的準備工作就做好了。現(xiàn)在,我們對一個簡單的數(shù)據(jù)集進行神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)。

1.4.1 螺旋狀數(shù)據(jù)集

本書在dataset目錄中提供了幾個便于處理數(shù)據(jù)集的類,本節(jié)將使用其中的dataset/spiral.py文件。這個文件中實現(xiàn)了讀取螺旋(旋渦)狀數(shù)據(jù)的類,其用法如下所示(ch01/show_spiral_dataset.py)。

        import sys
        sys.path.append('..')  # 為了引入父目錄的文件而進行的設(shè)定
        from dataset import spiral
        import matplotlib.pyplot as plt

        x, t = spiral.load_data()
        print ('x', x.shape) # (300, 2)
        print ('t', t.shape) # (300, 3)

在上面的例子中,要從ch01目錄的dataset目錄引入spiral.py。因此,上面的代碼通過sys.path.append('..')將父目錄添加到了import的檢索路徑中。

然后,使用spiral.load_data()進行數(shù)據(jù)的讀入。此時,x是輸入數(shù)據(jù), t是監(jiān)督標簽。觀察x和t的形狀,可知它們各自有300筆樣本數(shù)據(jù),其中x是二維數(shù)據(jù),t是三維數(shù)據(jù)。另外,t是one-hot向量,對應(yīng)的正確解標簽的類標記為1,其余的標記為0。下面,我們把這些數(shù)據(jù)繪制在圖上,結(jié)果如圖1-31所示。

圖1-31 學(xué)習(xí)用的螺旋狀數(shù)據(jù)集(用×▲●分別表示3個類)

如圖1-31所示,輸入是二維數(shù)據(jù),類別數(shù)是3。觀察這個數(shù)據(jù)集可知,它不能被直線分割。因此,我們需要學(xué)習(xí)非線性的分割線。那么,我們的神經(jīng)網(wǎng)絡(luò)(具有使用非線性的sigmoid激活函數(shù)的隱藏層的神經(jīng)網(wǎng)絡(luò))能否正確學(xué)習(xí)這種非線性模式呢?讓我們實驗一下。

因為這個實驗相對簡單,所以我們不把數(shù)據(jù)集分成訓(xùn)練數(shù)據(jù)、驗證數(shù)據(jù)和測試數(shù)據(jù)。不過,實際任務(wù)中會將數(shù)據(jù)集分為訓(xùn)練數(shù)據(jù)和測試數(shù)據(jù)(以及驗證數(shù)據(jù))來進行學(xué)習(xí)和評估。

1.4.2 神經(jīng)網(wǎng)絡(luò)的實現(xiàn)

現(xiàn)在,我們來實現(xiàn)一個具有一個隱藏層的神經(jīng)網(wǎng)絡(luò)。首先,import語句和初始化程序的__init__()如下所示(ch01/two_layer_net.py)。

        import sys
        sys.path.append('..')
        import numpy as np
        from common.layers import Affine, Sigmoid, SoftmaxWithLoss

        class TwoLayerNet:
            def__init__(self, input_size, hidden_size, output_size):
                I, H, O = input_size, hidden_size, output_size

                # 初始化權(quán)重和偏置
                W1 = 0.01 * np.random.randn(I, H)
                b1 = np.zeros(H)
                W2 = 0.01 * np.random.randn(H, O)
                b2 = np.zeros(O)

                # 生成層
                self.layers = [
                    Affine(W1, b1),
                    Sigmoid(),
                    Affine(W2, b2)
                ]
                self.loss_layer = SoftmaxWithLoss()
                  # 將所有的權(quán)重和梯度整理到列表中
                  self.params, self.grads = [], []
                  for layer in self.layers:
                      self.params += layer.params
                      self.grads += layer.grads

初始化程序接收3個參數(shù)。input_size是輸入層的神經(jīng)元數(shù),hidden_size是隱藏層的神經(jīng)元數(shù),output_size是輸出層的神經(jīng)元數(shù)。在內(nèi)部實現(xiàn)中,首先用零向量(np.zeros())初始化偏置,再用小的隨機數(shù)(0.01 *np.random.randn())初始化權(quán)重。通過將權(quán)重設(shè)成小的隨機數(shù),學(xué)習(xí)可以更容易地進行。接著,生成必要的層,并將它們整理到實例變量layers列表中。最后,將這個模型使用到的參數(shù)和梯度歸納在一起。

因為Softmax with Loss層和其他層的處理方式不同,所以不將它放入layers列表中,而是單獨存儲在實例變量loss_layer中。

接著,我們?yōu)門woLayerNet實現(xiàn)3個方法,即進行推理的predict()方法、正向傳播的forward()方法和反向傳播的backward()方法( ch01/two_layer_net.py)。

        def predict(self, x):
            for layer in self.layers:
                x = layer.forward(x)
            return x

        def forward(self, x, t):
            score = self.predict(x)
            loss = self.loss_layer.forward(score, t)
            return loss

        def backward(self, dout=1):
            dout = self.loss_layer.backward(dout)
            for layer in reversed(self.layers):
                dout = layer.backward(dout)
            return dout

如上所示,這個實現(xiàn)非常清楚。因為我們已經(jīng)將神經(jīng)網(wǎng)絡(luò)中要用的處理模塊實現(xiàn)為了層,所以這里只需要以合理的順序調(diào)用這些層的forward()方法和backward()方法即可。

1.4.3 學(xué)習(xí)用的代碼

下面,我們來看一下學(xué)習(xí)用的代碼。首先,讀入學(xué)習(xí)數(shù)據(jù),生成神經(jīng)網(wǎng)絡(luò)(模型)和優(yōu)化器。然后,按照之前介紹的學(xué)習(xí)的4個步驟進行學(xué)習(xí)。另外,在機器學(xué)習(xí)領(lǐng)域,通常將針對具體問題設(shè)計的方法(神經(jīng)網(wǎng)絡(luò)、SVM等)稱為模型。學(xué)習(xí)用的代碼如下所示(ch01/train_custom_loop.py)。

        import sys
        sys.path.append('..')
        import numpy as np
        from common.optimizer import SGD
        from dataset import spiral
        import matplotlib.pyplot as plt
        from two layer net import TwoLayerNet

        # ?設(shè)定超參數(shù)
        max_epoch = 300
        batch_size = 30
        hidden_size = 10
        learning_rate = 1.0

        # ?讀入數(shù)據(jù),生成模型和優(yōu)化器
        x, t = spiral.load_data()
        model = TwoLayerNet(input_size=2, hidden_size=hidden_size, output_size=3)
        optimizer = SGD(lr=learning_rate)

        # 學(xué)習(xí)用的變量
        data_size = len(x)
        max_iters = data_size // batch_size
        total_loss = 0
        loss_count = 0
        loss_list = []
        for epoch in range(max_epoch):
            # ?打亂數(shù)據(jù)
            idx = np.random.permutation(data_size)
            x = x[idx]
            t = t[idx]

            for iters in range(max_iters):
                batch_x = x[iters*batch_size:(iters+1)*batch_size]
                batch_t = t[iters*batch_size:(iters+1)*batch_size]

            # ?計算梯度,更新參數(shù)
            loss = model.forward(batch_x, batch_t)
            model.backward()
            optimizer.update(model.params, model.grads)
            total_loss += loss
            loss_count += 1

            # ?定期輸出學(xué)習(xí)過程
            if (iters+1) % 10 == 0:
                avg_loss = total_loss / loss_count
                print ('| epoch %d |  iter %d / %d | loss %.2f'
                      % (epoch + 1, iters + 1, max_iters, avg_loss))
                loss_list.append(avg_loss)
                total_loss, loss_count = 0, 0

首先,在代碼?的地方設(shè)定超參數(shù)。具體而言,就是設(shè)定學(xué)習(xí)的epoch數(shù)、mini-batch的大小、隱藏層的神經(jīng)元數(shù)和學(xué)習(xí)率。接著,在代碼?的地方進行數(shù)據(jù)的讀入,生成神經(jīng)網(wǎng)絡(luò)(模型)和優(yōu)化器。我們已經(jīng)將2層神經(jīng)網(wǎng)絡(luò)實現(xiàn)為了TwoLayerNet類,將優(yōu)化器實現(xiàn)為了SGD類,這里直接使用它們就可以。

epoch表示學(xué)習(xí)的單位。1個epoch相當(dāng)于模型“看過”一遍所有的學(xué)習(xí)數(shù)據(jù)(遍歷數(shù)據(jù)集)。這里我們進行300個epoch的學(xué)習(xí)。

在進行學(xué)習(xí)時,需要隨機選擇數(shù)據(jù)作為mini-batch。這里,我們以epoch為單位打亂數(shù)據(jù),對于打亂后的數(shù)據(jù),按順序從頭開始抽取數(shù)據(jù)。數(shù)據(jù)的打亂(準確地說,是數(shù)據(jù)索引的打亂)使用np.random.permutation()方法。給定參數(shù)N,該方法可以返回0到N-1的隨機序列,其實際的使用示例如下所示。

        >>> import numpy as np
        >>> np.random.permutation(10)
        array([7, 6, 8, 3, 5, 0, 4, 1, 9, 2])

        >>> np.random.permutation(10)
        array([1, 5, 7, 3, 9, 2, 8, 6, 0, 4])

像這樣,調(diào)用np.random.permutation()可以隨機打亂數(shù)據(jù)的索引。

接著,在代碼?的地方計算梯度,更新參數(shù)。最后,在代碼?的地方定期地輸出學(xué)習(xí)結(jié)果。這里,每10次迭代計算1次平均損失,并將其添加到變量loss_list中。以上就是學(xué)習(xí)用的代碼。

這里實現(xiàn)的神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)用的代碼在本書其他地方也可以使用。因此,本書將這部分代碼作為Trainer類提供出來。使用Trainer類,可以將神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)細節(jié)嵌入Trainer類。詳細的用法將在1.4.4節(jié)說明。

運行一下上面的代碼(ch01/train_custom_loop.py)就會發(fā)現(xiàn),向終端輸出的損失的值在平穩(wěn)下降。我們將結(jié)果畫出來,如圖1-32所示。

由圖1-32可知,隨著學(xué)習(xí)的進行,損失在減小。我們的神經(jīng)網(wǎng)絡(luò)正在朝著正確的方向?qū)W習(xí)!接下來,我們將學(xué)習(xí)后的神經(jīng)網(wǎng)絡(luò)的區(qū)域劃分(也稱為決策邊界)可視化,結(jié)果如圖1-33所示。

由圖1-33可知,學(xué)習(xí)后的神經(jīng)網(wǎng)絡(luò)可以正確地捕獲“旋渦”這個模式。也就說,模型正確地學(xué)習(xí)了非線性的區(qū)域劃分。像這樣,神經(jīng)網(wǎng)絡(luò)通過隱藏層可以實現(xiàn)復(fù)雜的表現(xiàn)力。深度學(xué)習(xí)的特征之一就是疊加的層越多,表現(xiàn)力越豐富。

圖1-32 損失的圖形:橫軸是學(xué)習(xí)的迭代次數(shù)(刻度值的10倍),豎軸是每10次迭代的平均損失

圖1-33 學(xué)習(xí)后的神經(jīng)網(wǎng)絡(luò)的決策邊界(用不同顏色描繪神經(jīng)網(wǎng)絡(luò)識別的各個類別的區(qū)域)

1.4.4 Trainer類

如前所述,本書中有很多機會執(zhí)行神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)。為此,就需要編寫前面那樣的學(xué)習(xí)用的代碼。然而,每次都寫相同的代碼太無聊了,因此我們將進行學(xué)習(xí)的類作為Trainer類提供出來。Trainer類的內(nèi)部實現(xiàn)和剛才的源代碼幾乎相同,只是添加了一些新的功能而已,我們在需要的時候再詳細說明其用法。

Trainer類的代碼在common/trainer.py中。這個類的初始化程序接收神經(jīng)網(wǎng)絡(luò)(模型)和優(yōu)化器,具體如下所示。

        model = TwoLayerNet(...)
        optimizer = SGD(lr=1.0)
        trainer = Trainer(model, optimizer)

然后,調(diào)用fit()方法開始學(xué)習(xí)。fit()方法的參數(shù)如表1-1所示。

表1-1 Trainer類的fit()方法的參數(shù)。表中的"(=XX)"表示參數(shù)的默認值

另外,Trainer類有plot()方法,它將fit()方法記錄的損失(準確地說,是按照eval_interval評價的平均損失)在圖上畫出來。使用Trainer類進行學(xué)習(xí)的代碼如下所示(ch01/train.py)。

        import sys
        sys.path.append('..')
        from common.optimizer import SGD
        from common.trainer import Trainer
        from dataset import spiral
        from two layer net import TwoLayerNet

        max_epoch = 300
        batch_size = 30
        hidden_size = 10
        learning_rate = 1.0

        x, t = spiral.load_data()
        model = TwoLayerNet(input_size=2, hidden_size=hidden_size, output_size=3)
        optimizer = SGD(lr=learning_rate)

        trainer = Trainer(model, optimizer)
        trainer.fit(x, t, max_epoch, batch_size, eval_interval=10)
        trainer.plot()

執(zhí)行這段代碼,會進行和之前一樣的神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)。通過將之前展示的學(xué)習(xí)用的代碼交給Trainer類負責(zé),代碼變簡潔了。本書今后都將使用Trainer類進行學(xué)習(xí)。

主站蜘蛛池模板: 格尔木市| 汽车| 绥江县| 铜鼓县| 娱乐| 保亭| 玛多县| 东乡族自治县| 临安市| 弋阳县| 定边县| 伊春市| 桐梓县| 墨竹工卡县| 嘉定区| 巩义市| 梓潼县| 蓬溪县| 瑞金市| 许昌市| 广水市| 会同县| 玉林市| 德化县| 华安县| 博野县| 西宁市| 马山县| 黄浦区| 万盛区| 恩平市| 汉阴县| 渭源县| 浦北县| 东宁县| 高台县| 烟台市| 合作市| 淮北市| 高雄县| 潮州市|