機器學習實戰_人工神經網絡

感知器:線性閾值單元(LTU)

clipboard.png

其中,step(z)爲階躍函數,最多見的在感知器中使用的階躍函數是 Heaviside 階躍函數。有時使用符號函數代替。git

clipboard.png

單一的 LTU 可被用做簡單線性二元分類。它計算輸入的線性組合,若是結果超過閾值,它輸出正類或者輸出負類(就像一個邏輯迴歸分類或線性 SVM)。圖 10-5 表示具備兩個輸入和三個輸出的感知器。該感知器能夠將實例同時分類爲三個不一樣的二進制類,這使得它是一個多輸出分類器算法

clipboard.png

感知器的權重進行以下調整:網絡

$$W_{i,j}^{Next}=W_{i,j}+\eta(\widehat{y}_j-y_j)x_i$$架構

  • 其中$W_{i,j}$ 是第i輸入神經元與第j個輸出神經元之間的鏈接權重。

每一個輸出神經元的決策邊界是線性的,所以感知機不能學習複雜的模式.klearn 提供了一個感知器類,它實現了一個 LTU 網絡。它能夠像你所指望的那樣使用,例如在 iris 數據集dom

import numpy as np 
from sklearn.datasets 
import load_iris from sklearn.linear_model import Perceptron
iris = load_iris() X = iris.data[:, (2, 3)]  # 花瓣長度,寬度 
y = (iris.target == 0).astype(np.int) 
per_clf = Perceptron(random_state=42) # Perceptron爲感知器模型
per_clf.fit(X, y)
y_pred = per_clf.predict([[2, 0.5]])

您可能已經認識到,感知器學習算法相似於隨機梯度降低。事實上,sklearn 的感知器類至關於使用具備如下超參數的 SGD 分類器:loss="perceptron",learning_rate="constant"(學習率),eta0=1,penalty=None(無正則化)。ide

事實證實,感知器的一些侷限性能夠經過堆疊多個感知器來消除。由此產生的人工神經網絡被稱爲多層感知器(MLP)。特別地,MLP 能夠解決 XOR異或問題函數

多層感知器(MLP)與反向傳播

MLP 由一個(經過)輸入層、一個或多個稱爲隱藏層的 LTU 組成,一個最終層 LTU 稱爲輸出層。除了輸出層以外的每一層包括偏置神經元,而且全鏈接到下一層。當人工神經網絡有兩個或多個隱含層時,稱爲深度神經網絡(DNN)性能

clipboard.png

爲了使算法可以正常工做,做者對 MLP 的體系結構進行了一個關鍵性的改變:改變了階躍函數,如下是經常使用的階躍激活函數學習

  • 邏輯函數 σ(z) = 1 / (1 + exp(–z))
  • 雙曲正切函數 tanh (z) = 2σ(2z) – 1
  • Relu 函數 ReLU (z) = max (0, z) (效果會更好一點)

這些流行的激活函數及其衍生物如圖所示測試

clipboard.png

MLP 一般用於分類,每一個輸出對應於不一樣的二進制類(例如,垃圾郵件/正常郵件,緊急/非緊急,等等)。當類是多類的(例如,0 到 9 的數字圖像分類)時,輸出層一般經過用共享的 softmax 函數替換單獨的激活函數來修改。

clipboard.png

用 TensorFlow 高級 API 訓練 MLP

與 TensorFlow 一塊兒訓練 MLP 最簡單的方法是使用高級 API TF.Learn,這與 sklearn 的 API 很是類似。DNNClassifier能夠很容易訓練具備任意數量隱層的深度神經網絡,而 softmax 輸出層輸出估計的類機率。例如,下面的代碼訓練兩個隱藏層的 DNN(一個具備 300 個神經元,另外一個具備 100 個神經元)和一個具備 10 個神經元的 SOFTMax 輸出層進行分類:

import tensorflow as  tf
feature_columns = tf.contrib.learn.infer_real_valued_columns_from_input(X_train)    # 構造數值類型的特徵列
dnn_clf = tf.contrib.learn.DNNClassifier(hidden_units=[300, 100], n_classes=10,
                                      feature_columns=feature_column)
dnn_clf.fit(x=X_train, y=y_train, batch_size=50, steps=40000)

若是你在 MNIST 數據集上運行這個代碼(在縮放它以後,例如,經過使用 skLearn 的StandardScaler),你實際上能夠獲得一個在測試集上達到 98.1% 以上精度的模型!這比咱們在以前訓練的最好的模型都要好:

>>> from sklearn.metrics import accuracy_score 
>>> y_pred = list(dnn_clf.predict(X_test)) 
>>> accuracy_score(y_test, y_pred) 
0.98180000000000001

TF.Learn 學習庫也爲評估模型提供了一些方便的功能:

>>> dnn_clf.evaluate(X_test, y_test) 
{'accuracy': 0.98180002, 'global_step': 40000, 'loss': 0.073678359}

NNClassifier基於 Relu 激活函數建立全部神經元層(咱們能夠經過設置超參數activation_fn來改變激活函數)。輸出層基於 SoftMax 函數,損失函數是交叉熵。

使用普通 TensorFlow 訓練 DNN

若是您想要更好地控制網絡架構,您可能更喜歡使用 TensorFlow 的較低級別的 Python API。在本節中,咱們將使用與以前的 API 相同的模型,咱們將實施 Minibatch 梯度降低來在 MNIST 數據集上進行訓練。 第一步是建設階段,構建 TensorFlow 圖。 第二步是執行階段,您實際運行計算圖譜來訓練模型。

構造階段:
首先咱們須要導入tensorflow庫。 而後咱們必須指定輸入和輸出的數量,並設置每一個層中隱藏的神經元數量:

import tensorflow as tf
n_inputs = 28*28 # MNIST
n_hidden1 = 300
n_hidden2 = 100
n_outputs = 10

接下來,可使用佔位符節點來表示訓練數據和目標。X的形狀僅有部分被定義。 咱們知道它將是一個 2D 張量(即一個矩陣),沿着第一個維度的實例和第二個維度的特徵,咱們知道特徵的數量將是28×28(每像素一個特徵) 可是咱們不知道每一個訓練批次將包含多少個實例。 因此X的形狀是(None, n_inputs)。 一樣,咱們知道y將是一個 1D 張量,每一個實例有一個入口,可是咱們再次不知道在這一點上訓練批次的大小,因此形狀(None)。

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

如今讓咱們建立一個實際的神經網絡。 佔位符X將做爲輸入層; 在執行階段,它將一次更換一個訓練批次(注意訓練批中的全部實例將由神經網絡同時處理)。 如今您須要建立兩個隱藏層和輸出層。 兩個隱藏的層幾乎相同:它們只是它們所鏈接的輸入和它們包含的神經元的數量不一樣。 輸出層也很是類似,但它使用 softmax 激活函數而不是 ReLU 激活函數。 因此讓咱們建立一個neuron_layer()函數,咱們將一次建立一個圖層。 它將須要參數來指定輸入,神經元數量,激活函數和圖層的名稱:

def neuron_layer(X, n_neurons, name, activation=None):    # 輸入、神經元數量、圖層名稱、激活函數
    with tf.name_scope(name):
        n_inputs = int(X.get_shape()[1])    # 輸入數量
        stddev = 2 / np.sqrt(n_inputs)
        init = tf.truncated_normal((n_inputs, n_neurons), stddev=stddev)
        W = tf.Variable(init, name="weights")
        b = tf.Variable(tf.zeros([n_neurons]), name="biases")
        z = tf.matmul(X, W) + b
        if activation == "relu":
            return tf.nn.relu(z)
        else:
            return z

咱們逐行瀏覽這個代碼:

  1. 首先,咱們使用名稱範圍來建立每層的名稱:它將包含該神經元層的全部計算節點。 這是可選的,但若是節點組織良好,則 TensorBoard圖形將會更加出色。
  2. 接下來,咱們經過查找輸入矩陣的形狀並得到第二個維度的大小來得到輸入數量(第一個維度用於實例)。
  3. 接下來的三行建立一個保存權重矩陣的W變量。它將是包含每一個輸入和每一個神經元之間的全部鏈接權重的2D張量;所以,它的形狀將是(n_inputs,n_neurons)。它將被隨機初始化,使用具備標準差爲2/√n的截斷的正態(高斯)分佈(使用截斷的正態分佈而不是常規正態分佈確保不會有任何大的權重,這可能會減慢訓練。).使用這個特定的標準差有助於算法的收斂速度更快(咱們將在第11章中進一步討論這一點),這是對神經網絡的微小調整之一,對它們的效率產生了巨大的影響)。重要的是爲全部隱藏層隨機初始化鏈接權重,以免梯度降低算法沒法中斷的任何對稱性。(例如,若是將全部權重設置爲 0,則全部神經元將輸出0,而且給定隱藏層中的全部神經元的偏差梯度將相同。 而後,梯度降低步驟將在每一個層中以相同的方式更新全部權重,所以它們將保持相等。換句話說,儘管每層有數百個神經元,你的模型就像每層只有一個神經元同樣。)
  4. 下一行建立一個誤差的b變量,初始化爲 0(在這種狀況下無對稱問題),每一個神經元有一個偏置參數。
  5. 而後咱們建立一個子圖來計算z = X·W + b。該向量化實現將有效地計算輸入的加權和加上層中每一個神經元的偏置,對於批次中的全部實例,僅需一次.
  6. 最後,若是激活參數設置爲relu,則代碼返回relu(z)(即max(0,z)),不然它只返回z。

好了,如今你有一個很好的函數來建立一個神經元層。 讓咱們用它來建立深層神經網絡! 第一個隱藏層以X爲輸入。 第二個將第一個隱藏層的輸出做爲其輸入。 最後,輸出層將第二個隱藏層的輸出做爲其輸入。

with tf.name_scope("dnn"):
        hidden1 = neuron_layer(X, n_hidden1, "hidden1", activation="relu")
    hidden2 = neuron_layer(hidden1, n_hidden2, "hidden2", activation="relu")
    logits = neuron_layer(hidden2, n_outputs, "outputs")

請注意,爲了清楚起見,咱們再次使用名稱範圍。 還要注意,logit 是在經過 softmax 激活函數以前神經網絡的輸出:爲了優化,咱們稍後將處理 softmax 計算

正如你所指望的,TensorFlow 有許多方便的功能來建立標準的神經網絡層,因此一般不須要像咱們剛纔那樣定義你本身的neuron_layer()函數。 例如,TensorFlow 的fully_connected()函數建立一個徹底鏈接的層,其中全部輸入都鏈接到圖層中的全部神經元。 它使用正確的初始化策略來負責建立權重和偏置變量,而且默認狀況下使用 ReLU 激活函數(咱們可使用activate_fn參數來更改它)咱們來調整上面的代碼來使用fully_connected()函數,而不是咱們的neuron_layer()函數。 只需導入該功能,並使用如下代碼替換 dnn 構建部分:

from tensorflow.contrib.layers import fully_connected
    with tf.name_scope("dnn"):
        hidden1 = fully_connected(X, n_hidden1, scope="hidden1")
        hidden2 = fully_connected(hidden1, n_hidden2, scope="hidden2")
        logits = fully_connected(hidden2, n_outputs, scope="outputs",
                                    activation_fn=None)
tensorflow.contrib包包含許多有用的功能,但它是一個還沒有分級成爲主要 TensorFlow API 一部分的實驗代碼的地方。 所以,full_connected()函數(和任何其餘contrib代碼)可能會在未來更改或移動。

如今咱們已經有了神經網絡模型,咱們須要定義咱們用來訓練的損失函數。TensorFlow 提供了幾種計算交叉熵的功能。 咱們將使用sparse_softmax_cross_entropy_with_logits()。 而後,咱們可使用 TensorFlow 的reduce_mean()函數來計算全部實例的平均交叉熵。

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

該sparse_softmax_cross_entropy_with_logits()函數等同於應用 SOFTMAX 激活函數,而後計算交叉熵,但它更高效,它妥善照顧的邊界狀況下,好比 logits 等於 0,這就是爲何咱們沒有較早的應用 SOFTMAX 激活函數。 還有稱爲softmax_cross_entropy_with_logits()的另外一個函數,該函數在標籤單熱載體的形式(而不是整數 0 至類的數目減 1)

咱們有神經網絡模型,咱們有損失函數,如今咱們須要定義一個GradientDescentOptimizer來調整模型參數以最小化損失函數

learning_rate = 0.01

with tf.name_scope("train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    training_op = optimizer.minimize(loss)

建模階段的最後一個重要步驟是指定如何評估模型。 咱們將簡單地將精度用做咱們的績效指標。 首先,對於每一個實例,經過檢查最高 logit 是否對應於目標類別來肯定神經網絡的預測是否正確。 爲此,您可使用in_top_k()函數。 這返回一個充滿布爾值的 1D 張量,所以咱們須要將這些布爾值轉換爲浮點數,而後計算平均值。 這將給咱們網絡的總體準確性.

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))

並且,像往常同樣,咱們須要建立一個初始化全部變量的節點,咱們還將建立一個Saver來將咱們訓練有素的模型參數保存到磁盤中:

init = tf.global_variables_initializer()
saver = tf.train.Saver()

建模階段結束。 這是不到 40 行代碼,但至關激烈:咱們爲輸入和目標建立佔位符,咱們建立了一個構建神經元層的函數,咱們用它來建立 DNN,咱們定義了損失函數,咱們 建立了一個優化器,最後定義了性能指標。 如今到執行階段。

執行階段
這部分要短得多,更簡單。 首先,咱們加載 MNIST。 咱們能夠像以前的章節那樣使用 ScikitLearn,可是 TensorFlow 提供了本身的助手來獲取數據,將其縮放(0 到 1 之間),將它洗牌,並提供一個簡單的功能來一次加載一個小批量:

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("/tmp/data/")

如今咱們定義咱們要運行的迭代數,以及小批量的大小:

n_epochs = 10001
batch_size = 50

如今咱們去訓練模型:

with tf.Session() as sess:
    init.run()
    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        acc_train = accuracy.eval(feed_dict={X: X_batch, y: y_batch})
        acc_test = accuracy.eval(feed_dict={X: mnist.test.images, y: mnist.test.labels})
        print(epoch, "Train accuracy:", acc_train, "Test accuracy:", acc_test)

    save_path = saver.save(sess, "./my_model_final.ckpt")

該代碼打開一個 TensorFlow 會話,並運行初始化全部變量的init節點。 而後它運行的主要訓練循環:在每一個時期,經過一些小批次的對應於訓練集的大小的代碼進行迭代。 每一個小批量經過next_batch()方法獲取,而後代碼簡單地運行訓練操做,爲當前的小批量輸入數據和目標提供。 接下來,在每一個時期結束時,代碼評估最後一個小批量和完整訓練集上的模型,並打印出結果。 最後,模型參數保存到磁盤。

如今神經網絡被訓練了,你能夠用它進行預測。 爲此,您能夠重複使用相同的建模階段,可是更改執行階段,以下所示:

with tf.Session() as sess:
    saver.restore(sess, "./my_model_final.ckpt") # or better, use save_path
    X_new_scaled = mnist.test.images[:20]
    Z = logits.eval(feed_dict={X: X_new_scaled})
    y_pred = np.argmax(Z, axis=1)

首先,代碼從磁盤加載模型參數。 而後加載一些您想要分類的新圖像。 記住應用與訓練數據相同的特徵縮放(在這種狀況下,將其從 0 縮放到 1)。 而後代碼評估對數點節點。 若是您想知道全部估計的類機率,則須要將softmax()函數應用於對數,但若是您只想預測一個類,則能夠簡單地選擇具備最高 logit 值的類(使用argmax()函數作的伎倆)。

注意隱藏層數量,每層隱藏層的神經元數量,激活函數的選擇

完整代碼:

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
from sklearn.metrics import accuracy_score
import numpy as np

if __name__ == '__main__':
    n_inputs = 28 * 28
    n_hidden1 = 300
    n_hidden2 = 100
    n_outputs = 10

    mnist = input_data.read_data_sets("/tmp/data/")

    X_train = mnist.train.images
    X_test = mnist.test.images
    y_train = mnist.train.labels.astype("int")
    y_test = mnist.test.labels.astype("int")

    X = tf.placeholder(tf.float32, shape= (None, n_inputs), name='X')
    y = tf.placeholder(tf.int64, shape=(None), name = 'y')

    with tf.name_scope('dnn'):
        hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu
                                  ,name= 'hidden1')

        hidden2 = tf.layers.dense(hidden1, n_hidden2, name='hidden2',
                                  activation= tf.nn.relu)

        logits = tf.layers.dense(hidden2, n_outputs, name='outputs')

    with tf.name_scope('loss'):
        xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels = y,
                                                                  logits = logits)
        loss = tf.reduce_mean(xentropy, name='loss')#全部值求平均

    learning_rate = 0.01

    with tf.name_scope('train'):
        optimizer = tf.train.GradientDescentOptimizer(learning_rate)
        training_op = optimizer.minimize(loss)

    with tf.name_scope('eval'):
        correct = tf.nn.in_top_k(logits ,y ,1)#是否與真值一致 返回布爾值
        accuracy = tf.reduce_mean(tf.cast(correct, tf.float32)) #tf.cast將數據轉化爲0,1序列

    init = tf.global_variables_initializer()

    n_epochs = 20
    batch_size = 50
    with tf.Session() as sess:
        init.run()
        for epoch in range(n_epochs):
            for iteration in range(mnist.train.num_examples // batch_size):
                X_batch, y_batch = mnist.train.next_batch(batch_size)
                sess.run(training_op,feed_dict={X:X_batch,
                                                y: y_batch})
            acc_train = accuracy.eval(feed_dict={X:X_batch,
                                                y: y_batch})
            acc_test = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
            print(epoch, "Train accuracy:", acc_train, "Test accuracy:", acc_test)
相關文章
相關標籤/搜索