受限玻尔兹曼机(RBM)与python在Tensorflow的实现
任何程序錯誤,以及技術疑問或需要解答的,請掃碼添加作者VX:1755337994
簡介
受限玻爾茲曼機是一種無監督,重構原始數據的一個簡單的神經網絡。
受限玻爾茲曼機先把輸入轉為可以表示它們的一系列輸出;這些輸出可以反向重構這些輸入。通過前向和后向訓練,訓練好的網絡能夠提取出輸入中最重要的特征。
為什么RBM很重要?
因為它能夠自動地從輸入中提取重要的特征。
RBM有什么用
用于協同過濾(Collaborative Filtering)
降維(dimensionality reduction)
分類(classification)
特征學習(feature leatning)
主題模型(topic modeling)
搭建深度置信網絡(Deep belief network)
RBM是生成模型嗎?
生成模型和判別模型的區別
判別模型: 考慮一個分類問題,如我們想根據車的一些特征分辨一輛轎車和一輛SUV。給定一個訓練集,一個算法如邏輯回歸,它嘗試找到一條可以直線,以這條直線作為決策邊界把轎車和SUV區分開。
生成模型: 根據汽車,我們可以建立一個模型,比如轎車是什么樣子的;然后再根據SUV, 我們建立另外一個SUV的模型;最后根據這個兩個模型,判斷一輛車是轎車還是SUV.
生成模型在輸入特征下有特定的概率分布。 生成模型中既可以使用監督學習和無監督:
在無監督學習中, 我們想要得到一個P(x)的模型, x是輸入向量;
在監督學習中,我們首先得到的是P(x|y), y是x的標記。舉個例子,如果y標記一輛車是轎車(0)或者SUV(1), 那么p(x|y=0)就描述了轎車的特征是怎么分布的,p(x|y=1)就描述了轎車的特征是怎么分布的。 如果我們能夠找到P(x|y)和P(y), 我們就能夠使用貝葉斯公式去估算P(y|x),因為P(y|x) = P(x|y)P(y)/P(x).
使用MINST 數據集展示如何使用RBMs
初始化并加載數據
這里要通過網絡獲取遠程的文件utils.py到本地,python2 和 python3通過網絡獲取文件是不一樣的。
這里的代碼用python3。
RBM的層
一個RBM有兩個層,第一層叫可視層(visible)或者輸入層,第二層是隱藏層( hidden layer)。MNIST數據庫的每一張圖片有784個像素,所以可視層必須有784個輸入節點。第二個隱藏層在這里設為i ii個神經元。每一個神經元是2態的(binary state), 稱為si。根據j jj個輸入單元,并由邏輯函數(logistic function) 產生一個概率輸出,決定每一個隱藏層的單元是開(si = 1)是還關(si =0)。 這里我們取?i = 500 i=500i=500.
第一層的每一個節點有一個偏差 (bias),使用vb表示;
第二層的每一個節點也有一個偏差,使用hb表示;
定義可視層和隱藏層之間的權重,行表示輸入節點,列表示輸出節點,這里權重W是一個784x500的矩陣。
W = tf.placeholder("float", [784, 500])訓練好RBM能做什么
當RBM被訓練好了,它就能夠在跟定一些隱藏值(如下雨)來計算一個事件(比如濕滑路面)的概率. 那就是說,RBM可以被看作是生成模型,它給每一個可能的二態向量( binary states vectors)生成一個概率。
這些二態向量( binary states vectors)多少種情況?
可視層可以有不同的二態(0或1),或者說有不同的設置。比如,當輸入層只有7個單元時,它有2 7 {2^{7}}27?中排列,每一種排列有它對應的概率(這里我們假設沒有偏差)
- (0,0,0,0,0,0,0) --> p(config1)=p(v1)=p(s1=0,s2=0, …, s7=0)
- (0,0,0,0,0,0,1) --> p(config2)=p(v2)=p(s1=0,s2=1, …, s7=1)
- (0,0,0,0,0,1,0) --> p(config3)=p(v3)=p(s1=1,s2=0, …, s7=0)
- (0,0,0,0,0,1,1) --> p(config4)=p(v4)=p(s1=1,s2=1, …, s7=1)
- etc.
所以, 如果我們有784個單元,對于全部的2 784 {2^{784}}2784種輸入情況,它會產生一個概率分布,P(v)。
如何訓練RBM
訓練分為兩個階段:1) 前向(forward pass) 2)后向( backward pass)或者重構(reconstruction):
階段1
前向:改變的是隱藏層的值。 輸入數據經過輸入層的所有節點傳遞到隱藏層。這個計算是隨機開始(This computation begins by making stochastic decisions about whether to transmit that input or not (i.e. to determine the state of each hidden layer)). 在隱藏層的節點上,X乘以W再加上h_bias. 這個結果再通過sigmoid函數產生節點的輸出或者狀態。因此,每個隱藏節點將有一個概率輸出。對于訓練集的每一行,生成一個概率構成的張量(tensor),這個張量的大小為[1X500], 總共55000個向量[h0=55000x500]。 接著我們得到了概率的張量,從所有的分布中采樣,h0。 那就是說,我們從隱藏層的概率分布中采樣激活向量(activation vector). 這些得到的樣本用來估算反向梯度(negative phase gradient).
X = tf.placeholder("float", [None, 784]) _h0= tf.nn.sigmoid(tf.matmul(X, W) + hb) #probabilities of the hidden units h0 = tf.nn.relu(tf.sign(_h0 - tf.random_uniform(tf.shape(_h0)))) #sample_h_given_X參考下面的代碼理解上面的代碼:
with tf.Session() as sess:a= tf.constant([0.7, 0.1, 0.8, 0.2])print sess.run(a)b=sess.run(tf.random_uniform(tf.shape(a)))print bprint sess.run(a-b)print sess.run(tf.sign( a - b))print sess.run(tf.nn.relu(tf.sign( a - b)))[0.7 0.1 0.8 0.2] [0.31160402 0.3776673 0.42522812 0.8557215 ] [ 0.38839597 -0.2776673 0.3747719 -0.6557215 ] [ 1. -1. 1. -1.] [1. 0. 1. 0.]階段2
反向(重構): RBM在可視層和隱藏層之間通過多次前向后向傳播重構數據。
所以在這個階段,從隱藏層(h0)采樣得到的激活向量作為輸入。相同的權重矩陣和可視層偏差將用于計算并通過sigmoid函數。其輸出是一個重構的結果,它近似原始輸入。
重構步驟:
如何計算梯度
為了訓練RBM, 我們必須使賦值到訓練集V上的概率乘積最大。假如數據集V,它的每一行看做是一個可視的向量v:
?
?
或者等效地最大化訓練集概率的對數
?
我們也可以定義一個目標函數并嘗試最小化它。為了實現這個想法,我們需要這個函數的各個參數的偏導數。從上面的表達式我們知道,他們都是由權重和偏差間接組成的函數,所以最小化目標函數就是優化權重。因此,我們可以使用隨機梯度下降(SGD)去找到最優的權重進而使目標函數取得最小值。在推導的時候,有兩個名詞,正梯度和負梯度。這兩個狀態反映了他們對模型概率密度的影響。正梯度取決于觀測值(X),負梯度只取決于模型。
正的階段增加訓練數據的可能性;
負的階段減少由模型生成的樣本的概率。
負的階段很難計算,所以我們用一個對比散度(Contrastive Divergence (CD))去近似它。它是按照這樣的方式去設計的:梯度估計的方向至少有一些準確。實際應用中,更高準確度的方法如CD-k 或者PCD用來訓練RBMs。 計算對比散度的過程中,我們要用吉布斯采樣(Gibbs sampling)對模型的分布進行采樣。
對比散度實際是一個用來計算和調整權重矩陣的一個矩陣。 改變權重W漸漸地變成了權重值的訓練。然后在每一步(epoch), W通過下面的公式被更新為一個新的值w’。
W ′ = W + α ? C D W' = W + \alpha * CDW′=W+α?CD
α \alphaα?是很小的步長,也就是大家所熟悉的學習率(Learning rate)
如何計算相對散度?
下面展示了單步相對散度的計算(CD-1)步驟:
1.從訓練集X中取訓練樣本,計算隱藏層的每個單元的概率并從這個概率分布中采樣得到一個隱藏層激活向量h0;
_ h 0 = s i g m o i d ( X ? W + h b ) \_h0 = sigmoid(X \otimes W + hb)_h0=sigmoid(X?W+hb)
h 0 = s a m p l e P r o b ( _ h 0 ) h0 = sampleProb(\_h0)h0=sampleProb(_h0)
2.計算X和h0的外積,這就是正梯度
w _ p o s _ g r a d = X ? h 0 w\_pos\_grad = X \otimes h0w_pos_grad=X?h0(Reconstruction in the first pass)
3.從h重構v1, 接著對可視層單元采樣,然后從這些采樣得到的樣本中重采樣得到隱藏層激活向量h1.這就是吉布斯采樣。
_ v 1 = s i g m o i d ( h 0 ? t r a n s p o s e ( W ) + v b ) \_v1=sigmoid(h0?transpose(W)+vb)_v1=sigmoid(h0?transpose(W)+vb)
v 1 = s a m p l e p r o b ( _ v 1 ) ? ( S a m p l e v g i v e n h ) v1=sampleprob(\_v1) \ (Sample v given h)v1=sampleprob(_v1)?(Samplevgivenh)
h 1 = s i g m o i d ( v 1 ? W + h b ) h1=sigmoid(v1?W+hb)h1=sigmoid(v1?W+hb)
4.計算v1和h1的外積,這就是負梯度。
w _ n e g _ g r a d = v 1 ? h 1 ? ( R e c o n s t r u c t i o n 1 ) w\_neg\_grad=v1?h1 \ (Reconstruction 1)w_neg_grad=v1?h1?(Reconstruction1)
5. 對比散度等于正梯度減去負梯度,對比散度矩陣的大小為784x500.
C D = ( w _ p o s _ g r a d ? w _ n e g _ g r a d ) / d a t a p o i n t s CD = (w\_pos\_grad - w\_neg\_grad) / datapointsCD=(w_pos_grad?w_neg_grad)/datapoints
什么是采樣(sampleProb)?
在前向算法中,我們隨機地設定每個hi的值為1,伴隨著概率s i g m o i d ( v ? W + h b ) sigmoid(v \otimes W + hb)sigmoid(v?W+hb);
在重構過程中,我們隨機地設定每一個vi的值為1,伴隨著概率s i g m o i d ( h ? t r a n s p o s e ( W ) + v b ) sigmoid(h \otimes transpose(W) + vb)sigmoid(h?transpose(W)+vb)
什么是目標函數?
目的:最大限度地提高我們從該分布中獲取數據的可能性
計算誤差: 每一步(epoch), 我們計算從第1步到第n步的平方誤差的和,這顯示了數據和重構數據的誤差。
創建一個回話并初始化向量:
cur_w = np.zeros([784, 500], np.float32) cur_vb = np.zeros([784], np.float32) cur_hb = np.zeros([500], np.float32) prv_w = np.zeros([784, 500], np.float32) prv_vb = np.zeros([784], np.float32) prv_hb = np.zeros([500], np.float32) sess = tf.Session() init = tf.global_variables_initializer() sess.run(init)查看第一次運行的誤差:
sess.run(err, feed_dict={X: trX, W: prv_w, vb: prv_vb, hb: prv_hb}) # 0.48134533整個算法的運算流程:
對于每一個epoch:對于每一batch:計算對比散度:對batch中的每一個數據點:w_pos_grad = 0, w_neg_grad= 0 (matrices)數據向前傳播,計算v(重構)和h 更新w_neg_grad = w_neg_grad + v1 ? h1對比散度=pos_grad和neg_grad的平均值除以輸入數據個數更新權重和偏差 W' = W + alpha * CD計算誤差重復下一epoch直到誤差足夠小或者在多個epoch下不再改變 #Parameters epochs = 5 batchsize = 100 weights = [] errors = []for epoch in range(epochs):for start, end in zip( range(0, len(trX), batchsize), range(batchsize, len(trX), batchsize)):batch = trX[start:end]cur_w = sess.run(update_w, feed_dict={ X: batch, W: prv_w, vb: prv_vb, hb: prv_hb})cur_vb = sess.run(update_vb, feed_dict={ X: batch, W: prv_w, vb: prv_vb, hb: prv_hb})cur_hb = sess.run(update_hb, feed_dict={ X: batch, W: prv_w, vb: prv_vb, hb: prv_hb})prv_w = cur_wprv_vb = cur_vbprv_hb = cur_hbif start % 10000 == 0:errors.append(sess.run(err, feed_dict={X: trX, W: cur_w, vb: cur_vb, hb: cur_hb}))weights.append(cur_w)print('Epoch: %d' % epoch,'reconstruction error: %f' % errors[-1]) plt.plot(errors) plt.xlabel("Batch Number") plt.ylabel("Error") plt.show()
最后的權重:
我們能夠獲得每一個隱藏的單元并可視化隱藏層和輸入之間的連接。使用tile_raster_images可以幫助我們從權重或者樣本中生成容易理解的圖片。它把784行轉為一個數組(比如25x20),圖片被重塑并像地板一樣鋪開。
tile_raster_images(X=cur_w.T, img_shape=(28, 28), tile_shape=(25, 20), tile_spacing=(1, 1)) import matplotlib.pyplot as plt from PIL import Image %matplotlib inline image = Image.fromarray(tile_raster_images(X=cur_w.T, img_shape=(28, 28) ,tile_shape=(25, 20), tile_spacing=(1, 1))) ### Plot image plt.rcParams['figure.figsize'] = (18.0, 18.0) imgplot = plt.imshow(image) imgplot.set_cmap('gray')
每一張圖片表示了隱藏層和可視層單元之間連接的一個向量。
下面觀察其中一個已經訓練好的隱藏層單元的權重,灰色代表權重為0,越白的地方權重越大,接近1.相反得, 越黑的地方,權重越負。 權重為正的像素使隱藏層單元激活的概率,負的像素會減少隱藏層單元被激活的概率。 所以我們可以知道特定的小塊(隱藏單元) 可以提取特征如果給它輸入。
我們再看看重構得到一張圖片
1)首先畫出一張原始的圖片
2) 把原始圖像向下一層傳播,并反向重構
3)畫出重構的圖片
img = Image.fromarray(tile_raster_images(X=rec, img_shape=(28, 28),tile_shape=(1, 1), tile_spacing=(1, 1))) plt.rcParams['figure.figsize'] = (2.0, 2.0) imgplot = plt.imshow(img) imgplot.set_cmap('gray')參考:
https://en.wikipedia.org/wiki/Restricted_Boltzmann_machine
http://deeplearning.net/tutorial/rbm.html
http://deeplearning4j.org/restrictedboltzmannmachine.html
http://imonad.com/rbm/restricted-boltzmann-machine/
總結
以上是生活随笔為你收集整理的受限玻尔兹曼机(RBM)与python在Tensorflow的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 数据库文件导入和导出、远程上
- 下一篇: C++总结篇(5)vector