1. 程式人生 > >TensorFlow損失函式(loss function)

TensorFlow損失函式(loss function)

神經網路模型的效果及優化的目標是通過損失函式來定義的。

1、經典損失函式

分類問題和迴歸問題是監督學習的兩大種類。

分類問題

常用方法:交叉熵(cross_entropy),它描述了兩個概率分佈之間的距離,當交叉熵越小說明二者之間越接近。它是分類問題中使用比較廣的一種損失函式。
給定兩個概率分佈p和q,通過q來表示p的交叉熵為:

H(p,q)=xp(x)logq(x)

交叉熵刻畫的是兩個概率分佈之間的距離,但是神經網路的輸出卻不一定是一個概率分佈。概率分佈刻畫了不同事件發生的概率。當事件總數是有限的情況下,概率分佈函式p(X=x)滿足:

xp(X=x)[0,1]xp(X=x)=1

如何將神經網路前向傳播得到的結果變成概率分佈,Softmax迴歸就是一個非常常用的辦法。
Softmax迴歸本身可以作為一個學習演算法來優化分類結果,但在TensorFlow中,Softmax迴歸的引數被去掉了,它只是一層額外的處理層,將神經網路的輸出變成一個概率分佈。下圖展示了加上Softmax迴歸的神經網路結構圖。

這裡寫圖片描述

交叉熵作為神經網路的損失函式時,p代表的是正確答案,q代表的是預測值。交叉熵刻畫的是兩個概率分佈的距離,交叉熵值越小,兩個概率分佈越接近。

案例:
有個三分類問題,樣例正確答案(1,0,0)。
某模型經過Softmax迴歸之後的預測答案是(0.5,0.4,0.1),那麼這個預測和正確答案之間的交叉熵為:

H((1,0,0),(0.5,0.4,0.1))=(1×log0.5+0×log0.4+0×log0.1)0.3
另一個模型的預測是(0.8,0.1,0.1),那麼這個預測和真實值之間的交叉熵是:
H((1,0,0),(0.8,0.1,0.1))=(1×log0.8+0×log0.1+0×log0.1)0.1
從直觀上可以很容易地知道第二個預測答案要優於第一個。通過交叉熵計算得到的結果也是一致的(第二個交叉熵的值更小)。

TensorFlow實現交叉熵,程式碼如下:

cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))) 

tf.clip_by_value函式可以將一個張量中的數值限制在一個範圍內,這樣就避免了一些運算錯誤(比如log0是無效的)。

y_:正確結果
y :預測結果

TensorFlow對交叉熵和softmax迴歸進行了統一封裝,我們可以直接使用如下程式碼實現使用softmax迴歸後的交叉熵損失函式:

cross_entropy = tf.nn.softmax_cross_entropy_with_logits(y,y_)

迴歸問題

迴歸問題解決的是對具體數值的預測。比如房價預測、銷量預測等都是迴歸問題。這些問題需要預測的不是一個事先定義好的類別,而是一個任意實數。解決回顧問題的神經網路一般只有一個輸出節點,這個節點的輸出值就是預測值。對於迴歸問題,最常用的損失函式是均方誤差(MSE,mean squared error )。它的定義如下:

MSE(y,y)=i=1n(yiyi)2n

其中yi為一個batch中第i個數據的正確答案,而yi為神經網路給出的預測值。
如下程式碼展示瞭如何通過TensorFlow實現均方誤差損失函式:
mse = tf.reduce_sum(tf.square(y_ -  y))

其中y代表了神經網路的輸出答案,y_代表了標準答案。

2、自定義損失函式

例:如果一個商品的成本是1元,但是利潤是10元,那麼少預測一個就少賺10元,而多預測一個少賺1元。
為了最大化預期利潤,需要將損失函式和利潤直接聯絡起來。下面公式給出了一個當預測多於真實值和預測少於真實值時有不同損失係數的損失函式:

Loss(y,y)=i=1nf(yi,yi),f(x,y)={a(xy)x>yb(yx)xy

yi為一個batch中第i個數據的正確答案,yi為神經網路得到的預測值,

a(xy)x>y表示正確答案多於預測答案的情況
b(yx)xy表示正確答案少於預測答案的情況

TensorFlow實現這個損失函式:

a= 10
b= 1
loss = tf.reduce_sum(tf.where(tf.greater(y, y_), (y - y_) * a, (y_ - y) * b))

(tf.select已被捨棄,使用tf.where替代)

再看一個tf.where和tf.greater使用的例子

import tensorflow as tf

v1 = tf.constant([1.0,2.0,3.0,4.0])
v2 = tf.constant([4.0,3.0,2.0,1.0])

sess = tf.InteractiveSession()
print(tf.greater(v1,v2).eval())
print(tf.where(tf.greater(v1,v2),v1,v2).eval())

輸出:

[False False  True  True]
[ 4.  3.  3.  4.]

損失函式對訓練結果的影響

下面通過一個簡單的神經網路程式來講解損失函式對模型訓練結果的影響。下面程式碼實現了一個擁有兩個輸入節點、一個輸出節點,沒有隱藏層的神經網路。

import tensorflow as tf
from numpy.random import RandomState

1.定義神經網路的相關引數和變數。

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)

2.設定自定義的損失函式。

#定義損失函式使得預測少了的損失大,於是模型應該偏向多的方向預測。
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)

3.生成模擬資料集。

#通過隨機數生成一個模擬資料集
rdm = RandomState(1)
X = rdm.rand(128,2)
#設定迴歸的正確值為兩個輸入的和加上一個隨機量。之所以要加上一個隨機量是為了加入不可預測的噪音,否則不同#損失函式的意義就不大了,因為不同損失函式都會在能完全預測正確的時候最低。一般來說噪音為一個均值為0的小#量,所以這裡的噪音設定為-0.05 ~ 0.05的隨機數
Y = [[x1+x2+(rdm.rand()/10.0-0.05)] for (x1, x2) in X]

4.訓練模型。

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) % 128
        end = (i*batch_size) % 128 + batch_size
        sess.run(tra