TensorFlow筆記-06-神經網絡優化-損失函數,自定義損失函數,交叉熵
TensorFlow筆記-06-神經網絡優化-損失函數,自定義損失函數,交叉熵
- 神經元模型:用數學公式比表示為:f(Σi xi*wi + b), f為激活函數
- 神經網絡 是以神經元為基本單位構成的
- 激活函數:引入非線性激活因素,提高模型的表達能力
常用的激活函數有relu、sigmoid、tanh等 - (1)激活函數relu:在Tensorflow中,用tf.nn.relu()表示
- (2)激活函數sigmoid:在Tensorflow中,用tf.nn.sigmoid()表示
- (3)激活函數tanh:在Tensorflow中,用tf.nn.tanh()表示
- 神經網絡的復雜度:可用神經網絡的的層數和神經網絡中待優化參數個數表示
- 神經網絡的層數:一般不計入輸入層,層數 = n個隱藏層 + 1個輸入層
- 神經網絡待優化的參數:神經網絡中所有參數w的個數 + 所有參數b的個數
- 例如:
在該神經網絡中,包含1個輸入層,1個隱藏層和1個輸出層,該神經網絡的參數為2層
在該神經網絡中,參數的個數是所有參數w的個數加上所有參數b的總數,第一層參數用三行四列的二階張量表示(即12個線上的權重w)再加上4個偏置b;第二層參數是四行二列的二階張量(即8個線上的權重w)再加上2個偏置b
總參數 = 34+4 + 42+2 = 26
損失函數
- 損失函數(loss):用來表示預測(y)與已知答案(y_)的差距。在訓練神經網絡時,通過不斷改變神經網絡中所有參數,使損失函數不斷減小,從而訓練出更高準確率的神經網絡模型
- 常用的損失函數有均方誤差,自定義和交叉熵等
- 均方誤差mse:n個樣本的預測值(y)與(y_)的差距。在訓練神經網絡時,通過不斷的改變神經網絡中的所有參數,使損失函數不斷減小,從而訓練出更高準確率的神經網絡模型
- 在Tensorflow中用loss_mse = tf.reduce_mean(tf.square(y_-y))
- 例如:
- 預測酸奶日銷量y,x1和x2是兩個影響日銷量的因素
- 應提前采集的數據有:一段時間內,每日的x1因素、x2因素和銷量y_。且數據盡量多
- 在本例子中用銷量預測產量,最優的產量應該等於銷量,由於目前沒有數據集,所以擬造了一套數據集。利用Tensorflow中函數隨機生成x1、x2,制造標準答案y_ = x1 + x2,為了真實,求和後還加了正負0.05的隨機噪聲
- 我們把這套自制的數據集餵入神經網絡,構建一個一層的神經網絡,擬合預測酸奶日銷量的函數
- 代碼tf07sale文件:https://xpwi.github.io/py/TensorFlow/tf07sale.py
# coding:utf-8
# 預測多或者預測少的影響一樣
# 導入模塊,生成數據集
import tensorflow as tf
import numpy as np
# 一次餵入神經網絡8組數據,數值不可以過大
BATCH_SIZE = 8
SEED = 23455
# 基於seed產生隨機數
rdm = np.random.RandomState(SEED)
# 隨機數返回32行2列的矩陣 表示32組 體積和重量 作為輸入數據集
X = rdm.rand(32, 2)
Y_ = [[x1+x2+(rdm.rand()/10.0-0.05)] for (x1, x2) in X]
print("X:\n", X)
print("Y:\n", Y_)
# 定義神經網絡的輸入,參數和輸出,定義前向傳播過程
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
# w1為2行1列
w1 = tf.Variable(tf.random_normal([2, 1], stddev=1, seed=1))
y = tf.matmul(x, w1)
# 定義損失函數及反向傳播方法
# 定義損失函數為MSE,反向傳播方法為梯度下降
loss_mse = tf.reduce_mean(tf.square(y_-y))
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(loss_mse)
# 其他優化方法
# train_step = tf.train.GMomentumOptimizer(0.001, 0.9).minimize(loss_mse)
# train_step = tf.train.AdamOptimizer(0.001).minimize(loss_mse)
# 生成會話,訓練STEPS輪
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
# 訓練模型20000輪
STEPS = 20000
for i in range(STEPS):
start = (i*BATCH_SIZE) % 32
end = start + BATCH_SIZE
sess.run(train_step, feed_dict={x: X[start:end], y_: Y_[start:end]})
# 沒500輪打印一次loss值
if i % 1000 == 0:
total_loss = sess.run(loss_mse, feed_dict={x: X, y_: Y_})
print("After %d training step(s), loss on all data is %g" %(i, total_loss))
print(sess.run(w1),"\n")
print("Final w1 is: \n", sess.run(w1))
運行結果
結果分析
有上述代碼可知,本例中神經網絡預測模型為y = w1x1 + w2x2,損失函數采用均方誤差。通過使損失函數值(loss)不斷降低,神經網絡模型得到最終參數w1 = 0.98,w2 = 1.02,銷量預測結果為y = 0.98x1 + 1.02x2。由於在生成數據集時,標準答案為y = x1 + x2,因此,銷量預測結果和標準答案已經非常接近,說明該神經網絡預測酸奶日銷量正確
自定義損失函數
- 自定義損失函數:根據問題的實際情況,定制合理的損失函數
- 例如:
- 對於預測酸奶日銷量問題,如果預測銷量大於實際銷量則會損失成本;如果預測銷量小於實際銷量則會損失利潤。在實際生活中,往往制造一盒酸奶的成本和銷售一盒酸奶的利潤不是等價的。因此,需要使用符合該問題的自定義損失函數
- 自定義損失函數為:loss = Σnf(y_, y)
- 其中,損失函數成分段函數:
- 損失函數表示
- 若預測結果y小於標準答案y_,損失函數為利潤乘以預測結果y與標準答案之差
- 若預測結果y大於標準答案y_,損失函數為成本乘以預測結果y與標準答案之差
用Tensorflow函數表示為:
- loss = tf.reduce_sum(tf.where(tf.greater(y, y_), COST(y-y_), PROFIT(y_-y)))
(1)第1種情況:若酸奶成本為1元,酸奶銷售利潤為9元,則制造成本小於酸奶利潤,因此希望預測結果y多一些。采用上述的自定義損失函數,訓練神經網絡模型
- 代碼tf07sale2文件:https://xpwi.github.io/py/TensorFlow/tf07sale2.py
# 第一種情況:酸奶成本1元,酸奶利潤9元
# 預測少了損失大,故不要預測少,故生成的模型會多預測一些
# 導入模塊,生成數據集
import tensorflow as tf
import numpy as np
# 一次餵入神經網絡8組數據,數值不可以過大
BATCH_SIZE = 8
SEED = 23455
COST = 1
PROFIT = 9
# 基於seed產生隨機數
rdm = np.random.RandomState(SEED)
# 隨機數返回32行2列的矩陣 表示32組 體積和重量 作為輸入數據集
X = rdm.rand(32, 2)
Y_ = [[x1+x2+(rdm.rand()/10.0-0.05)] for (x1, x2) in X]
# 定義神經網絡的輸入,參數和輸出,定義前向傳播過程
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
# w1為2行1列
w1 = tf.Variable(tf.random_normal([2, 1], stddev=1, seed=1))
y = tf.matmul(x, w1)
# 定義損失函數及反向傳播方法
# 定義損失函數使得預測少了的損失大,於是模型應該偏向多的放心預測
loss = tf.reduce_sum(tf.where(tf.greater(y, y_), COST*(y-y_), PROFIT*(y_-y)))
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(loss)
# 其他優化方法
# train_step = tf.train.GMomentumOptimizer(0.001, 0.9).minimize(loss)
# train_step = tf.train.AdamOptimizer(0.001).minimize(loss)
# 生成會話,訓練STEPS輪
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
# 訓練模型20000輪
STEPS = 20000
for i in range(STEPS):
start = (i*BATCH_SIZE) % 32
end = start + BATCH_SIZE
sess.run(train_step, feed_dict={x: X[start:end], y_: Y_[start:end]})
# 沒500輪打印一次loss值
if i % 1000 == 0:
total_loss = sess.run(loss, feed_dict={x: X, y_: Y_})
print("After %d training step(s), loss on all data is %g" %(i, total_loss))
print(sess.run(w1),"\n")
print("Final w1 is: \n", sess.run(w1))
運行結果
運行結果分析
由代碼執行結果可知,神經網絡最終參數為w1=1.03,w2=1.05,銷量預測結果為y = 1.03x1 + 1.05x2。由此可見,采用自定義損失函數預測的結果大於采用均方誤差的結果,更符合實際需求
(2)第2種情況:若酸奶成本為9元,酸奶銷售利潤為1元,則制造利潤小於酸奶成本,因此希望預測結果y小一些。采用上述的自定義損失函數,訓練神經網絡模型
- 代碼tf07sale3文件:https://xpwi.github.io/py/TensorFlow/tf07sale3.py
# 第二種情況:酸奶成本9元,酸奶利潤1元
# 預測多了損失大,故不要預測多,故生成的模型會少預測一些
# 導入模塊,生成數據集
import tensorflow as tf
import numpy as np
# 一次餵入神經網絡8組數據,數值不可以過大
BATCH_SIZE = 8
SEED = 23455
COST = 9
PROFIT = 1
# 基於seed產生隨機數
rdm = np.random.RandomState(SEED)
# 隨機數返回32行2列的矩陣 表示32組 體積和重量 作為輸入數據集
X = rdm.rand(32, 2)
Y_ = [[x1+x2+(rdm.rand()/10.0-0.05)] for (x1, x2) in X]
# 定義神經網絡的輸入,參數和輸出,定義前向傳播過程
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
# w1為2行1列
w1 = tf.Variable(tf.random_normal([2, 1], stddev=1, seed=1))
y = tf.matmul(x, w1)
# 定義損失函數及反向傳播方法
# 重新定義損失函數使得預測多了的損失大,於是模型應該偏向少的方向預測
loss = tf.reduce_sum(tf.where(tf.greater(y, y_), COST*(y-y_), PROFIT*(y_-y)))
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(loss)
# 其他優化方法
# train_step = tf.train.GMomentumOptimizer(0.001, 0.9).minimize(loss)
# train_step = tf.train.AdamOptimizer(0.001).minimize(loss)
# 生成會話,訓練STEPS輪
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
# 訓練模型20000輪
STEPS = 20000
for i in range(STEPS):
start = (i*BATCH_SIZE) % 32
end = start + BATCH_SIZE
sess.run(train_step, feed_dict={x: X[start:end], y_: Y_[start:end]})
# 沒500輪打印一次loss值
if i % 1000 == 0:
total_loss = sess.run(loss, feed_dict={x: X, y_: Y_})
print("After %d training step(s), loss on all data is %g" %(i, total_loss))
print(sess.run(w1),"\n")
print("Final w1 is: \n", sess.run(w1))
運行結果
運行結果分析
由執行結果可知,神經網絡最終參數為w1 = 0.96,w2 = 0.97,銷量預測結果為y = 0.96+x1 + 0.7*x2。
因此,采用自定義損失函數預測的結果小於采用均方誤差預測得結果,更符合實際需求
交叉熵
- 交叉熵(Cross Entropy):表示兩個概率分布之間的距離,交叉熵越大,兩個概率分布距離越遠,兩個概率分布越相異;交叉熵越小,兩個概率分布距離越近,兩個概率分布越相似
- 交叉熵計算公式:H(y_, y) = -Σy_ * log y
用 Tensorflow 函數表示
ce = -tf.reduce_mean(y_*tf.clip_by_value(y, le-12, 1.0)))
- 例如:
兩個神經網絡模型解決二分類問題中,已知標準答案為 y_ = (1, 0),第一個神經網絡模型預測結果為 y1 = (0.6, 0.4),第二個神經網絡模型預測結果為 y2 = (0.8, 0.2),判斷哪個神經網絡模型預測得結果更接近標準答案 根據交叉熵的計算公式得:
H(1, 0), (0.6, 0.4) = -(1 * log0.6 + 0log0.4) ≈ -(-0.222 + 0) = 0.222
H(1, 0), (0.8, 0.2) = -(1 log0.8 + 0*log0.2) ≈ -(-0.097 + 0) = 0.097
softmax 函數
- softmax 函數:將 n 分類中的 n 個輸出(y1, y2...yn)變為滿足以下概率分布要求的函數:
?x = P(X = x) ∈ [0, 1] - softmax 函數表示為:
- softmax 函數應用:在 n 分類中,模型會有 n 個輸出,即 y1,y2 ... n, 其中yi表示第 i 中情況出現的可能性大小。將 n 個輸出經過 softmax 函數,可得到符合概率分布的分類結果
在 Tensorflow 中,一般讓模型的輸出經過 softmax 函數,以獲得輸出分類的概率分布再與標準答案對比,求出交叉熵,得到損失函數,用如下函數實現:
ce = tf.nn.sparse_softmax_cross_entropy_with_logits(logits = y, labels = tf.argmax(y_, 1))
cem = tf.reduce_mean(ce)
更多文章:Tensorflow 筆記
- 本筆記不允許任何個人和組織轉載
TensorFlow筆記-06-神經網絡優化-損失函數,自定義損失函數,交叉熵