- 深度學(xué)習(xí)進階:自然語言處理
- (日)齋藤康毅
- 2805字
- 2021-02-07 09:25:57
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í)。
- 自然語言處理:基于預(yù)訓(xùn)練模型的方法
- 決策智能:鏈接數(shù)據(jù)、行為和結(jié)果的新智能
- 硅基物語·AI寫作高手:從零開始用ChatGPT學(xué)會寫作
- 新智元:機器+人類=超智能時代
- 賢二機器僧漫游人工智能
- 圖靈的大教堂:數(shù)字宇宙開啟智能時代
- ROS機器人開發(fā)實踐
- 搜索:開啟智能時代的新引擎
- 規(guī)則時代:虛擬現(xiàn)實、人工智能和區(qū)塊鏈構(gòu)建的游戲化未來
- 心與芯:我們與機器人的無限未來
- 深度學(xué)習(xí)與圖像識別:原理與實踐
- 洞察AIGC:智能創(chuàng)作的應(yīng)用、機遇與挑戰(zhàn)
- 成為AI高手:從DeepSeek開啟高效能
- 機器之心
- 從零開始玩轉(zhuǎn)ChatGPT