網(wǎng)絡(luò)的完整實(shí)踐指南)
1. 從“Hello, World!”到神經(jīng)網(wǎng)絡(luò)為什么TensorFlow 2.0是新的起點(diǎn)如果你剛開始接觸深度學(xué)習(xí)打開TensorFlow的官網(wǎng)看到滿屏的API文檔和教程可能會(huì)有點(diǎn)懵。尤其是當(dāng)你聽說TensorFlow 1.x和2.0幾乎是兩個(gè)不同的世界時(shí)這種困惑感會(huì)更強(qiáng)烈。很多人會(huì)直接搜索“pycharm如何安裝tensorflow2.0”這確實(shí)是第一步但安裝之后呢面對一個(gè)空白的Python文件你敲下的第一行代碼應(yīng)該是什么是去理解復(fù)雜的“圖卷積神經(jīng)網(wǎng)絡(luò)通俗理解”還是先搞清楚“bp神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)圖”我的建議是先忘掉那些復(fù)雜的術(shù)語。TensorFlow 2.0最大的變革就是它變得“像Python一樣思考”。在1.x時(shí)代你需要先構(gòu)建一個(gè)靜態(tài)的計(jì)算圖Graph然后在一個(gè)會(huì)話Session里“喂”數(shù)據(jù)并執(zhí)行整個(gè)過程和寫Python直覺是割裂的。而2.0版本尤其是默認(rèn)啟用了Eager Execution即時(shí)執(zhí)行后你可以像使用NumPy一樣逐行執(zhí)行運(yùn)算立即看到結(jié)果。這極大地降低了學(xué)習(xí)門檻讓你能把精力集中在神經(jīng)網(wǎng)絡(luò)本身的思想上而不是框架的“儀式感”上。所以這篇筆記的目標(biāo)讀者就是那些希望繞過歷史包袱直接上手現(xiàn)代深度學(xué)習(xí)工具的朋友。無論你是想理解“前饋神經(jīng)網(wǎng)絡(luò)”的基礎(chǔ)還是為后續(xù)學(xué)習(xí)“卷積神經(jīng)網(wǎng)絡(luò)”或“不同的神經(jīng)網(wǎng)絡(luò)”模型打底子這里的內(nèi)容都將為你構(gòu)建一個(gè)堅(jiān)實(shí)、直觀的起點(diǎn)。我們不追求一步登天去理解所有“神經(jīng)網(wǎng)絡(luò)多目標(biāo)算法”而是扎扎實(shí)實(shí)地從張量Tensor這個(gè)基本概念開始一步步搭建起對神經(jīng)網(wǎng)絡(luò)運(yùn)作方式的認(rèn)識(shí)。你會(huì)發(fā)現(xiàn)所謂復(fù)雜的模型其基礎(chǔ)構(gòu)件和思想都是相通的。2. 環(huán)境搭建與第一行代碼避開新手第一個(gè)坑在開始任何理論之前我們先讓代碼跑起來。環(huán)境配置是勸退新手的第一個(gè)關(guān)卡網(wǎng)上教程眾多但往往因?yàn)橄到y(tǒng)環(huán)境、Python版本或網(wǎng)絡(luò)問題而失敗。這里我提供一個(gè)經(jīng)過大量實(shí)踐驗(yàn)證的、最穩(wěn)妥的路徑。2.1 安裝不止于pip install tensorflow很多人會(huì)直接使用pip install tensorflow。這對于大多數(shù)只想快速體驗(yàn)的用戶來說確實(shí)是最簡單的方法。但是如果你后續(xù)需要用到GPU加速這對于訓(xùn)練稍大一點(diǎn)的模型至關(guān)重要或者你的Python環(huán)境比較復(fù)雜這個(gè)命令可能會(huì)帶來一些隱藏問題。更推薦的安裝方式是使用Anaconda來管理環(huán)境。Anaconda是一個(gè)強(qiáng)大的Python發(fā)行版和環(huán)境管理工具它能很好地解決不同項(xiàng)目間包版本沖突的問題。具體步驟如下安裝Anaconda從官網(wǎng)下載并安裝適合你操作系統(tǒng)的Anaconda。創(chuàng)建獨(dú)立環(huán)境打開終端或Anaconda Prompt執(zhí)行以下命令創(chuàng)建一個(gè)名為tf2的新環(huán)境并指定Python版本推薦3.7-3.9與TensorFlow 2.x兼容性最好conda create -n tf2 python3.8激活環(huán)境conda activate tf2安裝TensorFlow在激活的tf2環(huán)境中使用pip安裝TensorFlow。conda的通道有時(shí)更新較慢pip通常是獲取最新穩(wěn)定版的最佳途徑。pip install tensorflow如果你想安裝GPU版本并且已經(jīng)配置好了CUDA和cuDNN這是一個(gè)相對復(fù)雜的過程建議先熟悉基礎(chǔ)后再嘗試則安裝pip install tensorflow-gpu注意從TensorFlow 2.1開始tensorflow包已經(jīng)同時(shí)包含CPU和GPU支持如果你的系統(tǒng)有符合條件的CUDA環(huán)境它會(huì)自動(dòng)啟用GPU。因此通常直接安裝tensorflow即可。驗(yàn)證安裝是否成功 打開Python解釋器或創(chuàng)建一個(gè)新的.py文件輸入以下代碼import tensorflow as tf print(tf.__version__) print(“GPU是否可用”, tf.config.list_physical_devices(‘GPU’))如果成功輸出版本號(hào)例如2.10.0并且第二行可能列出你的GPU設(shè)備如果沒有GPU則顯示空列表那么恭喜你環(huán)境搭建成功。注意你可能會(huì)遇到各種安裝錯(cuò)誤最常見的是“超時(shí)”或“找不到滿足版本的包”。這通常是由于網(wǎng)絡(luò)問題或Python版本過高/過低導(dǎo)致的。解決方案包括使用國內(nèi)鏡像源如清華源、阿里云源進(jìn)行pip安裝或者檢查并調(diào)整你的Python版本。2.2 TensorFlow 2.0 核心對象初探張量Tensor安裝成功后我們來認(rèn)識(shí)一下TensorFlow的核心數(shù)據(jù)單元——張量Tensor。你可以把它理解為多維數(shù)組。在Eager Execution模式下張量的行為非常直觀。import tensorflow as tf # 創(chuàng)建一個(gè)標(biāo)量0維張量 scalar tf.constant(5) print(scalar) # 輸出tf.Tensor(5, shape(), dtypeint32) print(scalar.numpy()) # 輸出5 (轉(zhuǎn)換為NumPy數(shù)組) # 創(chuàng)建一個(gè)向量1維張量 vector tf.constant([1, 2, 3]) print(vector) # 輸出tf.Tensor([1 2 3], shape(3,), dtypeint32) # 創(chuàng)建一個(gè)矩陣2維張量 matrix tf.constant([[1, 2], [3, 4]]) print(matrix) # 輸出 # tf.Tensor( # [[1 2] # [3 4]], shape(2, 2), dtypeint32) # 進(jìn)行簡單的運(yùn)算 a tf.constant([[1, 2], [3, 4]]) b tf.constant([[5, 6], [7, 8]]) c tf.add(a, b) # 等價(jià)于 c a b print(c) # 輸出 # tf.Tensor( # [[ 6 8] # [10 12]], shape(2, 2), dtypeint32)通過上面的例子你可以立即看到運(yùn)算結(jié)果這和用NumPy幾乎沒有區(qū)別。shape屬性描述了張量的維度dtype描述了數(shù)據(jù)類型如int32,float32。理解shape是后續(xù)所有操作的基礎(chǔ)特別是在構(gòu)建神經(jīng)網(wǎng)絡(luò)層時(shí)數(shù)據(jù)流的形狀必須匹配。3. 前饋神經(jīng)網(wǎng)絡(luò)FNN的核心機(jī)制拆解現(xiàn)在我們進(jìn)入正題。當(dāng)你搜索“前饋神經(jīng)網(wǎng)絡(luò)”或“bp神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)圖”時(shí)看到的可能是一個(gè)由輸入層、隱藏層和輸出層組成的網(wǎng)絡(luò)圖以及復(fù)雜的數(shù)學(xué)公式。我們先拋開公式從數(shù)據(jù)和計(jì)算流的角度來理解它。3.1 神經(jīng)元與全連接層網(wǎng)絡(luò)的基石神經(jīng)網(wǎng)絡(luò)最基本的計(jì)算單元是“神經(jīng)元”。在一個(gè)全連接層Dense Layer中每個(gè)神經(jīng)元都與上一層的所有神經(jīng)元相連。它的計(jì)算可以概括為兩步線性變換output tf.matmul(input, weights) biasesinput輸入數(shù)據(jù)形狀為(batch_size, input_features)。weights權(quán)重矩陣形狀為(input_features, units)。units是該層神經(jīng)元的數(shù)量。biases偏置向量形狀為(units,)。tf.matmul是矩陣乘法。這一步實(shí)現(xiàn)了輸入的加權(quán)求和。激活函數(shù)final_output activation_function(output)線性變換的結(jié)果再經(jīng)過一個(gè)非線性函數(shù)激活函數(shù)如ReLU、sigmoid、tanh等。這是神經(jīng)網(wǎng)絡(luò)能夠擬合復(fù)雜非線性關(guān)系的關(guān)鍵。沒有激活函數(shù)多層網(wǎng)絡(luò)堆疊等價(jià)于一個(gè)單層線性網(wǎng)絡(luò)。在TensorFlow 2.0中我們不需要手動(dòng)定義這些權(quán)重和矩陣乘法。tf.keras.layers.Dense層封裝了這一切。import tensorflow as tf # 定義一個(gè)具有10個(gè)神經(jīng)元的全連接層使用ReLU激活函數(shù) dense_layer tf.keras.layers.Dense(units10, activation‘relu’) # 假設(shè)我們有一個(gè)輸入批量大小為1特征數(shù)為5 input_data tf.constant([[1.0, 2.0, 3.0, 4.0, 5.0]]) # shape: (1, 5) # 將輸入數(shù)據(jù)“通過”這個(gè)層 output dense_layer(input_data) print(output.shape) # 輸出: (1, 10)當(dāng)你第一次調(diào)用dense_layer(input_data)時(shí)層會(huì)自動(dòng)創(chuàng)建所需的權(quán)重和偏置這個(gè)過程稱為“構(gòu)建”。權(quán)重weights的形狀是(5, 10)偏置bias的形狀是(10,)。3.2 從層到模型Sequential API的直觀構(gòu)建單個(gè)層意義不大我們需要將多個(gè)層串聯(lián)起來形成一個(gè)“前饋”網(wǎng)絡(luò)數(shù)據(jù)從輸入層單向流到輸出層。tf.keras.Sequential是最簡單直觀的模型構(gòu)建方式它就像搭積木。假設(shè)我們要構(gòu)建一個(gè)用于手寫數(shù)字識(shí)別MNIST數(shù)據(jù)集的簡單網(wǎng)絡(luò)這個(gè)網(wǎng)絡(luò)結(jié)構(gòu)在“bp神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)圖”中非常經(jīng)典輸入層將28x28的圖片展平為784個(gè)特征。隱藏層1128個(gè)神經(jīng)元ReLU激活。隱藏層264個(gè)神經(jīng)元ReLU激活。輸出層10個(gè)神經(jīng)元對應(yīng)0-9十個(gè)數(shù)字Softmax激活將輸出轉(zhuǎn)化為概率分布。用Sequential可以這樣實(shí)現(xiàn)model tf.keras.Sequential([ # 展平層將二維輸入轉(zhuǎn)換為一維 tf.keras.layers.Flatten(input_shape(28, 28)), # 第一個(gè)全連接隱藏層 tf.keras.layers.Dense(128, activation‘relu’), # 第二個(gè)全連接隱藏層 tf.keras.layers.Dense(64, activation‘relu’), # 輸出層 tf.keras.layers.Dense(10, activation‘softmax’) ]) # 查看模型結(jié)構(gòu)摘要 model.summary()運(yùn)行model.summary()會(huì)打印出清晰的網(wǎng)絡(luò)結(jié)構(gòu)、每層的輸出形狀和參數(shù)數(shù)量。這是檢查模型是否按你預(yù)期構(gòu)建的重要工具。你會(huì)看到僅僅這樣一個(gè)簡單的網(wǎng)絡(luò)就有超過10萬個(gè)需要學(xué)習(xí)的參數(shù)權(quán)重和偏置。3.3 前向傳播數(shù)據(jù)如何流過網(wǎng)絡(luò)定義了模型結(jié)構(gòu)前向傳播就是順理成章的事情。你只需要將輸入數(shù)據(jù)比如一批圖片傳遞給模型對象。# 假設(shè)我們有一批隨機(jī)生成的“圖片”數(shù)據(jù)模擬MNIST批量大小為4 import numpy as np dummy_images np.random.random((4, 28, 28)).astype(np.float32) # 前向傳播獲得預(yù)測結(jié)果 predictions model(dummy_images) print(predictions.shape) # 輸出: (4, 10) print(predictions)輸出的predictions形狀是(4, 10)表示對4張圖片的預(yù)測結(jié)果每張圖片對應(yīng)一個(gè)長度為10的向量。由于輸出層使用了softmax這個(gè)向量的每個(gè)元素都在0到1之間且所有元素之和為1可以解釋為圖片屬于每個(gè)數(shù)字類別的概率。4. 訓(xùn)練神經(jīng)網(wǎng)絡(luò)損失、優(yōu)化器與反向傳播模型現(xiàn)在只會(huì)進(jìn)行隨機(jī)預(yù)測因?yàn)闄?quán)重是隨機(jī)初始化的。如何讓它學(xué)會(huì)正確的預(yù)測這就是訓(xùn)練過程其核心是反向傳播算法Backpropagation也就是“BP神經(jīng)網(wǎng)絡(luò)”中“BP”的由來。這個(gè)過程自動(dòng)化了梯度計(jì)算和參數(shù)更新。4.1 定義損失函數(shù)衡量“錯(cuò)誤”的尺度我們需要一個(gè)函數(shù)來量化模型的預(yù)測結(jié)果與真實(shí)標(biāo)簽之間的差距。這個(gè)函數(shù)叫損失函數(shù)Loss Function。對于多分類問題交叉熵?fù)p失Categorical Crossentropy是標(biāo)準(zhǔn)選擇。# 定義損失函數(shù) loss_fn tf.keras.losses.SparseCategoricalCrossentropy()這里使用SparseCategoricalCrossentropy是因?yàn)槲覀兊臉?biāo)簽通常是整數(shù)如“3”而不是one-hot編碼如[0,0,0,1,0,0,0,0,0,0]。如果是one-hot編碼則使用CategoricalCrossentropy。4.2 選擇優(yōu)化器決定“如何改進(jìn)”優(yōu)化器Optimizer決定了如何根據(jù)損失函數(shù)的梯度來更新網(wǎng)絡(luò)中的權(quán)重。最常用的是Adam優(yōu)化器它自適應(yīng)地調(diào)整學(xué)習(xí)率通常能獲得很好的效果且無需太多調(diào)參。# 定義優(yōu)化器設(shè)置學(xué)習(xí)率lr optimizer tf.keras.optimizers.Adam(learning_rate0.001)4.3 訓(xùn)練循環(huán)手動(dòng)實(shí)現(xiàn)一個(gè)Epoch為了深入理解我們先拋開model.fit()這種高級(jí)API手動(dòng)實(shí)現(xiàn)一個(gè)訓(xùn)練步驟Step和一個(gè)訓(xùn)練輪次Epoch。假設(shè)我們有一些訓(xùn)練數(shù)據(jù)x_train和標(biāo)簽y_train。# 假設(shè)的訓(xùn)練數(shù)據(jù) x_train np.random.random((1000, 28, 28)).astype(np.float32) # 1000張圖片 y_train np.random.randint(10, size(1000,)) # 1000個(gè)標(biāo)簽 # 將數(shù)據(jù)轉(zhuǎn)換為TensorFlow Dataset便于批處理 train_dataset tf.data.Dataset.from_tensor_slices((x_train, y_train)) train_dataset train_dataset.shuffle(buffer_size1024).batch(32) # 手動(dòng)訓(xùn)練一個(gè)Epoch for step, (batch_images, batch_labels) in enumerate(train_dataset): # 使用 tf.GradientTape 記錄前向傳播的計(jì)算過程用于計(jì)算梯度 with tf.GradientTape() as tape: # 1. 前向傳播 predictions model(batch_images, trainingTrue) # trainingTrue會(huì)啟用Dropout等僅在訓(xùn)練時(shí)使用的層 # 2. 計(jì)算當(dāng)前批次的損失值 loss_value loss_fn(batch_labels, predictions) # 3. 反向傳播計(jì)算損失相對于模型可訓(xùn)練變量的梯度 grads tape.gradient(loss_value, model.trainable_variables) # 4. 優(yōu)化器應(yīng)用梯度更新模型參數(shù) optimizer.apply_gradients(zip(grads, model.trainable_variables)) # 每100個(gè)batch打印一次損失 if step % 100 0: print(f“Step {step}: Loss {loss_value.numpy()}”)關(guān)鍵解釋tf.GradientTape()這是TensorFlow 2.0實(shí)現(xiàn)自動(dòng)微分求導(dǎo)的核心上下文管理器。在tape的上下文中執(zhí)行的所有TensorFlow操作都會(huì)被記錄以便之后計(jì)算梯度。tape.gradient(loss_value, model.trainable_variables)這個(gè)調(diào)用是反向傳播的魔法所在。它自動(dòng)計(jì)算損失函數(shù)loss_value相對于模型中所有可訓(xùn)練參數(shù)model.trainable_variables即各層的權(quán)重和偏置的梯度。optimizer.apply_gradients(...)優(yōu)化器拿到梯度后按照其算法如Adam更新參數(shù)完成一次學(xué)習(xí)。這個(gè)過程就是“反向傳播”的精髓前向計(jì)算得到損失反向計(jì)算得到每個(gè)參數(shù)應(yīng)該如何微調(diào)才能降低損失然后執(zhí)行調(diào)整。4.4 使用高級(jí)APImodel.compile() 與 model.fit()手動(dòng)循環(huán)有助于理解但在實(shí)際項(xiàng)目中我們幾乎總是使用Keras提供的高級(jí)API它們更簡潔、更健壯。# 編譯模型指定優(yōu)化器、損失函數(shù)和評估指標(biāo) model.compile(optimizer‘a(chǎn)dam’, loss‘sparse_categorical_crossentropy’, metrics[‘a(chǎn)ccuracy’]) # 訓(xùn)練模型 history model.fit(x_train, y_train, batch_size32, epochs5, # 在整個(gè)數(shù)據(jù)集上訓(xùn)練5輪 validation_split0.2) # 拿出20%的數(shù)據(jù)作為驗(yàn)證集model.compile()配置了訓(xùn)練所需的組件。model.fit()則接管了整個(gè)訓(xùn)練循環(huán)包括打亂數(shù)據(jù)、分批、前向傳播、計(jì)算損失、反向傳播、參數(shù)更新并在每個(gè)Epoch結(jié)束后在驗(yàn)證集上評估性能。history對象包含了訓(xùn)練過程中的損失和準(zhǔn)確率歷史可用于繪圖分析。5. 核心概念延伸與實(shí)戰(zhàn)避坑指南掌握了基礎(chǔ)的前饋網(wǎng)絡(luò)構(gòu)建和訓(xùn)練流程后我們需要深入一些關(guān)鍵細(xì)節(jié)這些都是新手容易踩坑的地方。5.1 激活函數(shù)選擇為什么ReLU是隱藏層的默認(rèn)首選在前面的例子中隱藏層我們都使用了activation‘relu’修正線性單元。為什么不是sigmoid或tanhSigmoid輸出范圍(0,1)曾常用于輸出層做二分類。但其在輸入值很大或很小時(shí)梯度會(huì)趨近于0稱為“梯度飽和”導(dǎo)致在深度網(wǎng)絡(luò)的反向傳播中梯度消失Vanishing Gradient深層的權(quán)重幾乎得不到更新。Tanh輸出范圍(-1,1)是零中心的比sigmoid稍好但同樣存在梯度飽和問題。ReLUf(x) max(0, x)。計(jì)算簡單在正區(qū)間梯度恒為1有效緩解了梯度消失問題加速了訓(xùn)練收斂。因此它成為現(xiàn)代深度學(xué)習(xí)隱藏層的默認(rèn)選擇。注意ReLU也有“Dead ReLU”問題即如果輸入始終為負(fù)梯度為0神經(jīng)元可能“死亡”不再更新。為此有一些變體如Leaky ReLU、PReLU等但在實(shí)踐中標(biāo)準(zhǔn)的ReLU在大多數(shù)情況下已經(jīng)足夠好。5.2 權(quán)重初始化不起眼但至關(guān)重要的步驟在model.summary()中我們看到模型有大量參數(shù)。這些參數(shù)在訓(xùn)練開始前必須被賦予初始值。不好的初始化如全零初始化會(huì)導(dǎo)致訓(xùn)練失敗或緩慢。TensorFlow Keras層有默認(rèn)的初始化器。對于Dense層默認(rèn)使用glorot_uniform也稱為Xavier均勻初始化它會(huì)根據(jù)輸入和輸出的神經(jīng)元數(shù)量來調(diào)整初始權(quán)重的范圍旨在保持前向和反向傳播中信號(hào)的方差穩(wěn)定。對于新手直接使用默認(rèn)初始化即可無需修改。當(dāng)你的網(wǎng)絡(luò)非常深或遇到訓(xùn)練困難時(shí)再考慮嘗試其他初始化方法如he_normal針對ReLU的初始化。5.3 過擬合與正則化當(dāng)模型“學(xué)得太好”模型在訓(xùn)練集上表現(xiàn)完美但在沒見過的數(shù)據(jù)測試集上表現(xiàn)糟糕這就是過擬合。它記住了訓(xùn)練數(shù)據(jù)的噪聲和細(xì)節(jié)而非一般規(guī)律。應(yīng)對過擬合的常用“武器”獲取更多數(shù)據(jù)最有效但通常成本最高。數(shù)據(jù)增強(qiáng)對現(xiàn)有數(shù)據(jù)做隨機(jī)變換如旋轉(zhuǎn)、縮放、裁剪圖片創(chuàng)造“新”數(shù)據(jù)。Dropout層在訓(xùn)練時(shí)隨機(jī)“丟棄”置零一層中一定比例的神經(jīng)元輸出迫使網(wǎng)絡(luò)不依賴于任何單個(gè)神經(jīng)元增強(qiáng)魯棒性。model tf.keras.Sequential([ tf.keras.layers.Flatten(input_shape(28, 28)), tf.keras.layers.Dense(128, activation‘relu’), tf.keras.layers.Dropout(0.5), # 隨機(jī)丟棄50%的神經(jīng)元 tf.keras.layers.Dense(64, activation‘relu’), tf.keras.layers.Dropout(0.3), # 隨機(jī)丟棄30%的神經(jīng)元 tf.keras.layers.Dense(10, activation‘softmax’) ])Dropout只在訓(xùn)練時(shí)生效在評估或預(yù)測時(shí)所有神經(jīng)元都參與但其輸出值會(huì)乘以保留概率如0.5以保持期望值一致。L1/L2權(quán)重正則化在損失函數(shù)中增加一項(xiàng)懲罰過大的權(quán)重值鼓勵(lì)模型使用更小的權(quán)重從而更簡單。# 在Dense層中添加L2正則化 tf.keras.layers.Dense(64, activation‘relu’, kernel_regularizertf.keras.regularizers.l2(0.001))5.4 梯度消失/爆炸深度網(wǎng)絡(luò)的頑疾這是訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)如“不同的神經(jīng)網(wǎng)絡(luò)”中的深層CNN或RNN時(shí)最常見的問題之一。梯度消失反向傳播時(shí)梯度值越來越小導(dǎo)致網(wǎng)絡(luò)淺層的參數(shù)更新緩慢甚至停滯。sigmoid/tanh激活函數(shù)和某些權(quán)重初始化容易導(dǎo)致此問題。梯度爆炸梯度值越來越大導(dǎo)致參數(shù)更新步長巨大模型無法收斂。解決方案使用ReLU及其變體緩解梯度消失。使用批標(biāo)準(zhǔn)化tf.keras.layers.BatchNormalization層。它會(huì)對每一層的輸入進(jìn)行標(biāo)準(zhǔn)化減均值、除標(biāo)準(zhǔn)差使數(shù)據(jù)分布更穩(wěn)定允許使用更高的學(xué)習(xí)率并能在一定程度上緩解梯度問題。梯度裁剪在優(yōu)化器中設(shè)置clipnorm或clipvalue防止梯度超過某個(gè)閾值。optimizer tf.keras.optimizers.Adam(learning_rate0.001, clipnorm1.0)合理的權(quán)重初始化如之前所述。6. 從基礎(chǔ)到應(yīng)用一個(gè)完整的MNIST手寫數(shù)字識(shí)別示例讓我們將所有知識(shí)點(diǎn)串聯(lián)起來完成一個(gè)真正可運(yùn)行的、完整的圖像分類項(xiàng)目。我們將使用經(jīng)典的MNIST數(shù)據(jù)集。import tensorflow as tf import numpy as np import matplotlib.pyplot as plt # 1. 加載數(shù)據(jù) (x_train, y_train), (x_test, y_test) tf.keras.datasets.mnist.load_data() # 2. 數(shù)據(jù)預(yù)處理 # 將像素值從0-255縮放到0-1之間有助于模型收斂 x_train, x_test x_train / 255.0, x_test / 255.0 # 3. 構(gòu)建模型 model tf.keras.Sequential([ tf.keras.layers.Flatten(input_shape(28, 28)), tf.keras.layers.Dense(128, activation‘relu’), tf.keras.layers.Dropout(0.2), # 添加Dropout防止過擬合 tf.keras.layers.Dense(64, activation‘relu’), tf.keras.layers.Dropout(0.2), tf.keras.layers.Dense(10, activation‘softmax’) ]) # 4. 編譯模型 model.compile(optimizer‘a(chǎn)dam’, loss‘sparse_categorical_crossentropy’, metrics[‘a(chǎn)ccuracy’]) # 5. 訓(xùn)練模型 print(“開始訓(xùn)練...”) history model.fit(x_train, y_train, epochs10, batch_size64, validation_split0.2, # 用20%訓(xùn)練數(shù)據(jù)作為驗(yàn)證 verbose1) # 顯示進(jìn)度條 # 6. 評估模型 print(“\n在測試集上評估...”) test_loss, test_acc model.evaluate(x_test, y_test, verbose2) print(f“\n測試準(zhǔn)確率{test_acc:.4f}”) # 7. 進(jìn)行預(yù)測 predictions model.predict(x_test[:5]) # 預(yù)測前5張測試圖片 predicted_labels tf.argmax(predictions, axis1).numpy() print(“前5張圖片的預(yù)測標(biāo)簽”, predicted_labels) print(“真實(shí)的標(biāo)簽”, y_test[:5]) # 8. 可視化訓(xùn)練過程可選 plt.figure(figsize(12, 4)) plt.subplot(1, 2, 1) plt.plot(history.history[‘a(chǎn)ccuracy’], label‘Training Accuracy’) plt.plot(history.history[‘val_accuracy’], label‘Validation Accuracy’) plt.xlabel(‘Epoch’) plt.ylabel(‘Accuracy’) plt.legend() plt.title(‘Training and Validation Accuracy’) plt.subplot(1, 2, 2) plt.plot(history.history[‘loss’], label‘Training Loss’) plt.plot(history.history[‘val_loss’], label‘Validation Loss’) plt.xlabel(‘Epoch’) plt.ylabel(‘Loss’) plt.legend() plt.title(‘Training and Validation Loss’) plt.show()運(yùn)行這段代碼你應(yīng)該能看到模型在測試集上的準(zhǔn)確率達(dá)到97%以上。通過觀察history繪制的圖表你可以清晰地看到模型在訓(xùn)練集和驗(yàn)證集上的表現(xiàn)判斷是否發(fā)生過擬合驗(yàn)證集指標(biāo)遠(yuǎn)差于訓(xùn)練集或欠擬合兩者都差。幾個(gè)關(guān)鍵的實(shí)操心得數(shù)據(jù)標(biāo)準(zhǔn)化是必須的將輸入數(shù)據(jù)如圖像像素值縮放到一個(gè)較小的范圍如0-1或-1到1能極大加速訓(xùn)練并提高模型穩(wěn)定性。對于MNIST除以255.0是最簡單的標(biāo)準(zhǔn)化。驗(yàn)證集是關(guān)鍵永遠(yuǎn)不要用測試集來調(diào)整模型參數(shù)如學(xué)習(xí)率、層數(shù)。validation_split或單獨(dú)的驗(yàn)證集用于在訓(xùn)練過程中監(jiān)控模型在未見數(shù)據(jù)上的表現(xiàn)是調(diào)整超參數(shù)和判斷過擬合的依據(jù)。從簡單開始不要一開始就構(gòu)建非常復(fù)雜的網(wǎng)絡(luò)。先用一個(gè)像本例這樣的簡單網(wǎng)絡(luò)如只有1-2個(gè)隱藏層跑通整個(gè)流程獲得一個(gè)基準(zhǔn)性能。然后再嘗試增加深度、調(diào)整超參數(shù)并觀察性能變化。理解model.fit()的輸出verbose1時(shí)你會(huì)看到每個(gè)Epoch的進(jìn)度條以及訓(xùn)練/驗(yàn)證的損失和指標(biāo)。如果訓(xùn)練損失持續(xù)下降但驗(yàn)證損失開始上升這就是過擬合的典型信號(hào)。通過這個(gè)完整的例子你已經(jīng)走完了使用TensorFlow 2.0構(gòu)建和訓(xùn)練一個(gè)前饋神經(jīng)網(wǎng)絡(luò)的全流程。這不僅僅是“Hello, World”而是一個(gè)具備實(shí)用價(jià)值的機(jī)器學(xué)習(xí)項(xiàng)目骨架。在此基礎(chǔ)上去探索更復(fù)雜的“卷積神經(jīng)網(wǎng)絡(luò)”處理圖像或是研究“圖卷積神經(jīng)網(wǎng)絡(luò)通俗理解”來處理圖結(jié)構(gòu)數(shù)據(jù)你都會(huì)發(fā)現(xiàn)其核心思想——層的堆疊、前向/反向傳播、損失最小化——是完全一致的。掌握了這些基礎(chǔ)你就擁有了打開深度學(xué)習(xí)大門的鑰匙。