在本文中會先介紹一些概念,然後給出一個簡單的完整神經網絡樣例程序。
激活函數:
首先激活函數可以解決線性不可分問題。讀者可以訪問https://playground.tensorflow.org/。通過網頁瀏覽器就可以訓練簡單神經網絡並實現可視化過程。截圖如下所示:
對於這個網頁怎麼用,讀者可以自行百度學習一下。上圖有一個Activation,如果選擇Linear則根本不能訓練出很好的分類結果。目前TensorFlow提供了7種不同的非線性激活函數,tf.nn.relu、tf.sigmoid、tf.tanh是比較常用的幾個。當然TensorFlow也支持自己定義的激活函數。
損失函數:
神經網絡模型的效果以及優化的目標是通過損失函數(loss function)來定義的。
分類問題(如手寫數字識別),如何判斷一個輸出向量和期望向量有多接近。交叉熵是常用的評判方法之一。交叉熵刻畫了兩個概率分佈之間的距離,它是分類問題中使用比較廣的一種損失函數。因爲交叉熵一般會與softmax迴歸一起使用,所以TensorFlow對這兩個功能進行了封裝,並提供了tf.nn.softmax_cross_entropy_with_logits(labels=y_,logits=y),其中y表示神經網絡的輸出結果,y_表示標準答案。
與分類問題不同,迴歸問題解決的是對具體數值的預測。對於迴歸問題,最常用的損失函數是均方誤差(MSE mean squared error)。當然也可以使用自己定義的損失函數。下面通過一個簡單的神經網絡程序來講解損失函數對模型訓練結果的影響。這個程序有兩個輸入節點、一個輸出節點,沒有隱藏層。
import tensorflow as tf
from numpy.random import RandomState
batch_size = 8
#兩個輸入節點
x = tf.placeholder(tf.float32,shape=(None,2),name='x-input')
#迴歸問題一般只有一個輸出節點
y_ = tf.placeholder(tf.float32,shape=(None,1),name='y-input')
#定義一個單層的神經網絡前向傳播的過程,這裏就是簡單的加權和
w1 = tf.Variable(tf.random_normal([2,1],stddev=1,seed=1))
y = tf.matmul(x,w1)
#定義預測多了和預測少了的成本
loss_less = 10
loss_more = 1
loss = tf.reduce_sum(tf.where(tf.greater(y,y_),
(y-y_)*loss_more,
(y_-y)*loss_less))
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)
#通過隨機數生成一個模擬數據集
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size,2)
Y = [[x1+x2+rdm.rand()/10.0-0.05] for (x1,x2) in X]
# 訓練神經網絡
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
STEPS = 5000
for i in range(STEPS):
start = (i*batch_size)%dataset_size
end = min(start+batch_size,dataset_size)
sess.run(train_step,
feed_dict={x:X[start:end],y_:Y[start:end]})
print(sess.run(w1))
運行以上代碼會得到w1的值爲[1.019,1.042],也就是得到的預測函數1.02x1+1.04x2,這比x1+x2要大。但如果將loss_less值調整爲1,less_more調整爲10,那麼最後的w1中的參數會略小於1,所以對於相同的神經網絡,不同的損失函數會對訓練得到的模型產生重要的影響。
訓練神經網絡的過程可以分爲以下三個步驟:
- 定義神經網絡的結構和前向傳播的輸出結果。
- 定義損失函數以及選擇反向傳播優化的算法。
- 生成會話(tf.Session)並且在訓練數據上反覆運行反向傳播優化算法。
下面爲在一個模擬數據集上進行的神經網絡訓練。
import tensorflow as tf
#通過numpy工具包生成模擬數據集
from numpy.random import RandomState
batch_size = 8
#定義神經網絡的參數
w1 = tf.Variable(tf.random_normal([2,3],stddev=1,seed=1))
w2 = tf.Variable(tf.random_normal([3,1],stddev=1,seed=1))
x = tf.placeholder(tf.float32,shape=(None,2),name='x-input')
y_ = tf.placeholder(tf.float32,shape=(None,1),name='y-input')
#定義神經網絡的前向傳播過程
a = tf.matmul(x,w1)
y = tf.matmul(a,w2)
#定義損失函數和反向傳播算法
y = tf.sigmoid(y)
cross_entropy = -tf.reduce_mean(
y_ * tf.log(tf.clip_by_value(y,1e-10,1.0))
+(1-y_)*tf.log(tf.clip_by_value(1-y,1e-10,1.0)))
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)
#通過隨機數生成一個模擬數據集
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size,2)
Y = [[int(x1+x2<1)] for (x1,x2) in X]
#創建一個會話來運行TensorFlow程序
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
#初始化變量
sess.run(init_op)
print(sess.run(w1))
print(sess.run(w2))
#設定訓練的輪數
STEPS = 10000
for i in range(STEPS):
#每次選取batch_size個樣本進行訓練
start = (i*batch_size)%dataset_size
end = min(start+batch_size,dataset_size)
#通過選取的樣本訓練神經網絡並更新參數
sess.run(train_step,
feed_dict={x:X[start:end],y_:Y[start:end]})
if i%1000==0:
#每隔一段時間計算在所有數據上的交叉熵並輸出
total_cross_entropy = sess.run(
cross_entropy,feed_dict={x:X,y_:Y}
)
print("After %d training step(s),cross entropy on all data is %g"%(i,total_cross_entropy))
print(sess.run(w1))
print(sess.run(w2))