tf2.0學習(三)——神經(jīng)網(wǎng)絡(luò)

之前的文章分別介紹了TensorFlow中張量的一些基本知識:
tf2.0學習(一)——基礎(chǔ)知識
tf2.0學習(二)——進階知識
現(xiàn)在介紹一下TensorFlow中關(guān)于神經(jīng)網(wǎng)絡(luò)的操作。

3.1 全連接層

全連接是有感知機發(fā)展起來的,由于感知機模型使用的是介躍激活函數(shù),他是不連續(xù)不可導的,這嚴重制約了該模型的潛力。如果把激活函數(shù)換成平滑連續(xù)可到的函數(shù),并堆疊多個網(wǎng)絡(luò)層來增強網(wǎng)絡(luò)的表達能力,就形成了我們接下來要介紹的全連接網(wǎng)絡(luò)。

3.1.1 張量方式實現(xiàn)

TensorFlow中提供了很好的矩陣運算和反向傳播算法,可以利用這一特性方便的用張量來實現(xiàn)全連接網(wǎng)絡(luò)。

x = tf.random.uniform([2, 784])   # x為模擬的輸入數(shù)據(jù)
# w1為隨機初始化的參數(shù)w1
w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
# b1為0初始化的bias
b1 = tf.Variable(tf.zeros([256]))
# 矩陣相乘 在加bias,即全連接過程
o1 = tf.matmul(x, w1) + b1
# relu激活函數(shù)
o1 = tf.nn.relu(o1)
o1.shape
TensorShape([2, 256])

3.1.2 層的方式實現(xiàn)

全連接作為很常用的網(wǎng)絡(luò)成知識,TensorFlow提供了更高層、更方便的實現(xiàn)方式:tf.keras.layers.Dense(units, activation)。其中unis為輸出的節(jié)點數(shù),activation為激活函數(shù)。

# 輸入數(shù)據(jù)
x = tf.random.normal([4, 28 * 28])

fc = tf.keras.layers.Dense(512, activation=tf.nn.relu)
h1 = fc(x)
h1.shape
TensorShape([4, 512])

如上所示可以很方便的構(gòu)建全連接網(wǎng)絡(luò)層。通過 fc.kernelfc.bias 可以獲取權(quán)重張量W和偏置張量b
在進行參數(shù)優(yōu)化時,可以用 fc.trainable_variables 獲取待優(yōu)化的參數(shù)。

3.2 神經(jīng)網(wǎng)絡(luò)

通過對神經(jīng)層(例如全連接層)的層層堆疊,并且保證前一層的輸出節(jié)點數(shù)與當前層的輸入節(jié)點數(shù)匹配,即可以構(gòu)建出任意層的神經(jīng)網(wǎng)絡(luò)。

3.2.1 張量的方式

x = tf.random.normal([4, 784])

w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
b1 = tf.Variable(tf.zeros([256]))

w2 = tf.Variable(tf.random.truncated_normal([256, 184], stddev=0.1))
b2 = tf.Variable(tf.zeros([184]))

w3 = tf.Variable(tf.random.truncated_normal([184, 10], stddev=0.1))
b3 = tf.Variable(tf.zeros([10]))

with tf.GradientTape() as tape:
    h1 = x @ w1 + b1 
    h1 = tf.nn.relu(h1)
    h2 = h1 @ w2 + b2 
    h2 = tf.nn.relu(h2)
    h3 = h2 @ w3 + b3 

其中h3可以直接作為輸出,也可以加個激活函數(shù)之后再作為輸出,需要視情況而定。
如果希望TensorFlow對一個過程自動求導的話,需要把這個過程的前向傳播放到tf.GradientTape()環(huán)境中,gradient()方法自動求解參數(shù)的梯 度,并利用 optimizers 對象更新參數(shù)。

3.2.2 層的方式

層的方式可以更加簡潔高效的實現(xiàn)神經(jīng)網(wǎng)絡(luò)。

fc1 = tf.keras.layers.Dense(256, activation=tf.nn.relu)
fc2 = tf.keras.layers.Dense(184, activation=tf.nn.relu)
fc3 = tf.keras.layers.Dense(10)

x = tf.random.normal([4, 784])
h1 = fc1(x)
h2 = fc2(h1)
h3 = fc3(h2)

我們同樣可以引入容器Sequential將多個神經(jīng)層封裝成一個大的類,可以更加簡介:

model = tf.keras.Sequential([
    tf.keras.layers.Dense(256, activation=tf.nn.relu),
    tf.keras.layers.Dense(184, activation=tf.nn.relu),
    tf.keras.layers.Dense(10)
])
out = model(x)

前向計算只需要調(diào)用一次網(wǎng)絡(luò)大類對象,就可以按順序計算完所有層。

3.3.3 優(yōu)化目標

我們把神經(jīng)網(wǎng)絡(luò)從輸入到輸出的過程叫做前向傳播。前向傳播的過程也是數(shù)據(jù)從輸入層經(jīng)過第一層,第二層。。。直到輸出層的過程。
前向傳播的最后一步就是完成誤差計算。
\begin{aligned} L= g(f_\theta(x), y) \end{aligned}
其中f_\theta代表了利用\theta參數(shù)化的神經(jīng)網(wǎng)絡(luò)模型,g稱為誤差函數(shù),用來表述當前網(wǎng)絡(luò)的與測試f_\theta(x)和真實值y之間的差距的度量。我們希望通過訓練集的數(shù)據(jù)D^{train}上學習到一組參數(shù)\theta使得訓練的誤差L最小。
從另一個角度理解神經(jīng)網(wǎng)絡(luò),他完成的是特征的維度變換的過程。比如 4 層的 MNIST 手寫數(shù)字圖片識別的全連接網(wǎng)絡(luò),它依次完成了784 → 256 → 128 → 64 → 10的特 征降維過程。

3.4 激活函數(shù)

激活函數(shù)實現(xiàn)了神經(jīng)網(wǎng)絡(luò)的非線性變換,但和階越函數(shù)和符號函數(shù)等不同,激活函數(shù)一般是平滑可導的,更適合梯度下降算法。

3.4.1 Sigmoid

Sigmoid函數(shù)也叫Logistic函數(shù),定義如下:
\begin{aligned} Sigmoid(x) = \frac{1}{1 + e^{-x}} \end{aligned}
他的一個優(yōu)良特性就是能把x\in R的輸入壓縮到x \in (0,1)的區(qū)間內(nèi)。這個區(qū)間的數(shù)值常被用來表示一下意義:

  • 概率分布
  • 信號強度
# x為(-6,6)的10個數(shù)
x = tf.linspace(-6, 6, 10)
tf.nn.sigmoid(x)
<tf.Tensor: shape=(10,), dtype=float64, numpy=
array([0.00247262, 0.00931596, 0.0344452 , 0.11920292, 0.33924363,
       0.66075637, 0.88079708, 0.9655548 , 0.99068404, 0.99752738])>

x中元素的范圍是[-6, 6],映射到(0,1)區(qū)間。

3.4.2 ReLU

ReLU(REctified Linear Unit,修正線性單元)激活函數(shù)提出前,Sigmoid是最常用的神經(jīng)網(wǎng)絡(luò)激活函數(shù)。但Sigmoid函數(shù)在輸入值較大或較小時,容易出現(xiàn)梯度值接近0的現(xiàn)象,梯度為0會是網(wǎng)絡(luò)參數(shù)得不到有效的更新,導致訓練不收斂。
\begin{aligned} ReLU(x) = max(0, x) \end{aligned}

x = tf.linspace(-6, 6, 10)
tf.nn.relu(x)
<tf.Tensor: shape=(10,), dtype=float64, numpy=
array([0.        , 0.        , 0.        , 0.        , 0.        ,
       0.66666667, 2.        , 3.33333333, 4.66666667, 6.        ])>

3.4.3 LeakyReLU

ReLU激活函數(shù)的問題在于,當x<0時,其梯度為0,同樣回導致梯度消失的問題,LeakyReLU就是為解決這一問題而提出的。
\begin{aligned} LeakyReLU(x) = \left\{\begin{array}{ll} x & x \ge 0 \\ px & x < 0 \end{array}\right. \end{aligned}
其中p為用戶設(shè)定的極小的超參數(shù),如0.02等。

x = tf.linspace(-6, 6, 10)
tf.nn.leaky_relu(x, alpha=0.01)
<tf.Tensor: shape=(10,), dtype=float64, numpy=
array([-0.06      , -0.04666667, -0.03333333, -0.02      , -0.00666667,
        0.66666667,  2.        ,  3.33333333,  4.66666667,  6.        ])>

3.4.4 Tanh

Tanh函數(shù)能夠?qū)?img class="math-inline" src="https://math.jianshu.com/math?formula=x%5Cin%20R" alt="x\in R" mathimg="1">的輸入壓縮到x \in (-1,1)的區(qū)間內(nèi)。
\begin{aligned} Tanh(x) = \frac{(e^x - e^{-x})}{(e^x + e^{-x})} \end{aligned}

x = tf.linspace(-6, 6, 10)
tf.nn.tanh(x)
<tf.Tensor: shape=(10,), dtype=float64, numpy=
array([-0.99998771, -0.99982316, -0.99745797, -0.96402758, -0.58278295,
        0.58278295,  0.96402758,  0.99745797,  0.99982316,  0.99998771])>

3.5 輸出層設(shè)計

最有一層作為神經(jīng)網(wǎng)絡(luò)的輸出層,要根據(jù)具體的任務(wù)、場景來判斷是否使用激活函數(shù),以及使用什么樣的激活函數(shù)。

  • 普通實數(shù)空間
    這種輸出比較普遍,譬如房價預(yù)測、年齡預(yù)測、股票預(yù)測,都屬于整個或部分連續(xù)的實數(shù)空間,輸出層可以不加激活函數(shù)(或用relu)。
  • [0, 1] 區(qū)間
    二分類問題,常用Sigmoid作為激活函數(shù)。
  • [0, 1]區(qū)間,且和為1
    多分類問題,常用Softmax作為激活函數(shù)。

3.6 誤差計算

在搭建完模型結(jié)構(gòu)之后,下一步就是選擇合適的誤差函數(shù)來計算誤差了。常見的誤差函數(shù)有mse,mae,hinge loss,cross entrpy等,其中均方誤差和交叉熵在深度學習中比較常見。

3.6.1 均方誤差

MSE(y, o) = \frac{1}{d_{out}} \sum_{i=1}^{d_{out}}(y_i - o_i)^2
tensorflow中:

o = tf.random.normal([2, 10])
y = tf.constant([1, 3])
y = tf.one_hot(y, depth=10)
loss = tf.keras.losses.MSE(y, o)
tf.reduce_sum(loss)

需要注意的是,tf.keras.losses.MSE()返回的是每個樣本的均方誤差,計算當前batch的均方誤差時,需要tf.reduce_mean(loss)
也可以通過層的方式計算:

mse = tf.keras.losses.MeanSquaredError()
mse(y, o)

3.6.2 交叉熵

信息學中有個重要的概念叫熵。
H(P) = - \sum_i P(i) log_2P(i)
熵用來衡量信息的不確定度。熵越大代表不確定性越大,信息量也就越大。
通過熵,引出交叉熵的定義:
H(p||q) = - \sum_i p(i) log_2 q(i)
通過變換,交叉熵可以分解為p的熵和p,q的KL散度的和:
H(p||q) = H(p) + D_{KL}(p||q)
其中KL散度為:
D_{KL}(p||q) = \sum_i p(i)\frac{p(i)}{q(i)}
KL散度是用來衡量兩個分布之間的距離的指標。當p與q之間的距離越大,KL散度也就越大。
最小化交叉熵損失函數(shù)的過程也是最大化正確類別的預(yù)測概率的過程。從這個角 度去理解交叉熵損失函數(shù),非常地直觀易懂。

3.7 神經(jīng)網(wǎng)絡(luò)的類型

全連接是最基本的網(wǎng)絡(luò)類型,對后續(xù)其他神經(jīng)網(wǎng)絡(luò)的研究有巨大貢獻。在學習過程中,全連接的前向傳播和反向傳播都比較容易理解。但由于全連接沒有參數(shù)共享機制,當特征比較多時,會產(chǎn)生大量的參數(shù),訓練過程十分消耗資源。隨著神經(jīng)網(wǎng)絡(luò)在計算機視覺、自然語言處理中廣泛研究,產(chǎn)生了很多其他的神經(jīng)網(wǎng)絡(luò)。

3.7.1 卷積神經(jīng)網(wǎng)絡(luò)(CNN)

計算機視覺領(lǐng)域的核心問題,是如何識別、理解圖片。由于圖片的維度較高,特征較多,如果用傳統(tǒng)的全連接網(wǎng)絡(luò),會產(chǎn)生巨大的參數(shù)量,從而使訓練十分困難。通過利用局部相關(guān)性和權(quán)值共享的思想,CNN應(yīng)運而生。CNN在計算機視覺領(lǐng)域的表現(xiàn)大大超過了其他算法,逐漸統(tǒng)治該領(lǐng)域。
其中CNN的平移不變性起著關(guān)鍵作用,圖片中的目標不管被如何平移,都能得到相同的結(jié)果。卷積+最大池化對平移不變性起了關(guān)鍵作用。

3.7.2 循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)

除了具有空間結(jié)構(gòu)的圖片外,具有序列信息的文本、信號等也是一種十分常見的數(shù)據(jù)形式。CNN由于沒有Memory機制,很難形成長程依賴,這對序列信息不太友好。RNN就是一種有一定Memory機制,一定程度上能解決長程依賴問題的神經(jīng)網(wǎng)絡(luò)。之后作為RNN的變種,LSTM、GRU等相對更具有長期記憶的模型提出來。

3.7.3 注意力機制(Attention)

RNN并不是NLP的終局,近年來隨著注意力機制的提出,克服了RNN不靈活,難以并行等缺點。2017年google提出了純注意力機制的網(wǎng)絡(luò)Transformer,后邊又出現(xiàn)了各種變種模型,Bert,GPT等。

3.7.4 圖卷機網(wǎng)絡(luò)

圖片、文本等數(shù)據(jù)具有規(guī)則的空間、時間結(jié)構(gòu),這種數(shù)據(jù)我們稱為歐幾里得數(shù)據(jù)。CNN和RNN都十分擅長處理這些數(shù)據(jù)。但還有一種數(shù)據(jù),如社交網(wǎng)絡(luò)、交通網(wǎng)絡(luò)、蛋白質(zhì)分子等,是一種不規(guī)則的拓撲結(jié)構(gòu)的數(shù)據(jù)。GCN隨之被提出來處理這類數(shù)據(jù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容