
簡介本資源是一份面向機器學習初學者與研究者的受限玻爾茲曼機RBMMATLAB實現代碼包聚焦無監(jiān)督特征學習與概率生成建模任務適用于圖像數據如手寫數字的隱層表示訓練與采樣生成。壓縮包共29個文件包含7個核心MATLAB腳本如RBM.m、trainRBM.m、gibbs.m、8個預訓練模型參數文件.mat格式含RBM50.mat、RBM200.mat等不同隱單元規(guī)模、10張可視化結果圖.jpg涵蓋權重圖、重構圖及損失曲線以及訓練/驗證數據文本digitstrain.txt等和說明文檔README.md。資源大小為8.86MB結構清晰模塊分工明確訓練主流程、Gibbs采樣、Sigmoid激活、權重可視化等功能均獨立封裝便于理解RBM前向傳播、對比散度CD訓練及采樣機制。目前已有592人學習下載可直接運行main.m復現經典RBM訓練過程并基于K5/K10/K20等預存模型快速開展特征提取或生成實驗。1. 從理論到實踐為什么選擇MATLAB實現RBM如果你正在機器學習或深度學習的入門階段尤其是對神經網絡中的生成模型感興趣那么“受限玻爾茲曼機”這個名字你一定不陌生。它不像卷積神經網絡那樣直接處理圖像也不像循環(huán)神經網絡那樣擅長序列但它在特征學習、協同過濾、降維乃至深度信念網絡的構建中扮演著奠基者的角色。很多教程和論文會告訴你RBM的數學原理但當你真正想動手跑通一個例子看看它如何從一堆隨機數中“學習”出數據的潛在結構時往往會卡在實現這一步。這時MATLAB就成為了一個極具吸引力的選擇。為什么是MATLAB對于算法研究者、在校學生以及需要快速驗證想法的工程師來說MATLAB提供了一個近乎“一站式”的環(huán)境。你不需要花大量時間去配置復雜的Python深度學習框架如TensorFlow或PyTorch及其依賴也不用擔心版本沖突。MATLAB的語法直觀矩陣運算原生高效并且內置了豐富的可視化工具這對于理解RBM這種基于概率和能量模型的“黑箱”學習過程至關重要。你可以實時看到權重矩陣的變化、重構誤差的下降曲線甚至可視化學習到的特征這種即時反饋對加深理解有巨大幫助。當然MATLAB并非生產部署的首選但其在算法原型開發(fā)、教學演示和科研探索中的便捷性是其他工具難以比擬的。本文將帶你從零開始在MATLAB中實現一個標準的二值受限玻爾茲曼機。我們不會止步于一個能運行的“玩具代碼”而是會深入每一步背后的動機解釋為什么參數要這樣初始化、為什么采樣要那樣做、對比散度算法究竟在干什么。我會分享在調試過程中遇到的典型“坑”比如學習率設置不當導致的震蕩、權重初始化太小造成的學習停滯以及如何正確評估一個RBM模型是否真的學到了東西。無論你是想完成課程作業(yè)還是為更復雜的深度模型打基礎這篇內容都將提供一條清晰的、可復現的路徑。2. 受限玻爾茲曼機核心原理速覽與MATLAB建模思路在動手寫代碼之前我們必須對RBM有一個清晰、直觀的理解這決定了我們如何用MATLAB的數據結構和運算來刻畫它。你可以把RBM想象成一個兩層、對稱連接的神經網絡但它沒有通常意義上的“輸出層”。底層叫可見層代表我們能看到的數據比如一張二值化圖片的像素。頂層叫隱藏層是我們希望機器自動學習到的、能夠解釋數據規(guī)律的特征。每個可見層神經元和每個隱藏層神經元之間都有連接權重但同層神經元之間沒有任何連接這就是“受限”的含義——它讓概率計算變得可處理。RBM是一個基于能量的模型。它為系統(tǒng)的每一種狀態(tài)即每一組具體的可見向量v和隱藏向量h的取值定義一個“能量”能量越低的狀態(tài)出現的概率越高。這個能量函數是核心E(v, h) -b*v - c*h - v*W*h這里v是可見層向量h是隱藏層向量b是可見層的偏置c是隱藏層的偏置W是連接權重矩陣。公式里的三項分別代表了可見單元自身、隱藏單元自身以及它們之間交互對能量的貢獻。我們的目標是調整參數W, b, c使得訓練數據即我們觀測到的v對應的能量盡可能低也就是讓模型認為這些數據出現的概率高。那么給定可見層狀態(tài)v隱藏層神經元h_j取值為1的概率是多少由于層內無連接這個計算是獨立并行的公式非常簡潔P(h_j1 | v) sigmoid(c_j sum_i(v_i * W_{ij}))在MATLAB里這就是一個sigmoid函數1./(1exp(-x))的應用。給定隱藏層狀態(tài)h計算可見層神經元v_i取值為1的概率也是對稱的P(v_i1 | h) sigmoid(b_i sum_j(h_j * W_{ij}))這種給定一層、另一層條件獨立且可并行計算的性質是RBM能夠進行高效吉布斯采樣的基礎也是后續(xù)對比散度算法的關鍵。我們的學習目標是最大化訓練數據的似然概率。通過推導權重的更新規(guī)則可以表示為ΔW learning_rate * ( v_i h_j_data - v_i h_j_model )尖括號代表求期望。v_i h_j_data是當可見層固定為訓練數據時隱藏層根據上述概率采樣或取其概率值即“均值場”然后計算v_i和h_j的關聯。這代表了數據驅動的“正能量”它試圖降低訓練數據的能量。v_i h_j_model則是模型本身定義的分布下的期望計算它理論上需要從模型分布中采樣直到平衡這非常耗時。Hinton提出的“對比散度”算法巧妙地解決了這個問題用訓練數據初始化可見層然后進行少數幾步通常是1步即CD-1吉布斯采樣可見→隱藏→可見來近似這個模型期望。這構成了我們MATLAB實現的核心迭代循環(huán)。在MATLAB中建模我們將把W定義為一個[n_visible, n_hidden]的矩陣b是[n_visible, 1]的列向量c是[n_hidden, 1]的列向量。訓練數據data是一個[n_samples, n_visible]的矩陣每一行是一個樣本。所有的運算都將利用MATLAB高效的矩陣乘法來實現避免低效的循環(huán)。3. MATLAB環(huán)境準備與數據預處理實戰(zhàn)工欲善其事必先利其器。在開始編寫RBM核心算法前我們需要確保MATLAB環(huán)境就緒并將數據處理好。這個過程看似簡單卻藏著不少初學者容易忽略的細節(jié)直接影響到后續(xù)訓練的成敗。首先確保你有一個能正常運行的MATLAB版本。對于RBM這類基礎算法其實對版本要求并不高R2016a之后的版本都完全夠用。關鍵在于你需要熟悉如何在MATLAB中組織你的項目。我強烈建議為這個RBM項目單獨創(chuàng)建一個文件夾比如命名為MyRBM_Project。在這個文件夾里你可以創(chuàng)建以下文件主腳本train_rbm.mRBM核心函數rbm.m以及可能的數據加載和可視化腳本。這樣做的好處是路徑清晰便于管理也方便你后續(xù)擴展成深度信念網絡。接下來是數據。RBM的可見層單元默認是二值的0或1。因此我們使用的數據必須是二值化的或者可以合理地解釋為概率。一個經典的數據集是MNIST手寫數字但它的像素值是0到255的灰度。直接使用是不行的。常見的預處理方法是設定一個閾值如128大于閾值置1否則置0。但更推薦的方法是將其歸一化到[0, 1]區(qū)間即將每個像素值除以255。此時像素值可以解釋為該像素點“激活”為1的概率。我們的RBM代碼將能夠處理這種連續(xù)概率值此時稱為“伯努利-高斯RBM”的可見層但采樣時仍按此概率進行二值化。在MATLAB中加載和預處理MNIST數據可以借助一些開源工具箱但為了理解本質我們可以用一個小型人造數據集開始。假設我們想學習一個簡單的“十字”圖案。我們可以創(chuàng)建一些5x5的二值圖像其中中心行和中心列的像素為1其余為0。在MATLAB中我們可以這樣生成數據% 生成合成數據十字圖案 n_samples 1000; % 1000個樣本 img_size 5; % 5x5圖像 data zeros(n_samples, img_size*img_size); % 初始化數據矩陣 for i 1:n_samples img zeros(img_size); img(ceil(img_size/2), :) 1; % 中心行置1 img(:, ceil(img_size/2)) 1; % 中心列置1 % 添加少量隨機噪聲使數據更真實 img img 0.1 * randn(img_size); % 添加高斯噪聲 img (img 0.5); % 二值化閾值0.5 data(i, :) img(:); % 展平為行向量并存入數據矩陣 end % 檢查數據維度應該是 1000 x 25 disp(size(data));注意在實際科研中你可能會用到loadMNISTImages等函數來讀取真實MNIST數據。但無論數據來源如何預處理的核心原則是一致的確保數據矩陣的每一行是一個樣本每一列是一個特征可見單元且值在[0,1]區(qū)間內。對于非二值數據務必進行歸一化。數據準備好后我們還需要思考一個問題需不需要劃分訓練集和測試集對于RBM這種無監(jiān)督學習模型我們通常用所有數據來訓練以學習數據的分布。評估則可以通過查看模型重構數據的能力或者用學習到的特征作為下游任務如分類的輸入。因此在初始實現階段我們可以用全部數據訓練。但一個好的習慣是始終保留一小部分數據作為“驗證集”用于監(jiān)控訓練過程是否過擬合雖然RBM的過擬合風險相對較低但仍可能發(fā)生。最后初始化一個隨機數種子是個好習慣這能確保你的實驗結果是可復現的。在MATLAB中可以使用rng(42)42是一個常用種子來固定隨機數生成器。這對于調試和對比不同參數的效果至關重要。4. RBM核心算法實現權重初始化與對比散度有了清晰的理論認識和準備好的數據我們現在可以著手實現RBM最核心的部分參數初始化和對比散度訓練循環(huán)。這是將數學公式轉化為可運行代碼的關鍵一步其中每一步的設計都有其道理。首先我們初始化參數W, b, c。初始化不當會導致訓練初期梯度消失或爆炸。一個廣泛使用的經驗法則是從均值為0、標準差較小的正態(tài)分布中隨機初始化權重W。標準差通常設為0.01或1/sqrt(n_visible)。偏置b和c可以初始化為0。在MATLAB中我們可以這樣寫function [W, b, c] rbm_init(n_visible, n_hidden) % 初始化RBM參數 % n_visible: 可見層單元數 % n_hidden: 隱藏層單元數 % 返回: 權重矩陣 W, 可見層偏置 b, 隱藏層偏置 c % 權重初始化使用較小的隨機值打破對稱性 std_dev 0.01; % 或者 1/sqrt(n_visible) W std_dev * randn(n_visible, n_hidden); % 偏置初始化為0 b zeros(n_visible, 1); c zeros(n_hidden, 1); end接下來是核心的訓練函數它實現了對比散度算法。我們將遵循CD-1的流程并采用“小批量”隨機梯度下降來加速訓練并增加穩(wěn)定性。函數的輸入包括數據、隱藏單元數、學習率、訓練輪次和批量大小。function [W, b, c, errors] train_rbm(data, n_hidden, learning_rate, n_epochs, batch_size) % 使用對比散度(CD-1)訓練RBM % data: 訓練數據每行一個樣本值在[0,1] % n_hidden: 隱藏層單元數 % learning_rate: 學習率 % n_epochs: 訓練輪次 % batch_size: 批量大小 % 返回: 訓練好的參數 W, b, c以及每輪的重構誤差 [n_samples, n_visible] size(data); num_batches ceil(n_samples / batch_size); % 初始化參數 [W, b, c] rbm_init(n_visible, n_hidden); errors zeros(n_epochs, 1); % 記錄每輪的平均重構誤差 for epoch 1:n_epochs err_sum 0; % 打亂數據順序 shuffled_idx randperm(n_samples); data_shuffled data(shuffled_idx, :); for batch 1:num_batches % 獲取當前小批量數據 batch_start (batch-1)*batch_size 1; batch_end min(batch*batch_size, n_samples); batch_data data_shuffled(batch_start:batch_end, :); batch_size_curr size(batch_data, 1); % 將數據矩陣轉置便于后續(xù)矩陣運算 (n_visible x batch_size) v0 batch_data; % 初始可見層狀態(tài)概率值 % --- 正向傳播計算隱藏層概率并采樣 --- % h0_prob: 給定v0時隱藏層激活的概率 (n_hidden x batch_size) h0_prob sigmoid(bsxfun(plus, c, W * v0)); % 等價于 c W*v0 h0_state h0_prob rand(size(h0_prob)); % 二值采樣 % --- 計算數據相關的統(tǒng)計量 --- % positive_associations: v_i h_j_data 的近似 positive_associations v0 * h0_state / batch_size_curr; positive_visible_bias mean(v0, 2); % v_i_data positive_hidden_bias mean(h0_prob, 2); % 這里用概率也可以用采樣后的狀態(tài) % --- 負相從模型中采樣CD-1--- % 從h0_state重構可見層 v1_prob sigmoid(bsxfun(plus, b, W * h0_state)); % 重構的可見層概率 v1_state v1_prob rand(size(v1_prob)); % 采樣得到v1 % 從v1_state再次計算隱藏層 h1_prob sigmoid(bsxfun(plus, c, W * v1_state)); % h1_state h1_prob rand(size(h1_prob)); % CD-1通常這里不采樣直接用概率 % --- 計算模型相關的統(tǒng)計量 --- % negative_associations: v_i h_j_model 的近似 (CD-1) negative_associations v1_state * h1_prob / batch_size_curr; negative_visible_bias mean(v1_state, 2); negative_hidden_bias mean(h1_prob, 2); % --- 參數更新 --- W W learning_rate * (positive_associations - negative_associations); b b learning_rate * (positive_visible_bias - negative_visible_bias); c c learning_rate * (positive_hidden_bias - negative_hidden_bias); % --- 計算當前批次的重構誤差用于監(jiān)控--- % 使用v0和重構的v1_prob之間的交叉熵或均方誤差 reconstruction_error -sum(sum(v0 .* log(v1_prob 1e-10) (1-v0) .* log(1-v1_prob 1e-10))) / batch_size_curr; err_sum err_sum reconstruction_error; end % 記錄本輪平均誤差 errors(epoch) err_sum / num_batches; % 可選每若干輪打印一次進度 if mod(epoch, 10) 0 fprintf(Epoch %d, Reconstruction Error: %f\n, epoch, errors(epoch)); end end end % Sigmoid輔助函數 function y sigmoid(x) y 1 ./ (1 exp(-x)); end這段代碼有幾個關鍵點需要解釋矩陣運算與維度注意我們始終將數據以列向量的形式堆疊成矩陣n_visible x batch_size。這樣W * v0就能一次性計算所有樣本的隱藏層輸入極大提升了效率。bsxfun函數用于處理偏置向量與矩陣的加法對于新版MATLAB直接使用運算符即可自動廣播。采樣策略在正向傳播中我們根據概率h0_prob進行了二值采樣得到h0_state。這是“隨機”RBM的標準做法。也有一種變體叫“均值場”方法直接使用概率值而不采樣這通常會使訓練更穩(wěn)定但可能丟失一些隨機性。在負相中CD-1算法通常只對可見層進行采樣得到v1_state而對第二次的隱藏層使用概率h1_prob來計算梯度這被證明是更有效的。重構誤差我們使用二進制交叉熵作為重構誤差它衡量了原始數據v0概率值與重構數據v1_prob概率值之間的差異。添加一個極小值1e-10是為了避免對數為負無窮。這個誤差是監(jiān)控訓練進程的重要指標它應該隨著訓練輪次增加而穩(wěn)步下降。批量更新我們不是用一個樣本更新一次參數而是用一個小批量batch_size的數據計算平均梯度后再更新。這能減少參數更新的方差使訓練過程更平滑也更能利用MATLAB的矩陣運算優(yōu)勢。5. 訓練過程監(jiān)控、調參與常見問題排查代碼寫好了直接運行可能不會一帆風順。訓練一個RBM就像烹飪火候學習率、食材比例網絡結構、時間訓練輪次都需要細心把控。這一節(jié)我們來聊聊如何監(jiān)控訓練過程調整關鍵參數并解決那些讓你抓狂的典型問題。首先學習率是首要超參數。學習率太大權重更新會“過沖”導致重構誤差劇烈震蕩甚至發(fā)散變成NaN。學習率太小訓練會慢如蝸牛誤差下降曲線幾乎是一條平線。對于RBM一個常見的起始值是0.01或0.1。我個人的經驗是可以先設為0.1觀察前幾十輪的重構誤差。如果誤差爆炸立刻降到0.01或0.05。如果誤差下降非常緩慢可以嘗試增大到0.2。一個更穩(wěn)健的策略是使用衰減的學習率比如每50輪將學習率乘以0.95。其次隱藏層單元數決定了模型的容量。單元太少模型無法捕捉數據的復雜結構重構誤差會停留在一個較高的平臺。單元太多則可能導致過擬合模型會記住訓練數據的噪聲而非一般規(guī)律。對于我們的5x5十字圖案25個可見單元隱藏單元數可以從10到50之間嘗試。一個粗略的經驗法則是隱藏單元數可以與可見單元數在同一數量級或略少。你可以通過觀察“權重可視化”來輔助判斷如果很多隱藏單元學習到的權重圖看起來是重復或模糊的可能意味著隱藏單元過多或訓練不足。批量大小影響梯度估計的噪聲和訓練速度。較小的批量如10 20會帶來噪聲更大的更新有時有助于跳出局部最優(yōu)但訓練不穩(wěn)定。較大的批量如100 200能提供更平滑的梯度估計訓練更穩(wěn)定但可能內存消耗更大。對于MNIST60000樣本批量大小128或256是常見選擇。對于我們的合成數據1000樣本批量大小64或128比較合適。訓練輪次需要通過監(jiān)控重構誤差來決定。在訓練開始時誤差應該快速下降。隨著輪次增加下降速度會變慢最終趨于平穩(wěn)。你可以繪制誤差隨輪次變化的曲線。當曲線在連續(xù)多輪比如50輪內不再有明顯下降變化小于一個閾值如1e-5就可以考慮停止訓練了。設置一個最大輪次如500作為保險?,F在讓我們把訓練和監(jiān)控腳本整合起來% 主腳本訓練并監(jiān)控RBM clear; close all; clc; % 1. 生成或加載數據 % 這里使用第3節(jié)生成的合成數據假設變量data已存在 % 或者加載MNIST等 % load(mnist_train.mat); % 假設數據在變量 train_x 中且已歸一化到[0,1] % data train_x; % 2. 設置超參數 n_hidden 20; % 隱藏層單元數 learning_rate 0.1; % 初始學習率 n_epochs 200; % 訓練輪次 batch_size 64; % 批量大小 % 3. 訓練RBM [W_trained, b_trained, c_trained, errors] train_rbm(data, n_hidden, learning_rate, n_epochs, batch_size); % 4. 可視化訓練過程 figure; plot(1:n_epochs, errors, b-, LineWidth, 1.5); xlabel(訓練輪次 (Epoch)); ylabel(平均重構誤差); title(RBM訓練誤差曲線); grid on; % 5. 可視化學習到的權重特征 % 將權重矩陣的每一列對應一個隱藏單元重塑為圖像尺寸 img_size sqrt(size(W_trained, 1)); % 假設是方形圖像 if img_size floor(img_size) % 確保可以重塑 figure; for i 1:min(25, n_hidden) % 最多顯示25個特征 subplot(5,5,i); w_img reshape(W_trained(:, i), img_size, img_size); imagesc(w_img); colormap(gray); axis image off; title(sprintf(Hid %d, i)); end sgtitle(RBM學習到的隱藏層特征權重列); end運行這段代碼后你會得到兩張圖。第一張是誤差曲線它應該是一條平滑下降的曲線。如果曲線震蕩嘗試降低學習率。如果曲線幾乎水平嘗試增大學習率或檢查權重初始化是否過小。第二張圖是權重可視化。每個子圖代表一個隱藏單元與所有可見單元連接的權重。對于一個學習良好的、處理圖像數據的RBM這些權重圖應該看起來像“邊緣檢測器”或“局部斑點”它們代表了模型從數據中提取的基礎特征。如果所有圖都是模糊的噪聲說明模型可能沒有學到有意義的東西需要檢查數據、學習率或訓練輪次。常見問題與排查重構誤差為NaN或Inf這幾乎總是因為學習率太大導致權重更新爆炸。立即降低學習率例如降到原來的1/10。同時檢查sigmoid函數的輸入是否過大導致指數運算溢出。可以在sigmoid函數中加入數值穩(wěn)定處理y 1./(1 exp(-max(min(x, 50), -50)));將輸入限制在[-50, 50]區(qū)間。誤差不下降檢查數據確保數據已正確歸一化到[0,1]。打印幾行數據看看。檢查初始化權重初始化標準差std_dev不能為0或太小。嘗試0.01或0.1。檢查學習率學習率可能太小。嘗試增大。檢查梯度更新在訓練循環(huán)中打印positive_associations和negative_associations的范數看看梯度是否非零且方向合理。訓練速度慢確保你使用了矩陣運算而不是在循環(huán)中對每個樣本單獨計算。對于大數據集可以考慮將數據轉換為single單精度類型以減少內存占用和加速計算但要注意精度損失。模型過擬合雖然RBM作為生成模型過擬合表現不如判別模型明顯但如果隱藏單元過多它可能會過度記憶訓練數據。除了減少隱藏單元可以引入權重衰減即在梯度更新中加入一個懲罰項W W lr * (positive_associations - negative_associations - weight_cost * W)其中weight_cost是一個小的正數如0.0002。6. 模型評估與應用重構、采樣與特征提取訓練完成后我們怎么知道這個RBM模型是好是壞它除了能降低一個叫“重構誤差”的數字還能做什么這部分我們將探討RBM的三個核心應用數據重構、隨機采樣和特征提取并在MATLAB中實現它們。這才是模型價值的真正體現。6.1 數據重構與可視化重構是檢驗RBM學習質量最直觀的方式。給定一個測試樣本我們讓RBM進行一次“正向傳播”數據→隱藏層和一次“反向傳播”隱藏層→可見層得到重構后的數據。比較原始數據和重構數據可以直觀看出模型抓住了哪些主要特征丟失了哪些細節(jié)。% 假設我們有一個測試樣本 test_sample (1 x n_visible 的行向量) test_sample data(1, :); % 取第一個訓練樣本作為示例 % 將行向量轉為列向量以便計算 v_test test_sample; % 正向傳播得到隱藏層概率/狀態(tài) h_prob sigmoid(c_trained W_trained * v_test); % h_state h_prob rand(size(h_prob)); % 采樣得到隱藏狀態(tài)這里我們用概率 % 反向傳播重構可見層 v_recon_prob sigmoid(b_trained W_trained * h_prob); % 使用概率h_prob進行重構 % 如果想得到二值重構可以采樣v_recon v_recon_prob rand(size(v_recon_prob)); % 可視化比較 img_size sqrt(length(v_test)); if img_size floor(img_size) figure; subplot(1,2,1); imagesc(reshape(v_test, img_size, img_size)); title(原始圖像); colormap(gray); axis image off; subplot(1,2,2); imagesc(reshape(v_recon_prob, img_size, img_size)); title(RBM重構圖像 (概率)); colormap(gray); axis image off; end % 計算該樣本的重構誤差交叉熵 recon_err_single -sum(v_test .* log(v_recon_prob1e-10) (1-v_test).*log(1-v_recon_prob1e-10)); fprintf(單個樣本重構誤差: %f\n, recon_err_single);一個好的RBM其重構圖像應該能清晰保留原始圖像的主體結構。對于十字圖案重構結果應該依然是一個清晰的十字。如果重構結果模糊或失真嚴重說明模型沒有充分學習到數據的分布。6.2 吉布斯采樣與生成新樣本RBM作為一個生成模型最酷的能力是從其學到的分布中“幻想”出新的、與訓練數據類似但又不完全相同的樣本。這個過程通過吉布斯采樣實現從一個隨機初始的可見層狀態(tài)開始交替地對隱藏層和可見層進行采樣經過足夠多的步驟后采樣得到的可見層狀態(tài)就來自于模型分布。function generated_sample rbm_gibbs_sample(W, b, c, n_steps, img_size) % 從訓練好的RBM中通過吉布斯采樣生成樣本 % n_steps: 采樣步數越多樣本越接近模型分布 % img_size: 生成圖像的尺寸用于可視化 n_visible size(W, 1); n_hidden size(W, 2); % 隨機初始化可見層可以全0.5或隨機二值 v rand(n_visible, 1) 0.5; % 隨機二值初始化 % v 0.5 * ones(n_visible, 1); % 或用概率0.5初始化 for step 1:n_steps % 給定v采樣h h_prob sigmoid(c W * v); h h_prob rand(n_hidden, 1); % 給定h采樣v v_prob sigmoid(b W * h); v v_prob rand(n_visible, 1); end generated_sample v; % 最終采樣得到的可見層狀態(tài) % 可視化生成的樣本 if nargin 5 ~isempty(img_size) figure; imagesc(reshape(generated_sample, img_size, img_size)); colormap(gray); axis image off; title(sprintf(吉布斯采樣生成樣本 (步數%d), n_steps)); end end % 使用訓練好的模型生成一個樣本 gen_sample rbm_gibbs_sample(W_trained, b_trained, c_trained, 1000, img_size);采樣步數n_steps需要足夠大以確保馬爾可夫鏈達到平穩(wěn)分布。通常可以從100步開始嘗試觀察生成的樣本是否穩(wěn)定。如果模型訓練得好生成的樣本應該看起來像訓練數據例如一個模糊但可辨的十字形狀。這是一個強有力的證明表明RBM確實捕捉到了數據的本質特征。6.3 特征提取將RBM作為預處理工具RBM的隱藏層激活可以看作是對輸入數據的一種“編碼”或“特征表示”。這些特征通常比原始數據更具代表性和區(qū)分度。我們可以用訓練好的RBM為下游任務如分類提取特征。% 為整個數據集提取隱藏層特征 % data_matrix 是 n_samples x n_visible 的數據矩陣 data_matrix data; % 假設是我們的訓練數據 n_samples size(data_matrix, 1); % 將數據轉置為 n_visible x n_samples 以便矩陣乘法 data_for_feat data_matrix; hidden_features_prob sigmoid(bsxfun(plus, c_trained, W_trained * data_for_feat)); % hidden_features_prob 的維度是 n_hidden x n_samples % 轉置回來得到 n_samples x n_hidden 的特征矩陣 extracted_features hidden_features_prob; % 現在extracted_features 可以作為新的特征輸入到邏輯回歸、SVM等分類器中。 fprintf(提取的特征矩陣維度: %d x %d\n, size(extracted_features));這里我們使用了隱藏層的概率值h_prob作為特征而不是采樣得到的二值狀態(tài)。概率值包含了更多信息通常作為特征效果更好。這些特征維度更低n_hidden維且是原始數據的一種非線性變換往往能提升后續(xù)分類器的性能。注意在深度信念網絡中正是通過堆疊多個RBM將每一層的隱藏層特征作為下一層的輸入從而逐層學習到越來越抽象的特征表示。我們這里實現的單層RBM是構建更復雜深度模型的基礎模塊。7. 性能優(yōu)化、擴展與踩坑實錄當你跑通了一個基礎的RBM并看到了初步結果后可能會想如何讓它跑得更快、更穩(wěn)、處理更復雜的數據這一節(jié)我將分享一些進階的優(yōu)化技巧、處理非二值數據的方法以及我在實踐中踩過的一些“坑”。7.1 性能優(yōu)化向量化與并行化我們之前的實現已經充分利用了MATLAB的矩陣運算這是最大的性能優(yōu)勢。但仍有優(yōu)化空間使用單精度數據如果內存和精度允許將數據和參數轉換為single類型可以加速計算并減少內存占用??梢栽跀祿虞d后使用data single(data);并在初始化參數時使用randn(..., single)。預分配數組在訓練循環(huán)中我們?yōu)槊總€批次計算h0_prob,v1_prob等。確保這些中間變量不會在循環(huán)中動態(tài)改變大小這有助于MATLAB的JIT即時編譯優(yōu)化。利用GPU對于大規(guī)模數據和大網絡MATLAB支持使用GPU加速。你可以使用gpuArray將數據和參數轉移到GPU上。例如data_gpu gpuArray(data);然后在GPU上進行所有矩陣運算。這通常能帶來數量級的加速但需要你有兼容的NVIDIA GPU和Parallel Computing Toolbox。7.2 處理實值數據高斯-伯努利RBM我們的RBM假設可見層是二值的。但現實中很多數據是實值的比如像素灰度值、音頻波形等。這時可以使用高斯-伯努利RBM。其核心改變在于可見層單元的條件分布從伯努利分布變?yōu)楦咚狗植颊龖B(tài)分布。這意味著可見層單元v_i的條件均值是sigmoid(b_i sum_j W_{ij} h_j)的函數但方差需要額外學習或固定。數據需要標準化為零均值和單位方差或某個固定方差這是高斯分布的要求。權重更新公式需要做相應調整。在MATLAB中實現時一個常見的簡化是固定可見層方差為1。此時可見層采樣公式變?yōu)関_i ~ N( mean_i, 1 )其中mean_i b_i sum_j W_{ij} h_j。 而重構誤差則從交叉熵變?yōu)榫秸`差。這需要對train_rbm函數中的采樣和重構誤差計算部分進行修改。這是一個重要的擴展方向讓你能處理MNIST原始灰度圖等數據。7.3 我的踩坑實錄與經驗技巧學習率與權重初始化的耦合我曾遇到過模型完全不學習的情況誤差曲線是一條直線。排查后發(fā)現是因為權重初始化標準差std_dev設得太小如1e-5導致sigmoid函數的輸入始終在0附近梯度極小。無論學習率多大更新都微乎其微。經驗權重初始化的尺度要與學習率匹配。通常用1/sqrt(n_visible)或0.01是安全的起點?!八郎窠浽眴栴}在訓練過程中某些隱藏單元可能因為初始權重不利或學習過程中梯度始終很小導致其激活概率永遠接近0或1不再對學習有貢獻。這被稱為“死神經元”。對策可以監(jiān)控隱藏層的平均激活度。如果某個單元的平均激活度在整個訓練集上長期接近0或1可以考慮在偏置更新中加入一個“稀疏性”懲罰鼓勵平均激活度接近一個目標值如0.1或者簡單地重新初始化這個神經元的權重。CD-K中K的選擇我們使用了CD-1即只進行一次吉布斯采樣。理論上K越大CD-3 CD-10對模型期望的近似越好但計算成本也越高。在實踐中CD-1對于很多問題已經足夠好并且是訓練深度信念網絡時的標準選擇。經驗除非你追求極致的模型性能并且有充足的計算資源否則CD-1是性價比最高的選擇。重構誤差下降但生成樣本質量差有時誤差曲線看起來很好但用吉布斯采樣生成的樣本卻是一團噪聲。這可能是因為模型陷入了某個局部最優(yōu)或者學習到的分布是多峰的而采樣過程沒有充分混合。排查嘗試增加吉布斯采樣的步數如5000步并使用不同的隨機種子初始化。同時檢查訓練數據的多樣性是否足夠。MATLAB版本與函數兼容性代碼中使用了bsxfun函數這在舊版MATLAB中是必須的。但在R2016b及以后版本MATLAB引入了隱式擴展可以直接使用、-、.*、./等運算符對維度匹配的數組和向量進行操作。如果你在新版MATLAB中看到關于bsxfun的警告可以安全地將其替換為直接運算符。例如h0_prob sigmoid(bsxfun(plus, c, W * v0));可以寫成h0_prob sigmoid(c W * v0);MATLAB會自動將列向量c擴展到與矩陣W*v0相同的列數。本文還有配套的精品資源點擊獲取