深度學習進階:多分類與TensorFlow

學習目標

  • 目標
    • 知道softmax迴歸的原理
    • 應用softmax_cross_entropy_with_logits實現softamx以及交叉熵損失計算
    • 應用matmul實現多隱層神經網絡的計算
  • 應用
    • 應用TensorFlow完成Mnist手寫數字勢識別

到目前爲止,我們所接觸的都是二分類問題,神經網絡輸出層只有一個神經元,表示預測輸出\hat{y}​y​^​​是正類的概率{P}(y=1|x), \hat{y} > {0.5}P(y=1∣x),​y​^​​>0.5則判斷爲正類,反之判斷爲負類。那麼對於多分類問題怎麼辦?

2.1.1 Softmax 迴歸

對於多分類問題,用 N表示種類個數,那麼神經網絡的輸出層的神經元個數必須爲L[output]=N, 每個神經元的輸出依次對應屬於N個類別當中某個具體類別的概率,即 P(y=N_1|x),..,P(y=N_n|x)P(y=N​1​​∣x),..,P(y=N​n​​∣x)。

輸出層即:

Z^{[L]} = W^{[L]}a^{[L-1]} + b^{[L]}Z​[L]​​=W​[L]​​a​[L−1]​​+b​[L]​​,Z的輸出值個數爲類別個數

需要對所有的輸出結果進行一下softmax公式計算:

a^{[L]}_i = \frac{e^{Z^{[L]}_i}}{\sum^C_{i=1}e^{Z^{[L]}_i}}a​i​[L]​​=​∑​i=1​C​​e​Z​i​[L]​​​​​​e​Z​i​[L]​​​​​​,並且滿足\sum^C_{i=1}a^{[L]}_i = 1∑​i=1​C​​a​i​[L]​​=1,我們來看一下計算案例:

 

2.1.2 交叉熵損失

對於softmax迴歸(邏輯迴歸代價函數的推廣,都可稱之爲交叉熵損失),它的代價函數公式爲:

L(\hat y, y) = -\sum^C_{j=1}y_jlog\hat y_jL(​y​^​​,y)=−∑​j=1​C​​y​j​​log​y​^​​​j​​

總損失函數可以記爲J = \frac{1}{m}\sum^m_{i=1}L(\hat y, y)J=​m​​1​​∑​i=1​m​​L(​y​^​​,y)

邏輯迴歸的損失也可以這樣表示,:

 

所以與softmax是一樣的,一個二分類一個多分類衡量。

對於真實值會進行一個one-hot編碼,每一個樣本的所屬類別都會在某個類別位置上標記。

 

上圖改樣本的損失值爲:

0log(0.10)+0log(0.05)+0log(0.15)+0log(0.10)+0log(0.05)+0log(0.20)+1log(0.10)+0log(0.05)+0log(0.10)+0log(0.10)0log(0.10)+0log(0.05)+0log(0.15)+0log(0.10)+0log(0.05)+0log(0.20)+1log(0.10)+0log(0.05)+0log(0.10)+0log(0.10)

注:關於one_hot編碼

 

框架使用

  • 便於編程:包括神經網絡的開發和迭代、配置產品;

  • 運行速度:特別是訓練大型數據集時;

目前最火的深度學習框架大概是 Tensorflow 了。Tensorflow 框架內可以直接調用梯度下降算法,極大地降低了編程人員的工作量。例如以下代碼:

2.1.3 案例:Mnist手寫數字識別神經網絡實現

2.1.3.1 數據集介紹

 

文件說明:

  • train-images-idx3-ubyte.gz: training set images (9912422 bytes)
  • train-labels-idx1-ubyte.gz: training set labels (28881 bytes)
  • t10k-images-idx3-ubyte.gz: test set images (1648877 bytes)
  • t10k-labels-idx1-ubyte.gz: test set labels (4542 bytes)

網址:http://yann.lecun.com/exdb/mnist/

2.1.3.2 特徵值

 

 

 

2.1.3.3 目標值

 

 

2.1.3.4 Mnist數據獲取API

TensorFlow框架自帶了獲取這個數據集的接口,所以不需要自行讀取。

  • from tensorflow.examples.tutorials.mnist import input_data
    • mnist = input_data.read_data_sets(FLAGS.data_dir, one_hot=True)
      • mnist.train.next_batch(100)(提供批量獲取功能)
      • mnist.train.images、labels
      • mnist.test.images、labels

2.1.3.5 網絡設計

我們採取兩個層,除了輸入層之外。第一個隱層中64個神經元,最後一個輸出層(全連接層)我們必須設置10個神經元的神經網絡。

 

2.1.3.6 全連接層計算

  • tf.matmul(a, b,name=None)+bias
    • return:全連接結果,供交叉損失運算
  • tf.train.GradientDescentOptimizer(learning_rate)
    • 梯度下降
    • learning_rate:學習率
    • method:
      • minimize(loss):最小優化損失

2.1.3.7 前期確定事情與流程

  • 確定網絡結構以及形狀
    • 第一層參數:輸入:x [None, 784] 權重:[784, 64] 偏置[64],輸出[None, 64]
    • 第二層參數:輸入:[None, 64] 權重:[64, 10] 偏置[10],輸出[None, 10]
  • 流程:
    • 獲取數據
    • 前向傳播:網絡結構定義
    • 損失計算
    • 反向傳播:梯度下降優化
  • 功能完善
    • 準確率計算
    • 添加Tensorboard觀察變量、損失變化
    • 訓練模型保存、模型存在加載模型進行預測

2.1.3.8 主網絡搭建流程

  • 獲取數據
mnist = input_data.read_data_sets("./data/mnist/input_data/", one_hot=True)
  • 定義數據佔位符,Mnist數據實時提供給placeholder
# 1、準備數據
# x [None, 784] y_true [None. 10]
with tf.variable_scope("mnist_data"):

    x = tf.placeholder(tf.float32, [None, 784])

    y_true = tf.placeholder(tf.int32, [None, 10])
  • 兩層神經元網絡結果計算
# 2、全連接層神經網絡計算
    # 類別:10個類別  全連接層:10個神經元
    # 參數w: [784, 10]   b:[10]
    # 全連接層神經網絡的計算公式:[None, 784] * [784, 10] + [10] = [None, 10]
    # 隨機初始化權重偏置參數,這些是優化的參數,必須使用變量op去定義
    # 要進行全連接層的矩陣運算 [None, 784]*[784, 64] + [64] = [None,64]
    # [None, 64]*[64, 10] + [10] = [None,10]
    with tf.variable_scope("fc_model"):
         # 第一層:隨機初始化權重和偏置參數,要使用變量OP 定義
        weight_1 = tf.Variable(tf.random_normal([784, 64], mean=0.0, stddev=1.0),
                             name="weightes_1")

        bias_1 = tf.Variable(tf.random_normal([64], mean=0.0, stddev=1.0),
                           name='biases_1')

        # 第二層:隨機初始化權重和偏置參數,要使用變量OP 定義
        weight_2 = tf.Variable(tf.random_normal([64, 10], mean=0.0, stddev=1.0),
                             name="weightes_2")

        bias_2 = tf.Variable(tf.random_normal([10], mean=0.0, stddev=1.0),
                           name='biases_2')

        # 全連接層運算
        # 10個神經元
        # y_predict = [None,10]
        y1 = tf.matmul(x, weight_1) + bias_1

        y_predict = tf.matmul(y1, weight_2) + bias_2
  • 損失計算與優化
    # 3、softmax迴歸以及交叉熵損失計算
    with tf.variable_scope("softmax_crossentropy"):

        # labels:真實值 [None, 10]  one_hot
        # logits:全臉層的輸出[None,10]
        # 返回每個樣本的損失組成的列表
        loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_true,
                                                                      logits=y_predict))

    # 4、梯度下降損失優化
    with tf.variable_scope("optimizer"):

        # 學習率
        train_op = tf.train.GradientDescentOptimizer(0.1).minimize(loss)

2.1.3.9 完善模型功能

  • 1、增加準確率計算
  • 2、增加變量tensorboard顯示
  • 3、增加模型保存加載
  • 4、增加模型預測結果輸出

如何計算準確率

 

  • equal_list = tf.equal(tf.argmax(y, 1), tf.argmax(y_label, 1))
  • accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))

  • 模型評估(計算準確性)

    # 5、得出每次訓練的準確率(通過真實值和預測值進行位置比較,每個樣本都比較)
    with tf.variable_scope("accuracy"):

        equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))

        accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))

 

 

增加變量tensorboard顯示

  • 在會話外當中增加以下代碼
 # (1)、收集要顯示的變量
    # 先收集損失和準確率
    tf.summary.scalar("losses", loss)
    tf.summary.scalar("acc", accuracy)

    # 維度高的張量值
    tf.summary.histogram("w1", weight_1)
    tf.summary.histogram("b1", bias_1)
    # 維度高的張量值
    tf.summary.histogram("w2", weight_2)
    tf.summary.histogram("b2", bias_2)

    # 初始化變量op
    init_op = tf.global_variables_initializer()

    # (2)、合併所有變量op
    merged = tf.summary.merge_all()
  • 在會話當中去創建文件寫入每次的變量值
# (1)創建一個events文件實例
file_writer = tf.summary.FileWriter("./tmp/summary/", graph=sess.graph)

# 運行合變量op,寫入事件文件當中
summary = sess.run(merged, feed_dict={x: mnist_x, y_true: mnist_y})

file_writer.add_summary(summary, i)

增加模型保存加載

創建Saver,然後保存

# 創建模型保存和加載
saver = tf.train.Saver()

# 每隔100步保存一次模型
if i % 100 == 0:

    saver.save(sess, "./tmp/modelckpt/fc_nn_model")

在訓練之前加載模型

# 加載模型
if os.path.exists("./tmp/modelckpt/checkpoint"):

    saver.restore(sess, "./tmp/modelckpt/fc_nn_model")

 

 

增加模型預測結果輸出

增加標誌位

tf.app.flags.DEFINE_integer("is_train", 1, "指定是否是訓練模型,還是拿數據去預測")
FLAGS = tf.app.flags.FLAGS

然後判斷是否訓練,如果不是訓練就直接預測,利用tf.argmax對樣本的真實目標值y_true,和預測的目標值y_predict求出最大值的位置

            # 如果不是訓練,我們就去進行預測測試集數據
            for i in range(100):

                # 每次拿一個樣本預測
                mnist_x, mnist_y = mnist.test.next_batch(1)

                print("第%d個樣本的真實值爲:%d, 模型預測結果爲:%d" % (
                                                      i+1,
                                                      tf.argmax(sess.run(y_true, feed_dict={x: mnist_x, y_true: mnist_y}), 1).eval(),
                                                      tf.argmax(sess.run(y_predict, feed_dict={x: mnist_x, y_true: mnist_y}), 1).eval()
                                                      )
                                                      )

 

 

4.7.3.6 完整代碼

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data


# 定義一個是否訓練、預測的標誌
tf.app.flags.DEFINE_integer("is_train", 1, "訓練or預測")

FLAGS = tf.app.flags.FLAGS


def full_connected_nn():
    """
    全連接層神經網絡進行Mnist手寫數字識別訓練
    :return:
    """
    mnist = input_data.read_data_sets("./data/mnist/input_data/", one_hot=True)

    # 1、獲取數據,定義特徵之和目標值張量
    # x
    with tf.variable_scope("data"):

        # 定義特徵值佔位符
        x = tf.placeholder(tf.float32, [None, 784], name="feature")

        # 定義目標值佔位符
        y_true = tf.placeholder(tf.int32, [None, 10], name="label")

    # 2、根據識別的類別數,建立全連接層網絡
    # 手寫數字10個類別
    # 設計了一層的神經網絡,最後一層,10個神經元
    # 確定網絡的參數weight [784, 10] bias[10]
    # 要進行全連接層的矩陣運算 [None, 784]*[784, 64] + [64] = [None,64]
    # [None, 64]*[64, 10] + [10] = [None,10]
    with tf.variable_scope("fc_model"):
        # 第一層:隨機初始化權重和偏置參數,要使用變量OP 定義
        weight_1 = tf.Variable(tf.random_normal([784, 64], mean=0.0, stddev=1.0),
                             name="weightes_1")

        bias_1 = tf.Variable(tf.random_normal([64], mean=0.0, stddev=1.0),
                           name='biases_1')

        # 第二層:隨機初始化權重和偏置參數,要使用變量OP 定義
        weight_2 = tf.Variable(tf.random_normal([64, 10], mean=0.0, stddev=1.0),
                             name="weightes_2")

        bias_2 = tf.Variable(tf.random_normal([10], mean=0.0, stddev=1.0),
                           name='biases_2')

        # 全連接層運算
        # 10個神經元
        # y_predict = [None,10]
        y1 = tf.matmul(x, weight_1) + bias_1

        y_predict = tf.matmul(y1, weight_2) + bias_2

    # 3、根據輸出結果與真是結果建立softmax、交叉熵損失計算
    with tf.variable_scope("softmax_cross"):

        # 先進性網絡輸出的值的概率計算softmax,在進行交叉熵損失計算
        all_loss = tf.nn.softmax_cross_entropy_with_logits(labels=y_true,
                                                           logits=y_predict,
                                                           name="compute_loss")
        # 求出平均損失
        loss = tf.reduce_mean(all_loss)

    # 4、定義梯度下降優化器進行優化
    with tf.variable_scope("GD"):

        train_op = tf.train.GradientDescentOptimizer(0.1).minimize(loss)

    # 5、求出每次訓練的準確率爲
    with tf.variable_scope("accuracy"):
        # 求出每個樣本是否相等的一個列表
        equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))

        # 計算相等的樣本的比例
        accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))

    # 1、收集要在tensorboard觀察的張量值
    # 數值型-->scalar 準確率,損失
    tf.summary.scalar("loss", loss)
    tf.summary.scalar("acc", accuracy)

    # 維度高的張量值
    tf.summary.histogram("w1", weight_1)
    tf.summary.histogram("b1", bias_1)
    # 維度高的張量值
    tf.summary.histogram("w2", weight_2)
    tf.summary.histogram("b2", bias_2)

    # 2、合併變量
    merged = tf.summary.merge_all()

    # 1、創建保存模型的OP
    saver = tf.train.Saver()

    # 開啓會話進行訓練
    with tf.Session() as sess:

        # 初始化變量OP
        sess.run(tf.global_variables_initializer())

        # 創建events文件
        file_writer = tf.summary.FileWriter("./tmp/summary/", graph=sess.graph)

        # 加載本地模型繼續訓練或者拿來進行預測測試集
        # 加載模型,從模型當中找出與當前訓練的模型代碼當中(名字一樣的OP操作),覆蓋原來的值
        ckpt = tf.train.latest_checkpoint("./tmp/model/")

        # 判斷模型是否存在
        if ckpt:
            saver.restore(sess, ckpt)

        if FLAGS.is_train == 1:

            # 循環訓練
            for i in range(2000):

                # 每批次給50個樣本
                mnist_x, mnist_y = mnist.train.next_batch(50)

                _, loss_run, acc_run, summary = sess.run([train_op, loss, accuracy, merged],
                                                feed_dict={x: mnist_x, y_true: mnist_y})

                print("第 %d 步的50個樣本損失爲:%f , 準確率爲:%f" % (i, loss_run, acc_run))

                # 3、寫入運行的結果到文件當中
                file_writer.add_summary(summary, i)

                # 每隔100步保存一次模型的參數
                if i % 100 == 0:

                    saver.save(sess, "./tmp/model/fc_nn_model")
        else:
            # 進行預測
            # 預測100個樣本
            for i in range(100):

                # label [1, 10]
                image, label = mnist.test.next_batch(1)

                # 直接運行網絡的輸出預測結果
                print("第 %d 樣本,真實的圖片數字爲:%d, 神經網絡預測的數字爲:%d " % (
                    i,
                    tf.argmax(label, 1).eval(),
                    tf.argmax(sess.run(y_predict, feed_dict={x: image, y_true: label}), 1).eval()
                    ))


    return None


if __name__ == '__main__':
    full_connected_nn()

4.7.4 調整學習率調整網絡參數帶來的問題

如果我們對網絡當中的學習率進行修改,也就是一開始我們並不會知道學習率填哪些值,也並不知道調整網絡的參數大小帶來的影響(第一部分第四節)。

  • 假設學習率調整到1,2
  • 假設參數調整到比較大的值,幾十、幾百

總結:參數調整了之後可能沒影響,是因爲網絡較小,可能並不會造成後面所介紹的梯度消失或者梯度爆炸

4.7.5總結

  • 掌握softmax公式以及特點
  • tensorflow.examples.tutorials.mnist.input_data 獲取Mnist數據
  • tf.matmul(a, b,name=None)實現全連接層計算
  • tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)實現梯度下降優化
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章