想學TensorFlow?先從低階API開始吧~某種程度而言,它可以幫助咱們更好地理解Tensorflow,更加靈活地控制訓練過程。本文演示瞭如何使用低階TensorFlow Core 搭建卷積神經網絡(ConvNet)模型,並演示了使用TensorFlow編寫自定義代碼的方法。
git
對不少開發人員來講,神經網絡就像一個「黑匣子」, 而TensorFlow Core的應用,則將咱們帶上了對深度神經網絡後臺「黑幕」的探索進程。數組
本文將MNIST手寫數字數據集加載到數據迭代器,使用圖和會話,搭建新的卷積神經網絡體系結構,使用不一樣選項訓練模型,做出預測,保存訓練模型。同時,也會提供完整的代碼以及Keras中的等效模型,以便使用Keras的用戶進行直接比較,加深瞭解,同時高階API在搭建神經網絡方面的強大功能也能獲得展現。網絡
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()
因爲標籤是整數值(例如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)
圖和會話
用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()
在第一批中,第一個圖像是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()
由於會話已關閉,而且建立了新會話,因此數據迭代器開始從新啓動,並再次顯示相同的圖像。
使用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
卷積神經網絡模型
下面將演示如何使用TensorFlow構建下圖所示的基本卷積神經網絡模型:
該體系結構有四個卷積層。前兩層有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']的形狀)。
構建卷積神經網絡圖
接下來構建用於模型訓練的數據流圖。
一個重要的概念是使用佔位符,所以首先使用它們建立卷積神經網絡。不過,還會展現如何在沒有佔位符的狀況下製做模型。
佔位符
咱們須要肯定如何未來自數據迭代器的數據輸入到模型圖中。爲此,能夠製做一個佔位符,指示將一些形狀爲[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 便可根據損失更新權重。
訓練模型
準確性
使用測試圖像來測量訓練期間的準確性。對於每一個圖像,執行模型推理,並使用具備最大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))
該模型將繼續用給定的訓練次數進行訓練。通過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))
預測
測試預測
使用測試準確性中的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)
預測新圖像批次
若是要預測一組新圖像,只需在會話中對一批圖像運行 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)
預測機率
預測的歸一化機率分佈可使用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')
該模型預測該數字爲9的機率很高。
有些數字對模型來講並非那麼容易預測。例如,這裏是測試集中的另外一個圖像(idx=3062)。
雖然模型的確預測8是最有可能的類,但它的機率只有0.48,並且其它幾個數字的機率增長了。
如下是一個錯誤的預測樣例(idx=259):
該模型錯誤地預測了0,而數字6只是可能性爲第二的預測。
保存模型
如前所述,只要會話仍然打開,就能夠進行預測。若是會話關閉,通過訓練的權重和誤差將丟失。使用tf.train.Saver能夠將會話和模型圖以及通過訓練的權重和誤差保存爲檢查點。
saver = tf.train.Saver()
saver.save(sess, './model.ckpt')
完整的代碼樣例
結合上面的信息,下面是一個腳本樣例,用於建立、訓練和保存卷積神經網絡並顯示對手寫數字樣本的預測。
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')
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")
總結
TensorFlow圖建立了計算數據流用於訓練模型,並且這些會話用於實際訓練中。本文展現了建立圖和在訓練對話中運行圖時使用的不一樣步驟和選項。當大體瞭解瞭如何建立圖並在會話中使用時,開發自定義神經網絡和使用TensorFlow Core來知足特定需求就會變得更容易。
顯而易見,keras更爲簡潔,而且該API有利於測試新的神經網絡。然而,對於新的人工智能開發人員來講,許多步驟多是「黑匣子」,而本教程能夠幫助展現其後臺發生的事情。
留言 點贊 發個朋友圈
咱們一塊兒分享AI學習與發展的乾貨
編譯組:陳楓、黃璡
相關連接:
https://towardsdatascience.com/guide-to-coding-a-custom-convolutional-neural-network-in-tensorflow-bec694e36ad3
如需轉載,請後臺留言,遵照轉載規範
推薦文章閱讀
長按識別二維碼可添加關注
讀芯君愛你