使用 tensorflow1.14, 數據集 cifar10
代碼
初始化
import tensorflow as tf
import pickle as pk
import numpy as np
import os
CIFAR_DIR = './cifar-10-batches-py'
數據集一覽
data_batch_1 ~ 5 爲訓練數據集, test_batch 爲測試集
os.listdir(CIFAR_DIR)
運行結果
['batches.meta',
'data_batch_1',
'data_batch_2',
'data_batch_3',
'data_batch_4',
'data_batch_5',
'readme.html',
'test_batch']
看看圖片的shape, 標籤等東西
with open(os.path.join(CIFAR_DIR, "data_batch_1"), "rb") as f:
data = pk.load(f, encoding='bytes') # load 默認使用ascii解碼, 這裏要用二進制來
print(type(data)) # <class 'dict'>
print(data.keys()) # dict_keys([b'batch_label', b'labels', b'data', b'filenames'])
print(type(data[b'batch_label'])) # <class 'bytes'>
print(type(data[b'labels'])) # <class 'list'>
print(type(data[b'data'])) # <class 'numpy.ndarray'>
print(type(data[b'filenames'])) # <class 'list'>
print("----------------------")
print(data[b'batch_label']) # b'training batch 1 of 5'
print(data[b'labels'][0:2]) # [6, 9]
print(data[b'data'].shape) # 10000張圖片, 32*32*3圖片
print(data[b'data'][0:2])
print(data[b'filenames'][0:2]) # [b'leptodactylus_pentadactylus_s_000004.png', b'camion_s_000148.png']
運行結果
<class 'dict'>
dict_keys([b'batch_label', b'labels', b'data', b'filenames'])
<class 'bytes'>
<class 'list'>
<class 'numpy.ndarray'>
<class 'list'>
----------------------
b'training batch 1 of 5'
[6, 9]
(10000, 3072)
[[ 59 43 50 ... 140 84 72]
[154 126 105 ... 139 142 144]]
[b'leptodactylus_pentadactylus_s_000004.png', b'camion_s_000148.png']
打開一張圖片看看, 像素有點低…
img_arr = data[b'data'][6666]
img_arr = img_arr.reshape((3, 32, 32)) # 一張圖片3072個像素, 前32*32是R層, 然後依次是G, B層, 所以得先分三層, 再分成32*32
img_arr = img_arr.transpose((1, 2, 0)) # 調換位置成 32 32 3 的格式, 轉成這樣後一個32*3就能代表一行的像素, 32行就是一張圖片
import matplotlib.pyplot as plt
from matplotlib.pyplot import imshow
%matplotlib inline
plt.imshow(img_arr)
數據預處理
- 定義一個數據預處理的類
- 設置訓練數據集
也可以使用 tensorflow.Dataset 來實現
def load_data(filename):
"""讀文件數據"""
with open(filename, 'rb') as f:
data = pk.load(f, encoding='bytes')
return data[b'data'], data[b'labels']
class CifarData:
"""對Cifar10的數據進行處理"""
def __init__(self, filenames, need_shuffle):
"""
filename: 訓練或者測試數據集的名稱
need_shuffle: 數據過完一遍之後(一個batch之後)是否需要打亂. 測試數據集不需要打亂
"""
all_data = [] # 存儲一個個圖片向量
all_labels = [] # 存儲圖片標籤
for filename in filenames:
data, labels = load_data(filename)
for item, label in zip(data, labels):
if label in [0, 1]: # 做二分類任務, 只取0, 1兩個標籤的圖
all_data.append(item) # 把label是0或1的圖片加入
all_labels.append(label)
self._data = np.vstack(all_data) # 將all_data變成 圖片數量*3072 的矩陣
# self._data = self._data / 255
self._data = self._data / 127.5 - 1 # 歸一化, 範圍調整在-1, 1之間
print(self._data.shape) # test
self._labels = np.hstack(all_labels) # 變成一長行
print(self._labels.shape) # test
self._num_examples = self._data.shape[0] # 圖片數量
self._need_shuffle = need_shuffle # 訓練集需要打亂, 測試不需要, 打亂增強泛化能力
self._indicator = 0 # 存儲當前的位置: 一個一個batch送到計算圖去運算, _indicator用來指明當前遍歷到的位置
if self._need_shuffle:
self._shuffle_data()
def _shuffle_data(self):
"""打亂"""
p = np.random.permutation(self._num_examples) # 得到一個 0~_num_examples 的混亂序列
self._data = self._data[p] # 把數據按照一個混亂的序列去排, 達到打亂的效果
self._labels = self._labels[p] # 做跟 _data 一樣的打亂
def next_batch(self, batch_size): # 批次(樣本)數目
"""return batch_size examples as a batch"""
if batch_size > self._num_examples: # 如果batch_size比整個數據集都大
raise Exception("batch size is larger than all examples")
end_indicator = self._indicator + batch_size # batch的結束位置
if end_indicator > self._num_examples:
if self._need_shuffle: # 允許打亂, 則複用之前的數據
self._shuffle_data()
self._indicator = 0
end_indicator = batch_size # 0 + batch_size
else: # 沒有數據了
raise Exception("have no more examples. if this is the first batch, then the batch size is lager than all examples. ")
batch_data = self._data[self._indicator: end_indicator] # batch大小的數據
batch_labels = self._labels[self._indicator: end_indicator] # 對應的label
# self._indicator += batch_size
self._indicator = end_indicator # 維護indicator的值
return batch_data, batch_labels
train_filenames = [os.path.join(CIFAR_DIR, "data_batch_%d" % i) for i in range(1, 6)]
test_filenames = [os.path.join(CIFAR_DIR, "test_batch")]
train_data = CifarData(train_filenames, need_shuffle=True)
test_data = CifarData(test_filenames, need_shuffle=False) # 測試機不需要打亂
############## test ###############
batch_data, batch_labels = train_data.next_batch(10)
print(batch_data.shape)
print(batch_data)
print(batch_labels)
運行結果
(10000, 3072) # 訓練集數據
(10000,) # 訓練集標籤
(2000, 3072) # 測試集數據
(2000,) # 測試集標籤
(10, 3072) # 一個batch10張圖
[[ 1. 1. 1. ... 1. 1.
1. ]
[-0.35686275 -0.43529412 -0.42745098 ... -0.03529412 0.12156863
0.12156863]
[ 0.09019608 0.01960784 0.12156863 ... -0.82745098 -0.85882353
-0.54509804]
...
[ 0.05098039 0.15294118 0.10588235 ... 0.33333333 0.31764706
0.30196078]
[ 0.08235294 -0.12941176 -0.12156863 ... 0.27843137 0.24705882
0.23921569]
[ 0.5372549 0.42745098 0.56862745 ... -0.25490196 -0.25490196
-0.2627451 ]]
[1 0 0 0 0 0 0 0 1 1]
構建模型圖
- 設置數據 , 數據標籤
- 初始化權重 (最後要訓練的), 以及偏置
- 設置 , 並轉爲概率值
- 定義損失函數, 準確率
- 定義優化器, 來最小化損失函數
x = tf.placeholder(tf.float32, [None, 3072]) # 圖片
y = tf.placeholder(tf.int64, [None]) # 圖片label
# None 表示還不確定幾張圖片, 或者說圖片的數量可變, 因爲 batch size可以被調整
# placeholder表示佔位, 先構建計算圖的結構
# shape [3072, 1]
w = tf.get_variable('w', [x.get_shape()[-1], 1], # 大小爲 x 的shape
initializer = tf.random_normal_initializer(0, 1)) # 使用正太分佈初始化
# shape [1, ]
b = tf.get_variable('b', [1], # 偏置, 維度與 w 的第二維一樣
initializer = tf.constant_initializer(0.0)) # 使用常量初始化
# shape [None, 3072] * [3072, 1] = [None, 1]
y_ = tf.matmul(x, w) + b # matrix multiple 矩陣乘
# shape [None, 1]
predict_y_1 = tf.nn.sigmoid(y_) # 將 y_變爲概率值
# shape [None] -> [None, 1]
y_reshaped = tf.reshape(y, (-1, 1)) # 轉成一樣的才能做差別分析
y_reshaped_float = tf.cast(y_reshaped, tf.float32) # tf對類型很敏感
# 均方誤差
loss = tf.reduce_mean(tf.square(y_reshaped_float - predict_y_1))
# 準確率
predict = predict_y_1 > 0.5 # bool 類似[true, false, true, true...]
correct_prediction = tf.equal(tf.cast(predict, tf.int64), y_reshaped)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float64))
# 定義一個梯度下降的方法
with tf.name_scope('train_op'):
train_op = tf.train.AdamOptimizer(1e-3).minimize(loss) # 學習率 0.001
神經元
init = tf.global_variables_initializer() # 用來執行初始化
batch_size = 20 # 一次喂20張圖片
train_steps = 100000 # 訓練次數
test_steps = 100 # 訓練集一共2000張圖片, 一次喂20張, 所以一共喂100次(因爲數據不打亂, 所以要嚴格等於)
# 一個神經元
with tf.Session() as sess:
sess.run(init)
for i in range(train_steps):
batch_data, batch_labels = train_data.next_batch(batch_size)
loss_val, acc_val, _ = sess.run(
[loss, accuracy, train_op], # 計算loss, accuracy, 然後訓練, 如果不加train_op則只計算不訓練
feed_dict={
x: batch_data,
y: batch_labels,
})
if (i+1) % 500 == 0:
print("[Train] Step: %d, loss: %4.5f, acc: %4.5f" \
% (i+1, loss_val, acc_val))
# 通過測試集看準確率
if (i+1) % 5000 == 0:
test_data = CifarData(test_filenames, need_shuffle=False) # 因爲沒有打亂, 數據集會被用完, 所以每次得新建一個CifarData
all_test_acc_val = [] # 存放準確率
for j in range(test_steps):
test_batch_data, test_batch_labels = test_data.next_batch(batch_size)
test_acc_val = sess.run(
[accuracy], feed_dict = {
x: test_batch_data,
y: test_batch_labels
}
)
all_test_acc_val.append(test_acc_val)
test_acc = np.mean(all_test_acc_val)
print("[Test] Step: %d, acc: %4.5f" % (i+1, acc_val))
訓練結果
[Train] Step: 500, loss: 0.24986, acc: 0.75000
[Train] Step: 1000, loss: 0.24355, acc: 0.75000
[Train] Step: 1500, loss: 0.15000, acc: 0.85000
[Train] Step: 2000, loss: 0.33586, acc: 0.65000
[Train] Step: 2500, loss: 0.10048, acc: 0.90000
[Train] Step: 3000, loss: 0.39996, acc: 0.60000
[Train] Step: 3500, loss: 0.14202, acc: 0.85000
[Train] Step: 4000, loss: 0.14709, acc: 0.85000
[Train] Step: 4500, loss: 0.20128, acc: 0.80000
[Train] Step: 5000, loss: 0.10128, acc: 0.90000
(2000, 3072)
(2000,)
.......
(2000,)
[Test] Step: 95000, acc: 0.70000
[Train] Step: 95500, loss: 0.25005, acc: 0.75000
[Train] Step: 96000, loss: 0.05000, acc: 0.95000
[Train] Step: 96500, loss: 0.20000, acc: 0.80000
[Train] Step: 97000, loss: 0.05003, acc: 0.95000
[Train] Step: 97500, loss: 0.25051, acc: 0.75000
[Train] Step: 98000, loss: 0.10230, acc: 0.90000
[Train] Step: 98500, loss: 0.19962, acc: 0.80000
[Train] Step: 99000, loss: 0.15157, acc: 0.85000
[Train] Step: 99500, loss: 0.20032, acc: 0.80000
[Train] Step: 100000, loss: 0.10221, acc: 0.90000
(2000, 3072)
(2000,)
[Test] Step: 100000, acc: 0.90000
單層神經網絡
需要修改原來的 CifarData的初始化函數, 不再只取兩種標籤的數據
class CifarData:
def __init__(self, filenames, need_shuffle):
all_data = []
all_labels = []
for filename in filenames: # 十個種類的數據
data, labels = load_data(filename)
all_data.append(data)
all_labels.append(labels)
self._data = np.vstack(all_data) # 將all_data變成 圖片數量*3072 的矩陣
self._data = self._data / 127.5 - 1 # 歸一化
self._labels = np.hstack(all_labels)
self._num_examples = self._data.shape[0]
self._need_shuffle = need_shuffle
self._indicator = 0
if self._need_shuffle:
self._shuffle_data()
模型圖需要修改 loss函數, 準確率
x = tf.placeholder(tf.float32, [None, 3072]) # None 表示還不確定幾張圖片
y = tf.placeholder(tf.int64, [None])
# shape: [3072, 10]
w = tf.get_variable('w', [x.get_shape()[-1], 10],
initializer = tf.random_normal_initializer(0, 1))
# shape: [10, ]
b = tf.get_variable('b', [10],
initializer = tf.random_normal_initializer(0, 1))
# shape: [None, 3072] * [3072, 10] = [None, 10]
y_ = tf.matmul(x, w) + b
# 平方差損失
"""
# something like [[0.01, 0.9,..., 0.03], [...],...], 每一項有10個值, 分別代表可能label幾的概率
predict_y = tf.nn.softmax(y_) # e^x / sum(e^x)
y_one_hot = tf.one_hot(y, depth=10, dtype=tf.float32) # 轉爲 onehot 編碼
loss = tf.reduce_mean(tf.square(y_one_hot - predict_y))
"""
# 交叉熵損失
loss = tf.losses.sparse_softmax_cross_entropy(labels=y, logits=y_)
# 包含的步驟: 1. y_->sofmax; 2. y->one_hot; 3. loss = ylogy_
# shape: [None,]
predict = tf.argmax(y_, 1) # 預測值. 返回y_的第二維中最大值的索引, 即爲預測結果
correct_prediction = tf.equal(predict, y)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float64))
with tf.name_scope('train_op'):
train_op = tf.train.AdamOptimizer(1e-3).minimize(loss)
訓練部分代碼不需要改變
如果報錯
注意點
- 圖片一般要歸一化處理, 因爲圖片的數值普遍較大, 使用sigmoid容易偏向一方, 最後的導致梯度消失
other
- 數據集下載
鏈接:https://pan.baidu.com/s/1sI90OHBjWvuJZDmmdj9pFA
提取碼:cnwp - InternalError: 2 root error(s) found. 均爲 Internal: Blas GEMM launch failed
solution 加入os.environ["CUDA_VISIBLE_DEVICES"] = "0" config = tf.ConfigProto(allow_soft_placement = True) gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction = 0.5) config.gpu_options.allow_growth = True sess0 = tf.InteractiveSession(config = config)