轉(zhuǎn)載請注明作者:夢里風林
Github工程地址:https://github.com/ahangchen/GDLnotes
歡迎star,有問題可以到Issue區(qū)討論
Google深度學習 官方教程地址
視頻/字幕下載
全連接神經(jīng)網(wǎng)絡(luò)
輔助閱讀:TensorFlow中文社區(qū)教程 - 英文官方教程
代碼見:full_connect.py
Linear Model
- 加載lesson 1中的數(shù)據(jù)集
- 將Data降維成一維,將label映射為one-hot encoding
def reformat(dataset, labels):
dataset = dataset.reshape((-1, image_size * image_size)).astype(np.float32)
# Map 0 to [1.0, 0.0, 0.0 ...], 1 to [0.0, 1.0, 0.0 ...]
labels = (np.arange(num_labels) == labels[:, None]).astype(np.float32)
return dataset, labels
TensorFlow Graph
-
使用梯度計算train_loss,用tf.Graph()創(chuàng)建一個計算單元
- 用tf.constant將dataset和label轉(zhuǎn)為tensorflow可用的訓練格式(訓練中不可修改)
- 用tf.truncated_normal生成正太分布的數(shù)據(jù)筝闹,作為W的初始值,初始化b為可變的0矩陣
- 用tf.variable將上面的矩陣轉(zhuǎn)為tensorflow可用的訓練格式(訓練中可以修改)
- 用tf.matmul實現(xiàn)矩陣相乘,計算WX+b蒲祈,這里實際上logit只是一個變量甘萧,而非結(jié)果
- 用tf.nn.softmax_cross_entropy_with_logits計算WX+b的結(jié)果相較于原來的label的train_loss,并求均值
- 使用梯度找到最小train_loss
optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(loss)
- 計算相對valid_dataset和test_dataset對應的label的train_loss
上面這些變量都是一種Tensor的概念梆掸,它們是一個個的計算單元扬卷,我們在Graph中設(shè)置了這些計算單元,規(guī)定了它們的組合方式,就好像把一個個門電路串起來那樣
TensorFLow Session
Session用來執(zhí)行Graph里規(guī)定的計算通殃,就好像給一個個門電路通上電通砍,我們在Session里,給計算單元沖上數(shù)據(jù)徒恋,That’s Flow.
-
重復計算單元反復訓練800次,提高其準確度
- 為了快速查看訓練效果欢伏,每輪訓練只給10000個訓練數(shù)據(jù)(subset)入挣,恩,每次都是相同的訓練數(shù)據(jù)
- 將計算單元graph傳給session
- 初始化參數(shù)
- 傳給session優(yōu)化器 - train_loss的梯度optimizer硝拧,訓練損失 - train_loss径筏,每次的預測結(jié)果,循環(huán)執(zhí)行訓練
with tf.Session(graph=graph) as session: tf.initialize_all_variables().run() for step in range(num_steps): _, l, predictions = session.run([optimizer, loss, train_prediction])
- 在循環(huán)過程中障陶,W和b會保留滋恬,并不斷得到修正
- 在每100次循環(huán)后,會用驗證集進行驗證一次抱究,驗證也同時修正了一部分參數(shù)
valid_prediction.eval()
- 最后用測試集進行測試
- 注意如果lesson 1中沒有對數(shù)據(jù)進行亂序化恢氯,可能訓練集預測準確度很高,驗證集和測試集準確度會很低
這樣訓練的準確度為83.2%
SGD
-
每次只取一小部分數(shù)據(jù)做訓練鼓寺,計算loss時勋拟,也只取一小部分數(shù)據(jù)計算loss
- 對應到程序中,即修改計算單元中的訓練數(shù)據(jù)妈候,
- 每次輸入的訓練數(shù)據(jù)只有128個指黎,隨機取起點,取連續(xù)128個數(shù)據(jù):
offset = (step * batch_size) % (train_labels.shape[0] - batch_size) batch_data = train_dataset[offset:(offset + batch_size), :] batch_labels = train_labels[offset:(offset + batch_size), :]
- 由于這里的數(shù)據(jù)是會變化的州丹,因此用tf.placeholder來存放這塊空間
tf_train_dataset = tf.placeholder(tf.float32, shape=(batch_size, image_size * image_size)) tf_train_labels = tf.placeholder(tf.float32, shape=(batch_size, num_labels))
- 計算3000次醋安,訓練總數(shù)據(jù)量為384000,比之前8000000少
準確率提高到86.5%墓毒,而且準確率隨訓練次數(shù)增加而提高的速度變快了
- 對應到程序中,即修改計算單元中的訓練數(shù)據(jù)妈候,
神經(jīng)網(wǎng)絡(luò)
- 上面SGD的模型只有一層WX+b吓揪,現(xiàn)在使用一個RELU作為中間的隱藏層,連接兩個WX+b
- 仍然只需要修改Graph計算單元為
Y = W2 * RELU(W1*X + b1) + b2
- 為了在數(shù)學上滿足矩陣運算所计,我們需要這樣的矩陣運算:
[n * 10] = RELU([n * 784] · [784 * N] + [n * N]) · [N * 10] + [n * 10]
- 這里N取1024柠辞,即1024個隱藏結(jié)點
- 于是四個參數(shù)被修改
weights1 = tf.Variable( tf.truncated_normal([image_size * image_size, hidden_node_count])) biases1 = tf.Variable(tf.zeros([hidden_node_count])) weights2 = tf.Variable( tf.truncated_normal([hidden_node_count, num_labels])) biases2 = tf.Variable(tf.zeros([num_labels]))
- 預測值計算方法改為
ys = tf.matmul(tf_train_dataset, weights1) + biases1 hidden = tf.nn.relu(ys) logits = tf.matmul(hidden, weights2) + biases2
- 計算3000次,可以發(fā)現(xiàn)準確率一開始提高得很快主胧,后面提高速度變緩叭首,最終測試準確率提高到88.8%
深度神經(jīng)網(wǎng)絡(luò)實踐
優(yōu)化
Regularization
在前面實現(xiàn)的RELU連接的兩層神經(jīng)網(wǎng)絡(luò)中习勤,加Regularization進行約束,采用加l2 norm的方法焙格,進行調(diào)節(jié):
代碼實現(xiàn)上图毕,只需要對tf_sgd_relu_nn中train_loss做修改即可:
- 可以用tf.nn.l2_loss(t)對一個Tensor對象求l2 norm
- 需要對我們使用的各個W都做這樣的計算(參考tensorflow官方example)
l2_loss = tf.nn.l2_loss(weights1) + tf.nn.l2_loss(weights2)
- 添加到train_loss上
- 這里還有一個重要的點,Hyper Parameter: β
- 我覺得這是一個拍腦袋參數(shù)眷唉,取什么值都行予颤,但效果會不同,我這里解釋一下我取β=0.001的理由
- 如果直接將l2_loss加到train_loss上冬阳,每次的train_loss都特別大蛤虐,幾乎只取決于l2_loss
- 為了讓原本的train_loss與l2_loss都能較好地對參數(shù)調(diào)整方向起作用,它們應當至少在同一個量級
- 觀察不加l2_loss肝陪,step 0 時驳庭,train_loss在300左右
- 加l2_loss后, step 0 時氯窍,train_loss在300000左右
- 因此給l2_loss乘0.0001使之降到同一個量級
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits, tf_train_labels)) + 0.001 * l2_loss
- 所有其他參數(shù)不變饲常,訓練3000次,準確率提高到92.7%
- 黑魔法之所以為黑魔法就在于荞驴,這個參數(shù)可以很容易地影響準確率,如果β = 0.002贯城,準確率提高到93.5%
OverFit問題
在訓練數(shù)據(jù)很少的時候熊楼,會出現(xiàn)訓練結(jié)果準確率高,但測試結(jié)果準確率低的情況
- 縮小訓練數(shù)據(jù)范圍:將把batch數(shù)據(jù)的起點offset的可選范圍變心芊浮(只能選擇0-1128之間的數(shù)據(jù)):
offset_range = 1000
offset = (step * batch_size) % offset_range
- 可以看到鲫骗,在step500后,訓練集就一直是100%踩晶,驗證集一直是77.6%执泰,準確度無法隨訓練次數(shù)上升,最后的測試準確度是85.4%
DropOut
采取Dropout方式強迫神經(jīng)網(wǎng)絡(luò)學習更多知識
參考aymericdamien/TensorFlow-Examples中dropout的使用
- 我們需要丟掉RELU出來的部分結(jié)果
- 調(diào)用tf.nn.dropout達到我們的目的:
keep_prob = tf.placeholder(tf.float32)
if drop_out:
hidden_drop = tf.nn.dropout(hidden, keep_prob)
h_fc = hidden_drop
- 這里的keep_prob是保留概率渡蜻,即我們要保留的RELU的結(jié)果所占比例术吝,tensorflow建議的語法是,讓它作為一個placeholder茸苇,在run時傳入
- 當然我們也可以不用placeholder排苍,直接傳一個0.5:
if drop_out:
hidden_drop = tf.nn.dropout(hidden, 0.5)
h_fc = hidden_drop
- 這種訓練的結(jié)果就是,雖然在step 500對訓練集預測沒能達到100%(起步慢)学密,但訓練集預測率達到100%后淘衙,驗證集的預測正確率仍然在上升
- 這就是Dropout的好處,每次丟掉隨機的數(shù)據(jù)腻暮,讓神經(jīng)網(wǎng)絡(luò)每次都學習到更多彤守,但也需要知道毯侦,這種方式只在我們有的訓練數(shù)據(jù)比較少時很有效
- 最后預測準確率為88.0%
Learning Rate Decay
隨著訓練次數(shù)增加,自動調(diào)整步長
- 在之前單純兩層神經(jīng)網(wǎng)絡(luò)基礎(chǔ)上具垫,添加Learning Rate Decay算法
- 使用tf.train.exponential_decay方法侈离,指數(shù)下降調(diào)整步長,具體使用方法官方文檔說的特別清楚
- 注意這里面的cur_step傳給優(yōu)化器做修,優(yōu)化器在訓練中對其做自增計數(shù)
- 與之前單純兩層神經(jīng)網(wǎng)絡(luò)對比霍狰,準確率直接提高到90.6%
Deep Network
增加神經(jīng)網(wǎng)絡(luò)層數(shù),增加訓練次數(shù)到20000
- 為了避免修改網(wǎng)絡(luò)層數(shù)需要重寫代碼饰及,用循環(huán)實現(xiàn)中間層
# middle layer
for i in range(layer_cnt - 2):
y1 = tf.matmul(hidden_drop, weights[i]) + biases[i]
hidden_drop = tf.nn.relu(y1)
if drop_out:
keep_prob += 0.5 * i / (layer_cnt + 1)
hidden_drop = tf.nn.dropout(hidden_drop, keep_prob)
- 初始化weight在迭代中使用
for i in range(layer_cnt - 2):
if hidden_cur_cnt > 2:
hidden_next_cnt = int(hidden_cur_cnt / 2)
else:
hidden_next_cnt = 2
hidden_stddev = np.sqrt(2.0 / hidden_cur_cnt)
weights.append(tf.Variable(tf.truncated_normal([hidden_cur_cnt, hidden_next_cnt], stddev=hidden_stddev)))
biases.append(tf.Variable(tf.zeros([hidden_next_cnt])))
hidden_cur_cnt = hidden_next_cnt
- 第一次測試時蔗坯,用正太分布設(shè)置所有W的數(shù)值,將標準差設(shè)置為1燎含,由于網(wǎng)絡(luò)增加了一層宾濒,尋找step調(diào)整方向時具有更大的不確定性,很容易導致loss變得很大
- 因此需要用stddev調(diào)整其標準差到一個較小的范圍(怎么調(diào)整有許多研究屏箍,這里直接找了一個來用)
stddev = np.sqrt(2.0 / n)
- 啟用regular時绘梦,也要適當調(diào)一下β,不要讓它對原本的loss造成過大的影響
- DropOut時赴魁,因為后面的layer得到的信息越重要卸奉,需要動態(tài)調(diào)整丟棄的比例,到后面的layer颖御,丟棄的比例要減小
keep_prob += 0.5 * i / (layer_cnt + 1)
- 訓練時榄棵,調(diào)節(jié)參數(shù),你可能遇到消失(或爆炸)的梯度問題潘拱,
訓練到一定程度后疹鳄,梯度優(yōu)化器沒有什么作用,loss和準確率總是在一定范圍內(nèi)徘徊 - 官方教程表示最好的訓練結(jié)果是芦岂,準確率97.5%瘪弓,
- 我的nn_overfit.py開啟六層神經(jīng)網(wǎng)絡(luò),
啟用Regularization禽最、DropOut腺怯、Learning Rate Decay,
訓練次數(shù)20000(應該還有再訓練的希望川无,在這里雖然loss下降很慢了瓢喉,但仍然在下降),訓練結(jié)果是舀透,準確率95.2%
覺得我的文章對您有幫助的話栓票,給個star可好?