這一節使用TF實現一個多層神經網絡模型來對MNIST
數據集進行分類,這里我們設計一個含有兩個隱藏層的神經網絡,在輸出部分使用softmax對結果進行預測。
使用高級API實現多層神經網絡###
這里我們使用tensorflow.contrib
包,這是一個高度封裝的包,里面包含了許多類似seq2seq、keras
一些實用的方法。
先引入數據
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("./") #自動下載數據到這個目錄
X_train = mnist.train.images
X_test = mnist.test.images
y_train = mnist.train.labels.astype("int")
y_test = mnist.test.labels.astype("int")
>>X_train
array([[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
...,
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.]], dtype=float32)
>>len(X_train)
55000
>>len(X_train[0])
784
>>X_train[0]
array([ 0., 0., 0., ..., 0., 0., 0.], dtype=float32)
>>y_test
array([7, 2, 1, ..., 4, 5, 6])
模型的主要代碼
features_cols = tf.contrib.learn.infer_real_valued_columns_from_input(X_train)
dnn_clf = tf.contrib.learn.DNNClassifier(hidden_units=[300,100], n_classes=10, feature_columns=features_cols)
dnn_clf.fit(X_train, y_train, batch_size=50, steps=10000)
from sklearn.metrics import accuracy_score
y_pred = dnn_clf.predict(X_test)
print(accuracy_score(y_test, list(y_pred)))
其中infer_real_valued_columns_from_input
這個方法根據名字可以看出,它是根據輸入的數據來推算出數據的類型,該例子中features_cols
的值為
[_RealValuedColumn(column_name='', dimension=784, default_value=None, dtype=tf.float32, normalizer=None)]
,短短幾行代碼就實現了一個多層神經網絡模型。並且可能會發現上面這些與之前介紹的有些不同,不需要對變量進行初始化,不需要創建session
,使用起來十分的簡單。
使用TF實現多層神經網絡###
高度封裝的API調用起來固然很爽,但是自己不了解內部的原理使用起來就不是那么的踏實,下面就使用TF實現同樣的模型,代碼主要分為兩部分,構建TF計算流圖和執行計算圖。希望讀者能夠對比上面的代碼來看接下來的部分。
構建TF計算流圖####
首先我們需要根據輸入的數據來設定輸入的參數,使用的數據集MNIST為28*28的矩陣,整個神經網絡包含兩個隱藏層
n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 100
n_output = 10
X = tf.placeholder(tf.float32,shape=(None,n_inputs),name='X')
y = tf.placeholder(tf.int64,shape=(None),name='y')#注意數據類型
上面使用占位符的方法來聲明模型的輸入X和y,需要注意的是占位符的數據類型
,在執行階段,占位符會被輸入的數據所替代。接下來我們需要創建模型的兩個隱藏層和輸出層,兩個隱藏使用Relu
作為激活函數,輸出層使用softmax。每一層需要指定節點的個數。
def neuron_layer(X,n_neurons,name,activation=None):
with tf.name_scope(name):
n_inputs = int(X.get_shape()[1]) #特征個數
stddev = 2 / np.sqrt(n_inputs)
init = tf.truncated_normal((n_inputs,n_neurons),stddev=stddev)
W = tf.Variable(init,name='weight')
b = tf.Variable(tf.zeros([n_neurons]),name='baise')
z = tf.matmul(X,W) + b
if activation == "relu":
return tf.nn.relu(z)
else:
return z
我將逐行的對上面代碼進行解釋:
- 1.為了方便在TensorBoard上面查看,每一層的神經網絡都創建一個
name_scope
。這一步是可選操作,如果不需要在TensorBoard查看那就可以忽略掉。 - 2.根據輸入的數據的形狀來獲取數據的特征個數(第二個維度)
- 3.接下來的代碼是創建權重矩陣
W
和偏置b
,權重W
不能使用0進行初始化,這樣會導致所有的神經元的輸出為0,出現對稱失效問題,這里使用truncated normal分布(Gaussian)來初始化權重,
tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
通過指定均值和標准方差來生成正態分布,拋棄那些大於2倍stddev的值。這樣將有助於加快訓練速度。在初始化b
的時候,每一層只有一個偏置,我們全部設置為0,這樣並不會出現對稱失效問題。
- 4.下面的是在每一個神經元中的操作\(y=X·W+b\),使用向量化運算計算輸入與權重的和運算
- 5.最后就是激活函數的選擇了
下面我們就開始像搭建積木一樣創建我們的神經網絡了,每一層的輸入為上一層的輸出:
with tf.name_scope("dnn"):
hidden1 = neuron_layer(X,n_hidden1,"hidden1",activation="relu")
hidden2 = neuron_layer(hidden1,n_hidden2,"hidden2",activation="relu")
logits = neuron_layer(hidden2,n_output,"output")
上面這一段代碼的輸出層並沒有經過softmax
激活函數,這是考慮到后續優化求解原因,在后續工作中單獨做處理。上面這段代碼就是一個神經網絡全連接的簡化版本,當然TF的contrib模塊也提供了全連接的函數fully_connected
。
from tensorflow.contrib.layers import fully_connected
with tf.name_scope("dnn"):
hidden1 = fully_connected(X, n_hidden1, scope="hidden1")#激活函數默認為relu
hidden2 = fully_connected(hidden1, n_hidden2, scope="hidden2")
logits = fully_connected(hidden2, n_outputs, scope="outputs",activation_fn=None)
現在,模型已經有了。接下來套路就是設計損失函數,優化損失函數求解參數。輸出層softmax輸出的為在各個類別上面的得分,損失函數使用交叉熵
\(-\sum{y'}log(y')\)。在這里我們使用TF提供的tf.nn.sparse_softmax_cross_entropy_with_logits(_sentinel=None, labels=None, logits=None, name=None)
來計算損失函數,該方法先計算softmax再計算cross entropy,主要有兩個參數需要考慮
- 1.labels:輸入的為標簽的index,例如本例子有10個類別,取值范圍為0-9
- 2.logits:為輸入到softmax激活函數之前的模型的輸出
最后再使用reduce_mean()
計算loss。
with tf.name_scope("loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y,logits=logits)#labels允許的數據類型有int32, int64
loss = tf.reduce_mean(xentropy,name="loss")
note:TF還提供了softmax_cross_entropy_with_logits()
,和上面方法的區別該方法輸入的label為一個one-hot向量。
到這里我們的模型和損失函數已經都有了,就到了優化階段,本文使用梯度下降方法
learning_rate = 0.01
with tf.name_scope("train"):
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
模型有了結果,就需要對得到的模型進行衡量。簡單起見,這里使用accuracy
作為評估指標,判斷模型輸出結果的最高值的index是否和label的index相等
with tf.name_scope("eval"):
correct = tf.nn.in_top_k(logits,y,1) #取值最高的一位
accuracy = tf.reduce_mean(tf.cast(correct,tf.float32)) #結果boolean轉為0,1
模型構建階段最后一個工作就是初始化里面的變量
init = tf.global_variables_initializer()
saver = tf.train.Saver()
執行計算流圖####
這一部分相對前面工作要簡單很多,
n_epoch = 400
batch_size = 50
with tf.Session() as sess:
init.run()
for epoch in range(n_epoch):
for iteration in range(mnist.train.num_examples // batch_size):#需要迭代的輪數
X_batch,y_batch = mnist.train.next_batch(batch_size)
sess.run(training_op,feed_dict={X:X_batch,y:y_batch})
acc_train = accuracy.eval(feed_dict={X:X_batch,y:y_batch})
acc_test = accuracy.eval(feed_dict={X:mnist.test.images,mnist.test.labels})
print (epoch,"Train accuracy", acc_train,"Test accuracy",acc_test)
saver.save(sess, "./my_model.pk")
上面這段代碼使用的是mini-batch方法訓練神經網絡,最后將模型持久化到本地。后續的使用
with tf.Session() as sess:
saver.restore(sess, "./my_model.pk") #加載
X_new_scaled = mnist.test.images[:20]
Z = logits.eval(feed_dict={X: X_new_scaled}) #模型
y_pred = np.argmax(Z, axis=1)
總結###
本文介紹了TF在實際數據集MNIST上面的使用,為input和target創建占位符,創建神經網絡的layer,得到一個DNN,並為整個模型設置損失函數,對損失函數進行優化求解,最后對模型進行評估。
又是一個凌晨12點,晚安~