本文主要內容
介紹人工神經網路的基礎知識。指導神經網路學習如何識別手寫數字。組合多個層來建立神經網路。理解神經網路從資料中學習的原理。從零開始實現一個簡單的神經網路。本章介紹人工神經網路(Artificial Neural Network,ANN)的核心概念。這類演算法對現代深度學習至關重要。人工神經網路的歷史可以追溯到20世紀40年代早期。歷經數十年,它在許多領域的應用都取得了巨大成功,但其基本思想都保留了下來。
人工神經網路的核心理念,是從神經科學中汲取靈感,創造出一類與人們猜想的大腦工作方式類似的演算法。其中特別地,我們採用神經元(neuron)的概念來作為人工神經網路的基礎構件。多個神經元組合為不同的層(layer),不同層之間以特定的方式彼此連線(connect),從而組織成一個網路(network)。給定輸入資料,神經元可以透過各層之間的連線逐層傳輸資訊。如果訊號足夠強,我們說神經元會被啟用(activate)。這樣資料透過網路層層傳播,到達最後一層,即輸出層,並得到預測(prediction)結果。然後我們可以把這些預測與期望輸出(expected output)進行比較,以計算預測的誤差(error),神經網路可以利用它來進行學習,並改進未來的預測。
雖然由大腦結構啟發的類比很有用,但我們不想在這裡過度強調它。我們確實對大腦的視覺皮層知之甚多,但這種類比有時會產生誤導,甚至引發錯誤的理解。我們認為最好將人工神經網路看作是試圖揭示生物體的學習機制的指導原則,就像飛機利用了空氣動力學原理,但並不會完全複製鳥類的功能。
為了使本章內容更為具體,我們提供了一個從零開始實現的基本神經網路。我們將應用這個神經網路來解決一個光學字元識別(Optical Character Recognition,OCR)領域的問題,即讓計算機預測影象中的手寫數字是哪一個。
在我們的OCR資料集中,每個影象都由畫素網格組成,因此必須分析畫素之間的空間關係才能確定它所代表的數字。圍棋等眾多棋類遊戲也是在網格上下棋的,也必須考慮棋盤上的空間關係才能選擇良好的動作。因此我們可能會期待OCR的機器學習技術也能夠應用到圍棋之類的遊戲中。事實證明,它們確實有效。第6章至第8章將介紹如何把這些方法應用於圍棋遊戲。
本章會盡量少涉及數學內容。如果讀者對線性代數、微積分和機率論的基礎知識不夠熟悉,或者想要一些簡短實用的溫習材料,我們建議先閱讀附錄A。此外,附錄B對神經網路學習過程中比較困難的部分(即反向傳播演算法)有詳細的數學介紹。如果讀者已經對神經網路有所瞭解,但還從未實現過神經網路程式碼,我們建議你立即跳到5.5節。如果你已熟悉神經網路的實現,請直接進入第6章,瞭解如何用神經網路來預測第4章中生成的棋局中的動作。
5.1 一個簡單的用例:手寫數字分類在詳細介紹神經網路之前,讓我們先認識一個具體用例。在本章中,我們將構建一個可以良好地預測手寫數字影象的應用,其準確率約為95%。值得注意的是,只需將影象的畫素值傳送給神經網路就可完成所有操作,這個演算法能夠自己學會如何抽取數字的結構資訊。
要實現這個目標,我們將採用改進版美國國家標準與技術研究所(Modified National Institute of Standards and Technology,MNIST)手寫數字資料集。這是一個經過深入研究的資料集,它在深度學習領域的地位相當於果蠅在生物研究界的地位。
在本章中,我們將使用NumPy庫來處理底層數學運算。NumPy是Python機器學習和科學計算的行業標準,本書的其餘部分也都會使用它。在嘗試執行本章的程式碼示例之前,讀者應當先透過慣用的包管理器來安裝NumPy。例如,如果使用的是pip,可以在shell裡執行pip install numpy來安裝它;如果使用的是Conda,可以執行conda install numpy。
5.1.1 MNIST手寫數字資料集MNIST資料集由60 000個影象組成,所有影象尺寸均為28畫素 × 28畫素。圖5-1展示了這個資料集的幾個樣本示例。對人類來說,識別出示例中的大多數影象都是小菜一碟。例如,人可以輕鬆地讀出第一行的數字7、5、3、9、3、0等,但資料集中也有少數影象連人類都難以判斷。例如,圖 5-1 中第5行的第4張圖片,很難分辨到底是4還是9。
圖5-1 取自MNIST手寫數字資料集的一些樣本。這是光學字元識別領域中經過充分研究的一個數據集
MNIST中每個影象都帶有標籤註釋,其內容是一個0~9的數字,用來表示影象所描繪的真實數字值。
在檢視具體資料之前,需要先載入它。這套資料可以在本書的GitHub程式碼庫中找到,放在資料夾dlgo/nn中名為mnist.pkl.gz的檔案裡。
這個資料夾還包含了本章將要編寫的全部程式碼。但和以往一樣,我們建議讀者按照本章的內容流程從零開始編寫程式碼。當然,你也可以嘗試直接執行GitHub程式碼庫中的程式碼。
5.1.2 MNIST資料的預處理由於這個資料集中的標籤是0~9的整數,我們可以使用一種稱為獨熱編碼(one-hot encoding)的技術將標籤值轉換為長度為10的向量,如程式碼清單5-1所示。例如,數字1可以轉換為一個長度為10的向量,其中槽位1的值為1,而其他9個槽位的值都是0。獨熱編碼是一種很有用的表示方式,它在各種機器學習場景中都有廣泛應用。用向量中的第一個槽位來表示標籤1,這種方式可以讓神經網路之類的演算法更容易區分不同的標籤。使用獨熱編碼,數字2表示為[0,0,1,0,0,0,0,0,0,0]。
程式碼清單5-1 用獨熱編碼對MNIST標籤進行編碼
import six.moves.cPickle as pickleimport gzipimport numpy as npdef encode_label(j): ⇽--- 將索引編碼為長度為10的向量 e = np.zeros((10, 1)) e[j] = 1.0 return e
獨熱編碼的優點是每個數字都有自己的槽位,這樣就可以使用神經網路為單個輸入影象輸出每個槽位的機率(probability),這在之後的計算中很有用處。
檢查檔案mnist.pkl.gz的內容,我們可以發現它包含了3個可用的資料池:訓練資料、驗證資料和測試資料。回憶第1章,我們使用訓練資料來訓練或擬合機器學習演算法,使用測試資料來評估演算法的學習效果。驗證資料可用於調整和驗證演算法的配置,在本章中可以先忽略。
MNIST資料集中的影象是二維的,高度和寬度均為28畫素。影象資料可以載入成維度為784(即28×28)的特徵向量(feature vector)。這裡我們完全丟棄了影象結構,只把畫素作為向量。這個向量的每個值可以取0或1,其中0表示白色,1表示黑色,如程式碼清單5-2所示。
程式碼清單5-2 將MNIST資料轉換變形並載入訓練資料與測試資料
def shape_data(data): features = [np.reshape(x, (784, 1)) for x in data[0]] ⇽--- 將輸入影象展平成維度為784的特徵向量 labels = [encode_label(y) for y in data[1]] ⇽--- 所有的標籤都用獨熱編碼進行編碼 return zip(features, labels) ⇽--- 建立特徵標籤對def load_data(): with gzip.open('mnist.pkl.gz', 'rb') as f: train_data, validation_data, test_data = pickle.load(f) ⇽--- 解壓並載入MNIST資料,得到3個數據集 return shape_data(train_data), shape_data(test_data) ⇽--- 忽略驗證資料,將其他兩個資料集轉換變形
這樣我們就得到了一個表示MNIST資料集的簡單形式:特徵和標籤都被編碼為向量。接下來的任務是設計一種機制來學習如何準確地將特徵對映到標籤上。具體而言,我們需要設計一種演算法來學習訓練資料的特徵與標籤,並根據測試資料給出的特徵預測出對應的標籤。
我們在下一節可以看到,神經網路可以很好地完成這項工作。我們先討論一種簡單的方法來揭示這類應用所需要解決的共同問題。識別數字對人類來說是一項相對簡單的任務,但我們很難精確地解釋人們如何做到這一點,也很難解釋我們是如何瞭解到我們所知的。這種現象,即知道如何做,卻無法解釋清楚為什麼知道,被稱為波拉尼悖論(Polanyi’s paradox)。這一點導致我們想向計算機明確描述如何解決這個問題變得特別困難。
在這個應用中扮演關鍵角色的重要問題是模式識別(pattern recognition),即每個手寫數字都有著和它的數字原型有關的特徵。例如,0大致是橢圓形的;又如,在許多國家中1用一條簡單的豎線表示。根據這些簡單的特徵邏輯,我們可以透過相互比較手寫數字,對手寫數字進行粗略地分類:給定一個數字8的影象,那麼與其他數字相比,它應該更接近於8的平均影象。程式碼清單5-3中的average_digit函式可以計算一個數字的平均影象。
程式碼清單5-3 計算相同數字的所有圖形的平均值
import numpy as npfrom dlgo.nn.load_mnist import load_datafrom dlgo.nn.layers import sigmoid_doubledef average_digit(data, digit): ⇽--- 為資料集中所有代表指定數字的樣本計算平均值 filtered_data = [x[0] for x in data if np.argmax(x[1]) == digit] filtered_array = np.asarray(filtered_data) return np.average(filtered_array, axis=0)train, test = load_data()avg_eight = average_digit(train, 8) ⇽--- 把數字8的平均值作為一個簡單模型的引數,用來檢測數字8
訓練集中的數字8的平均影象是什麼樣的?圖5-2給出了答案。
圖5-2 這是MNIST訓練集中的手寫數字8的平均影象。通常來說,將數百個不同影象取平均值,會得到一堆無法識別的斑點,但本例中的這個平均影象看起來仍然非常像數字8
由於手寫的數字個體之間的差距可能很大,因此平均影象會顯得有點兒模糊。圖中的情況確實符合這個預期,但它的形狀仍然能明顯看出是數字8。也許我們可以利用它來識別資料集中的其他數字8?程式碼清單5-4中的程式碼可以用來計算數字8的平均影象,並展示出圖5-2所示的影象。
程式碼清單5-4 計算並展示訓練集中數字8的平均影象
from matplotlib import pyplot as pltimg = (np.reshape(avg_eight, (28, 28)))plt.imshow(img)plt.show()
MNIST的訓練集中數字8的平均值avg_eight包含了數字8在影象中如何呈現的大量相關資訊。可以使用avg_eight作為一個簡單模型的引數來判斷某個表示數字的輸入向量x是否為8。在神經網路的場景中,我們在討論引數的時候,經常說權重,這時候就可以把avg_eight當作權重來用。
為方便起見,可以將這個向量進行轉置並得到W = np.transpose(avg_eight)。然後計算W和x的點積,它會將W和x的畫素值逐對相乘,並將所有784個結果值相加。如果我們的想法是正確的,並且如果x確實是數字8,那麼x的畫素在與W相同的地方應該有差不多的色調值。相反,如果x不是數字8,那麼它與W的重疊就會比較少。讓我們用幾個例子檢驗這個假設,如程式碼清單5-5所示。
程式碼清單5-5 使用點積計算一個數字影象與權重的接近程度
x_3 = train[2][0] ⇽--- 下標為2的訓練樣本是數字4x_18 = train[17][0] ⇽--- 下標為17的訓練樣本是數字8W = np.transpose(avg_eight)np.dot(W, x_3) ⇽--- 這一項的計算結果是20.1np.dot(W, x_18) ⇽--- 這一項的計算結果要大得多,約為54.2
我們選取兩個MNIST樣本,其中一個代表數字4,另一個代表數字8,並計算它們與權重W的點積。可以看到,數字8的點積結果為54.2,遠遠高於數字4的點積結果20.1。看起來我們走對路了。那麼,點積結果是多少的時候應當判斷為數字8呢?理論上兩個向量的點積可以算出任意實數值。我們可以把點積的輸出值進行轉換(transform)並對映到輸出範圍為[0, 1]的某個值,然後就可以試著設定一個如0.5這樣的判斷閾值,對於高於閾值的結果就可以判定為數字8了。
要進行轉換,可以使用sigmoid函式。sigmoid函式通常用σ來表示。對於一個實數x,sigmoid函式定義如下:
圖5-3展示了這個函式的大致形狀,讀者可以有一些直觀感受。
圖5-3 繪製sigmoid函式的圖形。sigmoid函式將實數值對映到[0, 1]的範圍內。在0附近, 它的斜率相當陡峭;而隨著引數值正向增大或負向減小,曲線逐漸變得平緩
接下來,在將sigmoid函式應用到點積的輸出之前,讓我們先在Python中編寫sigmoid函式,如程式碼清單5-6所示。
程式碼清單5-6 為double型別和向量型別的資料實現簡單的sigmoid函式
def sigmoid_double(x): return 1.0 / (1.0 + np.exp(-x))def sigmoid(z): return np.vectorize(sigmoid_double)(z)
注意,首先要寫一個接收double型別引數的函式sigmoid_double,然後再利用它來實現針對向量值的sigmoid函式,這個函式會在本章中多次應用。在實際計算之前請注意,2的sigmoid函式結果已經非常接近1,因此對於先前計算的兩個樣本,sigmoid(54.2)和sigmoid(20.1),就幾乎無法區分了。要解決這個問題,可以將點積的輸出朝0偏移(shifting)。我們把執行一次這樣的偏移操作稱為應用一個偏差項(bias term),通常寫作b。從樣本中可以計算出,偏差項的一個比較理想的估計值是b = −45。有了權重和偏差項,現在就可以按照程式碼清單5-7所示的方法來計算模型的預測值(prediction)了。
程式碼清單5-7 使用點積和sigmoid函式,根據權重與偏差計算出預測值
def predict(x, W, b): ⇽--- 透過對np.dot(W, x)+b的輸出應用sigmoid函式來計算出一個簡單預測值 return sigmoid_double(np.dot(W, x) + b)b = -45 ⇽--- 根據之前計算過的示例,將偏差項設定為−45print(predict(x_3, W, b)) ⇽--- 這個數字4的示例,其預測值接近0print(predict(x_18, W, b)) ⇽--- 這個數字8的示例,其預測值是0.96。看來這個啟發式規則是有效果的
從上面兩個示例x_3和x_18中,我們得到了令人滿意的結果。前者的預測值幾乎為0,而後者的預測值接近於1。我們把這個將W的輸入向量x對映到σ(Wx + b)並得到與x維度相同的向量的過程,稱為對率迴歸(logistic regression)。圖5-4展示了這個演算法用於維度為4的向量的示例。
圖5-4 對率迴歸的示例,將維度為4的輸入向量x對映到0~1的輸出值y。本圖表明輸出y依賴於輸入向量x中的所有4個值
為了更好地瞭解這個過程的工作效果,讓我們用它來計算所有訓練樣本和測試樣本的預測值。如前所述,我們可以定義一個截斷點,或決策閾值(decision threshold),用來決定預測值是否算作數字8。我們可以選擇準確率(accuracy)作為評估指標。準確率就是所有的預測結果中預測正確的比率,如程式碼清單5-8所示。
程式碼清單5-8 評估使用了決策閾值的模型預測
def evaluate(data, digit, threshold, W, b): ⇽--- 作為評估指標,我們可以選擇準確率,即所有預測結果中預測正確的比率 total_samples = 1.0 * len(data) correct_predictions = 0 for x in data: if predict(x[0], W, b) > threshold and np.argmax(x[1]) == digit: ⇽--- 將數字8的例項預測為8,是一次正確的預測 correct_predictions += 1 if predict(x[0], W, b) <= threshold and np.argmax(x[1]) != digit: ⇽--- 如果預測值低於設定的閾值,並且樣本也確實不是數字8,那麼這也是一次正確的預測 correct_predictions += 1 return correct_predictions / total_samples
讓我們使用這個評估函式來評估3個數據集的預測質量:訓練集、測試集和測試集中所有數字8的集合。和前面一樣,這裡將決策閾值設為0.5,權重為W,偏差項為b,如程式碼清單5-9所示。
程式碼清單5-9 計算3個數據集的預測準確率
evaluate(data=train, digit=8, threshold=0.5, W=W, b=b) ⇽--- 這個簡單模型在訓練資料上的準確率為78%(0.7814)evaluate(data=test, digit=8, threshold=0.5, W=W, b=b) ⇽--- 在測試資料上的準確率稍低,為77%(0.774 9)eight_test = [x for x in test if np.argmax(x[1]) == 8]evaluate(data=eight_test, digit=8, threshold=0.5, W=W, b=b) ⇽--- 只對測試集中的數字8的集合進行評估,則準確率僅為67%(0.666 3)
可以看到,使用訓練集的準確率最高,約為78%。這並不奇怪,因為我們的模型就是透過在訓練集上調校(calibrate)而得出的。但是對訓練集進行評估是沒有意義的,因為這樣做無法得知演算法的泛用效果(generalize),即對沒有見過的資料集的執行效果。在測試資料上的表現接近於在訓練資料上的表現,準確率約為77%。而最後一個準確率最值得注意:在所有數字8的測試集中,我們只達到了約66%的準確率,這相當於每遇到3個新數字8我們只能猜對其中2個。這個結果作為一個基線還可以接受,但遠遠不是能夠達到的最好結果。那麼到底哪裡出了問題呢?又有哪些地方可以做得更好?
模型現在只能區分特定數字(此處為數字8)與其他所有數字。由於訓練集和測試集中每個數字的影象數量分佈是均衡的(balanced),數字8的樣本大約只佔10%。因此,只要用一個始終預測數字為0的簡單模型,我們就能得到約90%的準確率。在分析解決分類問題的時候,需要特別注意這種分類不均衡(class imbalance)的情況。考慮到這一點,模型在測試資料集上77%的準確率就不再顯得優秀了。我們需要定義一個可以準確預測全部10個數字的模型。模型引數相當小。對於有數千種不同風格的手寫影象集合,我們採用的權重集大小卻只和單個影象相同。想要用這麼小的模型來捕獲這麼多樣的手寫風格變化是不現實的。必須找到一類演算法,它們可以有效地使用更多引數來捕獲資料中的可能變化。對於某個給定的預測值,我們只簡單地選取了一個閾值來判定該數字是否為8,而並沒有使用預測的具體值來評估模型的質量。例如,一個預測值為0.95的正確預測,肯定比預測值為0.51的預測結果更有說服力。必須找到合適的形式來表示預測值與真實輸出之間的接近程度。這個模型的引數是透過直覺的引導製作而出的。雖然作為第一次嘗試這可能是一個不錯的結果,但機器學習真正的優勢在於不需要把人們對資料的想法強加到演算法中,而是讓演算法自己去資料中學習。每當模型做出正確預測時,都需要強化這種行為,而每當輸出是錯誤的時候,也需要相應地調整模型。換句話說,需要設計一種能夠根據訓練資料的預測效果來更新模型引數的機制。雖然我們對這個簡單的應用和前面構建的樸素模型只進行了簡短而粗略的討論,但讀者應當已經能感覺到神經網路的很多特徵了。在5.2節中,我們會利用本節示例所建立的直覺,透過逐一解決上面提到的4個問題來正式進入神經網路話題的探討。
5.2 神經網路基礎我們應當如何改進OCR模型呢?前面的介紹已經有所暗示,神經網路可以在這類任務上做得非常出色,遠遠比我們製作的模型要好得多。但製作的模型能夠幫助我們理解構建神經網路的關鍵概念。本節就用神經網路的語言來重新描述5.1節介紹的模型。
5.2.1 將對率迴歸描述為簡單的神經網路在5.1節中,我們實現了用於二元分類(binary classification)的對率迴歸演算法。簡而言之,我們用一個特徵向量x來表示一個數據樣本,作為演算法的輸入,接著將它乘以權重矩陣W,然後再新增偏差項b。要得到一個0~1的預測值y,可以對它應用sigmoid函式:y = σ(Wx + b)。
這裡需要注意幾點。首先特徵向量x可以看成是神經元(有時稱為一個單元,即unit)的集合,它透過W和b與y相連。這個關係我們已經在圖5-4中看到了。另外,sigmoid可以看作一個啟用函式,因為這個函式接收Wx + b的計算結果,並將它對映到[0, 1]。如果將結果這樣解釋:當結果值接近1時,表示神經元y被啟用;當結果值接近0時,則表示神經元不被啟用,那麼我們可以把這個演算法設定看作是人工神經網路的一個簡單示例。
5.2.2 具有多個輸出維度的神經網路在5.1節的用例中,我們將手寫數字的識別問題簡化為一個二元分類問題:區分數字8與其他數字。但是我們真正感興趣的是預測所有10個分類,每個分類代表1個數字。從形式上說,要實現這一點並不困難,只要改變y、W和b所表示的內容即可。換句話說,需要變更模型的輸出、權重與偏差項。
首先,我們把y改為維度為10的向量:每個維度的值代表其中一個數字的可能性。
接下來讓我們相應地調整權重和偏差項。W之前是一個長度為784的向量,現在我們可以將它改為尺寸為(10, 784)的矩陣。這樣就可以對W和輸入向量x進行矩陣乘法,即Wx,其結果將是維度為10的向量。接著如果將偏差項設為維度為10的向量,就可以將其與Wx相加了。最後請注意,對於一個向量z,我們可以透過對它的每一個元素應用sigmoid函式來計算整個向量的sigmoid值:
圖5-5展示了這個稍作修改的設定,示例中有4個輸入神經元和2個輸出神經元。
圖5-5 在這個簡單的神經網路中,4個輸入神經元連線到2個輸出神經元:先將輸入向量與一個2 × 4的矩陣相乘,再與一個二維的偏差項相加,最後對得出結果的每個元素應用sigmoid函式,從而得到輸出
那麼我們的更改有什麼用呢?之前我們是將輸入向量x對映到一個單值y,而現在輸出y變成了一個向量。這樣我們就可以多次進行這種向量到向量的轉換,從而構建出一個更復雜的神經網路,我們稱之為前饋網路(feed-forward network)。
5.3 前饋網路讓我們快速回顧一下我們在5.2節中所做的工作。用更抽象的話來說,我們執行了以下兩個步驟。
(1)我們從一個輸入神經元向量x開始,並對它應用了一個簡單的變換,即z = Wx + b。線上性代數的語言中,這種變換稱為仿射線性變換(affine linear transformation)。為簡化描述,這裡我們用z作為中間變數來代替變換的結果。
(2)我們應用了一個啟用函式,即sigmoid函式y = σ(z)來獲得輸出神經元y。應用σ的結果可以給出y的啟用程度。
前饋網路的關鍵理念在於這個過程可以迭代重複應用,從而可以多次應用上面描述的兩個步驟組成的簡單構建塊。這些構建塊就是我們所謂的層。改用這個術語的話,我們可以說:透過堆疊(stack)許多層來組成的一個多層神經網路(multilayer neural network)。下面我們修改前面的例子,再多引入一層。現在需要執行以下兩個步驟。
(1)從輸入向量x開始,計算z1 = W1x + b1。
(2)從中間結果z1開始,可以計算輸出結果y,公式為y = W2z1 + b2。
注意,此處使用上標來表示向量所在的層,用下標來表示向量或矩陣中的位置。圖5-6展示了一個包含兩層而不是單層的網路示例。
圖5-6 一個兩層人工神經網路。輸入神經元x連線到一組中間單元z,後者再連線到輸出神經元y
此時有一點已經很明顯了:堆疊的層數並沒有特定的限制。我們可以堆疊很多層。此外,啟用函式也不必總是用sigmoid函式。實際上可供選擇的啟用函式非常多,我們將在第6章介紹其中的幾個。我們將網路中所有層中的函式按順序應用於一個或多個數據點的過程,稱為一個前向傳遞(forward pass)。之所以稱為前向傳遞,是因為在這個過程中,資料總是按照輸入到輸出的方向(在圖5-6中即從左到右)向前流動而從不回退。
採用這幾個術語,我們可以用圖5-7來表示一個包含3層的普通前饋網路。
圖5-7 3層前饋神經網路。在定義神經網路時,層數沒有限制,每層的神經元數量也沒有限制
回顧一下我們到目前為止所學的知識。把前面提到過的所有概念集合到一起,列成一個簡明的列表。
順序神經網路是一種將特徵(或輸入神經元)x對映到預測(或輸出神經元)y的結構。我們可以按照順序逐個堆疊一層層簡單函式來構建順序神經網路。神經網路的每一層都是一個將給定輸入對映到輸出的公式。用一批資料計算一層的輸出的過程,稱為一趟前向傳遞。類似地,要計算整個順序神經網路的前向傳遞,可以從輸入向量開始按順序計算每一層的前向傳遞來完成。sigmoid函式是一個啟用函式,它接受實值神經元的向量並對它們進行啟用,即對映到範圍[0, 1]裡的一個值。我們把接近1的數值算作啟用。給定權重矩陣W和偏差項b,應用仿射線性變換Wx + b來構成神經網路的一層。這一層通常稱為稠密層(dense layer)或全連線層(fully connected layer)。後面我們將統一用稠密層來稱呼它。根據實現的不同,稠密層可以內嵌啟用函式,也可以不內嵌。前者的意思是,可以把σ(Wx + b)整體作為單獨的一層,而不僅僅是其中的仿射線性變換。後者的意思是,也可以把啟用函式看作獨立於稠密層之外的單獨一層。在之後的實現中,我們採用後者。總的來說,在分析如何將函式進行拆分並重組為邏輯單元的過程中,是否將啟用函式內嵌到稠密層中其實並沒有本質區別,只是角度不同而已。前饋神經網路是由稠密層和啟用函式所組成的順序網路。出於歷史原因,這種架構也稱為多層感知機(multilayer perceptron,MLP)。但本書並沒有足夠空間來討論這個命名的歷史。所有既不是輸入又不是輸出的神經元都稱為隱藏單元(hidden unit)。相對應地,輸入和輸出神經元稱為可見單元(visible unit)。這麼稱呼是因為隱藏單元是屬於網路內部的,而可見的單元則是從網路外直接可觀察的。這麼說似乎有點兒牽強,因為我們通常都能夠訪問神經網路系統的任何部分,但瞭解這個術語並沒有壞處。同理,輸入和輸出之間的層稱為隱藏層,所有超過兩層的順序網路都至少有一個隱藏層。如果不做特殊說明,我們統一用x代表網路的輸入,用y代表網路的輸出;有時候會用上標來表示正在處理的樣本。堆疊許多層而構建的、具有大量隱藏層的大型網路,稱為深度神經網路(deep neural network),由此才有了深度學習這個說法。
非順序神經網路
在這個階段,讀者只需要關注順序神經網路。這種網路的所有層可以形成一個序列。在順序神經網路中,從輸入開始,每個後續的(隱藏)層都恰好有一個前導和一個後繼,最後以輸出層結束。對於將深度學習應用於圍棋這個需求,順序神經網路就足以涵蓋所有需要了。
通常來說,神經網路理論也允許任意非順序的體系結構。例如,在某些應用中,將兩層的輸出連線或相加(這樣會合並之前的兩層或多層)也是有道理的。在這種情況下,多個輸入將會合並,最後只得到一個輸出。
而在其他應用中,將一個輸入拆分成多個輸出也可能很有用。總而言之,層可以具有多個輸入和輸出。我們將在第11章和第12章分別介紹多輸入和多輸出網路。
具有l層的多層感知機的配置可以透過權重集(W = W1,…, Wl)、(偏差集b = b1,…, bl),以及為每一層選擇的啟用函式集來完全描述。但我們仍然缺少一個學習資料以及更新引數的重要素材——損失函式,以及如何最佳化它。
本文摘自《深度學習與圍棋》
這是一本深入淺出且極富趣味的深度學習入門書。本書選取深度學**年來最重大的突破之一 AlphaGo,將其背後的技術和原理娓娓道來,並配合一套基於 BetaGo 的開原始碼,帶領讀者從零開始一步步實現自己的“AlphaGo”。本書側重實踐,深入淺出,庖丁解牛般地將深度學習和AlphaGo這樣深奧的話題變得平易近人、觸手可及,內容非常精彩。全書共分為3個部分:第一部分介紹機器學習和圍棋的基礎知識,並構建一個最簡圍棋機器人,作為後面章節內容的基礎;第二部分分層次深入介紹AlphaGo背後的機器學習和深度學習技術,包括樹搜尋、神經網路、深度學習機器人和強化學習,以及強化學習的幾個高階技巧,包括策略梯度、價值評估方法、演員-評價方法 3 類技術;第三部分將前面兩部分準備好的知識整合到一起,並最終引導讀者實現自己的AlphaGo,以及改進版AlphaGo Zero。讀完本書之後,讀者會對深度學習這個學科以及AlphaGo的技術細節有非常全面的瞭解,為進一步深入鑽研AI理論、拓展AI應用打下良好基礎。本書不要求讀者對AI或圍棋有任何瞭解,只需要瞭解基本的Python語法以及基礎的線性代數和微積分知識。