【theano-windows】学习笔记八——预备知识
前言
按照上一個博客所說的,直接按照深度學習0.1文檔進行學習,當然在此之前我們需要了解這一系列教程所需要的數據集,以及一些概念性的東西
國際慣例,參考博客網址:
深度學習0.1文檔
深度學習0.1文檔-中文翻譯
基于梯度的學習
數據集網址
網盤下載鏈接: https://pan.baidu.com/s/1bpvqwDT 密碼: 7c2w
在進行操作前先導入基本的模塊
#-*- coding:utf-8 -*- import cPickle,gzip import numpy as np import theano import theano.tensor as T數據集簡介
就是把Lecun大佬的手寫數字數據集MNIST做了個序列化,并且分成了機器學習所需要的三種基本數據集:
- 訓練集: 學習模型參數
- 驗證集: 執行模型選擇和超參數的選擇
- 測試集: 測試訓練好的模型的分辨能力即泛化能力
下載完數據集以后, 就可以進行讀取操作
#導入數據集 f=gzip.open('mnist.pkl.gz','rb') train_set,valid_set,test_set=cPickle.load(f) f.close() #train_set中存儲了數據和標簽 a,b=train_set print a.shape#輸出數據大小(50000L, 784L) print b.shape#輸出標簽大小(50000L,)為了訓練性能著想, 教程建議將數據集存儲在共享變量中, 并且通過小批索引的方式獲取數據集. 存儲到共享變量中的原因與使用GPU有關. 將數據拷貝到GPU內存中需要很大功耗.如果按需拷貝(每次使用到的小批數據), 由于上述所說的功耗問題, GPU的代碼運行效率并不會比CPU快, 很可能更慢. 一旦存儲在共享變量中, theano就可以在構建共享變量的時候一次性拷貝GPU上所有的數據集. 隨后GPU可以通過從共享變量中利用切片來獲取小批數據, 無需從CPU內存中拷貝任何信息,因而避免了功耗.
注意由于數據點和它們的標簽往往具有不同的本質(標簽常是整型的,但是數據嘗嘗是實值), 因而我們建議對數據和標簽采用不同的變量存儲, 現將所有的數據存儲為float, 然后再將標簽用theano.tensor.cast轉換為int類型
#最好將數據集放入到共享變量中 def shared_dataset(data_xy):data_x,data_y=data_xyshared_x=theano.shared(np.asarray(data_x,dtype=theano.config.floatX))shared_y=theano.shared(np.asarray(data_y,dtype=theano.config.floatX))return shared_x,T.cast(shared_y,'int32') train_set_x,train_set_y=shared_dataset(train_set) test_set_x,test_set_y=shared_dataset(test_set) valid_set_x,valid_set_y=shared_dataset(valid_set) batch_size=500 #假裝獲取第三批數據(批索引是0,1,2,3.....) data=train_set_x[2*batch_size:3*batch_size] label=train_set_y[2*batch_size:3*batch_size]深度學習的監督優化基礎
分類器
0-1損失
l0,1=∑i=1|D|If(x(i))≠y(i)
目的是最小化沒有見過的樣本的最小錯誤量. 如果用f:RD→0,?,L代表預測函數, 那么損失可以被寫為
其中I稱為示性函數
Ix={1if x is True0otherwise
在本實例中, 預測函數的定義為
f(x)=argmaxkP(Y=k|x,θ)
上式的含義就是樣本x在模型參數θ中, 屬于每個標簽中概率最大的是第k個標簽. 在theano中, 損失函數的實現可以寫成: #0-1損失zero_one_loss=T.sum(T.neq(T.argmax(p_y_given_x),y))負對數似然
#最小化負對數似然NLL=- T.sum(T.log(p_y_given_x)[T.arange(y.shape[0]),y])
可以發現0-1損失是不可微的(它僅僅是是與不是)的加和. 所以我們看預測函數, 只要使得模型預測出真實標簽的概率P(Y=真實標簽|x(i);θ)最大即可,然后我們通常使用極大對數似然來解決這個最大化問題:
L(θ,D)=∑i=0|D|logP(Y=y(i)|x(i);θ)
可以發現這個損失函數的值并非0-1損失那樣帶包正確預測的數量, 但是從隨機初始化的分類器來看, 它們是非常相似的. 為了跟我們經常說的最小化損失函數保持一致, 可以定義最小化負對數似然(NLL)函數:
$$ NLL(\theta,D)=-\sum_{i=0}^{|D|}\log P(Y=y^{(i)}|x^{(i)};\theta) $$
在theano`中實現它:代碼標注: 看大括號里面的[T.arange(y.shape[0]),y], 第一項T.arange(y.shape[0])代表的就是樣本索引從0-n, 第二項就是每個樣本的標簽值.這句話說白了就是從概率矩陣p_y_given_x中取出第i個樣本對應的標簽為y(i)的概率
隨機梯度下降
普通的梯度下降法: 是沿著一些參數所定義的損失函數的誤差表面下降一小步, 如此反復不斷下降. 訓練集是大批量進入損失函數的, 偽代碼如下:
while True:loss = f(params)d_loss_wrt_params = ... # compute gradientparams -= learning_rate * d_loss_wrt_paramsif <stopping condition is met>:return params隨機梯度下降(SGD): 與普通的梯度下降原則上相同, 但是處理速度更快, 因為僅僅從部分樣本中估算梯度, 極端情況下就是從一個時間僅僅從一個單一樣本中估算梯度
# STOCHASTIC GRADIENT DESCENTfor (x_i,y_i) in training_set:# imagine an infinite generator# that may repeat examples (if there is only a finite training set)loss = f(params, x_i, y_i)d_loss_wrt_params = ... # compute gradientparams -= learning_rate * d_loss_wrt_paramsif <stopping condition is met>:return params而在深度學習中, 我們比較建議采用小批量隨機梯度下降(Minibatch SGD)方法, 一次采用多于一個的訓練樣本去計算梯度
for (x_batch,y_batch) in train_batches:# imagine an infinite generator# that may repeat examplesloss = f(params, x_batch, y_batch)d_loss_wrt_params = ... # compute gradient using theanoparams -= learning_rate * d_loss_wrt_paramsif <stopping condition is met>:return params特別注意: 如果你訓練固定次數, 批大小的設置對于參數的更新控制是非常重要的. 對批大小為1的數據訓練十次與對批大小為20的數據訓練十次, 達到的效果完全不同, 記住當改變批大小的時候, 要據此調整其他的參數.
批量隨機梯度下降在theano中實現如下:
d_loss_wrt_params=T.grad(loss,params) updates=[(params,params-learning_rate*d_loss_wrt_params)] MSGD=theano.function([x_batch,y_batch],loss,updates=updates) for (x_batch,y_batch) in train_batches:print ('Current loss is ',MSGD(x_batch,y_batch))if stopping_condition_is_met:return param
正則項
概念就不說了, 機器學習里面常用的減少過擬合方法, 一般采用L1或者L2正則項. 這里還說到了提前停止(early-stopping)也是正則化的一種方法. 詳細請看周志華老師《機器學習》P105頁緩解過擬合方法
L1和L2正則項
NLL(θ,D)=?∑i=0|D|logP(Y=y(i)|x(i);θ)
就是在損失函數后面添加一個額外項, 用于乘法某些參數
正常情況下, 我們的損失函數是這樣
加了正則項以后可能是這樣
E(θ,D)=NLL(θ,D)+λR(θ)orE(θ,D)=NLL(θ,D)+λ||θ||pp
其中||θ||p=(∑|θ|j=0|θj|p)1p, 這就是傳說中的p范數, 當p=2的時候就代表二范數, 而且也就是傳說中的權重衰減原則上來說, 在損失上增加正則項能夠增強神經網絡的平滑映射(通過乘法參數中較大的值, 減少了網絡的非線性程度). 最小化損失函數與正則項的和能夠在訓練集的擬合與一般解之間找到權衡. 為了遵循奧卡姆剃須刀準則(‘如無必要, 勿增實體’), 它們兩個的最小化能夠幫助我們找到最簡單的解.
L1 = T.sum(abs(param))#L1范數 L2 = T.sum(param ** 2)#L2范數 loss = NLL + lambda_1 * L1 + lambda_2 * L2#損失+L1正則+L2正則提前停止: 通過觀察模型在驗證集上的性能來減少過擬合. 如果模型在驗證集上停止了提升或者出現退化, 就需要我們停止優化了. 關于何時停止, 這是一個判斷, 并且很多啟發式算法存在, 這里提供了一個幾何增加耐心量的算法:
# 提前停止方法patience = 5000 # 迭代結束條件就看這個值與迭代多少次batch的iter大小 patience_increase = 2 # 結束時間延長 improvement_threshold = 0.995 # 小于這個值就認為性能沒有被再提升 validation_frequency = min(n_train_batches, patience/2)#驗證頻率best_params = None best_validation_loss = numpy.inf test_score = 0. start_time = time.clock()done_looping = False#是否結束循環 epoch = 0 while (epoch < n_epochs) and (not done_looping):epoch = epoch + 1#當前迭代次數for minibatch_index in range(n_train_batches):#當前迭代的批索引d_loss_wrt_params = ... # 計算梯度params -= learning_rate * d_loss_wrt_params # 參數更新# 從第0次開始迭代,計算得到當前迭代的累計批索引iter = (epoch - 1) * n_train_batches + minibatch_indexif (iter + 1) % validation_frequency == 0:#使用0-1損失驗證性能提升this_validation_loss = ... if this_validation_loss < best_validation_loss:#如果性能還在提升if this_validation_loss < best_validation_loss * improvement_threshold:#如果性能提升超過閾值,那么繼續迭代patience = max(patience, iter * patience_increase)best_params = copy.deepcopy(params)best_validation_loss = this_validation_loss#看看是否已經到達迭代終止時間if patience <= iter:done_looping = Truebreak這個代碼的意思就是迭代結束的條件一是達到了最大的迭代次數n_epoch,另一個就是利用patience控制是否提前結束迭代. 如果迭代過程中性能提升超過閾值, 就考慮將patience提升一下max(patience,iter*patience_increase), 反之性能并未提升或者達到穩定情況(閾值范圍之內), 那么就對比當前patience與總共使用批樣本訓練的次數(每次使用一批樣本就將iter+1), 一旦iter超過了patience就停止訓練了
注意: 這個驗證頻率validation_frequency要比patience小, 至少要在一次patience迭代期間驗證過兩次.所以使用了 validation_frequency = min( value, patience/2.)
測試
當循環完畢, 得到的參數是驗證集上效果最好的參數. 這樣我們使用同一個訓練集、測試集、驗證集去訓練多個模型, 我們比較每個模型最好的驗證集損失, 就認為我們選擇了最好的模型.
簡單總結
三個數據集: 訓練集、驗證集、測試集
優化方法: 訓練集被用于目標函數的可微近似的批量隨機梯度下降中
選擇模型: 看模型在驗證集上的性能能表現
存儲和讀取模型參數
存儲模型參數
save_file = open('path', 'wb') cPickle.dump(w.get_value(borrow=True), save_file, -1) cPickle.dump(v.get_value(borrow=True), save_file, -1) cPickle.dump(u.get_value(borrow=True), save_file, -1) save_file.close()讀取模型參數
save_file = open('path') w.set_value(cPickle.load(save_file), borrow=True) v.set_value(cPickle.load(save_file), borrow=True) u.set_value(cPickle.load(save_file), borrow=True)關于borrow看這里, 大概就是如果borrow=True, 那么對原始變量的更改會對當前的共享變量產生影響, 還是把例子貼出來吧
import numpy, theano np_array = numpy.ones(2, dtype='float32')s_default = theano.shared(np_array) s_false = theano.shared(np_array, borrow=False) s_true = theano.shared(np_array, borrow=True) np_array += 1 # now it is an array of 2.0 sprint(s_default.get_value()) print(s_false.get_value()) print(s_true.get_value()) ''' [ 1. 1.] [ 1. 1.] [ 2. 2.] '''【注】我可能用了假theano, 這個例子在我的環境中執行竟然結果都是[1,1]
后續
數據集和一些基本的訓練方法都搞定了, 下一步就得實戰一波了
- logistic回歸分類手寫數字
- 多層感知器
- 卷積
- 去噪自編碼dAE
- 堆疊去噪自編碼SdAE
- 受限玻爾茲曼機RBM
- 深度信念網
- 混合蒙特卡洛采樣
- LSTM
- RNN-RBM
總結
以上是生活随笔為你收集整理的【theano-windows】学习笔记八——预备知识的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 六大国有银行手机银行跨行转账要手续费吗?
- 下一篇: 农行燃梦白金卡是伪白金吗?教你从三个方面