在深度學習中經常出現一些問題導致訓練出來的效果不佳,這篇文章就說一說如何提升網絡訓練的質量。
索引:
- 欠擬合
- 過擬合
- 如何檢測過擬合
- 如何過擬合
- 動量梯度下降
- 學習率自適應
- 提前停止
- Dropout
- 隨機梯度下降
欠擬合 underfitting
就是模型的複雜度小於真實的複雜度,因此模型不能夠表達真實的情況。如果遇到無論怎麼訓練,訓練的accuracy很低,測試的accuracy很低,loss也下不去,這個時候很可能出現了underfitting。可以使用容量更大的模型來表達更加複雜的情況,或者更多的層數以及更多的節點。
1.png
提高模型容量(model capacity)如下圖可以解決欠擬合,然而在實際的應用中過擬合的情況更多
2.png
過擬合Overfitting(Generalization Performance泛化能力)
模型複雜度大於真實模型的複雜度。表現為訓練loss和訓練accuracy都很好,但是測試accuracy不好。
4.png
5.png
如何檢測overfitting:
- 使用交叉驗證,將數據集分為Train、Validation、Test三個部分,其中Validation做模型參數的挑選,test做最後的性能檢測
- 使用K-fold方式,將數據集劃分為K份,每次去K-1份用來做train,一份用來做validation,每個epoch切換train和validation的數據集,這樣既防止了死記硬背又防止了記憶的特性。這樣會對網絡有一定的提升(提升不算很大),Kera是提供了一個很方便的方法:network.fit(db_train, epochs=6, validation_split=0.1, validation_freq=2) 會將數據按照0.1和0.9來分。
<code>import tensorflow as tf
from tensorflow.keras import datasets, layers, optimizers, Sequential, metrics
def preprocess(x, y):
"""
x is a simple image, not a batch
"""
x = tf.cast(x, dtype=tf.float32) / 255.
x = tf.reshape(x, [28*28])
y = tf.cast(y, dtype=tf.int32)
y = tf.one_hot(y, depth=10)
return x,y
batchsz = 128
(x, y), (x_test, y_test) = datasets.mnist.load_data()
print('datasets:', x.shape, y.shape, x.min(), x.max())
idx = tf.range(60000)
idx = tf.random.shuffle(idx)
x_train, y_train = tf.gather(x, idx[:50000]), tf.gather(y, idx[:50000])
x_val, y_val = tf.gather(x, idx[-10000:]) , tf.gather(y, idx[-10000:])
print(x_train.shape, y_train.shape, x_val.shape, y_val.shape)
## train
db_train = tf.data.Dataset.from_tensor_slices((x_train,y_train))
db_train = db_train.map(preprocess).shuffle(50000).batch(batchsz)
db_val = tf.data.Dataset.from_tensor_slices((x_val,y_val))
db_val = db_val.map(preprocess).shuffle(10000).batch(batchsz)
db_test = tf.data.Dataset.from_tensor_slices((x_test, y_test))
db_test = db_test.map(preprocess).batch(batchsz)
sample = next(iter(db_train))
print(sample[0].shape, sample[1].shape)
network = Sequential([layers.Dense(256, activation='relu'),
layers.Dense(128, activation='relu'),
layers.Dense(64, activation='relu'),
layers.Dense(32, activation='relu'),
layers.Dense(10)])
network.build(input_shape=(None, 28*28))
network.summary()
network.compile(optimizer=optimizers.Adam(lr=0.01),
loss=tf.losses.CategoricalCrossentropy(from_logits=True),
metrics=['accuracy']
)
network.fit(db_train, epochs=6, validation_data=db_val, validation_freq=2)
print('Test performance:')
network.evaluate(db_test)
sample = next(iter(db_test))
x = sample[0]
y = sample[1] # one-hot
pred = network.predict(x) # [b, 10]
# convert back to number
y = tf.argmax(y, axis=1)
pred = tf.argmax(pred, axis=1)
print(pred)
print(y)/<code>
如何減輕Overfitting
原則:如果不是必要的就選擇最小的。
主流的做法:
- 提供更多的數據
- 降低模型的複雜度,數據集的大小和網絡的大小是相對的
- Dropout
- Data argumentation
- Early Stopping 使用Validation set來做一個提前的終結
- Regularization
Regularization
6.png
經過Regularization退化成更少次方的網絡結構,更低複雜度的網絡結構從而降低Overfitting,是一種weight decay的方法
通過下面的例子可以清楚的看到Regularization降低網絡的表達能力從而防止噪聲造成的overfitting
7.png
Regularization常用的兩種,第一種是在原來的loss的基礎上加上一範數,第二種是在原來的loss的基礎上加一個tensor的二範數,最常用的是第二種,注意這裡的lamda是一個超參數需要人為的調整。
8.png
在keras中可通過下面的代碼kera.regularizers.l2(lamda)進行Regularization的快速添加添加
9.png
另一種是人為的對每個w和b進行Regularization的處理,這樣的靈活性比較大:
10.png
momentum動量:
11.png
梯度更新最基本的公式是直接使用一個固定的學習率,然而這樣的方式的缺點很明顯吧,這裡額外的使用Zk,這個Zk是上一次的方向,這樣兩個方向加起來就是當前的更新方向和之前的更新方向的結合。
通過下面的兩個例子進行比較:
無動量,固定學習率時:可以發現在局部最優解的時候就已經停止了,並且在一開始的時候更新的方向是非常隨機的
12.png
使用動量時的更新:
13.png
在實際使用的時候很簡單直接使用內置的函數就可以不需要人為的完成,這裡也可以使用Adam優化器,對於Adam是沒有動量的這個參數的而是在內部優化完成:
14.png
Learning Rate Tuning:
一般剛開始設置一個大的學習率,隨著學習的進行動態的縮小學習率,調整非常的簡單但是有效,這是逐漸衰減
15.png
這是突然衰減,具體的策略可以自定義:
16.png
Early Stop:
由於訓練的epoch如果很大就有可能出現overfitting的情況,early stop就是說在出現overfitting之前就將模型停止掉,很好理解
17.png
通常通過Validation set來監測在最高點的時候停止,這個經驗的判斷,因為下降之後有可能又上升。
Dropout
learning less, learning better,
18.png
可以通過下圖比較dropout的效果:
19.png
添加Dropout的方法很簡單,直接layers.Dropout(斷掉的概率)即可
20.png
注意,如果做了dropout之後,因為train的時候會dropout但是在test的時候不要dropout,因此train和test的時候邏輯是不一樣的
- train – true
- validation – false
- test -false
21.png
Stochastic Gradient Descent 隨機梯度下降(SGD)
注意雖然叫做隨機但是不是真正的隨機,而是符合某種分步的下降,隨機是指從數據集中隨機選取batch,然後計算他的梯度的平均值,也就是把原來整個數據集的梯度的均值變成batch上所有梯度的均值,這樣因為由於顯存的限制不可能一次把所有數據都加載進來計算一次,因此要使用SGD的策略一次取一個批次來計算。
閱讀更多 熊冰 的文章