深度學習中Dropout優化的原理分析

本文主要參考博文:

1. http://www.aiuxian.com/article/p-1870737.html

2. http://shuokay.com/2016/06/14/dropout/

引言

在機器學習的一些模型中,如果模型的參數太多,而訓練樣本又太少的話,這樣訓練出來的模型很容易產生過擬合現象。在訓練bp網絡時經常遇到的這個問題,過擬合指的是模型在訓練數據上損失函數比較小,預測準確率較高,但是在測試數據上損失函數比較大,預測準確率較低。

常用的防止過擬合的方法是正則化,也就是在模型的損失函數中,對模型的參數進行“懲罰”,這樣的話這些參數就不會太大,而越小的參數說明模型越簡單,越簡單的模型則越不容易產生過擬合現象。因此在添加權值懲罰項後,應用梯度下降算法迭代優化計算時,如果參數theta比較大,則此時的正則項數值也比較大,那麼在下一次更新參數時,參數削減的也比較大,從而可以使擬合結果看起來更平滑,不至於過擬合。

Dropout是hintion最近幾年提出的,爲了防止模型過擬合,Dropout可以作爲一種trik供選擇。在hinton的論文摘要中指出,在每個訓練批次中,通過忽略一半的特徵檢測器(讓一半的隱層節點值爲0),可以明顯地減少過擬合現象。這種方式可以減少特徵檢測器間的相互作用,檢測器相互作用是指某些檢測器依賴其他檢測器才能發揮作用。

Dropout方法

訓練階段:

1.Dropout是在標準的bp網絡的的結構上,使bp網的隱層激活值以一定的比例v變爲0,即:按照一定比例v,隨機地讓一部分隱層節點失效

2.去掉權值懲罰項,取而代之的是限制權值的範圍,給每個權值設置一個上限範圍。如果在訓練更新的過程中,權值超過了這個上限,則把權值設置爲這個上限的值。這樣處理,不論權值更新量有多大,權值都不會過大。此外,還可以使算法採用一個比較大的學習率來加快學習速度,從而使算法在一個更廣闊的權值空間中搜索更好的權值,而不用擔心權值過大。

測試階段:

在前向傳播到輸出層前,隱含層節點的輸出值都要縮減到(1-v)倍,例如:正常的隱層輸出爲a,此時需要縮減爲a(1-v)。

這裏我的解釋是:假設比例v=0.5,也就是在訓練階段以0.5的比例忽略隱層節點,假設隱層有80個節點,每個節點輸出值爲1,那麼此時只有40個節點正常工作,也就是說總的輸出爲40個1和40個0,輸出總和爲40;而在測試階段,由於我們的權值已經訓練完成,此時就不再按照0.5的比例忽略隱層輸出,假設此時每個隱層的輸出還是1,那麼此時總的輸出爲80個1,明顯比dropout訓練時輸出大一倍(由於dropout比例爲0.5),所以爲了得到和訓練時一樣的輸出結果,就縮減隱層輸出爲a(1-v),即此時輸出80個0.5,總和也爲40。這樣就使得測試階段和訓練階段的輸出“一致”了。


如上圖, 左邊是我們常見的 full connect layer, 右邊是使用了 dropout 之後的效果。 其操作方法是, 首先設定一個 dropout ratio σ
σ是超參數, 範圍設置爲 (0,1), 表示在 Forward 階段需要隨機斷開連接的比例。每次 Forward 的時候都要隨機地斷開該比例的連接, 只更新剩下的 weight。 最後, 在 test/predict 的時候使用全部的連接, 不過, 這些 weights 全部都需要乘上 1σ。在具體的實現中略有不同。

Dropout原理分析

Dropout可以看做是一種模型平均,所謂模型平均,顧名思義,就是把來自不同模型的估計或者預測通過一定的權重平均起來,在一些文獻中也稱爲模型組合,它一般包括組合估計和組合預測。

Dropout中哪裏體現了“不同模型”;這個奧祕就是我們隨機選擇忽略隱層節點,在每個批次的訓練過程中,由於每次隨機忽略的隱層節點都不同,這樣就使每次訓練的網絡都是不一樣的,每次訓練都可以單做一個“新”的模型;此外,隱含節點都是以一定概率隨機出現,因此不能保證每2個隱含節點每次都同時出現,這樣權值的更新不再依賴於有固定關係隱含節點的共同作用,阻止了某些特徵僅僅在其它特定特徵下才有效果的情況。

這樣dropout過程就是一個非常有效的神經網絡模型平均方法,通過訓練大量的不同的網絡,來平均預測概率。不同的模型在不同的訓練集上訓練(每個批次的訓練數據都是隨機選擇),最後在每個模型用相同的權重來“融合”,有點類似boosting算法。

代碼詳解

caffe 源碼:

void DropoutLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
NeuronLayer<Dtype>::LayerSetUp(bottom, top);
threshold_ = this->layer_param_.dropout_param().dropout_ratio();
DCHECK(threshold_ > 0.);
DCHECK(threshold_ < 1.);
scale_ = 1. / (1. - threshold_);
uint_thres_ = static_cast<unsigned int>(UINT_MAX * threshold_);
}
template <typename Dtype>
void DropoutLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
const Dtype* bottom_data = bottom[0]->cpu_data();
Dtype* top_data = top[0]->mutable_cpu_data();
unsigned int* mask = rand_vec_.mutable_cpu_data();
const int count = bottom[0]->count();
if (this->phase_ == TRAIN) {
// 均值採樣,採樣的概率是留下的概率,即:1-threshold_,根據這個概率來斷開連接,但是保留下的數據除以(1-threshold_),這樣在test/predict的時候
就不需要像上面描述的那樣乘以(1-threshold_)
caffe_rng_bernoulli(count, 1. - threshold_, mask);
for (int i = 0; i < count; ++i) {
top_data[i] = bottom_data[i] * mask[i] * scale_;
}
} else {
caffe_copy(bottom[0]->count(), bottom_data, top_data);
}
}


在caffe中的使用方法:

layer {
name: "fc6"
type: "InnerProduct"
bottom: "pool5"
top: "fc6"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 0.1
}
}
}
layer {
name: "relu6"
type: "ReLU"
bottom: "fc6"
top: "fc6"
}
# 接在full connect後面, 需要指定 dropout ratio
layer {
name: "drop6"
type: "Dropout"
bottom: "fc6"
top: "fc6"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc7"
type: "InnerProduct"
bottom: "fc6"
top: "fc7"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 0.1
}
}
}
layer {
name: "relu7"
type: "ReLU"
bottom: "fc7"
top: "fc7"
}
layer {
name: "drop7"
type: "Dropout"
bottom: "fc7"
top: "fc7"
dropout_param {
dropout_ratio: 0.5
}
}

發佈了81 篇原創文章 · 獲贊 51 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章