深入研究MNIST
加載MNIST數(shù)據(jù)
如果您正在復(fù)制和粘貼本教程的代碼,請從這里開始,下面這兩行代碼將自動下載并讀取數(shù)據(jù):
from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('MNIST_data', one_hot=True)這里mnist是一個輕量級數(shù)據(jù)集,它將訓(xùn)練、驗證和測試集存儲為NumPy數(shù)組。 它還提供了一個函數(shù)來迭代數(shù)據(jù)minibatches,我們將在下面使用。
啟動TensorFlow InteractiveSession
TensorFlow依靠高效的C ++后端來完成它的計算。 與此后端的連接稱為會話。 TensorFlow程序的常見用法是首先創(chuàng)建一個圖形,然后在會話中啟動它。
在這里,我們使用便利的InteractiveSession類,這使得TensorFlow更加靈活地了解如何構(gòu)建代碼。 它允許您將構(gòu)建計算圖的操作與運行圖的操作交錯。 在IPython等交互式環(huán)境中工作時,這特別方便。 如果您沒有使用InteractiveSession,則應(yīng)在開始會話并啟動圖之前構(gòu)建整個計算圖。
import tensorflow as tf sess = tf.InteractiveSession()計算圖
為了在Python中進(jìn)行有效的數(shù)值計算,我們通常使用像NumPy這樣的庫來執(zhí)行復(fù)雜的操作,例如Python之外的矩陣乘法,使用另一種語言實現(xiàn)的高效代碼來完成這些操作。不幸的是,每一次操作都會返回到Python,這仍然會有很多開銷。如果要在GPU上運行計算或以分布式方式運行計算,則傳輸數(shù)據(jù)的成本很高,所以此開銷尤其糟糕。
TensorFlow也在Python之外進(jìn)行繁重的工作,但是為了避免這種開銷,還需要進(jìn)一步的工作。 TensorFlow不是獨立于Python運行的一個復(fù)雜操作,而是讓我們描述一個完全在Python之外運行的交互操作圖。 (像這樣的方法可以在幾個機器學(xué)習(xí)庫中看到)
因此,Python代碼的作用是構(gòu)建這個外部計算圖,并指定運行圖的每個部分。
建立一個Softmax回歸模型
在本節(jié)中,我們將建立一個單線性層的softmax回歸模型。在下一節(jié)中,我們將擴展到具有多層卷積網(wǎng)絡(luò)的softmax回歸的情況。
占位符
我們通過為輸入圖像和目標(biāo)輸出類創(chuàng)建節(jié)點來開始構(gòu)建計算圖。
x = tf.placeholder(tf.float32, shape=[None, 784]) y_ = tf.placeholder(tf.float32, shape=[None, 10])這里x和y_不是特定的值。相反,它們都是占位符 - 當(dāng)我們要求TensorFlow運行一個計算時,我們會輸入一個值。
輸入圖像x將由浮點數(shù)的二維張量組成。這里我們給它賦予一個[None,784]的形狀,其中784是一個單一的28×28像素MNIST圖像的維度,None表示與batch大小相對應(yīng)的第一維度可以是任意大小。目標(biāo)輸出類別y_也將由二維張量組成,其中每一行是一個唯一的10維向量,指示對應(yīng)的MNIST圖像是哪個數(shù)字(0到9)。
占位符的形狀參數(shù)是可選的,但它允許TensorFlow自動捕捉源自不一致張量形狀的錯誤。
變量
我們現(xiàn)在定義權(quán)重W,并為我們的模型賦予偏差b。我們可以把這些看作是額外的輸入,但是TensorFlow有一個更好的方法來處理它們:Variable(變量)。變量是存在于TensorFlow的計算圖中的一個值。它可以被使用,甚至被計算修改。在機器學(xué)習(xí)應(yīng)用中,通常將模型參數(shù)設(shè)置為變量。
W = tf.Variable(tf.zeros([784,10])) b = tf.Variable(tf.zeros([10]))我們將調(diào)用中的每個參數(shù)的初始值傳遞給tf.Variable。 在這種情況下,我們將W和b初始化為全0的張量。 W是784x10矩陣(因為我們有784個輸入特征和10個輸出),b是10維向量(因為我們有10個數(shù)字)。
在會話中使用變量之前,必須使用該會話進(jìn)行初始化變量。 這一步將已經(jīng)指定的初始值(在這里,張量全零),分配給每個變量。 這可以一次完成所有變量的賦值:
sess.run(tf.global_variables_initializer())預(yù)測類和損失函數(shù)
我們現(xiàn)在可以實現(xiàn)我們的回歸模型。 只需要一行! 我們將向量化的輸入圖像x乘以權(quán)重矩陣W,加上偏差b。
y = tf.matmul(x,W) + b我們可以很容易地指定一個損失函數(shù)。 損失函數(shù)描述模型的預(yù)測值在一個例子上有多差; 在訓(xùn)練所有的例子時,我們盡量減少損失函數(shù)的值。 在這里,我們的損失函數(shù)是目標(biāo)和應(yīng)用于模型預(yù)測的softmax激活函數(shù)之間的交叉熵。 正如在初學(xué)者教程中,我們使用同樣的公式:
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))請注意,tf.nn.softmax_cross_entropy_with_logits在模型的非標(biāo)準(zhǔn)化模型預(yù)測中內(nèi)部應(yīng)用softmax,并在所有類中進(jìn)行求和,tf.reduce_mean取這些和的平均值。
訓(xùn)練模型
現(xiàn)在我們已經(jīng)定義了我們的模型和訓(xùn)練損失函數(shù),接下來使用TensorFlow進(jìn)行訓(xùn)練是很簡單的。 由于TensorFlow知道整個計算圖,因此可以使用自動微分來查找相對于每個變量的損失的梯度。 TensorFlow有多種內(nèi)置的優(yōu)化算法。 對于這個例子,我們將使用最陡的梯度下降,步長為0.5,降低交叉熵。
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)TensorFlow在這一行中實際上是在計算圖中添加新的操作。 這些操作包括計算梯度、計算參數(shù)、更新步驟以及將更新步驟應(yīng)用于參數(shù)。
運行train_step時返回的參數(shù)會用于更新梯度下降。 因此,訓(xùn)練模型可以通過重復(fù)運行train_step來完成。
for _ in range(1000):batch = mnist.train.next_batch(100)train_step.run(feed_dict={x: batch[0], y_: batch[1]})我們在每次訓(xùn)練迭代中加載100個訓(xùn)練樣例。 然后我們運行train_step操作,使用feed_dict將訓(xùn)練樣例中的占位符張量x和y_替換。 請注意,您可以使用feed_dict來替換計算圖中的任何張量 - 它不僅限于占位符。
評估模型
我們的模型有多好?
首先我們要弄清楚我們在哪里預(yù)測了正確的標(biāo)簽。 tf.argmax是一個非常有用的函數(shù),它可以為您提供某個軸上張量的最大輸入索引。 例如,tf.argmax(y,1)是我們模型認(rèn)為對每個輸入最有可能的標(biāo)簽,而tf.argmax(y_,1)是真正的標(biāo)簽。 我們可以使用tf.equal來檢查我們的預(yù)測是否符合事實。
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))這里給出一個真值表。 為了確定什么分?jǐn)?shù)是正確的,我們將布爾值轉(zhuǎn)換為浮點數(shù),然后取平均值。 例如,[True,False,True,True]將變成[1,0,1,1],這將變?yōu)?.75。
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))最后,我們可以評估我們的測試數(shù)據(jù)的準(zhǔn)確性。 這里應(yīng)該是大約92%的準(zhǔn)確率。
print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))構(gòu)建一個多層卷積網(wǎng)絡(luò)
在MNIST上獲得92%的準(zhǔn)確性是不好的。 這幾乎是令人尷尬的壞事。 在這一節(jié)中,我們將解決這個問題,從一個非常簡單的模型跳到一些中等復(fù)雜的問題:一個小的卷積神經(jīng)網(wǎng)絡(luò)。 這將使我們達(dá)到約99.2%的準(zhǔn)確性 - 不是最先進(jìn)的,但是值得學(xué)習(xí)。
下面是一個用TensorBoard創(chuàng)建的關(guān)于我們將要構(gòu)建的模型的圖表:
權(quán)重初始化
要創(chuàng)建這個模型,我們需要創(chuàng)建很多權(quán)重和偏差。 一般應(yīng)該用少量的噪聲初始化權(quán)重,以防止對稱性破壞,并防止0梯度。 由于我們使用的是ReLU神經(jīng)元,為了避免“死神經(jīng)元”,初始化這些神經(jīng)元是一個很好的做法。 在我們構(gòu)建模型的時候不要重復(fù)這樣的操作,而是創(chuàng)建兩個函數(shù)來為我們做這件事。
def weight_variable(shape):initial = tf.truncated_normal(shape, stddev=0.1)return tf.Variable(initial)def bias_variable(shape):initial = tf.constant(0.1, shape=shape)return tf.Variable(initial)卷積和池化
TensorFlow也為卷積和池化操作提供了很大的靈活性。 我們?nèi)绾翁幚磉吔?#xff1f; 我們的步幅是多少? 在這個例子中,我們選擇vanilla版本。 我們使步幅大小為1,并在周圍填充零,以便輸出與輸入大小相同。 我們的pooling是超過2x2的max pooling。 為了保證我們的代碼更清晰,我們也將這些操作抽象為函數(shù)。
def conv2d(x, W):return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')def max_pool_2x2(x):return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1], padding='SAME')tf.nn.conv2d
conv2d(input,filter,strides,padding,use_cudnn_on_gpu=True,data_format='NHWC',name=None )- input:張量。必須是以下類型之一:half,float32。一個四維張量。維度順序根據(jù)data_format的值來解釋,詳見下文。
- filter:張量。必須具有與輸入相同的類型。形狀的四維張量[filter_height,filter_width,in_channels,out_channels]
- strides:整數(shù)列表。一維長度的張量4.輸入每個維度的滑動窗口的步幅。維度順序由data_format的值決定,詳見下文。
- padding:來自“SAME”,“VALID”的字符串。要使用的填充算法的類型。
- use_cudnn_on_gpu:一個可選的布爾。默認(rèn)為True。
- data_format:來自“NHWC”,“NCHW”的可選字符串。默認(rèn)為“NHWC”。指定輸入和輸出數(shù)據(jù)的數(shù)據(jù)格式。使用默認(rèn)格式“NHWC”,數(shù)據(jù)按照[batch,height,width,channels]的順序存儲。或者,格式可以是“NCHW”,數(shù)據(jù)存儲順序為:[batch,channels,height,width]。
- name:操作的名稱(可選)。
tf.nn.max_pool
max_pool(value,ksize,strides,padding,data_format='NHWC',name=None )- value:由data_format指定的格式的4維張量。
- ksize:4元素的1維 int型張量。 輸入張量表示窗口每個維度的大小。
- strides:4元素的1維int型張量。 輸入張量表示滑動窗口每個維度的步幅。
- padding:一個字符串,可以是’VALID’ 或 ‘SAME’。
- data_format:一個字符串。 支持“NHWC”,“NCHW”和“NCHW_VECT_C”。
- name:操作的可選名稱。
第一卷積層
我們現(xiàn)在可以實現(xiàn)我們的第一層。 它將由卷積組成,然后是max pooling。 卷積將為每個5x5 patch計算32個特征。 它的權(quán)重張量是[5,5,1,32]的形狀。 前兩個維度是patch大小,下一個是輸入通道的數(shù)量,最后一個是輸出通道的數(shù)量。 每個輸出通道還會有帶有一個偏差向量的分量。
W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32])為了應(yīng)用該層,我們首先將x重塑為4維張量,第二維和第三維對應(yīng)于圖像的寬度和高度,并且最后一個維度對應(yīng)于色彩通道的數(shù)量。
x_image = tf.reshape(x, [-1, 28, 28, 1])然后,我們將x_image與權(quán)重張量進(jìn)行卷積,加上偏差,應(yīng)用ReLU函數(shù),最后使用max pooling。 max_pool_2x2方法將圖像大小減小到14x14。
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1)第二卷積層
為了建立一個深層網(wǎng)絡(luò),我們堆疊了這種類型的幾個層。 第二層將為每個5x5 patch有64個特征。
W_conv2 = weight_variable([5, 5, 32, 64]) b_conv2 = bias_variable([64])h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2)密集連接層
現(xiàn)在圖像尺寸已經(jīng)減小到7x7,我們添加了一個1024個神經(jīng)元的全連接圖層,以允許在整個圖像上進(jìn)行處理。 我們將pooling層中的張量重塑為一批向量,乘以權(quán)重矩陣,添加一個偏差,并應(yīng)用一個ReLU。
W_fc1 = weight_variable([7 * 7 * 64, 1024]) b_fc1 = bias_variable([1024])h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)Dropout
為了減少過擬合,我們將在讀出層之前應(yīng)用dropout。 我們創(chuàng)建一個占位符,用于在dropout期間保持神經(jīng)元輸出的概率。 這可以讓我們在訓(xùn)練過程中關(guān)閉dropout,并在測試過程中將其關(guān)閉。 TensorFlow的tf.nn.dropout可以自動處理縮放神經(jīng)元輸出和掩蔽它們,所以dropout只是在沒有任何附加縮放的情況下工作。(對于這個小卷積網(wǎng)絡(luò),性能實際上幾乎是相同的,沒有丟失。 dropout對于減少過擬合通常是非常有效的,但是是在訓(xùn)練非常大的神經(jīng)網(wǎng)絡(luò)。)
keep_prob = tf.placeholder(tf.float32) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)tf.nn.dropout
dropout(x,keep_prob,noise_shape=None,seed=None,name=None )- x:浮點張量。
- keep_prob:與x相同類型的張量。 每個元素被保留的概率。
- noise_shape:int32類型的一維張量,表示隨機生成的保留/丟棄標(biāo)志。
- seed:一個Python整數(shù)。 用于創(chuàng)建隨機種子。 有關(guān)行為,請參閱tf.set_random_seed。
- name:此操作的名稱(可選)。
對于概率keep_prob,輸出按1 / keep_prob放大的輸入元素,否則輸出0。
讀出層
最后,我們添加一個圖層,就像上面的一層softmax回歸一樣。
W_fc2 = weight_variable([1024, 10]) b_fc2 = bias_variable([10])y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2訓(xùn)練和評估模型
這個模型有多好? 為了訓(xùn)練和評估,我們將使用與上述簡單的一層SoftMax網(wǎng)絡(luò)幾乎相同的代碼。
不同之處在于:
- 我們將用更復(fù)雜的ADAM優(yōu)化器替代最陡的梯度下降優(yōu)化器。
- 我們將在feed_dict中包含附加參數(shù)keep_prob來控制丟失率。
- 我們將在訓(xùn)練過程中每100次迭代添加一次記錄。
我們也將使用tf.Session而不是tf.InteractiveSession。 這更好地分離了創(chuàng)建圖(模型說明)的過程和評估圖(模型擬合)的過程。 它通常使更清晰的代碼。tf.Session是在一個塊內(nèi)創(chuàng)建的,所以一旦塊退出,它就會被自動銷毀。
運行這個代碼。 請注意,它會進(jìn)行20,000次訓(xùn)練迭代,可能需要一段時間(可能長達(dá)半小時),具體取決于您的處理器。
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv)) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))with tf.Session() as sess:sess.run(tf.global_variables_initializer())for i in range(20000):batch = mnist.train.next_batch(50)if i % 100 == 0:train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0})print('step %d, training accuracy %g' % (i, train_accuracy))train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})print('test accuracy %g' % accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))運行此代碼后的最終測試集精度應(yīng)該約為99.2%。
我們已經(jīng)學(xué)會了如何使用TensorFlow快速,輕松地構(gòu)建,訓(xùn)練和評估相當(dāng)復(fù)雜的深度學(xué)習(xí)模型。
總結(jié)
- 上一篇: 推荐你的最佳睡眠时间
- 下一篇: 成功的诀窍是什么?