【手把手帶你入門深度學習之150行代碼的漢字識別系統】學習筆記 ·002 訓練神經網絡

立即學習:https://edu.csdn.net/course/play/24719/279509?utm_source=blogtoedu

 

目錄

 一、神經網絡訓練代碼

二、思路總結

1、數據集圖片數據、目標值的導入

2、目標值轉化爲one_hot編碼

3、神經網絡中樣本、目標的輸入

4、神經網絡的搭建

5、損失的計算與優化

6、批處理的方法

7、準確率的計算

8、模型的保存

三、API總結


 

一、神經網絡訓練代碼

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編碼

步驟如下:

  1. 原本一個樣本只有一個目標值

  2. 一共600個樣本,目標值總的可能性有3種(某種目標的one_hot編碼就只可能是[1, 0, 0]、[0, 1, 0]、[0, 0, 1])

  3. 先生成好一個全零的、形狀爲(600, 3)的np數組𝐴,準備將值1填入對應標籤位置的位置

  4. 準備好每個樣本在𝐴展平後的數組𝐵中的one_hot特徵下標

  5. 將1填入上一步中展平下標對應在數組𝐴中的位置

3、神經網絡中樣本、目標的輸入

可以通過一個佔位符來實現,個數的那一維填寫None,代表暫時不知道每次訓練的樣本數量,在實際訓練中會根據每批次訓練數量加以給定。

比如,我們這裏的樣本數爲600,每個樣本的形狀都是(100, 100, 3),也就是總的樣本的形狀爲(600, 100, 100, 3),那麼我們樣本輸入的佔位符的形狀就應該爲(None, 100, 100, 3);同樣地,我們one_hot目標值的形狀爲(600, 3),那麼我們樣本目標值的佔位符形狀就應該爲(None, 3)

4、神經網絡的搭建

步驟如下:

  1. 接入一個卷積層(tf.layers.conv2d)

    第一個參數(inputs)爲輸入數據

    第二個參數(filters)爲卷積核的數量

    第三個參數(kernel_size)爲卷積核的大小

    (這裏因爲我們的數據比較簡單、類別也少,所以filters取了比較小的值4)

    (如果像是imagenet那種大型數據集一般會取到128或者256那樣)

  2. 接入一個池化層(用於減少數據量)(tf.layers.max_pooling2d)

    第一個參數(inputs)爲輸入

    第二個參數(pool_size)爲池化核的大小

    第三個參數(strides)爲卷積核的步長

    (這裏我們都把pool_size和strides設置爲2)

  3. 加上一個激活函數ReLU

  4. 把神經網絡的輸出拍扁,做成一個一維數組

  5. 添加幾個全連接層(tf.layers.dense)

    第一個參數(inputs)爲輸入

    第二個參數(unit)爲輸出的維度大小(改變inputs的最後一維),同時也是神經元的個數

    (一開始中間加入的是一層,後來發現一層全連接層訓練的效果不怎麼好,改成了兩層)

  6. 再接入一個全連接層得到輸出

    (注意,輸出的分類數要和目標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、批處理的方法

步驟如下:

  1. 設置每批次的訓練數量batch_size、訓練的趟數

  2. 每趟訓練前件訓練用的輸入數據隨機打亂

  3. 計算好每一趟訓練的批次數量,對每一批次用batch_size個樣本進行訓練

7、準確率的計算

步驟如下:

  1. 找到預測值、目標值的每一個樣本在one_hot編碼中的橫向下標

    tf.argmax(pred, 1, name="output")
    tf.argmax(label_in, 1)
  2. 對比預測值、目標值的下標值是否相等,由此得到一個判斷對應樣本的預測值與目標值是否相等的tf張量

    correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(label_in, 1))
  3. 從上面的比對張量中計算平均值,得到這一批次的準確率

  4. 在一趟訓練結束後,將所有批次準確率相加求和,再除以訓練批次數量,就得到了這一趟訓練的平均準確率

8、模型的保存

步驟如下:

  1. 構造一個tf模型存儲器

    saver = tf.train.Saver()
  2. 指定會話、模型存儲路徑

    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)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章