使用TensorFlow實現二分類
使用TensorFlow構建一個神經網路來實現二分類,主要包括輸入資料格式、隱藏層數的定義、損失函式的選擇、優化函式的選擇、輸出層。下面通過numpy來隨機生成一組資料,通過定義一種正負樣本的區別,通過TensorFlow來構造一個神經網路來實現二分類。
一、神經網路結構
輸入資料:定義輸入一個二維陣列(x1,x2),資料通過numpy來隨機產生,將輸出定義為0或1,如果x1+x2<1,則y為1,否則y為0。
隱藏層:定義兩層隱藏層,隱藏層的引數為(2,3),兩行三列的矩陣,輸入資料通過隱藏層之後,輸出的資料為(1,3),t通過矩陣之間的乘法運算可以獲得輸出資料。
損失函式:使用交叉熵作為神經網路的損失函式,常用的損失函式還有平方差。
優化函式:通過優化函式來使得損失函式最小化,這裡採用的是Adadelta演算法進行優化,常用的還有梯度下降演算法。
輸出資料:將隱藏層的輸出資料通過(3,1)的引數,輸出一個一維向量,值的大小為0或1。
二、TensorFlow程式碼的實現
-
import tensorflow as tf
-
from numpy.random import RandomState
-
if __name__ == "__main__":
-
#定義每次訓練資料batch的大小為8,防止記憶體溢位
-
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)
-
#定義損失函式和反向傳播演算法
-
#使用交叉熵作為損失函式
-
#tf.clip_by_value(t, clip_value_min, clip_value_max,name=None)
-
#基於min和max對張量t進行截斷操作,為了應對梯度爆發或者梯度消失的情況
-
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y,1e-10,1.0)))
-
# 使用Adadelta演算法作為優化函式,來保證預測值與實際值之間交叉熵最小
-
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)
-
#通過隨機函式生成一個模擬資料集
-
rdm = RandomState(1)
-
# 定義資料集的大小
-
dataset_size = 128
-
# 模擬輸入是一個二維陣列
-
X = rdm.rand(dataset_size,2)
-
#定義輸出值,將x1+x2 < 1的輸入資料定義為正樣本
-
Y = [[int(x1+x2 < 1)] for (x1,x2) in X]
-
#建立會話執行TensorFlow程式
-
with tf.Session() as sess:
-
#初始化變數 tf.initialize_all_variables()
-
init = tf.initialize_all_variables()
-
sess.run(init)
-
#設定神經網路的迭代次數
-
steps = 5000
-
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]})
-
#每迭代1000次輸出一次日誌資訊
-
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))
-
#輸出引數w1
-
print(w1.eval(session=sess))
-
#輸出引數w2
-
print(w2.eval(session=sess))
-
'''
-
After 0 training step(s),cross entropy on all data is 0.0674925
-
After 1000 training step(s),cross entropy on all data is 0.0163385
-
After 2000 training step(s),cross entropy on all data is 0.00907547
-
After 3000 training step(s),cross entropy on all data is 0.00714436
-
After 4000 training step(s),cross entropy on all data is 0.00578471
-
[[-1.96182752 2.58235407 1.68203771]
-
[-3.46817183 1.06982315 2.11788988]]
-
[[-1.82471502]
-
[ 2.68546653]
-
[ 1.41819501]]
-
'''
--------------------------------2018/9/16更新(非常感謝al_kk的提醒)---------------------------------------
上面的TensorFlow二分類我是參考Google深度學習框架,al_kk評論說這個損失函式的定義存在問題,之前沒有仔細的去考慮這個問題,al_kk提醒之後,我發現這個損失函式的定義的確存在問題,經過測試發現這個模型也存在一些問題。其實,我們的主要目的是想去學習一個x1+x2=1的直線,來區分0和1兩類不同的類別,下面我對這個模型進行了一些修改並說明一下為什麼這個損失函式的定義存在問題。
一、為什麼說這個損失函式的定義存在問題呢?
上面程式中定義的輸入的y的shape為[1],也就是y的類別為0或1,對於單分類問題交叉熵損失函式的定義應該為
其中n為y的種類,在上面的例子中需要包含0和1的y_*log(y)(y_表示真實類別,y表示預測類別),而上面的例子中只包含了一個y_*log(y),在上例中正確的損失函式定義應該為loss = y_*log(y) + (1-y_) * log(1-y)。為了便於大家理解,我引用al_kk:“如果只有一個類別的交叉熵即y_ * log(y),如果真實類別y_為0,那麼無論預測值y為任何值的時候,損失函式始終為0”。除此之外,大家可以想一下,當預測值始終為1的時候,那麼損失函式是不是就會一直為0,這也是為什麼輸出預測值y的時候,y的值都是大於1的。如果將y的shape改為[2]的話,就可以使用y_*log(y)。
二、修改之後的二分類程式
-
import tensorflow as tf
-
import numpy as np
-
from numpy.random import RandomState
-
import matplotlib.pyplot as plt
-
if __name__ == "__main__":
-
#定義神經網路的引數
-
w = tf.Variable(tf.random_normal([2,1],stddev=1,seed=1))
-
b = tf.Variable(tf.random_normal([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")
-
#定義神經網路的前向傳播過程
-
y = tf.matmul(x,w) + b
-
#定義損失函式和反向傳播演算法
-
#使用交叉熵作為損失函式
-
#tf.clip_by_value(t, clip_value_min, clip_value_max,name=None)
-
#基於min和max對張量t進行截斷操作,為了應對梯度爆發或者梯度消失的情況
-
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)))
-
# 使用Adadelta演算法作為優化函式,來保證預測值與實際值之間交叉熵最小
-
train_step = tf.train.AdamOptimizer(0.01).minimize(cross_entropy)
-
#通過隨機函式生成一個模擬資料集
-
rdm = RandomState(1)
-
# 定義資料集的大小
-
dataset_size = 100
-
# 模擬輸入是一個二維陣列
-
X = rdm.rand(dataset_size,2)
-
#定義輸出值,將x1+x2 < 1的輸入資料定義為正樣本
-
Y = [[int(x1+x2 < 1)] for (x1,x2) in X]
-
#建立會話執行TensorFlow程式
-
with tf.Session() as sess:
-
#初始化變數 tf.initialize_all_variables()
-
init = tf.initialize_all_variables()
-
sess.run(init)
-
#設定神經網路的迭代次數
-
steps = 1000
-
for i in range(steps):
-
#通過選取樣本訓練神經網路並更新引數
-
for (input_x,input_y) in zip(X,Y):
-
input_x = np.reshape(input_x,(1,2))
-
input_y = np.reshape(input_y,(1,1))
-
sess.run(train_step,feed_dict={x:input_x,y_:input_y})
-
#每迭代1000次輸出一次日誌資訊
-
if i % 100 == 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))
-
#預測輸入X的類別
-
pred_Y = sess.run(y,feed_dict={x:X})
-
#繪製圖像
-
for input_x,output_y in zip(X,pred_Y):
-
if output_y < 1:
-
plt.scatter(input_x[0],input_x[1],marker="o",color="blue",s=20)
-
else:
-
plt.scatter(input_x[0],input_x[1],marker="^",color="red",s=20)
-
#繪製x1+x2 = 1直線
-
plt.plot([0,1],[1,0])
-
plt.show()