在本程式碼研究室中,您將瞭解如何建構和訓練可辨識手寫數字的類神經網路。過程中,當您強化類神經網路來達到 99% 的準確度後,您也會發現深度學習專家用於有效訓練模型的貿易工具。
這個程式碼研究室使用了 MNIST 資料集,這個資料集內含 60,000 個已加上標籤的數位資料集,可持續產生數十年的博士學位。只要使用不到 100 行 Python / TensorFlow 程式碼,就能解決問題。
課程內容
- 什麼是類神經網路以及如何訓練網路
- 如何使用 tf.keras 建構基本的 1 層類神經網路
- 如何新增更多圖層
- 如何設定學習率時間表
- 如何建立卷積類神經網路
- 如何使用正規化技巧:放棄、批次正規化
- 過度配適的情形
軟硬體需求
使用瀏覽器。本講習課程完全配合 Google Colaboratory 進行。
說明
如果您發現此研究室中有任何疑慮,或是您認為需要改善,請告訴我們。我們透過 GitHub 問題處理意見回饋 [feedback link]。
這個研究室使用了 Google Colaboratory,而且你完全不需要設定。您可以從 Chromebook 執行 Chromebook。請開啟下方的檔案並執行相關儲存格,以便熟悉 Colab 筆記本。
其他操作說明:
選取 GPU 後端
在 Colab 選單中,依序選取 [Runtime >Change runtime type] (變更執行階段類型) 和 [GPU]。首次執行時,會自動連線至執行階段,或者您也可以使用右上角的 [連線] 按鈕。
筆記本執行作業
只要按一下儲存格,再按下 Shift-ENTER 鍵即可逐一執行儲存格。您也可以使用 Runtime > Run all 執行整個筆記本。
目錄
所有筆記本都有目錄。開啟左邊的黑色箭頭即可開啟。
隱藏的儲存格
部分儲存格只會顯示標題。這是 Colab 專用的筆記本功能。您可以按兩下標題來查看程式碼,但這通常不那麼有趣。通常支援或視覺化功能。您仍須執行這些儲存格,才能定義當中的函式。
我們會先觀看類神經網路火車請開啟下方的筆記本並執行所有儲存格。目前還未留意這個代碼,我們之後會加以說明。
在執行筆記本時,把重點放在視覺化。詳情請見以下說明。
訓練資料
我們有一份已加上標籤的資料集資料集,以確定每張圖片代表的意義,也就是介於 0 和 9 之間的數字。在筆記本中,您會看到以下摘錄:
我們將為其 10 類(0、..、9)內建造的內部數字分據。判斷方式為根據內部參數設定正確值,讓分類功能正常運作。這個「正確值」是透過訓練流程學習,因為這種訓練方法需要「含有已加上標籤的資料集」的影像和相關的正確答案。
我們如何知道經過訓練的類神經網路是否成效良好?使用訓練資料集對網路進行測試。該資料集在訓練期間已經多次出現,而且在資料集上的成效相當優異。我們需要另一個已加上標籤的資料集,在訓練時一律看不到,以評估「現實世界」的網路效能。又稱為「驗證資料集」
訓練
隨著訓練進度,一次可進行一批訓練資料,內部模型參數會隨之更新,而模型在辨識手寫數字時,模型也會更加完善。您可以在訓練圖形中查看:
右邊則是「準確率」是指系統可正確識別的百分比。並隨著訓練進度逐漸增多。
在左側,我們可以看到 “quot;lossloss””。為了推動培訓,我們將定義「匹配」函式,代表系統辨識數字有多少,並嘗試將模型最小化。您在這裡看到,訓練進度和驗證資料都會隨著訓練進度而減少,這是好的。這也表示類神經網路正在學習。
X 軸表示「訓練週期」數量,或是整個資料集的疊代作業數量。
預測
模型經過訓練後,就能用來辨識手寫數字。下一張視覺化圖表,以英文 (第一行) 及 10,000 位數的驗證資料集,以該數字顯示後,網站在幾位數上的效能表現。預測類別會顯示在每個數字下方 (如果發生錯誤,會以紅色顯示)。
如您所見,這個初始模式不太理想,但仍能正確識別部分數字。它的最後數據測試精度在 90% 左右,不適用於我們最初的簡化模型,但它仍然意味著它缺少 10,000 個數據中的 1000 個數字。這已經可以顯示很多,這就是所有答案似乎不正確 (紅色) 的原因。
張量
資料會以矩陣的形式儲存。28x28 像素的灰階圖像可融入 28x28 的 2D 矩陣。但如果是彩色圖片,我們需要更多的尺寸。每像素有 3 個色值(紅色、綠色、藍色),因此需要一個三個表,尺寸是 [28, 28, 3]。要存儲一批 128 個彩色圖像,需要一個具有尺寸 [128, 28, 28, 3] 的 4D 表。
這些多維度表格稱為“張量”,其尺寸清單是它們的「形狀」。
如果已經知道下一段的所有粗體字詞,您可以移至下一項練習。如果您才剛進入深度學習,歡迎閱讀本篇文章,請繼續閱讀本信。
對於以圖層順序建構的模型,Keras 提供 Sequential API。舉例來說,使用 3 個密集層的圖片分類工具可以在 Keras 中以下列形式編寫:
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=[28, 28, 1]),
tf.keras.layers.Dense(200, activation="relu"),
tf.keras.layers.Dense(60, activation="relu"),
tf.keras.layers.Dense(10, activation='softmax') # classifying into 10 classes
])
# this configures the training of the model. Keras calls it "compiling" the model.
model.compile(
optimizer='adam',
loss= 'categorical_crossentropy',
metrics=['accuracy']) # % of correct answers
# train the model
model.fit(dataset, ... )
單一密集圖層
MNIST 資料集中的手寫數字是 28x28 像素的灰階圖像。將這些資料分類的最簡單方法是使用 28x28=784 像素做為 1 層類神經網路的輸入內容。
類神經網路中的每個「神經元」都會對其所有輸入內容進行加權加總,加入稱為「偏誤」的常數,然後再透過一些非線性「活化函式」對結果提供結果。“重量”和&hl=“偏差” 是將通過訓練設定的數據。這些變數一開始是以隨機值初始化。
上圖代表一個具有 10 個輸出神經元的 1 層神鏡網絡,因為我們希望將數字分別為 10 個等級(0 到 9)。
使用矩陣乘法
以處理多組圖片的方式為類神經網路層,如何以矩陣乘法表示:
使用權重矩陣 W 中的第一個資料欄,我們會計算第一個圖片的所有像素的加權總和。此總和相當於第一個神經元。使用體重的第二欄,我們會對第二種神經元進行同樣的操作,以至第 10 個神經元為止。然後,可以為其他 99 張圖片重複執行此作業。如果我們為 X 包含 100 個圖像的矩陣,我們 10 個神經元的加重和,我們在 100 個圖像上計算的設計只是 X.W,是矩陣乘法。
每個神經元現在必須新增偏誤 (常數)。因為我們擁有 10 個神經元,所以擁有 10 個偏誤。我們將此向量稱為 10 個值 b。必須新增至先前計算矩陣的每一行。我們使用「魔術」這項神奇的功能,只要寫一個簡單的加號就行了。
我們最後應用了激活功能,例如“softmax”; (如下所述),得到一個描述 1 層神腦網絡的公式,適用於 100 個圖像:
在 Keras 中
透過 Keras 等高階類神經網路程式庫,我們就不需要實作這個公式。但請務必瞭解,類神經網路層只是許多乘法和增益。在 Keras 中,稠密層會顯示為:
tf.keras.layers.Dense(10, activation='softmax')
深入探討
鏈結類神經網路層相當容易。第一層會計算像素的加權總和。後續層會計算前一層輸出的加權總和。
唯一的差別在於,除了神經元數量以外,您也可以選擇啟用功能。
啟動函式:relu、softmax 和 sigmoid
通常所有圖層都會採用「relut」這個啟用功能。分類器中的最後一層會使用「softmax」。
同樣地,「神經元」會計算所有輸入內容的加權總和,並新增名為「偏誤」的值,並且透過啟用函式提供結果。
最常用的啟用函式稱為「整行 RELU」,表示整行線性單位。如上圖所示,這個功能非常簡單。
類神經網路中的傳統啟動功能是「ig 」
適用於分類的 Softmax 啟用
我們的神經網絡的最後一個有 10 個神經元,因為我們希望把手動數字分據到 10 個等級 (0,..9)。它為 0 和 1 之間的 10 個數字輸出,表示此數字為 0、1、2 等的可能性。以最後一個圖層來說,我們會使用名為「&maxt」的啟用函式。
通過獲取每個元素的指數然後使向量正常化,通常通過將其除以其“L1”,例如在向量上施加 maxmax。正常值(即綁定值之和)使正常化值加起為 1 可以被解譯為概率。
啟用前最後一個圖層的輸出內容有時也稱為「logits”」。如果此向量是 L = [L0, L1, L2, L3, L4, L5, L6, L7, L8, L9],那裡:
跨熵損失
現在,類神經網路會透過輸入圖片產生預測資料,因此必須測量這些模型的良好性,也就是網路能夠告訴我們和正確答案之間的距離,通常稱為「標籤」。提醒您,資料集中的所有圖片均有正確的標籤。
任何距離單位都適用,但對分類問題而言,所謂的「交叉熵距離」為最有效。我們將此情況稱為錯誤或「損失」函式:
漸層下降
“&tt;訓練”;類神經網路實際上是指使用訓練圖片和標籤來調整權重和偏誤,以減少交叉熵損失的功能。以下說明這項功能的運作方式。
交叉熵是指權重、偏誤、訓練圖片像素及其已知類別的函式。
如果我們根據所有權重和所有偏誤,計算交叉熵的部分衍生性,因此得到一個「梯度」,也就是特定影像、標籤,以及權重和偏誤的現值。請記住,我們可能有數百萬的權重和偏誤,因此計算梯度聽起來像是很多工作。幸好,TensorFlow 有我們為你代勞。漸層的數學屬性代表「向上移動」。由於我們想要將 1-19 的交叉熵偏低,因此相反地往前走。我們會按梯度的分數更新權重和偏誤。然後,在訓練迴圈中,再次進行相同的批次訓練圖片與標籤。但願上述的對話是融合了資訊量。
少量的批次處理和動力
您可以只針對一個範例圖片計算梯度,並立即更新權重和偏誤,但對一批圖片執行這類調整 (例如 128 張圖片) 代表梯度,可更準確地反映不同範例圖片所施加的限制,因此可更輕易地產生解法。迷你批次的大小是一種可調整的參數。
這種技術有時也稱為「隨機梯度下降」,但有更顯著的優勢:使用批次作業也代表處理較大的矩陣,而且通常較容易針對 GPU 和 TPU 進行最佳化。
但有時轉換程序可能有些困難,即使梯度向量全為零,甚至可以停止。這是否代表我們找到了最低要求?不一定。漸層元件的最小值或最大值可以是零。使用漸層數百萬個元素的漸層向量時,如果這些元素全都是 0,則每個 0 所對應的 1 值和 5 點的相對應機率都相當低。在許多維度的空間中,馬鞍是很常見的情況,因此我們不想讓這些點停止。
插圖:馬鞍縫。梯度為 0,但不是所有方向的下限。(圖片出處:Wikimedia: By Nicoguaro - Own Work, CC BY 3.0)
解決方法是為最佳化演算法增加一些動能,以便在不停下來一路前進的航運點。
詞彙
batch 或 mini-batch:系統一律會對訓練資料和標籤批次執行訓練。這麼做有助於演算法進行融合。「批次」維度通常是資料張量的第一維度。舉例來說,形狀 [100, 192, 192, 3] 的張量包含 100 張 192x192 像素的圖片,每個像素有三個值 (RGB)。
交叉熵損失:分類器中常有特殊損失函式。
密集層:每層神經元的圖層,與每個圖層上方的所有神經元連結。
features:類神經網路的輸入內容有時也稱為「功能」。自行判斷資料集中哪些部分 (或部分組合) 要提供給類神經網路來取得良好的預測,就稱為「特徵工程」。
labels:屬於「類別」的另一個名稱,或監督式分類問題中的正確答案
學習率:在訓練迴圈的各個疊代中,更新權重和偏誤的梯度百分比。
logits:套用啟用函式之前,一層神經元的輸出稱為「logits」。此字詞來自「邏輯函式」(即「 Sigmoid 函式」)。過去是最受歡迎的啟用函式,「邏輯式函式之前的神經輸出」 -「縮短」
loss:用來比對類神經網路輸出內容與正確答案的錯誤
neuron:計算輸入的加權總和、新增偏誤,並透過啟用函式動態饋給結果。
one-hot 編碼:第 3 類類別 (共 5 類) 是以 5 個元素的向量編碼,但第 3 個全為零,除了第 3 個之前為第 1 個。
relu:已更正的線性單位。神經元的熱門啟動功能。
sigmoid:過去也是熱門的一項啟動功能,仍可用於特殊情況下。
softmax:這種特殊啟用函式適用於向量,可增加最大元件和其他所有元素之間的差異,並將向量的正規化為 1,讓該值可解讀為機率的向量。做為分類器的最後一個步驟。
tensor:「張量」類似於矩陣,不過擁有任意數量的維度。1 維張量是向量。二維張量是矩陣。然後,您可以具有 3、4、5 或更多尺寸的張量。
回到研究筆記本,讓我們先閱讀程式碼。
我們來瀏覽這個筆記本中的所有儲存格。
儲存格「參數」
批次大小、訓練訓練週期數以及資料檔案的位置定義在這裡。資料檔案由 Google Cloud Storage (GCS) 代管,因此位址的開頭是 gs://
儲存格「匯入」
這裡會匯入所有必要的 Python 程式庫,包括 TensorFlow 以及用於視覺化的 matplotlib。
儲存格「視覺公用程式 [RUN ME]」
這個儲存格內含不相關的視覺化程式碼。這項功能預設為收合狀態,但您可以在開啟時查看程式碼,只要按兩下即可。
Cell "tf.data.Dataset: 剖析檔案及準備訓練和驗證資料集"
這個儲存格使用 tf.data.Dataset API 從資料檔案載入 MNIST 資料集。您不必花太多時間在這個儲存格上。如果您對於 tf.data.Dataset API 感興趣,請參考以下教學課程:TPU 速度資料管道。目前,基本資訊包括:
MNIST 資料集中的圖片和標籤 (正確答案) 會儲存在固定長度的記錄中,並儲存於 4 個檔案中。您可以透過專屬固定記錄函式載入檔案:
imagedataset = tf.data.FixedLengthRecordDataset(image_filename, 28*28, header_bytes=16)
現在,我們有圖片位元組的資料集,因此需要將圖片解碼為圖片。我們定義出此函式的函式。圖片不會經過壓縮,因此這個函式無需解碼任何內容 (decode_raw
基本上不會有任何作用)。然後,圖片會轉換成 0 到 1 之間的浮點值。我們可以在本文重塑它是一個 2D 圖像,但現在我們將它保留為一個 28*28 大小的像素陣列,因為那是我們最初的稠密層所期望的。
def read_image(tf_bytestring):
image = tf.decode_raw(tf_bytestring, tf.uint8)
image = tf.cast(image, tf.float32)/256.0
image = tf.reshape(image, [28*28])
return image
系統會使用 .map
將這項函式套用到資料集,並取得圖片資料集:
imagedataset = imagedataset.map(read_image, num_parallel_calls=16)
我們會對標籤進行讀取及解碼作業,也會將 .zip
圖片和標籤合併:
dataset = tf.data.Dataset.zip((imagedataset, labelsdataset))
現在我們有成對的資料集 (圖片、標籤)。這是我們的模型。我們還沒準備好在訓練函式中使用這個函式:
dataset = dataset.cache()
dataset = dataset.shuffle(5000, reshuffle_each_iteration=True)
dataset = dataset.repeat()
dataset = dataset.batch(batch_size)
dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)
tf.data.Dataset API 具有準備資料集所需的所有公用程式函式:
.cache
會在 RAM 中快取資料集。這是小型資料集,可以正常運作。.shuffle
會使用 5000 個元素的重組順序。訓練資料的重組相當重要。.repeat
會循環播放資料集。我們會進行多次訓練 (包含多訓練週期)。.batch
會將多張圖片和標籤提取到一個小小裝置。最後,.prefetch
可在 CPU 上訓練目前的批次時,使用 CPU 來準備下一個批次。
驗證資料集以類似的方式準備。現在已準備好定義模型,並使用這個資料集來訓練模型。
手機「Keras 模型」
我們所有的模型都會是多層的連續序列,因此我們可以使用 tf.keras.Sequential
樣式來建立這些模型。一開始,這裡是單一密層。它有 10 個神經元,因為我們將手寫數字分成 10 個類別。它採用了「softmax」,因為啟動是分類器中的最後一層。
Keras 模型也需要瞭解輸入的形狀。tf.keras.layers.Input
可用來定義該值。這裡的輸入向量是像素值長度為 28*28 的平面向量。
model = tf.keras.Sequential(
[
tf.keras.layers.Input(shape=(28*28,)),
tf.keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='sgd',
loss='categorical_crossentropy',
metrics=['accuracy'])
# print model layers
model.summary()
# utility callback that displays training curves
plot_training = PlotTraining(sample_rate=10, zoom=1)
使用 model.compile
函式在 Keras 中設定模型。這裡使用基本最佳化工具 'sgd'
(隨機梯度)。分類模型需要有跨熵損失函式,稱為 Keras 中的 'categorical_crossentropy'
。最後,我們會要求模型計算 'accuracy'
指標,也就是已分類圖片的百分比。
Keras 提供絕佳的 model.summary()
公用程式,可列印您建立的模型詳細資訊。您的種類教師新增了 PlotTraining
公用程式 (定義為「視覺化公用程式」儲存格),以便在訓練過程中顯示各種訓練曲線。
儲存格「訓練並驗證模型」
訓練是呼叫 model.fit
並傳入訓練和驗證資料集的方式。根據預設,Keras 會在每個訓練週期結束時進行一輪驗證。
model.fit(training_dataset, steps_per_epoch=steps_per_epoch, epochs=EPOCHS,
validation_data=validation_dataset, validation_steps=1,
callbacks=[plot_training])
在 Keras 中,您可以使用回呼新增在訓練期間新增自訂行為。這是這堂講習課程的動態更新訓練實作方式。
儲存格「以視覺化方式呈現預測」
模型訓練完成後,您可以呼叫 model.predict()
取得模型預測結果:
probabilities = model.predict(font_digits, steps=1)
predicted_labels = np.argmax(probabilities, axis=1)
我們準備了一組以本機字型轉譯的列印數字做為測試。請記住,類神經網路會從最後的「softmax」傳回 10 個機率的向量。為了找到這個標籤,我們必須找出最高機率。從 numpy 程式庫執行 np.argmax
。
提醒您,我們必須瞭解 128 張圖片,因此模型會傳回 128 個向量的機率,因此需要用到 axis=1
參數。輸出張量的形狀為 [128, 10]。我們正在計算每個圖片所傳回的 10 個機率的 argmax,因此 axis=1
(第一個軸是 0)。
這個簡單的模型已經辨識出 90% 的數字,不好,但是現在您會大幅改善了。
為了提高辨識準確度,我們在類神經網路中加入了更多圖層。
我們只使用 softmax 做為最後一個圖層的啟用函式,因為這最適合用來進行分類。但是在中繼中間層,我們會使用最傳統的啟用功能: sigmoid:
例如,您的模型可能如下所示 (別忘了逗號,tf.keras.Sequential
使用逗號分隔的圖層清單):
model = tf.keras.Sequential(
[
tf.keras.layers.Input(shape=(28*28,)),
tf.keras.layers.Dense(200, activation='sigmoid'),
tf.keras.layers.Dense(60, activation='sigmoid'),
tf.keras.layers.Dense(10, activation='softmax')
])
請看您模型中的「摘要」。參數現在至少要增加 10 倍。應該是 10 倍的效果!但基於某些原因,這 ...
這座失望似乎還被一頂屋頂遭到破壞。發生錯誤。
您剛遇到了神奇網絡,因為人們在 80 和 90 和 90 和 90 的人設計他們。這也難怪他為這個概念付出這麼多點子,從而叫做「AI 冬天」。的確,類神經網路在您加入層時也會變得更加困難。
事訴得,具有多層(今天 20、50 甚至 100 個)的深神鎖網絡可以很好地工作,提供了一些數據髒污技巧,以使它們融合。探索這幾個簡單的技巧就是 2010 年#49 年代深度學習深度學習再新的原因之一。
啟用 RELU
Sigmoid 啟用函式實際上在深層網路中相當困難。它會壓縮 0 到 1 之間的所有值,當您重複執行此動作時,神經元輸出及其梯度可以完全消失。我們已經在歷史上提到這個問題,但現代聯播網使用的 RELU (向量線性單位) 看起來會像這樣:
另一方面,其快速導引的值為 1,至少在右側。啟用 RELU 功能後,即使某些神經元的梯度能夠為零,系統也一律會有明確的非零梯度,並且能夠以良好的速度繼續訓練。
更好的最佳化工具
像這裡這樣的高度維度 - 我們有 10,000 個權重和偏誤的順序,也就是「加/減點」。這些積分並非當地最小的點,但漸層仍為零時,梯度下降最佳化工具會停留在該處。TensorFlow 有各式各樣的可用最佳化器,包括一些能處理大量專業能力的最佳化器,可以安全地通過馬尾。
隨機初始化
就在訓練前初始化權重偏誤的藝術本身是研究領域,且發表了多篇主題相關論文。您可以前往這裡查看 Keras 的所有初始化器。幸好,Keras 會自動執行正確的操作,並使用 'glorot_uniform'
初始化功能,在大多數情況下都是最佳選擇。
Keras 已經做了正確的事情,因此你無須採取任何行動。
NaN ???
交叉熵公式包含對數,而 log(0) 不是數字 (NaN,假設您偏好數字)。交叉熵的輸入內容可以是 0 嗎?輸入內容來自 softmax,這個數值基本上是指數,指數為零。所以我們很安全!
真的嗎?在美妙的數據世界中,我們很安全,但在電腦世界中,exp(-150),以 float32 格式表示,它是像 ZERO 所得到的,以及交叉熵聲明。
不過,什麼都不用做,因為 Keras 會處理這個問題並計算 softmax,然後以交叉的熵處理,確保資訊的穩定性,避免發生廣泛的 NaN。
成功嗎?
目前您的準確率應該已經達到 97%。本次研討會的目標,將會大幅超過 99%,因此請再接再厲。
如果您遇到困難,請參考以下解決方法:
也許我們能加快訓練速度?Adam 轉換最佳化工具中的預設學習率為 0.001。我們來設法增加。
加快步速似乎沒有太大幫助,且這些噪音是什麼?
訓練曲線相當嘈雜,而且會仔細查看兩個驗證曲線:它們從上到下跳。這表示我們太快完成了。我們可以回顧先前的速度,不過還有更好的方法。
絕佳的解決方案就是要快速開始,讓指數逐漸減少。在 Keras 中,您可以使用 tf.keras.callbacks.LearningRateScheduler
回呼來完成此作業。
複製代碼的實用程式碼:
# lr decay function
def lr_decay(epoch):
return 0.01 * math.pow(0.6, epoch)
# lr schedule callback
lr_decay_callback = tf.keras.callbacks.LearningRateScheduler(lr_decay, verbose=True)
# important to see what you are doing
plot_learning_rate(lr_decay, EPOCHS)
別忘了使用您建立的 lr_decay_callback
。新增至 model.fit
的回呼清單:
model.fit(..., callbacks=[plot_training, lr_decay_callback])
這項改變的影響非常令人驚豔。您會發現大部分的雜訊都消失了,且測試的精確度目前持續超過 98%。
模型現在似乎還不錯。請再接再厲。
這對你有幫助嗎?
但實際上,驗證結果仍維持 98% 的進度,請查看驗證損失。上漲了!學習演算法只用於訓練資料,並據此將訓練損失最佳化。不過,系統絕對不會顯示驗證資料,因此過了一段時間後,驗證作業將不再受到驗證損失的影響。不過,有時甚至會收回。
這不會立即影響模型的實際識別能力,但可防止您執行許多疊代作業,這通常表示訓練已無法獲得正面效果。
這類連線通常稱為「過度配適」。當您看到這個連線時,您可以嘗試套用正規化技巧,也就是「丟棄」丟棄技術會在每次訓練疊代時隨機挑選神經神經元。
最終成效
重新出現雜訊 (出乎意料的是,滴水器的運作原理)。驗證損失似乎已不再那麼多,但整體來說,比沒有丟棄的情況更高。但驗證準確度略為下降。這個結果相當令人失望。
看來放棄的方案似乎不是正確的解決方案,或是「過度配適」的概念比較複雜,且背後的某些原因無法轉化為「丟棄」問題?要修正嗎?
什麼是「過度配適」?如果類神經網路能夠學習「不當」的特性,就會發生過度配適的情形。這種訓練方式適用於訓練範例,但對於實際的資料並不甚理想。有些定理技巧 (例如脫落) 可激勵使用者以更有效率的方式學習,但也過度配適。
類神經網路在手上的問題自由度過多時,就會發生基本過度配適的問題。可想而知,由於其中的神經元系統可以儲存所有的訓練圖片,然後透過模式比對來辨識。這會完全無法在實際的資料上失敗。類神經網路必須受到一些限制,因此系統無法在訓練過程中強制瞭解其所學內容。
如果您的訓練資料非常少,即使是小型網路也能直接進行分析,您也會看到「過度配適」。一般來說,您必須取得大量資料才能訓練類神經網路。
最後,如果這本書已經完成所有實驗,針對不同規模的網路進行了實驗,以確保其自由度受限、採用「丟棄」以及對大量資料進行訓練,您可能還是會遇到效能等級問題,導致效能還未改善。這表示您的類神經網路 (目前的形狀) 無法從資料中擷取更多資訊,就如同這裡的例子一樣。
還記得我們使用圖片的方式,是否分割成單一向量?這個想法很糟糕。手寫數字是由形狀所組成,當我們簡化像素時,就會捨棄圖形資訊。不過,有一種類神經網路可運用形狀資訊:卷積式網路。讓我們來試試看。
如果您遇到困難,請參考以下解決方法:
如果已經知道下一段的所有粗體字詞,您可以移至下一項練習。如果您剛開始使用卷積類神經網路,請繼續閱讀本文。
插圖:使用兩個連續的濾鏡篩選圖片,這些濾鏡由 4x4x3=48 顆可學習的權重組成。
這種簡單的捲積類神經網路在 Keras 中看起來會像這樣:
model = tf.keras.Sequential([
tf.keras.layers.Reshape(input_shape=(28*28,), target_shape=(28, 28, 1)),
tf.keras.layers.Conv2D(kernel_size=3, filters=12, activation='relu'),
tf.keras.layers.Conv2D(kernel_size=6, filters=24, strides=2, activation='relu'),
tf.keras.layers.Conv2D(kernel_size=6, filters=32, strides=2, activation='relu'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(10, activation='softmax')
])
在卷積網路的一層中,一個「神經元」只能將上方像素的加權總和疊加在圖片的小型區域。它會新增偏誤,透過啟用函式來加總總和,就像在一般濃度層中的神經元一樣。然後,再以相同的權重對整張圖片重複執行這項作業。請記住,在密集層中,每個神經元都有各自的體重。這裡的單個「修補」會重覆在圖片上沿著兩個方向滑動 (即「卷積」)。輸出結果的值與圖片中的像素相同,但邊緣必須加上邊框間距。這是一項篩選作業。在上面的圖像中,它使用 4x4x3=48 重量的顯示器。
但是,48 體重的不足。為提高自由度,我們會重複執行相同操作的權重組合。這會產生一組新的篩選器輸出。這裡我們稱之為「頻道」。輸出在輸入圖片中與 R、G、B 頻道相近。
加入兩組 (或更多) 組權重後,可新增一個維度,加總為一個張量。這讓我們更能為卷積層加上權重張量的通用形狀。由於輸入和輸出通道的數量是參數,因此我們可以開始堆疊及鏈結卷積層。
插圖:卷積類神經網路可將「立方」的資料轉換為其他「立方體」資料。
分離式捲積,最大集區
通過以 2 或 3 步距執行卷積,我們也可以將生成的數據立方減小,水平。一般有兩種做法:
- 定序卷積:上述滑動濾鏡,01.1 步幅
- 最大池化:施加 MAX 操作的滑動窗口(通常在 2x2 補丁上,每 2 像素重複一次)
插圖:將運算視窗滑動 3 像素可減少輸出值。定序卷積或最大池化(2 個 2x2 窗口中的最大值,2 個步幅滑)是一個在水平尺寸上縮小數據立體的方法。
最終圖層
在最後一個卷積層之後,資料會以「立方體」形式呈現。您可以透過兩種方式將動態饋給傳送到最終密集層。
第一種方法是將資料區塊分割為向量,然後將該資料提供給 Softmax 圖層。有時甚至還可以在 softmax 圖層之前加入密集圖層。這個數值在體重數上通常相當昂貴。卷積網路末端的濃密層可能包含整個類神經網路的一半以上的權重。
我們不可以使用昂貴的密集層,也可以將收到的資料分割為多個部分,只要類別數量眾多、將其值平均化,並且透過 softmax 啟用函式進行分類即可。建立分類頭部的成本為 0 重。在 Keras 中,有一個圖層:tf.keras.layers.GlobalAveragePooling2D()
。
跳到下一個部分,即可針對問題建立卷積網路。
讓我們建立一個用於手寫數字的捲積網路,我們會在頂端使用三個卷積層,將傳統的傳統 maxmax 讀取層置於底部,然後將其與一個完整連接的圖層連結:
請注意,第二卷積層和第三卷積層的步幅為 2,為什麼它們將輸出值的數量為 28x28 到 14x14,然後為 7x7。
我們來撰寫 Keras 程式碼。
在第一個卷積層之前,需要特別留意。的確,它期待 3D 的立方段;數據,我們的數據集到目前為止已設定好密集圖層,而所有圖片的像素也分割成一個向量。我們需要將它們重新調整成 28x28x1 圖片 (1 個頻道即為灰階圖片):
tf.keras.layers.Reshape(input_shape=(28*28,), target_shape=(28, 28, 1))
您可以使用這一行,而不使用現有的 tf.keras.layers.Input
圖層。
在 Keras 中,「relu'-activate 卷積層」的語法如下:
tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu')
如果是急迫的捲積,請寫成:
tf.keras.layers.Conv2D(kernel_size=6, filters=24, padding='same', activation='relu', strides=2)
如果要將某個資料分割成一個向量,以便使用密集層來使用:
tf.keras.layers.Flatten()
如為密集圖層,語法則未變更:
tf.keras.layers.Dense(200, activation='relu')
您的模型是否突破了 99% 的準確度障礙?十分接近... 但要檢查驗證損失曲線鈴聲響起了嗎?
此外,請查看預測數據。您應該已經能夠看到 10,000 個測試數字中的大部分都已正確辨識。只剩下大約 41⁄2 行的檢測(10,000 個中大約 110 位)
如果您遇到困難,請參考以下解決方法:
在先前的訓練課程中,可清楚顯示過度配適的跡象 (但仍無法達到 99% 的準確率)。我們應該再次嘗試下車嗎?
這次做得怎樣?
這次下車地點似乎很順利,驗證損失不再是嚴重的,最終的準確度應該高於 99%。恭喜!
我們第一次嘗試套用投射方式時,我們認為這是過度配適的問題,實際上該問題是發生在類神經網路的架構中。我們必須取得卷積層才能實現進一步的目標
這次似乎過度配適是問題造成的原因,而下跌對你有幫助。請記住,有許多因素會導致訓練和驗證損失曲線中斷,且驗證損失將會逐漸減少。過度配適 (自由度過強,網路不良使用) 只是其中一種。如果資料集太小,或者類神經網路的架構不足,您可能會發現曲線曲線上出現類似的行為,但是放棄作業沒有幫助。
最後,我們來嘗試加入批次正規化。
實際上,這在理論上是記住幾項規則:
現在我們先播放這本書,然後在每個類神經網路層 (最後最後一個) 中新增一個批次準則圖層。請不要將它加入最後的「softmax」圖層。因此不適用。
# Modify each layer: remove the activation from the layer itself.
# Set use_bias=False since batch norm will play the role of biases.
tf.keras.layers.Conv2D(..., use_bias=False),
# Batch norm goes between the layer and its activation.
# The scale factor can be turned off for Relu activation.
tf.keras.layers.BatchNormalization(scale=False, center=True),
# Finish with the activation.
tf.keras.layers.Activation('relu'),
目前準確率如何?
有一個輕微的調校(BATCH_SIZE=64,學習率衰減參數 0.666,稠密層 0.3 上的放射率)和幸運,你可以得到 99.5%。學習率和放棄情形調整是以「最佳做法」為基礎;使用批次準則:
- 批次準則可協助類神經網路融合,通常讓您更快訓練。
- 批次常態是正規化器。您通常可以減少用到的丟棄量,或甚至完全不使用丟棄功能。
解決方案筆記本的訓練執行時間為 99.5%:
您可以在 GitHub 上的 mlengine 資料夾中找到支援雲端版的程式碼,以及在 Google Cloud AI Platform 上執行程式碼的操作說明。您必須建立 Google Cloud 帳戶並啟用計費功能,才能執行這個部分。完成研究室所需的資源通常少於幾美元 (假設一個 GPU 的訓練時間為 1 小時)。如何準備帳戶:
- 建立 Google Cloud Platform 專案 (http://cloud.google.com/console)。
- 啟用計費功能。
- 安裝 GCP 指令列工具 (請參閱 GCP SDK)。
- 建立 Google Cloud Storage 值區 (位於「
us-central1
」地區)。這個值區將用於暫存訓練程式碼及儲存訓練過的模型。 - 啟用必要的 API 並要求必要的配額 (執行訓練指令一次,系統會顯示錯誤訊息,告知要啟用的項目)。
您已經建構第一個類神經網路,並持續訓練它,系統的精確度可達 99%。在這個過程中,技巧並非專門用於 MNIST 資料集,實際上在採用類神經網路時廣泛使用。目前我們特別提供「峭壁」來當做實驗室的禮物,還有用於卡通的實驗版卡片。可以記住您學到的內容:
後續步驟
- 完全連線和卷積式網路後,您不妨看看循環類神經網路。
- 為了在分散式基礎架構上在雲端執行訓練或推論,Google Cloud 提供了 AI 平台。
- 最後,我們非常樂於提供意見。如果您發現此研究室中有任何疑慮,或是您認為需要改善,請告訴我們。我們透過 GitHub 問題處理意見回饋 [feedback link]。
作者:Martin Görner Twitter:@martin_gorner |
這個實驗室的所有卡通圖片版權:alexpokusay / 123RF 圖庫相片