代碼詳解:TensorFlow Core帶你探索深度神經網絡「黑匣子」

image.png


想學TensorFlow?先從低階API開始吧~某種程度而言,它可以幫助咱們更好地理解Tensorflow,更加靈活地控制訓練過程。本文演示瞭如何使用低階TensorFlow Core 搭建卷積神經網絡(ConvNet)模型,並演示了使用TensorFlow編寫自定義代碼的方法。
git


對不少開發人員來講,神經網絡就像一個「黑匣子」, 而TensorFlow Core的應用,則將咱們帶上了對深度神經網絡後臺「黑幕」的探索進程。數組


本文將MNIST手寫數字數據集加載到數據迭代器,使用圖和會話,搭建新的卷積神經網絡體系結構,使用不一樣選項訓練模型,做出預測,保存訓練模型。同時,也會提供完整的代碼以及Keras中的等效模型,以便使用Keras的用戶進行直接比較,加深瞭解,同時高階API在搭建神經網絡方面的強大功能也能獲得展現。網絡


image.png

MNIST數據集session


MNIST是一組28x28大小的手寫數字的灰度圖像,其中訓練集有60000張圖像,測試集有10000張圖像。ide


首先,加載並處理MNIST圖像。函數


預計訓練模型輸入形狀爲[batch_size, height, width, channels]([批量大小,高度,寬度,通道])。因爲灰度圖是單通道圖像,它們的形狀爲 [60000,28,28],所以須要添加通道維度,使得形狀爲 [60000,28,28,1]。並且圖像的數據類型是uint8(像素值範圍爲0-255),所以須要將其除以255,才能縮放到0-1之間的範圍。這樣第一張圖像就可顯示爲一個樣例。性能


attn_layer = AttentionLayer(name='attention_layer')([encoder_out,decoder_out])學習


import tensorflow as tf
測試

import numpy as np優化


mnist = tf.keras.datasets.mnist

(train_images, train_labels),(test_images, test_labels) = mnist.load_data()


# Normalize and reshape images to [28,28,1]

train_images  = np.expand_dims(train_images.astype(np.float32) / 255.0, axis=3)

test_images = np.expand_dims(test_images.astype(np.float32) / 255.0, axis=3)


# View a sample image

import matplotlib.pyplot as plt


plt.figure(dpi=100)

plt.imshow(np.squeeze(train_images[0]), cmap='gray')

plt.title('Number: {}'.format(train_labels[0]))

plt.show()


image.png


因爲標籤是整數值(例如0、一、2),即分類值,須要轉換成one-hot編碼(例如[1,0,0], [0,1,0], [0,0,1])用於訓練。將Keras的to_categorical編碼器轉換標籤,也能夠選擇用Scikit-learn的OneHotEncoder 編碼器。


以下所示,測試集標籤不用轉化成one-hot編碼。


from keras.utils import to_categorical

train_labels = to_categorical(train_labels)


image.png

圖和會話


用TensorFlow建立模型分爲兩個步驟:建立一個圖;在會話中執行圖。


圖概述了計算數據流。這種數據流圖決定在張量(多維數據陣列)上執行操做的時間和方式。圖形能夠在會話內部或外部建立,但只能在會話內部使用。只有在會話中才能初始化張量,執行操做,訓練模型。


數據迭代器


爲了演示數據流圖和會話,建立數據集迭代器。因爲MNIST圖像和真值標籤是Numpy數組的切片,所以能夠經過將數組傳遞到tf.data.dataset.from_tensor_slices方法中來建立數據集。而後爲數據集建立迭代器。咱們但願它在每次運行時返回一個 batch_size(批量大小)圖像數量和標籤數量,若是不肯定訓練次數,則返回重複。


batch_size = 128

dataset = tf.data.Dataset.from_tensor_slices((train_images, train_labels))

iterator = dataset.repeat().batch(batch_size).make_initializable_iterator()

data_batch = iterator.get_next(


如今咱們定義了一個數據流圖,爲了得到一批新圖像和新標籤,咱們在會話中運行data_batch。


可是還未完成。雖然數據流圖建立好了,但圖像尚未被徹底傳遞進來。爲此,迭代器須要在會話中初始化。


sess = tf.Session()

sess.run(iterator.initializer)


如今有一個會話正在運行,只需運行data_batch就能夠檢索第一批圖像。圖像的形狀爲[batch_size, height, width, channels],標籤的形狀爲[batch_size, classes]。能夠經過如下方式檢查:


batch_images, batch_labels = sess.run(data_batch)


print('Images shape: {}'.format(batch_images.shape))

print('Labels shape: {}'.format(batch_labels.shape)


Images shape: (128, 28, 28, 1)

Labels shape: (128, 10)


經過兩次運行data_batch來顯示前兩批中的第一個圖像。


# Get the first batch of images and display first image

batch_images, batch_labels = sess.run(data_batch)

plt.subplot(1, 2, 1)

plt.imshow(np.squeeze(batch_images)[0], cmap='gray')


# Get a second batch of images and display first image

batch_images, batch_labels = sess.run(data_batch)

plt.subplot(1, 2, 2)

plt.imshow(np.squeeze(batch_images)[0], cmap='gray')

plt.show()


image.png


在第一批中,第一個圖像是5。在第二批中,第一個圖像是1。


而後能夠經過sess.close()關閉會話。可是,請記住,當會話關閉時,信息將丟失。例如,如果以下圖所示關閉並從新啓動會話,那麼數據迭代器將從頭開始啓動。(注意,迭代器須要在每一個會話中初始化。)


# New session

sess = tf.Session()

sess.run(iterator.initializer)


# Get the first batch of images and display first image

batch_images, batch_labels = sess.run(data_batch)

plt.subplot(1, 2, 1)

plt.imshow(np.squeeze(batch_images)[0], cmap='gray')


# Close and restart session

sess.close()

sess = tf.Session()

sess.run(iterator.initializer)


# Get a second batch of images and display first image

batch_images, batch_labels = sess.run(data_batch)

plt.subplot(1, 2, 2)

plt.imshow(np.squeeze(batch_images)[0], cmap='gray')

plt.show()


image.png


由於會話已關閉,而且建立了新會話,因此數據迭代器開始從新啓動,並再次顯示相同的圖像。


使用with語句


會話也能夠經過「with」語句啓動和自動關閉。在「with」塊的末尾,會話將按指示關閉。


with tf.Session() as sess:

    sess.run(iterator.initializer)


    for _ in range(2):

        batch_images, batch_labels = sess.run(data_batch)

        img = np.squeeze(batch_images)[0]

        plt.imshow(img, cmap='gray')

        plt.show()


# sess is closed


image.png


image.png

卷積神經網絡模型


下面將演示如何使用TensorFlow構建下圖所示的基本卷積神經網絡模型:


image.png


該體系結構有四個卷積層。前兩層有16個過濾器,後兩層有32個過濾器,全部過濾器的尺寸爲3x3。四個卷積層中都添加了誤差和relu激活函數。最後兩層是徹底鏈接(密集)的層。


權值和誤差


卷積神經網絡中的初始權重須要對稱破缺的隨機值,以便網絡可以學習。xavier_initializer設定項「旨在保持全部層中梯度比例大體相同」,一般用於初始化模型的權重。經過使用帶有tf.get_variable的初始值設定項來建立層的權重。每一個卷積層都有形狀爲[filter_height, filter_width, in_channels, out_channels]的濾波器。因爲緻密層是徹底鏈接的,而且沒有3x3大小的濾波器,所以它們的形狀爲[in_channels, out_channels]。同時,還建立了誤差,每一個誤差的大小與相應層的out_channels 的大小相同,並用零初始化。

 

建立weights(權值)和biases(參數) 字典,以便組織和簡化。


weights = {

    # Convolution Layers

    'c1': tf.get_variable('W1', shape=(3,3,1,16), \

            initializer=tf.contrib.layers.xavier_initializer()),

    'c2': tf.get_variable('W2', shape=(3,3,16,16), \

            initializer=tf.contrib.layers.xavier_initializer()),

    'c3': tf.get_variable('W3', shape=(3,3,16,32), \

            initializer=tf.contrib.layers.xavier_initializer()),

    'c4': tf.get_variable('W4', shape=(3,3,32,32), \

            initializer=tf.contrib.layers.xavier_initializer()),


    # Dense Layers

    'd1': tf.get_variable('W5', shape=(7*7*32,128),

            initializer=tf.contrib.layers.xavier_initializer()),

    'out': tf.get_variable('W6', shape=(128,n_classes),

            initializer=tf.contrib.layers.xavier_initializer()),

}

biases = {

    # Convolution Layers

    'c1': tf.get_variable('B1', shape=(16), initializer=tf.zeros_initializer()),

    'c2': tf.get_variable('B2', shape=(16), initializer=tf.zeros_initializer()),

    'c3': tf.get_variable('B3', shape=(32), initializer=tf.zeros_initializer()),

    'c4': tf.get_variable('B4', shape=(32), initializer=tf.zeros_initializer()),


    # Dense Layers

    'd1': tf.get_variable('B5', shape=(128), initializer=tf.zeros_initializer()),

    'out': tf.get_variable('B6', shape=(n_classes), initializer=tf.zeros_initializer()),

}


由於MNIST圖像是灰度的,因此第一層的in_channels爲1。輸出層須要將out_channels 設爲10,由於有10個類。其餘層中的濾波器數量能夠根據性能或速度進行調整,可是in_channels中的每個濾波器都須要與前一層的out_channels 相同。在下面建立卷積神經網絡時,將解釋第一個緻密層採用7*7*32大小的緣由。


卷積層


TensorFlow有一個tf.nn.conv2d函數,可用於將張量與權重卷積。爲了簡化卷積層,建立一個函數,它接受輸入數據x 並應用一個具備權重W的二維卷積,添加一個誤差b,使用relu函數激活。


#Define 2D convolutional function

def conv2d(x, W, b, strides=1):

    x = tf.nn.conv2d(x, W, strides=[1,1,1,1], padding='SAME')

    x = tf.nn.bias_add(x, b)

    return tf.nn.relu(x)


模型圖


另外一個函數可用於繪製模型圖。這將把MNIST圖像做爲data(數據),並使用不一樣層中的權重和誤差。


如體系結構所示,在第二層和第四層卷積層以後,層的高度和寬度都會減少。池化操做將滑動一個窗口,只使用區域內的單個最大值。經過使用2x2窗口(ksize=[1,2,2,1])和跨距2(strides=[1,2,2,1]),尺寸將減小一半。這有助於減小模型大小,同時保留最重要的功能。對於輸入的奇數尺寸,輸出形狀由除以2後的上限肯定(例如7/2=4)。


在第四卷積層以後,張量須要在徹底鏈接層以前進行整形,使其變平。徹底鏈接層的權重不是用於二維卷積,而是僅用於矩陣乘法,所以不使用conv2d 函數。


在最後兩層之間添加一個脫落層樣本,幫助減小過分擬合,其中權值降低的機率爲0.2。脫落層只能在訓練期間使用,所以若是模型用於預測,則應包含training 標記以繞過該層。若是在預測過程當中包含了脫落層,那麼模型的輸出將不一致,而且因爲隨機降低的權重而具備較低的準確性。


def conv_net(data, weights, biases, training=False):

    # Convolution layers

    conv1 = conv2d(data, weights['c1'], biases['c1']) # [28,28,16]

    conv2 = conv2d(conv1, weights['c2'], biases['c2']) # [28,28,16]

    pool1 = tf.nn.max_pool(conv2, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')

    # [14,14,16]


    conv3 = conv2d(pool1, weights['c3'], biases['c3']) # [14,14,32]

    conv4 = conv2d(conv3, weights['c4'], biases['c4']) # [14,14,32]

    pool2 = tf.nn.max_pool(conv4, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')

    # [7,7,32]


    # Flatten

    flat = tf.reshape(pool2, [-1, weights['d1'].get_shape().as_list()[0]])

    # [7*7*32] = [1568]


    # Fully connected layer

    fc1 = tf.add(tf.matmul(flat, weights['d1']), biases['d1']) # [128]

    fc1 = tf.nn.relu(fc1) # [128]


    # Dropout

    if training:

        fc1 = tf.nn.dropout(fc1, rate=0.2)


    # Output

    out = tf.add(tf.matmul(fc1, weights['out']), biases['out']) # [10]

    return out


不一樣層的形狀在代碼註釋中給出。從28x28圖像開始,兩個最大池層將大小縮小了兩倍,所以其寬度和高度減少到(28/2)/2=7大小。在用於密集層以前,須要對權重進行扁平化和重塑。因爲在第四個卷積層中有32個濾波器,所以扁平層的第一個形狀尺寸爲7x7x32(這就是爲何使用它來得到weights['d1']的形狀)。


image.png

構建卷積神經網絡圖


接下來構建用於模型訓練的數據流圖。


一個重要的概念是使用佔位符,所以首先使用它們建立卷積神經網絡。不過,還會展現如何在沒有佔位符的狀況下製做模型。


佔位符


咱們須要肯定如何未來自數據迭代器的數據輸入到模型圖中。爲此,能夠製做一個佔位符,指示將一些形狀爲[batch_size, height, width, channels] 的張量輸入conv_net函數。將height (高度)和width (寬度)設置爲28,將channels (通道)設置爲1,並經過將其設置爲None(無),將批次大小保留爲變量。


如今可使用帶有weights (權重)和biases (誤差)字典的Xtrain 佔位符做爲conv_net函數的輸入來獲取logits輸出。


Xtrain = tf.placeholder(tf.float32, shape=(None, 28, 28, 1))

logits = conv_net(Xtrain, weights, biases)


此步驟將容許咱們經過將一個或多個圖像輸入Xtrain 佔位符來訓練模型。


損失


該模型基於一組10個互斥類對圖像進行分類。在訓練過程當中,經過使用softmax將logits轉換爲圖像屬於每一個類的相對機率,而後經過softmax的交叉熵計算損失。


這些均可以用TensorFlow的softmax交叉熵一步完成。測量一批圖像的損失,可使用tf.reduce_mean 獲取批處理的平均損失。


咱們能夠再次爲圖使用ytrain佔位符,該圖將用於提供MNIST標籤。回想一下標籤通過了one-hot編碼,批處理中的每一個圖像都有一個標籤,因此形狀應該是[batch_size, n_classes]。


ytrain = tf.placeholder(tf.float32, shape=(None, n_classes))

loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=logits,

                                                             labels=ytrain))


優化器


該損失用於經過網絡對損失進行反向傳播來更新模型權重和誤差。學習率用於縮小更新,從而防止權重在優化值周圍分散或跳躍。


Adam優化器是由具備動量的Adagrad和RMSProp結合而成,並已證實能夠與卷積神經網絡一塊兒良好運行。這裏我使用學習率爲1e-4的Adam優化器。而後計算梯度,並經過運行最小化方法更新權重。


optimizer = tf.train.AdamOptimizer(1e-4)

train_op = optimizer.minimize(loss)


如今train_op是訓練模型的關鍵,由於它能運行optimizer.minimize。爲了訓練模型,只需將圖像輸入到圖中,而後運行train_op 便可根據損失更新權重。


image.png

訓練模型


準確性


使用測試圖像來測量訓練期間的準確性。對於每一個圖像,執行模型推理,並使用具備最大logit值的類做爲預測。準確性就是正確預測的分數。咱們須要使用整數值而不是one-hot編碼來比較預測與標籤,所以須要使用argmax(這就是test_labels未轉換爲one-hot編碼的緣由,不然必須將其轉換回標籤)。


準確性可以使用TensorFlow的準確性運算符測量,該運算符有兩個輸出。第一個輸出是不更新度量的準確性,第二個輸出是咱們將使用的輸出,它用更新度量返回準確性。


test_predictions = tf.nn.softmax(conv_net(test_images, weights, biases))

acc,acc_op = tf.metrics.accuracy(predictions=tf.argmax(test_predictions,1),

                                 labels=tests_labels )


初始化變量


須要初始化局部變量(臨時變量,如準確性中的total 和count 度量)和全局變量(如模型權重和誤差),以便在會話中使用。


sess.run(tf.global_variables_initializer())

sess.run(tf.local_variables_initializer())


輸入圖像


回想一下,對於每一批,都會建立一組須要輸入到佔位符Xtrain 和ytrain中新的圖像和真值。如上圖所示,能夠從data_batch(數據批次)中得到batch_images  (批次圖像)和batch_labels (批次標籤)。而後,經過建立一個以佔位符爲鍵,數據爲值的字典,將它們輸入佔位符,而後在sess.run()中運行train_op 時將字典傳遞到feed_dict 參數。


batch_images, batch_labels = sess.run(data_batch)

feed_dict = {Xtrain: batch_images, ytrain: batch_labels}

sess.run(train_op, feed_dict=feed_dict)


訓練會話


經過在會話中調用 train_op,圖中的全部步驟都將按上述方式運行。咱們將使用tqdm 查看每次訓練期間訓練的進程。在每次訓練以後,測量並打印出準確性。


from tqdm import tqdm

nepochs = 5


sess = tf.Session()

sess.run(tf.global_variables_initializer())

sess.run(tf.local_variables_initializer())

sess.run(iterator.initializer)


for epoch in range(nepochs):

    for step in tqdm(range(int(len(train_images)/batch_size))):


        # Batched data

        batch_images, batch_labels = sess.run(data_batch) 


        # Train model

        feed_dict = {Xtrain: batch_images, ytrain: batch_labels}

        sess.run(train_op, feed_dict=feed_dict)


    # Test model

    accuracy = sess.run(acc_op)


    print('\nEpoch {} Accuracy: {}'.format(epoch+1, accuracy))


image.png

該模型將繼續用給定的訓練次數進行訓練。通過5次訓練,該模型的準確性約爲0.97。


無佔位符訓練


上面的樣例演示了在圖像中輸入的佔位符,可是能夠將conv_net和batch_labels 中的batch_images直接使用在tf.nn.softmax_cross_entropy_with_logits_v2的labels 參數中,使該圖更加簡潔。而後能夠訓練模型,便沒必要向圖中輸入任何張量,以下所示:


batch_images, batch_labels = iterator.get_next()

logits = conv_net(batch_images, weights, biases, training=True)

loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=logits,

                                                                 labels=batch_labels))

train_op = optimizer.minimize(loss)


sess = tf.Session()

sess.run(tf.global_variables_initializer())

sess.run(tf.local_variables_initializer())

sess.run(iterator.initializer)


for epoch in range(nepochs):


    for step in tqdm(range(int(len(train_images)/batch_size)))

        sess.run(train_op)


    accuracy = sess.run(acc_op)

    print('\nEpoch {} Accuracy: {}'.format(epoch+1, accuracy))


訪問其它節點


能夠經過在會話中單獨或在列表中調用圖中的不一樣節點來訪問它們。若是運行節點列表,則會給出輸出列表。例如,假設但願在訓練期間看到每一個圖像批的損失,這能夠經過在會話中運行train_op列表中的loss來完成。在這裏,將使用tqdm打印每次訓練期間每一個批次的損失。


for epoch in range(nepochs):


    prog_bar = tqdm(range(int(len(train_images)/batch_size)))


    for step in prog_bar:

        _,cost = sess.run([train_op,loss])


        prog_bar.set_description("cost: {}".format(cost))


    accuracy = sess.run(acc_op)


    print('\nEpoch {} Accuracy: {}'.format(epoch+1, accuracy))

 

image.png


image.png

預測


測試預測


使用測試準確性中的test_predictions (測試_預測)來對性能進行可視化,這些性能用於對樣例圖像進行分類。在這裏,顯示前25個測試圖像,並使用整數預測做爲標題。


onehot_predictions = sess.run(test_predictions)

predictions = np.argmax(np.squeeze(onehot_predictions), axis=1)


f, axarr = plt.subplots(5, 5, figsize=(25,25))


for idx in range(25):

    axarr[int(idx/5), idx%5].imshow(np.squeeze(test_images[idx]), cmap='gray')

    axarr[int(idx/5), idx%5].set_title(str(predictions[idx]),fontsize=50) 


image.png


預測新圖像批次


若是要預測一組新圖像,只需在會話中對一批圖像運行 conv_net函數便可進行預測。例如,如下內容可用於獲取測試集中前25個圖像的預測:


predictions = sess.run(tf.argmax(conv_net(test_images[:25], weights, biases), axis=1))


而後能夠像上面同樣將predictions (預測)用於顯示圖像。


預測單一圖像


回想一下,模型指望輸入具備形狀[batch_size, height, width, channels]。這意味着,若是對單個圖像進行預測,則須要擴展圖像的維度,使第一個維度(batch_size)成爲一個單一維度。這裏預測了測試集中的第1000個圖像。


img = test_images[1000]

print(img.shape) # [28, 28, 1]


img = np.expand_dims(img, 0)

print(img.shape) # [1, 28, 28, 1]


prediction = sess.run(tf.argmax(conv_net(img, weights, biases), axis=1))

plt.imshow(np.squeeze(img), cmap='gray')

plt.title(np.squeeze(prediction), fontsize=40)


image.png


預測機率


預測的歸一化機率分佈可使用softmax,按conv_net 模型中的logits來計算。


idx = 1000

img = np.expand_dims(test_images[idx], 0)

plt.imshow(np.squeeze(img), cmap='gray')


# Predict value

prediction = conv_net(img, weights, biases)

probabilities = tf.nn.softmax(prediction)

probs = sess.run(probabilities)

probs = np.squeeze(probs)


# Plot probabilities

plt.figure(dpi=150)

plt.bar(range(10), probs)

for i in range(10):

  plt.text(i-0.3, probs[i]+0.05, s='{:.2f}'.format(probs[i]), size=10)

plt.ylim([0,1.1])

plt.xticks(range(10))

plt.ylabel('Probability')

plt.xlabel('Predicted Number')



image.png


image.png


該模型預測該數字爲9的機率很高。


有些數字對模型來講並非那麼容易預測。例如,這裏是測試集中的另外一個圖像(idx=3062)。


image.png


image.png

 

雖然模型的確預測8是最有可能的類,但它的機率只有0.48,並且其它幾個數字的機率增長了。


如下是一個錯誤的預測樣例(idx=259):


image.png


image.png


該模型錯誤地預測了0,而數字6只是可能性爲第二的預測。


image.png

保存模型


如前所述,只要會話仍然打開,就能夠進行預測。若是會話關閉,通過訓練的權重和誤差將丟失。使用tf.train.Saver能夠將會話和模型圖以及通過訓練的權重和誤差保存爲檢查點。


saver = tf.train.Saver()

saver.save(sess, './model.ckpt')


image.png

完整的代碼樣例


結合上面的信息,下面是一個腳本樣例,用於建立、訓練和保存卷積神經網絡並顯示對手寫數字樣本的預測。


import tensorflow as tf

import numpy as np

from tqdm import tqdm

from keras.datasets import mnist

from keras.utils import to_categorical

import matplotlib.pyplot as plt


# MNIST Dataset

(train_images, train_labels),(test_images, test_labels) = mnist.load_data()

train_images  = np.expand_dims(train_images.astype(np.float32) / 255.0, axis=3)

test_images = np.expand_dims(test_images.astype(np.float32) / 255.0, axis=3)

train_labels = to_categorical(train_labels)


# Training parameters

batch_size = 128

n_epochs = 5

n_classes = 10

learning_rate = 1e-4


# 2D Convolutional Function

def conv2d(x, W, b, strides=1):

    x = tf.nn.conv2d(x, W, strides=[1,1,1,1], padding='SAME')

    x = tf.nn.bias_add(x, b)

    return tf.nn.relu(x)


# Define Weights and Biases

weights = {

    # Convolution Layers

    'c1': tf.get_variable('W1', shape=(3,3,1,16), \

            initializer=tf.contrib.layers.xavier_initializer()),

    'c2': tf.get_variable('W2', shape=(3,3,16,16), \

            initializer=tf.contrib.layers.xavier_initializer()),

    'c3': tf.get_variable('W3', shape=(3,3,16,32), \

            initializer=tf.contrib.layers.xavier_initializer()),

    'c4': tf.get_variable('W4', shape=(3,3,32,32), \

            initializer=tf.contrib.layers.xavier_initializer()),


    # Dense Layers

    'd1': tf.get_variable('W5', shape=(7*7*32,128),

            initializer=tf.contrib.layers.xavier_initializer()),

    'out': tf.get_variable('W6', shape=(128,n_classes),

            initializer=tf.contrib.layers.xavier_initializer()),

}

biases = {

    # Convolution Layers

    'c1': tf.get_variable('B1', shape=(16), initializer=tf.zeros_initializer()),

    'c2': tf.get_variable('B2', shape=(16), initializer=tf.zeros_initializer()),

    'c3': tf.get_variable('B3', shape=(32), initializer=tf.zeros_initializer()),

    'c4': tf.get_variable('B4', shape=(32), initializer=tf.zeros_initializer()),


    # Dense Layers

    'd1': tf.get_variable('B5', shape=(128), initializer=tf.zeros_initializer()),

    'out': tf.get_variable('B6', shape=(n_classes), initializer=tf.zeros_initializer()),

}


# Model Function

def conv_net(data, weights, biases, training=False):

    # Convolution layers

    conv1 = conv2d(data, weights['c1'], biases['c1']) # [28,28,16]

    conv2 = conv2d(conv1, weights['c2'], biases['c2']) # [28,28,16]

    pool1 = tf.nn.max_pool(conv2, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')

    # [14,14,16]


    conv3 = conv2d(pool1, weights['c3'], biases['c3']) # [14,14,32]

    conv4 = conv2d(conv3, weights['c4'], biases['c4']) # [14,14,32]

    pool2 = tf.nn.max_pool(conv4, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')

    # [7,7,32]


    # Flatten

    flat = tf.reshape(pool2, [-1, weights['d1'].get_shape().as_list()[0]])

    # [7*7*32] = [1568]


    # Fully connected layer

    fc1 = tf.add(tf.matmul(flat, weights['d1']), biases['d1']) # [128]

    fc1 = tf.nn.relu(fc1) # [128]


    # Dropout

    if training:

        fc1 = tf.nn.dropout(fc1, rate=0.2)


    # Output

    out = tf.add(tf.matmul(fc1, weights['out']), biases['out']) # [10]

    return out


# Dataflow Graph

dataset = tf.data.Dataset.from_tensor_slices((train_images,train_labels)).repeat().batch(batch_size)

iterator = dataset.make_initializable_iterator()

batch_images, batch_labels = iterator.get_next()

logits = conv_net(batch_images, weights, biases, training=True)

loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=logits, labels=batch_labels))

optimizer = tf.train.AdamOptimizer(learning_rate)

train_op = optimizer.minimize(loss)

test_predictions = tf.nn.softmax(conv_net(test_images, weights, biases))

acc,acc_op = tf.metrics.accuracy(predictions=tf.argmax(test_predictions,1), labels=test_labels)


# Run Session

with tf.Session() as sess:

    # Initialize Variables

    sess.run(tf.global_variables_initializer())

    sess.run(tf.local_variables_initializer())

    sess.run(iterator.initializer)


    # Train the Model

    for epoch in range(n_epochs):

        prog_bar = tqdm(range(int(len(train_images)/batch_size)))

        for step in prog_bar:

            _,cost = sess.run([train_op,loss])

            prog_bar.set_description("cost: {:.3f}".format(cost))

        accuracy = sess.run(acc_op)


        print('\nEpoch {} Accuracy: {:.3f}'.format(epoch+1, accuracy))


    # Show Sample Predictions

    predictions = sess.run(tf.argmax(conv_net(test_images[:25], weights, biases), axis=1))

    f, axarr = plt.subplots(5, 5, figsize=(25,25))

    for idx in range(25):

        axarr[int(idx/5), idx%5].imshow(np.squeeze(test_images[idx]), cmap='gray')

        axarr[int(idx/5), idx%5].set_title(str(predictions[idx]),fontsize=50)


    # Save Model

    saver = tf.train.Saver()

    saver.save(sess, './model.ckpt')



image.png

Keras的等效模型


Keras是一種高階API,能夠經過使用其順序模型API(功能API也是對更復雜的神經網絡的一種選擇)大大簡化上述代碼。


請注意,不須要使用會話。此外,模型權重和誤差不須要在模型外部定義。它們是在添加層時建立的,並且它們的形狀是自動計算的。


from keras.models import Sequential, Model

from keras.layers import Dense, Activation, Dropout, Flatten

from keras.layers import Conv2D, MaxPooling2D, Input

from keras.optimizers import Adam

from keras.datasets import mnist

from keras.utils import to_categorical

import numpy as np


# MNIST Dataset

(train_images, train_labels),(test_images, test_labels) = mnist.load_data()

train_images  = np.expand_dims(train_images.astype(np.float32) / 255.0, axis=3)

test_images = np.expand_dims(test_images.astype(np.float32) / 255.0, axis=3)

train_labels = to_categorical(train_labels)

test_labels = to_categorical(test_labels)


# Training parameters

batch_size = 128

n_epochs = 5

n_classes = 10


# Create the model

model = Sequential()


# Convolution Layers

model.add(Conv2D(16, 3, activation='relu', input_shape=(28, 28, 1), padding='same'))

model.add(Conv2D(16, 3, activation='relu', padding='same'))

model.add(MaxPooling2D(pool_size=2, padding='same'))

model.add(Conv2D(32, 3, activation='relu', padding='same'))

model.add(Conv2D(32, 3, activation='relu', padding='same'))

model.add(MaxPooling2D(pool_size=2, padding='same'))


# Dense Layers

model.add(Flatten())

model.add(Dense(128, activation='relu'))

model.add(Dropout(0.2))

model.add(Dense(n_classes, activation='softmax')) 


# Optimizer

optimizer = Adam(lr=1e-4)


# Compile

model.compile(loss='categorical_crossentropy',

              optimizer=optimizer,

              metrics=['categorical_accuracy'])


# Train model

model.fit(train_images,

          train_labels,

          epochs=n_epochs,

          batch_size=batch_size,

          validation_data=(test_images, test_labels)

          )


# Show Sample Predictions

predictions = model.predict(test_images[:25])

predictions = np.argmax(predictions, axis=1)

f, axarr = plt.subplots(5, 5, figsize=(25,25))

for idx in range(25):

    axarr[int(idx/5), idx%5].imshow(np.squeeze(test_images[idx]), cmap='gray')

    axarr[int(idx/5), idx%5].set_title(str(predictions[idx]),fontsize=50)


# Save Model

model.save_weights("model.h5")



image.png

總結


TensorFlow圖建立了計算數據流用於訓練模型,並且這些會話用於實際訓練中。本文展現了建立圖和在訓練對話中運行圖時使用的不一樣步驟和選項。當大體瞭解瞭如何建立圖並在會話中使用時,開發自定義神經網絡和使用TensorFlow Core來知足特定需求就會變得更容易。


顯而易見,keras更爲簡潔,而且該API有利於測試新的神經網絡。然而,對於新的人工智能開發人員來講,許多步驟多是「黑匣子」,而本教程能夠幫助展現其後臺發生的事情。


image.png

留言 點贊 發個朋友圈

咱們一塊兒分享AI學習與發展的乾貨


編譯組:陳楓、黃璡

相關連接:

https://towardsdatascience.com/guide-to-coding-a-custom-convolutional-neural-network-in-tensorflow-bec694e36ad3


如需轉載,請後臺留言,遵照轉載規範


推薦文章閱讀


ACL2018論文集50篇解讀

EMNLP2017論文集28篇論文解讀

2018年AI三大頂會中國學術成果全連接

ACL2017 論文集:34篇解讀乾貨全在這裏

10篇AAAI2017經典論文回顧


長按識別二維碼可添加關注

讀芯君愛你


圖片

相關文章
相關標籤/搜索