立即學習:https://edu.csdn.net/course/play/24719/279509?utm_source=blogtoedu
目錄
一、神經網絡訓練代碼
import tensorflow as tf
import cv2
import glob
import os
import numpy as np
def data_reader(dataset_path):
data_list = []
label_list = []
for cls_path in glob.glob(os.path.join(dataset_path, '*')):
for file_name in glob.glob((os.path.join(cls_path, '*'))):
img = cv2.imread(file_name)
data_list.append(img)
label_list.append(int(cls_path[-1]))
data_np = np.array(data_list)
label_np = np.array(label_list)
return data_np, label_np
def shuffle_data(data, label):
idx = np.arange(len(data))
np.random.shuffle(idx)
return data[idx, ...], label[idx, ...]
def train(data, label):
data_in = tf.placeholder(tf.float32, [None, 100, 100, 3], name="data_in") # None實際上指的是batch的大小,batch的大小可以在運行時改變
label_in = tf.placeholder(tf.float32, [None, 3]) # 準備構造一個one_hot的label,而我們的數據總共有3個類
# 一個小的卷積網絡,用來處理圖片
# (如果數據量大,需要構造一個大一點的神經網絡,可以在卷積網絡部分複製一下,就可以構造一個比較深的神經網絡)
# (但是神經網絡並不是越深越好,可能會涉及過擬合、梯度消失等問題)
# (目前網絡參數的取值,主要還是根據數據集特徵和大小,還有靠我們的直覺的經驗。)
# (所以神經網絡也叫作當代鍊金術)
out = tf.layers.conv2d(data_in, 4, 3, padding='same') # out形狀爲[?, 100, 100, 4]
out = tf.layers.max_pooling2d(out, 2, 2, padding='same') # out形狀爲[?, 50, 50, 4]
out = tf.nn.relu(out) # out形狀爲[?, 50, 50, 4]
# 把提取出來的特徵壓扁成一個一維數組
out = tf.reshape(out, (-1, int(np.prod(out.get_shape()[1:])))) # out形狀爲[?, 10000]
# 送入全連接層
out = tf.layers.dense(out, 2000, activation=tf.nn.relu)
# 再加一個全連接層(按理說,我們數據量很少,應該會過擬合,也就數準確率接近100%)
out = tf.layers.dense(out, 256, activation=tf.nn.relu)
# 得到輸出(輸出的分類數要和輸入的label的分類數一樣,不然會報錯)
pred = tf.layers.dense(out, 3)
# 將one_hot變回值0、1、2
out_label = tf.argmax(pred, 1, name="output")
# 計算交叉熵損失
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=label_in, logits=pred))
# 梯度下降、優化損失
train_op = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)
# 初始化變量的op
init_op = tf.initialize_all_variables()
# 定義一些參數
batch_size = 16
# 進行會話
with tf.Session() as sess:
sess.run(init_op)
# 進行多次訓練
for epoch in range(50):
# (在訓練的時候,我們不希望每次取到的數據的順序都是一樣的,這樣很容易導致過擬合)
# (因此我們一般會加一個shuffle,也就是在輸入到網絡前,把數據順序打亂)
datas, labels = shuffle_data(data, label)
# 準確率統計
total_loss = 0
avg_accuracy = 0
# 按批次訓練
num_batch = len(data) // batch_size
for batch_idx in range(num_batch):
# 計算每個batch開始和結束時候的下標
start_idx = batch_idx * batch_size
end_idx = (batch_idx + 1) * batch_size
# 準備好輸入層的數據
# (...代表省略後面的形狀)
feed_dict = {data_in: datas[start_idx: end_idx, ...],
label_in: labels[start_idx: end_idx, ...]}
# 計算準確率
correct_prediction = tf.equal(out_label, tf.argmax(label_in, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
# 進行訓練
_, loss_out, acc = sess.run([train_op, loss, accuracy], feed_dict=feed_dict)
# 準確率統計
total_loss += loss_out
avg_accuracy += acc
# 統計每批次準確率
print("avg_accuracy", avg_accuracy / num_batch)
# 準確率夠高則保存模型
if avg_accuracy / num_batch > 0.94:
saver = tf.train.Saver()
saver.save(sess, './model/model')
break
def dense_to_one_hot(label, num_class):
num_label = label.shape[0]
index_offset = np.arange(num_label) * num_class
label_one_hot = np.zeros((num_label, num_class))
label_one_hot.flat[index_offset + label.ravel()] = 1
return label_one_hot
if __name__ == '__main__':
dataset_path = "./dataset"
data, label = data_reader(dataset_path)
one_hot_label = dense_to_one_hot(label, 3)
train(data, one_hot_label)
二、思路總結
1、數據集圖片數據、目標值的導入
圖片數據
首先,用cv讀取圖片,將圖片一個一個添加到一個list裏面去。
其次,用np(numpy)將圖片的列表轉化爲np數組。
圖片目標值
首先,由於圖片被按照目錄分類
在讀取圖片時,每讀取一張圖,就將圖片的上級目錄的名字作爲一個目標值。(這裏是0、1、2)
2、目標值轉化爲one_hot編碼
步驟如下:
-
原本一個樣本只有一個目標值
-
一共600個樣本,目標值總的可能性有3種(某種目標的one_hot編碼就只可能是[1, 0, 0]、[0, 1, 0]、[0, 0, 1])
-
先生成好一個全零的、形狀爲(600, 3)的np數組𝐴,準備將值1填入對應標籤位置的位置
-
準備好每個樣本在𝐴展平後的數組𝐵中的one_hot特徵下標
-
將1填入上一步中展平下標對應在數組𝐴中的位置
3、神經網絡中樣本、目標的輸入
可以通過一個佔位符來實現,個數的那一維填寫None,代表暫時不知道每次訓練的樣本數量,在實際訓練中會根據每批次訓練數量加以給定。
比如,我們這裏的樣本數爲600,每個樣本的形狀都是(100, 100, 3),也就是總的樣本的形狀爲(600, 100, 100, 3),那麼我們樣本輸入的佔位符的形狀就應該爲(None, 100, 100, 3);同樣地,我們one_hot目標值的形狀爲(600, 3),那麼我們樣本目標值的佔位符形狀就應該爲(None, 3)
4、神經網絡的搭建
步驟如下:
-
接入一個卷積層(tf.layers.conv2d)
第一個參數(inputs)爲輸入數據
第二個參數(filters)爲卷積核的數量
第三個參數(kernel_size)爲卷積核的大小
(這裏因爲我們的數據比較簡單、類別也少,所以filters取了比較小的值4)
(如果像是imagenet那種大型數據集一般會取到128或者256那樣)
-
接入一個池化層(用於減少數據量)(tf.layers.max_pooling2d)
第一個參數(inputs)爲輸入
第二個參數(pool_size)爲池化核的大小
第三個參數(strides)爲卷積核的步長
(這裏我們都把pool_size和strides設置爲2)
-
加上一個激活函數ReLU
-
把神經網絡的輸出拍扁,做成一個一維數組
-
添加幾個全連接層(tf.layers.dense)
第一個參數(inputs)爲輸入
第二個參數(unit)爲輸出的維度大小(改變inputs的最後一維),同時也是神經元的個數
(一開始中間加入的是一層,後來發現一層全連接層訓練的效果不怎麼好,改成了兩層)
-
再接入一個全連接層得到輸出
(注意,輸出的分類數要和目標label的分類數相同,否則會報錯)
5、損失的計算與優化
損失計算
計算交叉熵損失
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=label_in, logits=pred))
優化
使用Adam優化算法
train_op = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)
6、批處理的方法
步驟如下:
-
設置每批次的訓練數量batch_size、訓練的趟數
-
每趟訓練前件訓練用的輸入數據隨機打亂
-
計算好每一趟訓練的批次數量,對每一批次用batch_size個樣本進行訓練
7、準確率的計算
步驟如下:
-
找到預測值、目標值的每一個樣本在one_hot編碼中的橫向下標
tf.argmax(pred, 1, name="output") tf.argmax(label_in, 1)
-
對比預測值、目標值的下標值是否相等,由此得到一個判斷對應樣本的預測值與目標值是否相等的tf張量
correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(label_in, 1))
-
從上面的比對張量中計算平均值,得到這一批次的準確率
-
在一趟訓練結束後,將所有批次準確率相加求和,再除以訓練批次數量,就得到了這一趟訓練的平均準確率
8、模型的保存
步驟如下:
-
構造一個tf模型存儲器
saver = tf.train.Saver()
-
指定會話、模型存儲路徑
saver.save(sess, './目錄/模型名')
這裏需要注意的是,我們需要爲輸入數據佔位符data_in、預測值out_label增加上各自的name,這是爲了之後模型讀取的方便
三、API總結
API |
作用 |
使用示例 |
glob.glob |
返回某個路徑下所有被匹配的文件/目錄路徑生成的列表 |
for cls_path in glob.glob(os.path.join(dataset_path, '*')): ...... |
np.array |
根據某種數據類型的值生成相應的np數組 |
data_np = np.array(data_list) |
np.arange |
按照指定的start、stop、step生成一個指定的np一維等差數組 |
index_offset = np.arange(num_label) * num_class |
np.zeros |
按照指定的shape生成元素全爲0的np數組 |
label_one_hot = np.zeros((num_label, num_class)) |
*.flat[???] |
根據數組展平後的下標獲取數組元素 |
label_one_hot.flat[index_offset + label.ravel()] = 1 |
np.random.shuffle |
隨機打亂一串數 |
np.random.shuffle(idx) |
tf.placeholder |
創建一個tf佔位符 |
label_in = tf.placeholder(tf.float32, [None, 3]) |
tf.Session |
創建一個tf會話 |
with tf.Session() as sess: ...... |
tf.initialize_all_variables |
創建一個tf變量初始化op |
init_op = tf.initialize_all_variables() |
tf.train.Saver |
創建一個tf會話存儲器 |
saver = tf.train.Saver() |
*.save |
保存會話中的訓練模型 |
saver.save(sess, './model/model') |
tf.argmax |
找出張量在某一維度上的最大值對應的下標 |
out_label = tf.argmax(pred, 1, name="output") |
tf.reduce_mean |
計算平均值 |
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) |
tf.cast |
將某個張量的數據類型進行轉換 |
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) |
tf.equal |
按元素比較兩個張量,返回相等情況對應的張量 |
correct_prediction = tf.equal(out_label, tf.argmax(label_in, 1)) |
tf.nn. softmax_cross_entropy _with_logits_v2 |
計算交叉熵損失 |
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=label_in, logits=pred)) |
tf.layers.conv2d |
構建卷積層 |
out = tf.layers.conv2d(data_in, 4, 3, padding='same') |
tf.layers.max_pooling2d |
構建池化層 |
out = tf.layers.max_pooling2d(out, 2, 2, padding='same') |
tf.nn.relu |
激活函數ReLU |
out = tf.nn.relu(out) |
tf.layers.dense |
構造全連接層 |
out = tf.layers.dense(out, 2000, activation=tf.nn.relu) |