正則化
爲了避免過擬合問題,一個常用的方法是正則化(regularization)。正則化的思想就是在損失函數中加入刻畫模型複雜的指標。假設用於刻畫模型在訓練數據上表現的損失函數爲J(θ),那麼在優化時不是直接優化J(θ),而是優化J(θ)+λR(w),其中R(w)刻畫的是模型的複雜度,而λ表示模型複雜損失在總損失中的比例。這裏這裏θ表示的是一個神經網絡中所有的參數,包括變身的權重w和偏置項b。常用的刻畫模型複雜度的函數R(w)有兩種,一種是L1正則化
另一種是L2正則化
二者的基本思想都是希望通過限制權重的大小,使得模型不能任意擬合訓練數據中的隨機噪聲。但是這兩種正則化的方法也有很大的區別。首先,L1正則化會讓參數變得更稀疏,而L2正則化不會。之所以L2正則化不會讓參數變得稀疏的原因是當參數很小時,比如0.001,這個參數的平方基本上就可以忽略了,於是模型不會進一步將這個參數調整爲0。其次,L1正則化的計算公式不可導,而L2正則化公式可導。因爲在優化時需要計算損失函數的偏導數,所以對含有L2正則化損失函數的優化要更加簡潔。優化帶L1正則化的損失函數要更加複雜,而且優化方法也有很多種。在實踐中,也可以將L1正則化和L2正則化同時使用:
一個簡單的帶L2正則化的損失函數
loss = tf.reduce_mean(tf.square(y_-y))+
tf.contrib.layers.l2_regularizer(lambda)(w))
w爲需要計算正則化損失的參數。當神經網絡的參數增多之後,這樣的方式首先可能導致損失函數loss的定義很長,可讀性差且容易出錯。但更主要的是,當網絡結構複雜之後定義網絡結構的部分和計算損失函數的部分可能不在同一函數裏,這樣通過變量這種方式計算損失函數就不方便了。爲了解決這個問題,可以使用Tensorflow中提供的集合(collection)。
示例演示
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
data = []
label = []
np.random.seed(0)
#以原點爲圓心,半徑爲1的圓把散點分成紅藍兩部分,並加入如隨機噪聲
for i in range(150):
x1 = np.random.uniform(-1, 1)
x2 = np.random.uniform(0, 2)
data.append([np.random.normal(x1, 0.1), np.random.normal(x2, 0.1)])
if x1**2 + x2**2 <= 1:
label.append(0)
else:
label.append(1)
data = np.hstack(data).reshape(-1, 2)
label = np.hstack(label).reshape(-1, 1)
plt.scatter(data[:,0], data[:,1], c=np.squeeze(label),
cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.show()
#獲取一層神經網絡邊上的權重,並將這個權重的L2正則化損失
#加入名爲losses的集合中
def get_weight(shape, lambda1):
#生成一個變量
var = tf.Variable(tf.random_normal(shape), dtype=tf.float32)
#add_to_collection函數將這個新生成變量的L2正則化損失項加入集合
#這函數的第一個參數losses是集合的名字,第二個參數是要加入這個集合的內容
tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(lambda1)(var))
return var
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
#定義每一層網絡中節點的個數
layer_dimension = [2, 10, 5, 3, 1]
#神經網絡的層數
n_layers = len(layer_dimension)
#這個變量維護前進傳播時最深層的節點,開始的時候是輸入層
cur_layer = x
#當前層的節點
in_dimension = layer_dimension[0]
#通過一個循環來生成5層全連接的神經網絡結構
for i in range(1, n_layers):
#layer_dimension[i]爲下一層的節點個數
out_dimension = layer_dimension[i]
#生成當前層中權重的變量,並將這個變量的L2正則化損失加入計算圖上的集合
weight = get_weight([in_dimension, out_dimension], 0.003)
bias = tf.Variable(tf.constant(0.1, shape=[out_dimension]))
#使用eLU函數
cur_layer = tf.nn.elu(tf.matmul(cur_layer, weight) + bias)
#進入下一層之前將一層的節點個數更新爲當前層節點個數
in_dimension = layer_dimension[i]
y = cur_layer
#在定義神經網絡前向傳播的同時已經將所有的L2正則化損失加入了圖上的集合,
#這裏只需要計算刻畫模型在訓練數據上表現的損失函數
mse_loss = tf.reduce_mean(tf.square(y_ - y))
#將均方差損失函數加入損失集合
tf.add_to_collection('losses', mse_loss)
#get_collection返回一個列表,這列表是所有這個集合中的元素。在這個樣例中,
#這元素就是損失函數的不同部分,將它們加起來就可以得到最終的損失函數
loss = tf.add_n(tf.get_collection('losses'))
#定義不帶正則項的損失函數
train_op = tf.train.AdamOptimizer(0.001).minimize(mse_loss)
TRAINING_STEPS = 40000
with tf.Session() as sess:
tf.global_variables_initializer().run()
for i in range(TRAINING_STEPS):
sess.run(train_op, feed_dict={x: data, y_: label})
if i % 2000 == 0:
print("After %d steps, mse_loss: %f" % (i,
sess.run(mse_loss, feed_dict={x: data, y_: label})))
#畫出訓練後的分割曲線
xx, yy = np.mgrid[-1.2:1.2:0.1, -0.2:2.2:.01]
grid = np.c_[xx.ravel(), yy.ravel()]
probs = sess.run(y, feed_dict={x: grid})
probs = probs.reshape(xx.shape)
plt.scatter(data[:,0], data[:,1], c=np.squeeze(label),
cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.contour(xx, yy, probs, levels=[.5], cmap="Greys", vmin=0, vmax=.1)
plt.show()
運行結果
下面我們加入L2正則化
#定義帶正則項的損失函數
train_op = tf.train.AdamOptimizer(0.001).minimize(loss)
TRAINING_STEPS = 40000
with tf.Session() as sess:
tf.global_variables_initializer().run()
for i in range(TRAINING_STEPS):
sess.run(train_op, feed_dict={x: data, y_: label})
if i % 2000 == 0:
print("After %d steps, loss: %f" % (i,
sess.run(mse_loss, feed_dict={x: data, y_: label})))
#畫出訓練後的分割曲線
xx, yy = np.mgrid[-1.2:1.2:0.1, -0.2:2.2:.01]
grid = np.c_[xx.ravel(), yy.ravel()]
probs = sess.run(y, feed_dict={x: grid})
probs = probs.reshape(xx.shape)
plt.scatter(data[:,0], data[:,1], c=np.squeeze(label),
cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.contour(xx, yy, probs, levels=[.5], cmap="Greys", vmin=0, vmax=.1)
plt.show()