天天看點

《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介

文章目錄

  • 第10章 人工神經網絡簡介

      1 從生物神經元到人工神經元
         1.1 生物神經元
         1.2 具有神經元的邏輯計算
         1.3 感覺器
         1.4 多層感覺器和反向傳播
      2 用TensorFlow的進階API來訓練MLP
      3 使用純TensorFlow訓練DNN
         3.1 建構階段
         3.2 執行階段
         3.3 使用神經網絡
      4 微調神經網絡的超參數
         4.1 隐藏層的個數
         4.2 每個隐藏層中的神經元數
         4.3 激活函數
      5 練習

1. 從生物神經元到人工神經元

   我們從鳥類那裡學會了飛翔,有很多發明都是

被自然所啟發

。這麼說來看看

大腦

的組成,啟發我們建構智能機器,就合乎情理了。這就是

人工神經網絡ANN(Artificial Neural Network)

的根本來源。

   人工神經網絡是深度學習的

核心中的核心

。它們通用、強大、可擴充,使它成為解決複雜機器學習任務的理想選擇。比如,數以億計的圖檔分類,擊敗世界冠軍的AlphaGo。

1.1 生物神經元

   它是在動物的大腦皮層中的非凡細胞。生物神經元通過這些突出接受從其他細胞發來的很短的電脈沖,即信号。當一個神經元在一定時間内收到足夠多的信号,就會發出自己的信号。

   超級複雜的計算可以通過這些簡單的神經元來完成。

1.2 具有神經元的邏輯計算

   生物神經元的簡化模型,

人工神經元:它有一個或多個二進制 (開\關) 輸入 和 一個輸出。

《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介
《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介

  

邏輯非

的應用場景,比如dropout。

1.3 感覺器Perceptron

   感覺器是最簡單ANN架構。它是基于一個線性門檻值單元(LTU,Linear Threshold Unit)的人工神經元。

《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介

   分析上圖,x和w沒啥好說的,就是普通的數字或向量,那麼做變換的其實是神經元,它做了哪些操作? ① 權重求和 z = w t ⋅ x z = w^t \cdot x z=wt⋅x;② 經過階躍函數進一步變換函數空間 step(z)。③ 最後的輸出: h w ( x ) = s t e p ( w t ⋅ x ) 。 h_w(x) = step(w^t \cdot x)。 hw​(x)=step(wt⋅x)。

   單個LTU結構可以用于線性二值分類,輸出為一個機率,如果該機率超過了門檻值就是正,否則為負(跟LR和SVM一樣)

  

感覺器Perceptron

就是多個LTU單元的單層全連接配接NN結構。

注意:X1、X2 是特征 特征 , 1為偏差特征,永遠為1!!!

《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介

   總的來看,

上面這個感覺器結構做了什麼?

它将一個執行個體(

x1 x2是單個執行個體的2個特征

)分為3個不同的二進制類,是以它是

多輸出分類器

。當然也可以做成單輸出分類器,在後面再加一層單個LTU單元的輸出就好了,此時擁有2層的感覺器叫

多層感覺器

(MLP, Multi-Layer Perceptron)。

   感覺器訓練算法很大程度上受hebb’s定律的啟發,同時處于激活狀态的細胞是會連在一起的。這個規律後來被稱為

hebb定律

(又叫hebbinan學習):當2個神經元有相同的輸出時,它們之間的連結權重就會增強。perceptron就是根據這個規則的變體來訓練。

   感覺器

訓練算法 (權重更新)

w i j n e x t s t e p = w i j + η ( y ^ j − y j ) x i w_{ij}^{next step} = w_{ij} + \eta(\hat y_j-y_j)x_i wijnextstep​=wij​+η(y^​j​−yj​)xi​

   w i j w_{ij} wij​是第i個輸入神經元和第j個輸出神經元的連結權重;

   x i x_i xi​是目前訓練執行個體的第i個輸入值;

   y ^ j \hat y_j y^​j​是目前訓練執行個體的第j個輸出神經元的輸出,即預測值;

   y i y_i yi​是目前訓練執行個體的第j個輸出神經元的目标輸出,即真實值;

   η \eta η 是學習率。

  

注意:

感覺器的每個輸出神經元的

決策邊界是線性的

,是以無法學習複雜的模式。(這點跟LR一樣)

   sklearn實作了一個單一LTU忘了的Perceptron類。

import numpy as np
from sklearn.datasets import load_iris
from sklearn.linear_model import Perceptron

iris = load_iris()
X = iris.data[:, (2, 3)]  # petal length, petal width
y = (iris.target == 0).astype(np.int)

per_clf = Perceptron(max_iter=100, tol=-np.infty, random_state=42)
per_clf.fit(X, y)

y_pred = per_clf.predict([[2, 0.5]])

a = -per_clf.coef_[0][0] / per_clf.coef_[0][1] #前兩個系數相除
b = -per_clf.intercept_ / per_clf.coef_[0][1]  #截距 除以 系數

axes = [0, 5, 0, 2]

x0, x1 = np.meshgrid(
        np.linspace(axes[0], axes[1], 500).reshape(-1, 1),# 0 ~ 5之間産生500個等差數列的數
        np.linspace(axes[2], axes[3], 200).reshape(-1, 1),# 0 ~ 2之間産生200個等差數列的數
    )
#生成測試執行個體
X_new = np.c_[x0.ravel(), x1.ravel()] # 按列合并
y_predict = per_clf.predict(X_new)
zz = y_predict.reshape(x0.shape)

plt.figure(figsize=(10, 4))
plt.plot(X[y==0, 0], X[y==0, 1], "bs", label="Not Iris-Setosa")
plt.plot(X[y==1, 0], X[y==1, 1], "yo", label="Iris-Setosa")

#畫出決策邊界
plt.plot([axes[0], axes[1]], [a * axes[0] + b, a * axes[1] + b], "k-", linewidth=3)
from matplotlib.colors import ListedColormap
custom_cmap = ListedColormap(['#9898ff', '#fafab0'])

plt.contourf(x0, x1, zz, cmap=custom_cmap)#正負樣本區域 展示不同顔色
plt.xlabel("Petal length", fontsize=14)
plt.ylabel("Petal width", fontsize=14)
plt.legend(loc="lower right", fontsize=14)
plt.axis(axes)

# save_fig("perceptron_iris_plot")
plt.show()
           
《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介

  

注意

:感覺器隻能根據一個固定的門檻值來做預測,而不是像LR輸出一個機率,是以從靈活方面來說應該使用LR而不是Perception。

1.4 多層感覺器和反向傳播

   多層感覺器,就是多個感覺器堆疊起來。

《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介

   反向傳播的實質其實就是

複合函數求導的鍊式法則

。 反向傳播的

訓練過程

   ① 先正向做一次預測,度量誤差;

   ② 反向的周遊每個層次來度量每個連接配接的誤差;

   ③ 微調每個連接配接的權重來降低誤差(梯度下降)。

   反向傳播可以合作的

激活函數

,除了邏輯函數sigmoid等外,最流行的是2個:

   ① 雙曲正切函數 t a n h ( z ) = 2 σ ( 2 z ) − 1 tanh(z)=2 \sigma(2z)-1 tanh(z)=2σ(2z)−1

   ② ReLU函數 R e L U ( z ) = m a x ( 0 , z ) ReLU(z) = max(0,z) ReLU(z)=max(0,z)

z = np.linspace(-5, 5, 200)

plt.figure(figsize=(11,4))

plt.subplot(121)
plt.plot(z, np.sign(z), "r-", linewidth=1, label="Step")
plt.plot(z, sigmoid(z), "g--", linewidth=2, label="Sigmoid")
plt.plot(z, np.tanh(z), "b-", linewidth=2, label="Tanh")
plt.plot(z, relu(z), "m-.", linewidth=2, label="ReLU")
plt.grid(True)
plt.legend(loc="center right", fontsize=14)
plt.title("Activation functions", fontsize=14)
plt.axis([-5, 5, -1.2, 1.2])

plt.subplot(122)
plt.plot(z, derivative(np.sign, z), "r-", linewidth=1, label="Step")
plt.plot(0, 0, "ro", markersize=5)
plt.plot(0, 0, "rx", markersize=10)
plt.plot(z, derivative(sigmoid, z), "g--", linewidth=2, label="Sigmoid")
plt.plot(z, derivative(np.tanh, z), "b-", linewidth=2, label="Tanh")
plt.plot(z, derivative(relu, z), "m-.", linewidth=2, label="ReLU")
plt.grid(True)
#plt.legend(loc="center right", fontsize=14)
plt.title("Derivatives", fontsize=14)
plt.axis([-5, 5, -0.2, 1.2])

save_fig("activation_functions_plot")
plt.show()
           
《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介

2. 用TensorFlow的進階API來訓練MLP

   需要用到tf.contrib包 和 sklearn結合,contrib裡面的東西經常疊代,屬于第三方提供的代碼庫,這裡就不描述了。

3. 使用純TensorFlow訓練DNN

3.1 建構階段

#shuffle分批分桶
def shuffle_batch(X, y, batch_size):
    rnd_idx = np.random.permutation(len(X))
    n_batches = len(X) // batch_size
    for batch_idx in np.array_split(rnd_idx, n_batches):
        X_batch, y_batch = X[batch_idx], y[batch_idx]
        yield X_batch, y_batch #yield生成器,節省記憶體

n_inputs = 28*28  # MNIST
n_hidden1 = 300 #隐層1的神經元數量
n_hidden2 = 100 #隐層2的神經元數量
n_outputs = 10  #輸出層的神經元數量,對于MNIST為多輸出,0 - 9 共10種數字

reset_graph()
#------------------建構階段 --------------------
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X") #占位符,相當于先定義出來因變量X
y = tf.placeholder(tf.int32, shape=(None), name="y") 

#建構nn結構
with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, name="hidden1",
                              activation=tf.nn.relu)
    hidden2 = tf.layers.dense(hidden1, n_hidden2, name="hidden2",
                              activation=tf.nn.relu)
    logits = tf.layers.dense(hidden2, n_outputs, name="outputs")
    y_proba = tf.nn.softmax(logits)

#定義損失函數
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")
    
#定義優化器和最小化損失函數的op
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))

           

3.2 執行階段

  

#------------------執行階段 --------------------
init = tf.global_variables_initializer() # 定義全局變量初始化器
saver = tf.train.Saver() #定義saver用于儲存模型

n_epochs = 20 #疊代輪次
n_batches = 50 #每個批次的執行個體數量

with tf.Session() as sess:
    init.run() #初始化變量
    for epoch in range(n_epochs):
        for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch}) #開始訓練
        acc_batch = accuracy.eval(feed_dict={X: X_batch, y: y_batch}) #每個批次的訓練集精确率
        acc_valid = accuracy.eval(feed_dict={X: X_valid, y: y_valid}) #每個批次的驗證集的精确率
        print(epoch, "Batch accuracy:", acc_batch, "Validation accuracy:", acc_valid)

    save_path = saver.save(sess, "./my_model_final.ckpt") #儲存模型
           
《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介
《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介

   當然也可以自定義層結構,其他代碼跟上面一樣:

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="kernel")
        b = tf.Variable(tf.zeros([n_neurons]), name="bias")
        Z = tf.matmul(X, W) + b
        if activation is not None:
            return activation(Z)
        else:
            return Z
#唯一差別是這裡使用了我們自定義的層結構,而不是dense
with tf.name_scope("dnn"):
    hidden1 = neuron_layer(X, n_hidden1, name="hidden1",
                           activation=tf.nn.relu)
    hidden2 = neuron_layer(hidden1, n_hidden2, name="hidden2",
                           activation=tf.nn.relu)
    logits = neuron_layer(hidden2, n_outputs, name="outputs")
           

3.3 使用神經網絡

   前面已經将訓練好的NN儲存成了ckpt檔案,我們可以直接取出來用于預測:

with tf.Session() as sess:
    saver.restore(sess, "./my_model_final.ckpt") # or better, use save_path
    X_new_scaled = X_test[:20] #這裡需要特征縮放  0 ~ 1
    Z = logits.eval(feed_dict={X: X_new_scaled}) # logits為nn最後的輸出節點
    y_pred = np.argmax(Z, axis=1) #取出最大值的索引下标,即為預測圖檔
Z    
y_pred
           
《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介
《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介

4. 微調神經網絡的超參數

   有太多超參數需要調整:層數、每層神經元數、每層的激活函數類型、初始化邏輯的權重等等。是以,了解每個超參數的合理取值會很有幫助。

4.1 隐藏層的個數

   ① 大多數問題可以用一個或兩個隐藏層來處理,此時可以增加神經元的數量。比如,對于MINST資料集,一個隐藏層擁有數百個神經元就可以達到97%的精度,2層可以獲得超過98%的精度。

   ② 非常複雜的問題,比如大圖檔的分類,語音識别,通常需要數十層的隐藏層,此時每層的神經元數量要非常少。當然他們也需要超大的資料集。

隐藏層多神經元少的目的是為了訓練起來更加快速

。不過,很少會有人從頭建構這樣的網絡:更常見的是

重用

别人訓練好的用來處理類似任務的網絡。

4.2 每個隐藏層中的神經元數(

重要

)

   ① 對于輸入層和輸出層,由具體任務要求決定,比如MNIST輸出10種數字,輸出層神經元數就是10;

   ② 對于隐藏層,

經驗

是以漏鬥型來定義其尺寸,每層的神經元數依次減少,原因:許多低級功能可以合并成數量更少的進階功能。

   ③ 對于以上經驗也不是那麼絕對,可以逐漸增加神經元的數量,直到過拟合。通常來說,通過增加每層的神經元數量比增加層數會産生更多的消耗。

   ④ 一個

更簡單的方式:

使用更多的層次和神經元,然後提前設定

1 早停

避免過拟合

,或者使用

2 dropout

正則化技術。這被稱為 彈力褲 方法。

4.3 激活函數

   大多數情況下,可以在隐藏層中使用ReLU激活函數(或其變種)。它比其他激活函數快,因為梯度下降對于大資料值沒有上限,會導緻它無法終止。

   對于輸出層,Softmax對于分類任務( 若分類是互斥的) 來說是一個不錯的選擇。

對于回歸任務,完全可以不使用激活函數?

5. 練習

   在MNIST資料集上訓練一個深度MLP,看看預測準确度能不能超過98%。嘗試一些

額外的功能

儲存檢查點,中斷後從檢查點恢複,添加彙總,用tensorboard繪制學習曲線

from datetime import datetime
#定義日志路徑
def log_dir(prefix=""):
    now = datetime.utcnow().strftime("%Y%m%d%H%M%S")
    root_logdir = "tf_logs"
    if prefix:
        prefix += "-"
    name = prefix + "run-" + now
    return "{}/{}/".format(root_logdir, name)

n_inputs = 28*28  # MNIST
n_hidden1 = 300
n_hidden2 = 100
n_outputs = 10

reset_graph()

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

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, name="hidden1",
                              activation=tf.nn.relu)
    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")
    loss_summary = tf.summary.scalar('log_loss', 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))
    accuracy_summary = tf.summary.scalar('accuracy', accuracy)

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

#定義二進制日志檔案writer
file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())

m, n = X_train.shape

# -------------- 執行計算圖--------------------
n_epochs = 10001
batch_size = 50
n_batches = int(np.ceil(m / batch_size))

checkpoint_path = "./tmp/my_deep_mnist_model.ckpt" #第一次訓練時路徑不對
checkpoint_epoch_path = checkpoint_path + ".epoch"
final_model_path = "./my_deep_mnist_model"

best_loss = np.infty
epochs_without_progress = 0
max_epochs_without_progress = 50

with tf.Session() as sess:
    if os.path.isfile(checkpoint_epoch_path):
        # if the checkpoint file exists, restore the model and load the epoch number
        with open(checkpoint_epoch_path, "rb") as f:
            start_epoch = int(f.read())
        print("Training was interrupted. Continuing at epoch", start_epoch)
        saver.restore(sess, checkpoint_path)
    else:
        start_epoch = 0
        sess.run(init)

    for epoch in range(start_epoch, n_epochs):
        for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val, loss_val, accuracy_summary_str, loss_summary_str = sess.run([accuracy, loss, accuracy_summary, loss_summary], feed_dict={X: X_valid, y: y_valid})
        file_writer.add_summary(accuracy_summary_str, epoch)
        file_writer.add_summary(loss_summary_str, epoch)
        if epoch % 5 == 0:
            print("Epoch:", epoch,
                  "\tValidation accuracy: {:.3f}%".format(accuracy_val * 100),
                  "\tLoss: {:.5f}".format(loss_val))
            #儲存目前模型
            saver.save(sess, checkpoint_path)
            #儲存目前疊代輪次到.epoch字尾的檔案中
            with open(checkpoint_epoch_path, "wb") as f:
                f.write(b"%d" % (epoch + 1))
            if loss_val < best_loss:
                saver.save(sess, final_model_path)
                best_loss = loss_val
            else:
                epochs_without_progress += 5
                if epochs_without_progress > max_epochs_without_progress:
                    print("Early stopping")
                    break

#模型訓練完成後,删除檢查點檔案
os.remove(checkpoint_epoch_path)
           
《神經網絡和深度學習 學習筆記》(二)人工神經網絡簡介

繼續閱讀