文章寫的不清晰請大家原諒QAQ
這篇文章我們將用 CIFAR-10數據集做一個很簡易的圖片分類器。 在 CIFAR-10數據集包含了60,000張圖片。在此數據集中,有10個不同的類別,每個類別中有6,000個圖像。每幅圖像的大小為32 x 32像素。雖然這么小的尺寸通常給人類識別正確的類別帶來了困難,但它實際上是對計算機模型的簡化並且減少了分析圖像所需的計算。
CIFAR-10數據集
我們可以通過輸入模型的大量數字序列將這些圖像輸入到我們的模型中。每個像素由三個浮點數標識,這三個浮點數表示該像素的紅色,綠色和藍色值(RGB值)。所以每個圖像有32 x 32 x 3 = 3,072 個值0.
使用非常大的卷積神經網絡可以實現高質量的結果,你可以在這個連接中學習Rodrigo Benenson’s page
下載CIFAR-10數據集,網址:Python version of the dataset, 並把他安裝在我們分類器代碼所在的文件夾下
先上源代碼
模型的源代碼:
import numpy as np import tensorflow as tf import time import data_helpers beginTime = time.time() batch_size = 100 learning_rate = 0.005 max_steps = 1000 data_sets = data_helpers.load_data() # Define input placeholders images_placeholder = tf.placeholder(tf.float32, shape=[None, 3072]) labels_placeholder = tf.placeholder(tf.int64, shape=[None]) # Define variables (these are the values we want to optimize) weights = tf.Variable(tf.zeros([3072, 10])) biases = tf.Variable(tf.zeros([10])) # Define the classifier's result logits = tf.matmul(images_placeholder, weights) + biases # Define the loss function loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=labels_placeholder)) # Define the training operation train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss) # Operation comparing prediction with true label correct_prediction = tf.equal(tf.argmax(logits, 1), labels_placeholder) # Operation calculating the accuracy of our predictions accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) with tf.Session() as sess: # Initialize variables sess.run(tf.global_variables_initializer()) # Repeat max_steps times for i in range(max_steps): # Generate input data batch indices = np.random.choice(data_sets['images_train'].shape[0], batch_size) images_batch = data_sets['images_train'][indices] labels_batch = data_sets['labels_train'][indices] # Periodically print out the model's current accuracy if i % 100 == 0: train_accuracy = sess.run(accuracy, feed_dict={ images_placeholder: images_batch, labels_placeholder: labels_batch}) print('Step {:5d}: training accuracy {:g}'.format(i, train_accuracy)) # Perform a single training step sess.run(train_step, feed_dict={images_placeholder: images_batch, labels_placeholder: labels_batch}) # After finishing the training, evaluate on the test set test_accuracy = sess.run(accuracy, feed_dict={ images_placeholder: data_sets['images_test'], labels_placeholder: data_sets['labels_test']}) print('Test accuracy {:g}'.format(test_accuracy)) endTime = time.time() print('Total time: {:5.2f}s'.format(endTime - beginTime))
處理數據集的代碼
import numpy as np import pickle import sys def load_CIFAR10_batch(filename): '''load data from single CIFAR-10 file''' with open(filename, 'rb') as f: if sys.version_info[0] < 3: dict = pickle.load(f) else: dict = pickle.load(f, encoding='latin1') x = dict['data'] y = dict['labels'] x = x.astype(float) y = np.array(y) return x, y def load_data(): '''load all CIFAR-10 data and merge training batches''' xs = [] ys = [] for i in range(1, 6): filename = 'cifar-10-batches-py/data_batch_' + str(i) X, Y = load_CIFAR10_batch(filename) xs.append(X) ys.append(Y) x_train = np.concatenate(xs) y_train = np.concatenate(ys) del xs, ys x_test, y_test = load_CIFAR10_batch('cifar-10-batches-py/test_batch') classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'] # Normalize Data mean_image = np.mean(x_train, axis=0) x_train -= mean_image x_test -= mean_image data_dict = { 'images_train': x_train, 'labels_train': y_train, 'images_test': x_test, 'labels_test': y_test, 'classes': classes } return data_dict def reshape_data(data_dict): im_tr = np.array(data_dict['images_train']) im_tr = np.reshape(im_tr, (-1, 3, 32, 32)) im_tr = np.transpose(im_tr, (0, 2, 3, 1)) data_dict['images_train'] = im_tr im_te = np.array(data_dict['images_test']) im_te = np.reshape(im_te, (-1, 3, 32, 32)) im_te = np.transpose(im_te, (0, 2, 3, 1)) data_dict['images_test'] = im_te return data_dict def gen_batch(data, batch_size, num_iter): data = np.array(data) index = len(data) for i in range(num_iter): index += batch_size if (index + batch_size > len(data)): index = 0 shuffled_indices = np.random.permutation(np.arange(len(data))) data = data[shuffled_indices] yield data[index:index + batch_size] def main(): data_sets = load_data() print(data_sets['images_train'].shape) print(data_sets['labels_train'].shape) print(data_sets['images_test'].shape) print(data_sets['labels_test'].shape) if __name__ == '__main__': main()
首先我們導入了tensorflow numpy time 以及自己寫的data_help包
time是為了計算整個代碼的運行時間。 data_help是將數據集做成我們訓練用的數據結構
data_help中的load_data()會把60000張的CIFAR數據集分成兩塊:500000張的訓練集和100000張的測試集,具體來說他會返回這樣的一個包含如下內容的字典
images_train
: 訓練集。一個500000張 包含3072(32x32像素點x3顏色通道)值labels_train
: 訓練集的50,000個標簽(每個標簽在0到9之間,代表訓練圖像所屬的10個類別中的哪一個)images_test
: 測試集(10,000 by 3,072)labels_test
: 測試集的10,000個標簽classes
: 10個文本標簽,用於將數字類值轉換為單詞(例如0代表'plane',1代表'car')
然后我們就可以開始建立我們的模型了
先頂兩個tensroflow的占位符 這些占位符不包含任何數據,但僅指定輸入數據的類型和形狀:
images_placeholder = tf.placeholder(tf.float32, shape=[None, 3072])
labels_placeholder = tf.placeholder(tf.int64, shape=[None]) #值得注意的是,這邊的Dtype是int 還有shape是沒有維度的(一維的)
然后我們定義偏置和權重
weights = tf.Variable(tf.zeros([3072, 10]))
biases = tf.Variable(tf.zeros([10]))
我們的輸入由3,072個浮點數組成,但我們尋找的輸出是10個不同的整數值之一,代表一個類別。我們如何從3,072個值到單個值?
我們采用的簡單方法是分別查看每個像素。對於每個像素和每個可能的類別,我們想知道該像素的顏色是增加還是減少屬於特定類別的概率。例如,如果第一個像素是紅色 - 並且如果汽車的圖像通常具有紅色的第一個像素,那么我們希望汽車類別的分數增加。我們通過將紅色通道值乘以正數並將其添加到汽車類別得分來實現此目的。
同樣,如果馬圖像在位置1很少有紅色像素,我們希望該分數降低。這意味着乘以小數或負數並將結果添加到馬匹得分中。對於10個類別中的每個類別,我們在每個像素上重復此步驟,然后總結所有3,072個值以獲得單個總分。這是我們的3,072像素值的總和,由該類別的3,072參數權重加權。這里的最終結果是我們將得到10個分數 - 每個類別一個。最高分給我們分類。
使用矩陣,我們可以大大簡化用於將像素值與權重值相乘並總結結果的方案。我們用3,072維向量表示單個圖像。如果我們將此向量乘以3,072 x 10權重矩陣,則結果是一個10維矩陣,其中包含我們想要的加權和。
3,072 x 10矩陣中的實際值是模型參數。但是,如果它們是隨機的並且毫無意義,那么輸出也將是。在這里,我們可以看到訓練數據的值,它准備模型以最終自己確定參數值。
在上面的兩行代碼中,我們通知TensorFlow 3,072 x 10加權參數矩陣 - 所有這些參數在開始時都具有初始值0。我們還定義了第二個參數:包含偏差的10維數組。偏差不直接與圖像數據相互作用,而是加到加權和 - 每個分數的起點。想象一個全黑圖像:所有像素值都是0,因此它的所有類別得分都是0(與權重矩陣中的值無關)。偏見允許我們從非零類別分數開始。
訓練方案的工作原理如下:首先,我們輸入訓練數據並讓模型使用當前參數值進行預測。使用正確的類別對該預測進行比較,並且該比較的數值結果稱為損失。損失值越小,類別預測越接近正確的類別 - 反之亦然。目的是盡量減少損失。但在我們看一下損失最小化之前,讓我們來看看如何計算損失。
# Define loss function
loss=tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits,
labels_placeholder))
TensorFlow通過提供處理所有這些的功能來處理我們的所有細節。然后,我們可以將logits中包含的模型預測與labels_placeholder(正確的類別標簽)進行比較。 sparse_softmax_cross_entropy_with_logits()的輸出是每個圖像的損失值。最后,我們計算所有輸入圖像的平均損失值。
tf.nn.sparse_softmax_cross_entropy_with_logits()這個函數的功能就是計算labels和logits之間的交叉熵(cross entropy)。
import tensorflow as tf input_data = tf.Variable([[0.2, 0.1, 0.9], [0.3, 0.4, 0.6]], dtype=tf.float32) output = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=input_data, labels=[0, 2]) with tf.Session() as sess: init = tf.global_variables_initializer() sess.run(init) print(sess.run(output)) # [ 1.36573195 0.93983102]
這邊順便介紹一下tf.nn.softmax_cross_entopy_with_logits()
tf.nn.softmax_cross_entropy_with_logits( _sentinel=None, labels=None, logits=None, dim=-1, name=None )
第一個參數基本不用。此處不說明。
第二個參數label的含義就是一個分類標簽,所不同的是,這個label是分類的概率,比如說[0.2,0.3,0.5],labels的每一行必須是一個概率分布。
現在來說明第三個參數logits,logit本身就是是一種函數,它把某個概率p從[0,1]映射到[-inf,+inf](即正負無窮區間)。這個函數的形式化描述為:logit=ln(p/(1-p))。
我們可以把logist理解為原生態的、未經縮放的,可視為一種未歸一化的log 概率,如是[4, 1, -2]
於是,Softmax的工作則是,它把一個系列數從[-inf, +inf] 映射到[0,1],除此之外,它還把所有參與映射的值累計之和等於1,變成諸如[0.95, 0.05, 0]的概率向量。這樣一來,經過Softmax加工的數據可以當做概率來用。
也就是說,logits是作為softmax的輸入。經過softmax的加工,就變成“歸一化”的概率(設為q),然后和labels代表的概率分布(設為q),於是,整個函數的功能就是前面的計算labels(概率分布p)和logits(概率分布q)之間的交叉熵
(1)如果labels的每一行是one-hot表示,也就是只有一個地方為1(或者說100%),其他地方為0(或者說0%),還可以使用tf.sparse_softmax_cross_entropy_with_logits()。之所以用100%和0%描述,就是讓它看起來像一個概率分布。
(2)tf.nn.softmax_cross_entropy_with_logits()函數已經過時 (deprecated),它在TensorFlow未來的版本中將被去除。取而代之的是
tf.nn.softmax_cross_entropy_with_logits_v2()。
(3)參數labels,logits必須有相同的形狀 [batch_size, num_classes] 和相同的類型(float16, float32, float64)中的一種,否則交叉熵無法計算。
(4)tf.nn.softmax_cross_entropy_with_logits 函數內部的 logits 不能進行縮放,因為在這個工作會在改函數內部進行(注意函數名稱中的 softmax ,它負責完成原始數據的歸一化),如果 logits 進行了縮放,那么反而會影響計算正確性。
-------------------------------------------------------------------------------------------------------------------------------------------
最后,我們計算所有輸入圖像的平均損失值。
# Define training operation
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)
如何改變參數值以減少損失? TensorFlow在這里發光,使用一種稱為自動微分的技術,它根據參數值計算損耗的梯度。它計算每個參數對總體損失的影響,以及減少或增加少量用於減少損失的程度。它試圖通過遞歸調整所有參數值來提高准確性。完成此步驟后,將使用下一個圖像組重新啟動該過程。
TensorFlow包含各種優化技術,用於將梯度信息轉換為參數的更新。對於本教程中的目的,我們選擇簡單的梯度下降選項,該選項僅檢查模型的當前狀態以確定如何更新參數,而不考慮先前的參數值。
對輸入圖像進行分類,將預測與正確的類別進行比較,計算損失以及調整參數值的過程重復了很多次。計算持續時間和成本會隨着更大,更復雜的模型而迅速升級,但我們這里的簡單模型不需要太多耐心或高性能設備就能看到有意義的結果。
我們代碼中的下兩行(下面)采取精度測量。沿維度1的logg的argmax返回具有最高分數的類別的索引,這是類別標簽預測。這些標簽通過tf.equal()與正確的類別類別標簽進行比較,后者返回一個布爾值向量 - 它被轉換為浮點值(0或1),其平均值是正確預測圖像的分數。
# Operation comparing prediction with true label
correct_prediction = tf.equal(tf.argmax(logits, 1), labels_placeholder)
# Operation calculating the accuracy of our predictions
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
現在我們已經定義了TensorFlow圖,我們可以運行它。該圖可在sess變量中訪問(見下文)。我們立即初始化之前創建的變量。現在,變量定義初始值已分配給變量。
迭代訓練過程開始並重復max_steps次。
# Run the TensorFlow graph
with tf.Session() as sess:
# Initialize variables
sess.run(tf.initialize_all_variables())
# Repeat max_steps times
for i in range(max_steps):
接下來的幾行代碼隨機從訓練數據中選擇一些圖像:
# Generate batch of input data
indices = np.random.choice(data_sets['images_train'].shape[0], batch_size)
images_batch = data_sets['images_train'][indices]
labels_batch = data_sets['labels_train'][indices]
上面的第一行代碼選擇0和訓練集大小之間的batch_size隨機索引。然后通過選擇這些索引處的圖像和類別標簽來構建批次。
來自訓練數據的結果圖像和類別組稱為批次。批量大小表示執行參數更新步驟的頻率。首先,我們平均特定批次中所有圖像的損失,然后通過梯度下降更新參數。
如果不是在批處理后停止並對訓練集中的所有圖像進行分類,我們將能夠計算真正的平均損失和真正的梯度而不是使用批處理時的估計。但是每個參數更新步驟需要更多的計算。在另一個極端,我們可以將批量大小設置為1,並在每個圖像后執行參數更新。這將導致更頻繁的更新,但更新將更加不穩定,並且往往不會朝着正確的方向前進。通常,在這兩個極端之間的某種方法可以最快地改善結果。通常最好選擇盡可能大的批量大小,同時仍然能夠將所有變量和中間結果放入內存中。
每100次迭代,檢查訓練數據批次的當前准確度。
# Periodically print out the model's current accuracy
if i % 100 == 0:
train_accuracy = sess.run(accuracy, feed_dict={
images_placeholder: images_batch, labels_placeholder: labels_batch})
print('Step {:5d}: training accuracy {:g}'.format(i, train_accuracy))
這是訓練循環中最重要的一行,我們建議模型執行單個訓練步驟:
# Perform the training step
sess.run(train_step, feed_dict={images_placeholder: images_batch,
labels_placeholder: labels_batch})
已經在TensorFlow圖形定義中提供了所有數據。 TensorFlow知道梯度下降更新取決於損失的值,而損失的值又取決於logits,后者取決於權重,偏差和實際輸入批次。
現在只需將批量訓練數據輸入模型,這是通過提供一個飼料字典來完成的,其中當前的訓練數據批次被分配給上面定義的占位符。
培訓結束后,我們轉而在測試集上運行模型。由於這是模型第一次遇到測試集,因此圖像對模型來說是全新的。
記住,目標是評估訓練有素的模型處理未知數據的能力
# After finishing the training, evaluate on the test set
test_accuracy = sess.run(accuracy, feed_dict={
images_placeholder: data_sets['images_test'],
labels_placeholder: data_sets['labels_test']})
print('Test accuracy {:g}'.format(test_accuracy))
最后一行打印了培訓和運行模型的持續時間。
endTime = time.time()
print('Total time: {:5.2f}s'.format(endTime - beginTime))