首頁 > 軟體

使用TensorFlow實現DNN

2020-06-16 16:58:55

這一節使用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=XW+b  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輸出的為在各個類別上面的得分,損失函數使用交叉熵
y  log(y  ) −∑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,並為整個模型設定損失函數,對損失函數進行優化求解,最後對模型進行評估。

本文永久更新連結地址http://www.linuxidc.com/Linux/2017-12/149553.htm


IT145.com E-mail:sddin#qq.com