戰(zhàn))
簡介本資源是一份面向本科及碩士階段教學(xué)與自學(xué)的Matlab神經(jīng)網(wǎng)絡(luò)基礎(chǔ)實(shí)踐材料聚焦多層感知器MLP模型的反向傳播算法原理與工程實(shí)現(xiàn)幫助學(xué)習(xí)者深入理解前饋結(jié)構(gòu)、激活函數(shù)、梯度計(jì)算與權(quán)重更新等核心機(jī)制。壓縮包共2000個文件主體為4236張訓(xùn)練過程可視化圖像如螺旋數(shù)據(jù)分類結(jié)果圖fig4600–fig5000、最終決策邊界圖輔以4個關(guān)鍵M文件含網(wǎng)絡(luò)構(gòu)建MLP_NN.m、前向/反向傳播主邏輯、激活函數(shù)及其導(dǎo)數(shù)實(shí)現(xiàn)另有4個points數(shù)據(jù)點(diǎn)集支撐實(shí)驗(yàn)復(fù)現(xiàn)整體體積197.48MB結(jié)構(gòu)清晰便于分階段調(diào)試與效果對比。已有1330人下載學(xué)習(xí)配套代碼完整可運(yùn)行適配Matlab 2019a包含從數(shù)據(jù)生成、網(wǎng)絡(luò)初始化、迭代訓(xùn)練到性能評估的全流程腳本特別適合神經(jīng)網(wǎng)絡(luò)入門者動手驗(yàn)證理論、觀察收斂過程并建立直觀認(rèn)知。1. 從零開始為什么用Matlab手搓一個MLP如果你正在學(xué)習(xí)機(jī)器學(xué)習(xí)尤其是神經(jīng)網(wǎng)絡(luò)那么“反向傳播”和“多層感知器”這兩個詞一定如雷貫耳。網(wǎng)上有無數(shù)現(xiàn)成的框架比如TensorFlow、PyTorch幾行代碼就能搭出一個強(qiáng)大的網(wǎng)絡(luò)。那么為什么我們還要費(fèi)勁地用Matlab從頭實(shí)現(xiàn)一個MLP呢這就像學(xué)開車自動擋固然方便但真正理解離合、油門和變速箱如何協(xié)同工作才能讓你從“會開”變成“懂車”。手搓一個MLP正是為了讓你徹底搞懂神經(jīng)網(wǎng)絡(luò)內(nèi)部那套“傳動系統(tǒng)”——前向傳播如何計(jì)算輸出反向傳播又如何根據(jù)誤差調(diào)整每一個神經(jīng)元的“權(quán)重”和“偏置”。Matlab在這個學(xué)習(xí)過程中扮演了一個絕佳的角色。它強(qiáng)大的矩陣運(yùn)算能力讓復(fù)雜的數(shù)學(xué)公式可以非常直觀地用幾行代碼表達(dá)出來避免了Python中NumPy等庫的底層細(xì)節(jié)干擾。你可以清晰地看到每一次迭代中數(shù)據(jù)如何流動參數(shù)如何更新誤差如何下降。這個過程會讓你對梯度下降、鏈?zhǔn)椒▌t這些核心概念有刻骨銘心的理解而不是僅僅停留在調(diào)用model.fit()的層面。當(dāng)你親手實(shí)現(xiàn)并調(diào)試成功一個MLP后再去看那些高級框架的API會有一種“原來如此”的通透感。這篇文章我就帶你從最基礎(chǔ)的數(shù)學(xué)原理出發(fā)用Matlab一步步構(gòu)建、訓(xùn)練并可視化一個完整的MLP過程中遇到的每一個坑和對應(yīng)的填坑技巧我都會毫無保留地分享給你。2. MLP的核心架構(gòu)與數(shù)學(xué)原理拆解在動手寫代碼之前我們必須把MLP的“設(shè)計(jì)圖紙”和“工作原理”搞清楚。一個典型的多層感知器MLP通常包含一個輸入層、一個或多個隱藏層以及一個輸出層。每一層都由若干個神經(jīng)元或稱為節(jié)點(diǎn)組成層與層之間通過權(quán)重矩陣和偏置向量全連接。2.1 前向傳播信息如何從輸入流向輸出前向傳播的過程就是數(shù)據(jù)從輸入層開始逐層經(jīng)過加權(quán)求和、加上偏置、再通過一個非線性激活函數(shù)最終得到輸出層結(jié)果的過程。我們用數(shù)學(xué)公式來精確描述它。假設(shè)我們的網(wǎng)絡(luò)有L層輸入層不計(jì)入層數(shù)。我們用上標(biāo)[l]表示第l層的相關(guān)參數(shù)。W[l]: 第l層的權(quán)重矩陣維度為(n[l], n[l-1])其中n[l]是第l層的神經(jīng)元個數(shù)。W[l]的第i行第j列元素w_{ij}^{[l]}表示第l-1層第j個神經(jīng)元到第l層第i個神經(jīng)元的連接權(quán)重。b[l]: 第l層的偏置向量維度為(n[l], 1)。Z[l]: 第l層的線性計(jì)算結(jié)果加權(quán)輸入Z[l] W[l] * A[l-1] b[l]。A[l]: 第l層的激活值輸出A[l] g[l](Z[l])其中g(shù)[l]()是第l層的激活函數(shù)。我們約定A[0]就是輸入數(shù)據(jù)X。以一個簡單的3層網(wǎng)絡(luò)1個隱藏層為例處理單個樣本x列向量時(shí)輸入層A[0] x隱藏層Z[1] W[1] * A[0] b[1],A[1] g[1](Z[1])例如g[1]可以是ReLU或Sigmoid輸出層Z[2] W[2] * A[1] b[2],A[2] g[2](Z[2])例如二分類問題g[2]用Sigmoid多分類用Softmax這里的A[2]就是網(wǎng)絡(luò)的最終預(yù)測輸出y_hat。在實(shí)際編程中我們通常一次性處理一個批量的樣本比如m個此時(shí)輸入X的維度是(n[0], m)每一列是一個樣本。矩陣運(yùn)算的優(yōu)勢就體現(xiàn)出來了上述公式完全適用只是A[l]和Z[l]的維度變成了(n[l], m)計(jì)算過程完全向量化效率極高。2.2 反向傳播誤差如何指導(dǎo)參數(shù)更新前向傳播得到了預(yù)測值y_hat我們通過損失函數(shù)J如均方誤差MSE、交叉熵?fù)p失來計(jì)算它與真實(shí)標(biāo)簽y之間的誤差。反向傳播的核心任務(wù)就是計(jì)算損失函數(shù)J關(guān)于網(wǎng)絡(luò)中每一個參數(shù)W[l]和b[l]的梯度偏導(dǎo)數(shù)即?J/?W[l]和?J/?b[l]。然后我們就可以用梯度下降法來更新參數(shù)使損失減小。反向傳播的精髓是鏈?zhǔn)椒▌t。我們從輸出層開始反向逐層計(jì)算梯度。這里我們推導(dǎo)最關(guān)鍵的幾個公式以單個樣本的均方誤差損失為例J 0.5 * (y_hat - y)^2輸出層激活函數(shù)為Sigmoid。首先計(jì)算輸出層的誤差δ[L]L是最后一層δ[L] ?J/?Z[L] (A[L] - y) ⊙ g[L](Z[L])其中⊙表示逐元素相乘Hadamard積。對于Sigmoid輸出g(z) g(z)*(1-g(z))所以δ[L] (A[L] - y) ⊙ A[L] ⊙ (1 - A[L])。然后反向傳播這個誤差到前一l層δ[l] (W[l1]^T * δ[l1]) ⊙ g[l](Z[l])這個公式是反向傳播的引擎。W[l1]^T * δ[l1]將后一層的誤差“分配”回本層再乘以本層激活函數(shù)的導(dǎo)數(shù)就得到了本層的誤差δ[l]。最后利用本層的誤差δ[l]和前一層激活值A(chǔ)[l-1]計(jì)算參數(shù)的梯度?J/?W[l] δ[l] * A[l-1]^T?J/?b[l] δ[l]注意這里是針對單個樣本的梯度。對于m個樣本的批量梯度是每個樣本梯度的平均值。注意激活函數(shù)導(dǎo)數(shù)的選擇至關(guān)重要。如果你在隱藏層使用Sigmoid其導(dǎo)數(shù)g(z)的最大值只有0.25。當(dāng)網(wǎng)絡(luò)層數(shù)較深時(shí)多個小于1的導(dǎo)數(shù)連乘會導(dǎo)致梯度指數(shù)級減小這就是著名的“梯度消失”問題。因此在現(xiàn)代網(wǎng)絡(luò)中隱藏層更推薦使用ReLURectified Linear Unit或其變體。ReLU的導(dǎo)數(shù)在輸入為正時(shí)為1為負(fù)時(shí)為0能有效緩解梯度消失加速訓(xùn)練。我們在代碼實(shí)現(xiàn)時(shí)會重點(diǎn)考慮這一點(diǎn)。3. Matlab實(shí)現(xiàn)從初始化到訓(xùn)練循環(huán)理論鋪墊完畢現(xiàn)在進(jìn)入實(shí)戰(zhàn)環(huán)節(jié)。我們將實(shí)現(xiàn)一個具有一個隱藏層的MLP用于解決一個簡單的二分類問題例如異或問題XOR。3.1 網(wǎng)絡(luò)初始化好的開始是成功的一半?yún)?shù)的初始化不能隨意。如果全部初始化為0那么同一層所有神經(jīng)元的梯度會完全一樣導(dǎo)致它們學(xué)習(xí)不到不同的特征對稱性破壞問題。常見的初始化方法有隨機(jī)初始化W randn(n[l], n[l-1]) * 0.01。小的隨機(jī)數(shù)打破對稱性乘0.01是為了防止初始值過大導(dǎo)致激活函數(shù)如Sigmoid飽和梯度接近于0。Xavier初始化適用于Sigmoid、Tanh等激活函數(shù)。W randn(n[l], n[l-1]) * sqrt(1/n[l-1])。He初始化專為ReLU及其變體設(shè)計(jì)。W randn(n[l], n[l-1]) * sqrt(2/n[l-1])。我們選擇ReLU作為隱藏層激活函數(shù)因此采用He初始化。輸出層用Sigmoid其權(quán)重可以用較小的隨機(jī)數(shù)初始化。function [parameters] initialize_parameters(n_x, n_h, n_y) % 初始化網(wǎng)絡(luò)參數(shù) % n_x: 輸入層維度 % n_h: 隱藏層神經(jīng)元數(shù)量 % n_y: 輸出層維度 % 返回包含W1, b1, W2, b2的結(jié)構(gòu)體 rng(1); % 設(shè)置隨機(jī)種子確保結(jié)果可復(fù)現(xiàn) W1 randn(n_h, n_x) * sqrt(2.0 / n_x); % He初始化 b1 zeros(n_h, 1); % 偏置初始化為0是常見的做法 W2 randn(n_y, n_h) * 0.01; % 輸出層權(quán)重小隨機(jī)初始化 b2 zeros(n_y, 1); parameters struct(); parameters.W1 W1; parameters.b1 b1; parameters.W2 W2; parameters.b2 b2; end3.2 前向與反向傳播的函數(shù)實(shí)現(xiàn)接下來我們實(shí)現(xiàn)單次前向傳播和反向傳播的函數(shù)。這里我們處理的是批量數(shù)據(jù)。function [A2, cache] forward_propagation(X, parameters) % 前向傳播 % X: 輸入數(shù)據(jù)維度 (n_x, m) % parameters: 包含W1,b1,W2,b2的結(jié)構(gòu)體 % 返回: % A2: 輸出層激活值 (預(yù)測值) % cache: 緩存Z1, A1, Z2, A2用于反向傳播 W1 parameters.W1; b1 parameters.b1; W2 parameters.W2; b2 parameters.b2; % 隱藏層 Z1 W1 * X b1; A1 relu(Z1); % 使用ReLU激活函數(shù) % 輸出層 Z2 W2 * A1 b2; A2 sigmoid(Z2); % 使用Sigmoid激活函數(shù)輸出概率 cache struct(Z1, Z1, A1, A1, Z2, Z2, A2, A2); end function [grads] backward_propagation(parameters, cache, X, Y) % 反向傳播計(jì)算梯度 % parameters: 網(wǎng)絡(luò)參數(shù) % cache: 前向傳播緩存 % X: 輸入數(shù)據(jù) % Y: 真實(shí)標(biāo)簽 % 返回: 包含各參數(shù)梯度的結(jié)構(gòu)體 grads m size(X, 2); % 樣本數(shù)量 W2 parameters.W2; A1 cache.A1; A2 cache.A2; % 輸出層誤差 dZ2 dZ2 A2 - Y; % 對于Sigmoid輸出和交叉熵?fù)p失這是簡化形式 % 詳細(xì)推導(dǎo)J -[y*log(a)(1-y)*log(1-a)], ?J/?Z2 a - y % 輸出層梯度 dW2 (1/m) * (dZ2 * A1); db2 (1/m) * sum(dZ2, 2); % 按行求和 % 隱藏層誤差 dZ1 dA1 W2 * dZ2; dZ1 dA1 .* relu_backward(cache.Z1); % 點(diǎn)乘ReLU的導(dǎo)數(shù) % 隱藏層梯度 dW1 (1/m) * (dZ1 * X); db1 (1/m) * sum(dZ1, 2); grads struct(dW1, dW1, db1, db1, dW2, dW2, db2, db2); end % 激活函數(shù)及其導(dǎo)數(shù) function A relu(Z) A max(0, Z); end function dZ relu_backward(Z) % ReLU的導(dǎo)數(shù)輸入0時(shí)為1否則為0 dZ double(Z 0); end function A sigmoid(Z) A 1 ./ (1 exp(-Z)); end實(shí)操心得梯度公式的簡化。上面backward_propagation函數(shù)中dZ2 A2 - Y是一個非常重要的簡化。它是由Sigmoid激活函數(shù)和二元交叉熵?fù)p失函數(shù)共同作用的結(jié)果。如果你使用其他損失函數(shù)如均方誤差或其他輸出層激活函數(shù)如Softmax這個公式會不同。理解這個簡化的來源能讓你在修改網(wǎng)絡(luò)結(jié)構(gòu)時(shí)正確推導(dǎo)梯度而不是死記硬背代碼。3.3 參數(shù)更新與訓(xùn)練循環(huán)有了梯度我們就可以用梯度下降法更新參數(shù)。我們實(shí)現(xiàn)一個基本的批量梯度下降。function [parameters] update_parameters(parameters, grads, learning_rate) % 使用梯度下降更新參數(shù) parameters.W1 parameters.W1 - learning_rate * grads.dW1; parameters.b1 parameters.b1 - learning_rate * grads.db1; parameters.W2 parameters.W2 - learning_rate * grads.dW2; parameters.b2 parameters.b2 - learning_rate * grads.db2; end現(xiàn)在把所有部分組合起來形成完整的訓(xùn)練循環(huán)。function [parameters, costs] model(X, Y, n_h, learning_rate, num_iterations, print_cost) % 訓(xùn)練MLP模型 % X, Y: 訓(xùn)練數(shù)據(jù)和標(biāo)簽 % n_h: 隱藏層神經(jīng)元數(shù)量 % learning_rate: 學(xué)習(xí)率 % num_iterations: 迭代次數(shù) % print_cost: 每1000次迭代是否打印損失 % 返回訓(xùn)練好的參數(shù)和損失歷史 n_x size(X, 1); n_y size(Y, 1); costs []; % 記錄損失 % 1. 初始化參數(shù) parameters initialize_parameters(n_x, n_h, n_y); % 2. 訓(xùn)練循環(huán) for i 1:num_iterations % 前向傳播 [A2, cache] forward_propagation(X, parameters); % 計(jì)算損失二元交叉熵 m size(X, 2); logprobs Y .* log(A2) (1 - Y) .* log(1 - A2); cost - (1/m) * sum(logprobs(:)); cost squeeze(cost); % 確保cost是標(biāo)量 % 反向傳播 grads backward_propagation(parameters, cache, X, Y); % 更新參數(shù) parameters update_parameters(parameters, grads, learning_rate); % 記錄損失 if mod(i, 100) 0 costs [costs, cost]; end % 打印損失 if print_cost mod(i, 1000) 0 fprintf(迭代次數(shù) %i 損失值 %f \n, i, cost); end end % 繪制損失曲線 if print_cost figure; plot(costs); xlabel(迭代次數(shù) (每100次)); ylabel(損失); title([學(xué)習(xí)率 num2str(learning_rate)]); grid on; end end4. 實(shí)戰(zhàn)測試解決異或問題與關(guān)鍵調(diào)試技巧理論正確不代表代碼能跑通更不代表能有效學(xué)習(xí)。我們用經(jīng)典的異或XOR問題來測試我們的MLP。XOR問題的輸入輸出如下輸入 (X): [0,0; 0,1; 1,0; 1,1] 輸出 (Y): [0; 1; 1; 0]這是一個簡單的非線性可分問題單層感知機(jī)無法解決但帶有一個隱藏層的MLP可以。4.1 數(shù)據(jù)準(zhǔn)備與模型訓(xùn)練% 準(zhǔn)備XOR數(shù)據(jù) X [0 0; 0 1; 1 0; 1; 1]; % 維度 (2, 4) Y [0 1 1 0]; % 維度 (1, 4) % 定義超參數(shù) n_h 4; % 隱藏層4個神經(jīng)元 learning_rate 0.1; num_iterations 10000; print_cost true; % 訓(xùn)練模型 [parameters, costs] model(X, Y, n_h, learning_rate, num_iterations, print_cost);運(yùn)行后你應(yīng)該能看到損失值隨著迭代次數(shù)增加而穩(wěn)步下降最終趨近于0。繪制出的損失曲線應(yīng)該是單調(diào)遞減的可能會有小幅波動。4.2 模型預(yù)測與決策邊界可視化訓(xùn)練完成后我們用訓(xùn)練好的參數(shù)進(jìn)行預(yù)測并可視化其學(xué)到的決策邊界。function [predictions] predict(parameters, X) % 使用訓(xùn)練好的模型進(jìn)行預(yù)測 A2, ~ forward_propagation(X, parameters); predictions (A2 0.5); % 以0.5為閾值進(jìn)行二分類 end % 在訓(xùn)練集上預(yù)測 predictions_train predict(parameters, X); fprintf(訓(xùn)練集準(zhǔn)確率: %f %%\n, mean(double(predictions_train Y)) * 100); % 可視化決策邊界 function plot_decision_boundary(parameters, X, Y) % 設(shè)置網(wǎng)格范圍 x1 linspace(-0.5, 1.5, 100); x2 linspace(-0.5, 1.5, 100); [X1_grid, X2_grid] meshgrid(x1, x2); % 將網(wǎng)格點(diǎn)展開為輸入格式 X_grid [X1_grid(:); X2_grid(:)]; % 預(yù)測網(wǎng)格上每一點(diǎn)的類別 A2, ~ forward_propagation(X_grid, parameters); Z_grid A2 0.5; Z_grid reshape(Z_grid, length(x2), length(x1)); % 繪制等高線決策邊界 figure; contourf(x1, x2, Z_grid, LineColor, none); colormap([0.9 0.9 1; 1 0.9 0.9]); % 淺藍(lán)和淺紅 hold on; % 繪制原始數(shù)據(jù)點(diǎn) scatter(X(1, Y0), X(2, Y0), 100, b, filled, DisplayName, Class 0); scatter(X(1, Y1), X(2, Y1), 100, r, filled, DisplayName, Class 1); xlabel(x1); ylabel(x2); title(MLP學(xué)到的決策邊界 (XOR問題)); legend(Location, best); axis([-0.5 1.5 -0.5 1.5]); hold off; end % 調(diào)用函數(shù)繪圖 plot_decision_boundary(parameters, X, Y);如果一切順利你將看到一張圖其中藍(lán)色和紅色點(diǎn)被一條復(fù)雜的非線性邊界完美分開這條邊界能將(0,0)和(1,1)歸為一類藍(lán)色(0,1)和(1,0)歸為另一類紅色。這直觀地證明了你的MLP成功學(xué)習(xí)到了XOR的非線性關(guān)系。4.3 調(diào)試與優(yōu)化當(dāng)網(wǎng)絡(luò)不學(xué)習(xí)時(shí)該怎么辦在實(shí)際操作中你的網(wǎng)絡(luò)可能一開始并不收斂損失值可能居高不下甚至變成NaN。以下是幾個最常見的坑和排查思路損失值為NaN或無限大爆炸檢查學(xué)習(xí)率這是頭號嫌犯。學(xué)習(xí)率太大可能導(dǎo)致梯度更新步伐過大參數(shù)在優(yōu)化空間中“跳崖”損失爆炸。嘗試將學(xué)習(xí)率降低一個數(shù)量級比如從0.1降到0.01或0.001。檢查數(shù)據(jù)輸入數(shù)據(jù)X或標(biāo)簽Y中是否有異常值極大、極小或NaN確保數(shù)據(jù)是干凈的。檢查激活函數(shù)在Sigmoid函數(shù)中如果Z非常大exp(-Z)可能下溢為0導(dǎo)致計(jì)算log(0)產(chǎn)生-Inf。確保前向傳播的值在合理范圍內(nèi)。可以考慮在Sigmoid函數(shù)中加入數(shù)值穩(wěn)定處理A 1 ./ (1 exp(-max(min(Z, 50), -50)))。損失值下降非常緩慢或停滯學(xué)習(xí)率太小與爆炸相反學(xué)習(xí)率太小會導(dǎo)致收斂極慢。可以嘗試適當(dāng)增大學(xué)習(xí)率。初始化問題如果權(quán)重初始化值太小可能導(dǎo)致初始激活值非常小對于Sigmoid/Tanh其梯度也接近0學(xué)習(xí)幾乎停滯。嘗試使用Xavier或He初始化而不是簡單的小隨機(jī)數(shù)。激活函數(shù)飽和如果使用Sigmoid/Tanh且輸入絕對值很大梯度會接近0導(dǎo)致“梯度消失”。隱藏層改用ReLU是解決此問題的標(biāo)準(zhǔn)做法。檢查梯度計(jì)算是否正確這是最根本的。實(shí)現(xiàn)一個梯度檢查函數(shù)。利用導(dǎo)數(shù)的定義對每個參數(shù)進(jìn)行數(shù)值近似求導(dǎo)與你反向傳播計(jì)算的解析梯度進(jìn)行比較。如果兩者差異很大比如相對誤差大于1e-7說明你的反向傳播代碼有bug。過擬合在復(fù)雜數(shù)據(jù)集上網(wǎng)絡(luò)可能很快在訓(xùn)練集上達(dá)到高精度但在測試集上表現(xiàn)很差。獲取更多數(shù)據(jù)最有效的方法。正則化在損失函數(shù)中加入L2正則化項(xiàng)懲罰大的權(quán)重。這需要在損失計(jì)算和梯度計(jì)算中都加入正則化項(xiàng)。Dropout在訓(xùn)練時(shí)隨機(jī)“丟棄”一部分神經(jīng)元防止神經(jīng)元之間產(chǎn)生復(fù)雜的共適應(yīng)關(guān)系。踩坑實(shí)錄梯度檢查的重要性。在我第一次實(shí)現(xiàn)時(shí)損失一直不降。我花了大量時(shí)間調(diào)整學(xué)習(xí)率、初始化方法都無濟(jì)于事。最后我寫了一個梯度檢查函數(shù)發(fā)現(xiàn)db2的解析梯度和數(shù)值梯度差了幾個數(shù)量級。仔細(xì)檢查代碼發(fā)現(xiàn)我在計(jì)算db2時(shí)錯誤地用了mean(dZ2, 1)按列求平均而正確的應(yīng)該是mean(dZ2, 2)按行求平均因?yàn)閎2是列向量。這個細(xì)微的錯誤導(dǎo)致偏置更新方向完全錯誤。教訓(xùn)是在確信反向傳播正確之前不要盲目調(diào)參梯度檢查是調(diào)試神經(jīng)網(wǎng)絡(luò)代碼的“金標(biāo)準(zhǔn)”。5. 超越基礎(chǔ)擴(kuò)展功能與性能考量一個能跑通的MLP只是起點(diǎn)。要讓其更實(shí)用、更健壯我們還需要考慮一些擴(kuò)展功能。5.1 實(shí)現(xiàn)L2正則化L2正則化通過在損失函數(shù)中增加權(quán)重的平方和項(xiàng)來抑制權(quán)重的大小防止過擬合。修改后的損失函數(shù)為J_reg J (lambda/(2*m)) * sum(W^2)其中l(wèi)ambda是正則化超參數(shù)。我們需要修改前向傳播的損失計(jì)算和反向傳播的梯度計(jì)算。% 在前向傳播的損失計(jì)算中假設(shè)parameters是一個包含所有W的結(jié)構(gòu)體數(shù)組 function cost compute_cost_with_regularization(A2, Y, parameters, lambda) m size(Y, 2); cross_entropy_cost - (1/m) * sum( Y .* log(A2) (1-Y) .* log(1-A2), all); % 計(jì)算所有權(quán)重矩陣的L2范數(shù)平方和 L2_cost 0; fields fieldnames(parameters); for i 1:length(fields) field fields{i}; if startsWith(field, W) % 只對權(quán)重矩陣正則化 L2_cost L2_cost sum(parameters.(field).^2, all); end end L2_cost (lambda / (2*m)) * L2_cost; cost cross_entropy_cost L2_cost; end % 在反向傳播的梯度計(jì)算中需要加上正則化項(xiàng)的梯度 % 對于dW[l]正則化項(xiàng)的梯度是 (lambda/m) * W[l] % 因此更新后的梯度為dW[l]_reg dW[l] (lambda/m) * W[l] % db的梯度不變5.2 實(shí)現(xiàn)不同的優(yōu)化器基礎(chǔ)的梯度下降Batch Gradient Descent每次使用全部數(shù)據(jù)計(jì)算梯度對于大數(shù)據(jù)集很慢。更常用的優(yōu)化器是小批量梯度下降每次迭代隨機(jī)抽取一小批mini-batch數(shù)據(jù)計(jì)算梯度。這需要在訓(xùn)練循環(huán)中增加數(shù)據(jù)打亂和分批的邏輯。帶動量的梯度下降引入“速度”變量使參數(shù)更新不僅考慮當(dāng)前梯度還累積之前的梯度方向有助于加速收斂并減少震蕩。Adam結(jié)合了動量和自適應(yīng)學(xué)習(xí)率的優(yōu)點(diǎn)是目前最常用的優(yōu)化器。以帶動量的梯度下降為例我們需要在更新參數(shù)時(shí)維護(hù)一個速度變量VV beta * V (1 - beta) * dWW W - learning_rate * V其中beta是動量參數(shù)通常取0.9。5.3 使用向量化操作處理批量數(shù)據(jù)我們的代碼已經(jīng)利用了Matlab的矩陣運(yùn)算是向量化的。但要處理真正的批量數(shù)據(jù)你需要將數(shù)據(jù)組織成矩陣Xn_x * m和Yn_y * m。前向和反向傳播中的所有操作都應(yīng)該是矩陣運(yùn)算避免使用for循環(huán)遍歷樣本這是Matlab高效運(yùn)行的關(guān)鍵。5.4 超參數(shù)調(diào)優(yōu)實(shí)戰(zhàn)網(wǎng)絡(luò)性能很大程度上取決于超參數(shù)的選擇。一個簡單的調(diào)優(yōu)流程可以是確定網(wǎng)絡(luò)架構(gòu)先從1-2個隱藏層開始每層神經(jīng)元數(shù)量可以嘗試比如[4, 8, 16, 32]。選擇學(xué)習(xí)率這是最重要的超參數(shù)。常用策略是進(jìn)行對數(shù)尺度搜索例如嘗試[0.1, 0.03, 0.01, 0.003, 0.001]。確定迭代次數(shù)觀察損失曲線直到損失收斂或開始過擬合。引入正則化如果出現(xiàn)過擬合訓(xùn)練損失低驗(yàn)證損失高嘗試加入L2正則化調(diào)整lambda如[0, 0.01, 0.1, 1]。使用驗(yàn)證集將數(shù)據(jù)分為訓(xùn)練集、驗(yàn)證集和測試集。用驗(yàn)證集來評估不同超參數(shù)組合的效果選擇在驗(yàn)證集上性能最好的模型最后用測試集報(bào)告最終性能。你可以編寫一個簡單的網(wǎng)格搜索或隨機(jī)搜索腳本來自動化這個過程。6. 從Matlab實(shí)現(xiàn)到理解現(xiàn)代深度學(xué)習(xí)框架通過這個手搓MLP的項(xiàng)目我希望你獲得的不僅僅是幾行能運(yùn)行的Matlab代碼。更重要的是理解背后的“為什么”為什么需要非線性激活函數(shù)沒有它多層網(wǎng)絡(luò)等價(jià)于單層線性變換無法解決XOR這類非線性問題。反向傳播的本質(zhì)是什么是鏈?zhǔn)椒▌t的高效應(yīng)用將最終誤差公平地“分?jǐn)偂苯o每一個該負(fù)責(zé)的參數(shù)。初始化、學(xué)習(xí)率、激活函數(shù)如何影響訓(xùn)練你通過調(diào)試親身感受到了。當(dāng)你理解了這些再去使用PyTorch或TensorFlow你會明白nn.Linear,nn.ReLU,optim.SGD,loss.backward()這些語句背后在做什么。你會知道如何選擇初始化方法如何設(shè)置合理的學(xué)習(xí)率如何診斷梯度消失/爆炸問題。這個從底層實(shí)現(xiàn)中獲得的直覺是任何高級框架的教程都無法直接給你的。最后你可以嘗試挑戰(zhàn)自己用Matlab實(shí)現(xiàn)一個更深的網(wǎng)絡(luò)比如3個隱藏層在更復(fù)雜的數(shù)據(jù)集如螺旋數(shù)據(jù)集或簡單的圖像分類數(shù)據(jù)集上進(jìn)行測試并加入Dropout、Batch Normalization等現(xiàn)代技巧。這個過程會充滿挑戰(zhàn)但每解決一個問題你對神經(jīng)網(wǎng)絡(luò)的理解就會加深一層。本文還有配套的精品資源點(diǎn)擊獲取