1. 程式人生 > >cifar-10 分類 tensorflow 程式碼

cifar-10 分類 tensorflow 程式碼

看了黃文堅、唐源的《TensorFlow實戰》對mnist分類的cnn教程,然後上網搜了下,發現挺多博主貼了對mnist分類的tensorflow程式碼,我想在同樣框架下測試cifar-10分類效果,折騰了好幾天,匯入資料、資料預處理等,終於把程式碼寫出來了,寫個部落格總結下,畢竟也是花了很多精力改了一堆bug的成果。官網上也有cifar-10分類的官方程式碼,模組化很棒,封裝很好,然而。。。一大堆的def,看的很難受,所以我基於最基本的框架匯入cifar資料,寫了一個簡單易懂的程式碼,執行測試cnn效果。
本人初入深度學習,程式碼能力差。本部落格也只適合初學者看,學業不精,請大家不吝賜教。

cifar-10分類資料集為60000張32 * 32的彩色圖片,總共有10個類別,其中50000張訓練集,10000張測試集,官網地址http://www.cs.toronto.edu/~kriz/cifar.html,提供資料集下載
資料集中圖片諸如以下
這裡寫圖片描述
資料集下載在這裡
這裡寫圖片描述

這裡重點說明資料集的儲存格式。下載解壓後為5個batch(圖中data_batch_1,2,3,4,5)的訓練集,1個batch(圖中test_batch)的測試集。每個batch中10000張圖片。
這裡寫圖片描述
每個檔案中資料儲存格式為dict字典,鍵值為b’data’的為圖片資料,是一個10000 * 3072(32 * 32 * 3)的numpy向量,10000表示圖片張數,3072中前1024個表示Red通道資料,中間1024個表示Green通道資料,最後1024個表示Blue通道資料,資料範圍是0-255,表示畫素點灰度。鍵值為b’labels’表示對應的標籤,是一個長度為10000的list,資料範圍是0-9,分別表示10個類別。
另外要說明的是卷積濾波器卷積的是32 * 32 * 3格式的資料,32 * 32代表圖片一個通道格式,3表示RGB 3個通道,然而依據其資料表示格式,在 reshape 3072維度的向量的時候必須首先reshape成3 * 32 * 32格式的向量,否則會破壞圖片原本格式,怎麼辦呢,轉置!類似於矩陣的轉置,三維向量也有轉置,tensorflow提供transpose方法對三維向量作轉置。

輸入圖片資料要做歸一化處理,使其資料範圍為0-1之間,輸出標籤資料要做one_hot處理,比如3表示為[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]。
下面貼出程式碼,在win7下用python3.5執行的,模型用的黃文堅、唐源的《TensorFlow實戰》中的分類mnist的cnn引數。以100條資料為1個batch,迭代一萬次在測試集上準確率為66.27%,迭代三萬次準確率約為71%。理論上來說迭代10W次準確率能上80%,然而網路過於簡單,往往迭代到一定次數的時候會出現loss nan。可能是由於步長過大的原因,不僅如此,網路的其他地方,比如對權重係數進行L2正則化,作滑動平均,學習率衰減等等,還可以繼續優化,最近沒有時間調這些超引數,而且渣渣機子跑10W次要20h的樣子,實在是跑不動,網路程式碼先就這樣吧,有空了或者裝置好了再繼續優化這個網路。

import tensorflow as tf
import pickle
import numpy as np
import pandas as pd

train_data = {b'data':[], b'labels':[]} #兩個items都是list形式
# 5*10000的訓練資料和1*10000的測試資料,資料為dict形式,train_data[b'data']為10000 * 3072的numpy向量
# 3072個數字表示圖片特徵,前1024個表示紅色通道,中間1024表示綠色通道,最後1024表示藍色通道
# train[b'labels']為長度為10000的list,每一個list數字對應以上上3072維的一個特徵

# 載入訓練資料
for i in range(5):
    with open("data/CIFAR-10/cifar-10-batches-py/data_batch_" + str(i + 1), mode='rb') as file:
        data = pickle.load(file, encoding='bytes')
        train_data[b'data'] += list(data[b'data'])
        train_data[b'labels'] += data[b'labels']

# 載入測試資料
with open("data/CIFAR-10/cifar-10-batches-py/test_batch", mode='rb') as file:
    test_data = pickle.load(file, encoding='bytes')

# 定義一些變數
NUM_LABLES = 10 # 分類結果為10類
FC_SIZE = 512   # 全連線隱藏層節點個數
BATCH_SIZE = 100 # 每次訓練batch數
lamda = 0.004   # 正則化係數,這裡添加了正則化但是沒有使用

sess = tf.InteractiveSession()

# 卷積層權重初始化,隨機初始化均值為0,方差為0.1
def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev = 0.1)
    return tf.Variable(initial)

# 卷積層偏置初始化為常數0.1
def bias_variable(shape):
    initial = tf.constant(0.1, shape = shape)
    return tf.Variable(initial)

# 定義卷積操作,卷積步長為1. padding = 'SAME' 表示全0填充
def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides = [1, 1, 1, 1], padding = 'SAME')

# 定義最大池化操作,尺寸為2,步長為2,全0填充
def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize = [1, 2, 2, 1],strides = [1, 2, 2, 1], padding = 'SAME')

# 對輸入進行佔位操作,輸入為BATCH*3072向量,輸出為BATCH*10向量
x = tf.placeholder(tf.float32, [None, 3072])
y_ = tf.placeholder(tf.float32, [None, NUM_LABLES])
# 對輸入進行reshape,轉換成3*32*32格式
x_image = tf.reshape(x, [-1, 3, 32, 32])
# 轉置操作,轉換成濾波器做卷積所需格式:32*32*3,32*32為其二維卷積操作維度
x_image = tf.transpose(x_image, [0, 2, 3, 1])

# 第一層卷積,濾波器引數5*5*3, 32個
W_conv1 = weight_variable([5, 5, 3, 32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # 卷積
h_pool1 = max_pool_2x2(h_conv1)  # 池化

# 第二層卷積,濾波器引數5 * 5 * 32, 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)

# 將8 * 8 * 64 三維向量拉直成一行向量
h_pool2_flat = tf.reshape(h_pool2, [-1, 8 * 8 * 64])

# 第一層全連線
W_fc1 = weight_variable([8 * 8 * 64, FC_SIZE])
b_fc1 = bias_variable([FC_SIZE])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
# 對隱藏層使用dropout
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

# 第二層全連線
W_fc2 = weight_variable([FC_SIZE, NUM_LABLES])
b_fc2 = bias_variable([NUM_LABLES])
y = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

w1_loss = lamda * tf.nn.l2_loss(W_fc1)  # 對W_fc1使用L2正則化
w2_loss = lamda * tf.nn.l2_loss(W_fc2)  # 對W_fc2使用L2正則化
# 交叉熵損失
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices = [1]))
# 總損失
loss = w1_loss + w2_loss + cross_entropy
# 用AdamOptimizer優化器訓練
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

# 計算準確率
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) #tf.cast將資料轉換成指定型別

# 開始訓練
# tf.global_variables_initializer().run()
sess.run(tf.initialize_all_variables())

# 對資料範圍為0-255的訓練資料做歸一化處理使其範圍為0-1,並將list轉成numpy向量
x_train = np.array(train_data[b'data']) / 255
# 將訓練輸出標籤變成one_hot形式並將list轉成numpy向量
y_train = np.array(pd.get_dummies(train_data[b'labels']))

# 對資料範圍為0-255的測試資料做歸一化處理使其範圍為0-1,並將list轉成numpy向量
x_test = test_data[b'data'] / 255
# 將測試輸出標籤變成one_hot形式並將list轉成numpy向量
y_test = np.array(pd.get_dummies(test_data[b'labels']))

# 訓練
for i in range(10000):
    # 100條資料為1個batch,輪流訓練
    start = i * BATCH_SIZE % 50000
    train_step.run(feed_dict = {x: x_train[start: start + BATCH_SIZE],
                                    y_: y_train[start: start + BATCH_SIZE], keep_prob: 0.5})
    # 每迭代100次在前200條個測試集上測試訓練效果
    if i % 100 == 0:
        # 測試準確率
        train_accuracy = accuracy.eval(feed_dict={x: x_test[0: 200],
                                                  y_: y_test[0: 200], keep_prob: 1.0})
        # 該次訓練的損失
        loss_value = cross_entropy.eval(feed_dict = {x: x_train[start: start + BATCH_SIZE],
                                    y_: y_train[start: start + BATCH_SIZE], keep_prob: 0.5})
        print("step %d, trainning accuracy, %g loss %g" % (i, train_accuracy, loss_value))

#測試
test_accuracy = accuracy.eval(feed_dict = {x: x_test, y_: y_test, keep_prob: 1.0})
print("test accuracy %g" % test_accuracy)