首頁>教育>

出處:https://yuerblog.cc/2020/12/14/tensorflow-%E9%80%8F%E8%BF%87%E6%95%B0%E5%AD%A6%E7%90%86%E8%A7%A3model/

本文透過一個tensorflow例子,通俗的說明神經網路是如何工作的,以便我們更自信的駕馭它。

什麼是神經網路?

神經網路就是一個數學問題,無論我們的網路結構多麼複雜,它仍舊可以透過一個y與x之間的很複雜的數學公式進行表達。

當我們看到一張神經網路圖結構時,

我們可以堅信一件事情:

當我們輸入x時,一定可以經過某個數學表示式得到y,其數學形式可以高度簡化為:y=f1(f2(f3(f4(x)))),巢狀函式越多則網路越深。

這裡f1,f2,f3,f4代表了4組數學變換,具體是y= w x+ b 還是y= w log(x)還是y=x^2+ b ,這就取決於各種模型理論的發明了,我們完全不必展開。

當神經網路輸出y之後,我們可以計算與真實y之間的誤差(loss),此時誤差函式表達為:

loss = y’ – f1(f2(f3(f4(x))))

此時我們會發現loss比較大,證明模型擬合樣本不是很好,怎麼最佳化呢?

在相同的輸入x情況下,要想要loss值變小,只能調整f1()、f2()、f3()、f4()這些數學變換中的權重係數(例如:上面的w和b)。

每個係數調整多少呢?變大還是變小呢?如果隨機碰運氣那就訓練不出什麼模型了。

所以出現了梯度下降調整權重係數的方案:

為了讓loss可以在當前輸入的x情況下更小,我們可以將x視為常量,將f1中某個權重係數w視為變數,對loss函式求關於該w係數的導數,這樣就可以明確w係數如何調整才能讓loss函式影象向更低的位置移動,這就是梯度下降的原理。

loss函式本身是數學公式表達的,因此對loss求各個係數的導數也是數學公式推導問題,因此網路中所有權重係數均可以得到調整,即向樣本進一步擬合。

經過反覆用不同的x輸入到模型,進行loss計算與所有權重的梯度下降,模型整體上會不斷的更好擬合訓練樣本,達到一種”全域性最優”。

以tensorflow為例

我們藉由tensorflow的functional api構造模型,以此來幫助到工作實踐。

(程式碼地址: https://github.com/owenliang/tf-graph-explore/blob/main/tf-graph-explore.ipynb )

我實現了一個簡單的”雙塔”網路結構:

圖中的input、dense1、dense2、concat、dense3共5個layer,也就是我們之前說的f1()、f2()、f3()等數學函式…

輸入層接受輸入x,其形狀為2列的向量:

inputs=tf.keras.Input(shape=(2,),dtype=tf.float32,name=’input’)

定義網路要使用的3個全連線層:

dense1=tf.keras.layers.Dense(1,activation=None,name=’dense1′)  dense2=tf.keras.layers.Dense(2,activation=None,name=’dense2′)  dense3=tf.keras.layers.Dense(1,activation=None,name=’dense3′)

三個神經元均不使用啟用函式,其中dense1有1個神經元、dense2有2個神經元、dense3有1個神經元,神經元的個數決定了輸出向量的維度:

dense層的數學公式都是y=wx+b,不同神經元個數的區別是w和b係數的形狀。dense1有1個神經元,假設w=[ [0.3], [0.4] ],b=0.2,輸出y長相為[0.24]dense2有2個神經元,假設w=[ [0.3, 0.6], [0.4, 0.12]],b=[0.1, 0.35],輸出y長相為[0.1, 0.2]w的行數與x的列數相等,w的列數與神經元的個數相等,這些w和b矩陣的初始化都由tensorflow自行判斷生成。

然後我們按網路結構連線這些layer:

outputs1=dense1(inputs)  outputs2=dense2(inputs)  outputs3=tf.keras.layers.concatenate([outputs1,outputs2],axis=1,name=’concat’)  outputs4=dense3(outputs3)

這裡將dense1和dense2的輸出向量進行了concatenate連線,這裡讓人困惑的在於向量連線似乎並不能用數學符號表達,這似乎違背了神經網路是數學問題的事實,但其實連線2個向量是可以用數學公式表達的:

這裡dense1輸出是1維向量,dense2輸出的2維向量,連線後應該是3維向量。

在數學中只需要準備一個長度為3的全1向量K,讓它的第1維與dense1向量做乘法,另外2維與dense2向量做乘法,最後加起來就連線起來的效果了,總之是數學可表達的,那麼就不會影響我們對神經網路的認知。

接下來可以定義出model:

model=tf.keras.Model(inputs=inputs,outputs=outputs4)model.summary()

檢視到所有layer:

Model: "functional_1"__________________________________________________________________________________________________Layer (type)                    Output Shape         Param #     Connected to                     ==================================================================================================input (InputLayer)              [(None, 2)]          0                                            __________________________________________________________________________________________________dense1 (Dense)                  (None, 1)            3           input[0][0]                      __________________________________________________________________________________________________dense2 (Dense)                  (None, 2)            6           input[0][0]                      __________________________________________________________________________________________________concat (Concatenate)            (None, 3)            0           dense1[0][0]                                                                                      dense2[0][0]                     __________________________________________________________________________________________________dense3 (Dense)                  (None, 1)            4           concat[0][0]                     ==================================================================================================Total params: 13Trainable params: 13Non-trainable params: 0

我們關注一下每一層的權重引數個數params,可以看到dense1有3個引數需要學習,也就是w帶來的2個和b帶來的1個;dense2則是w帶來的4個和b帶來的2個;

concat層沒有需要學習的引數,因為K就是一個長度為3的[1,1,1]常量向量。

畫出網路結構會更加清晰(圖片在上面已經貼過了):

tf.keras.utils.plot_model(model, “graph.png”, show_shapes=True)

我們隨機生成10個樣本,x是2維的,y是1維的:

x=tf.random.normal(shape=(10,2))  y=tf.random.normal(shape=(10,1))  print(x)  print(y)tf.Tensor(  [[-0.8464397 -0.3152412 ]  [ 0.9817092 -0.57270414]  [ 0.86039394 0.57590604]  [-1.5055276 0.45981622]  [ 1.40179 1.0307338 ]  [ 0.5882102 2.671993 ]  [ 0.5666892 -0.33787787]  [ 0.36999676 0.5678155 ]  [ 2.131917 0.33147094]  [-0.23225114 0.84211487]], shape=(10, 2), dtype=float32)  tf.Tensor(  [[-0.9018226 ]  [-0.83541167]  [-0.70780784]  [ 0.43620512]  [-1.2712636 ]  [ 0.39236164]  [ 0.11044435]  [ 2.7505376 ]  [ 0.64985305]  [-1.4352192 ]], shape=(10, 1), dtype=float32)

然後向model輸入10行樣本x,計算返回10個y:

pred_y = model(data)  print(pred_y)tf.Tensor(  [[ 1.4824426 ]  [ 0.5423604 ]  [-1.0151732 ]  [ 2.2172787 ]  [-1.9427378 ]  [-1.8751484 ]  [ 2.486527 ]  [ 0.03885526]  [-0.62673503]  [ 0.23725389]], shape=(10, 1), dtype=float32)

為了最佳化模型,我們需要計算pred_y與y之間的誤差loss,並對loss函式在當前x輸入的情況下對各個權重係數進行梯度求導:

loss_f = tf.keras.losses.MeanAbsoluteError()with tf.GradientTape() as tape:    pred_y = model(data) # 模型計算    loss = loss_f(y, pred_y) # 計算誤差    print(loss) # 列印損失grads=tape.gradient(loss, model.trainable_variables) # 反向傳播誤差到各層, 對各權重係數求梯度print(grads)

我們將整個loss函式的完整數學表示式(計算過程)用tape錄製下來,這樣tensorflow可以自動幫我們求出所有各個權重係數的導數,grads是網路中所有權重引數的梯度:

tf.Tensor(1.6826286, shape=(), dtype=float32)  [<tf.Tensor: shape=(2, 1), dtype=float32, numpy=  array([[ 0.37924558],  [-0.17607355]], dtype=float32)>, <tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.], dtype=float32)>, <tf.Tensor: shape=(2, 2), dtype=float32, numpy=  array([[-0.28494048, 1.0743711 ],  [ 0.13229021, -0.4988016 ]], dtype=float32)>, <tf.Tensor: shape=(2,), dtype=float32, numpy=array([0., 0.], dtype=float32)>, <tf.Tensor: shape=(3, 1), dtype=float32, numpy=  array([[ 0.02184648],  [ 1.1978055 ],  [-0.9128995 ]], dtype=float32)>, <tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.], dtype=float32)>]

列印當前Loss是1.6826286。

列印grads是按某種順序排列好的模型中的各個權重的導數,稍後將這些導數應用到對應的權重引數上即可。

optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)

optimizer.apply_gradients(zip(grads, model.trainable_variables)) # 將梯度應用到各個權重係數

利用zip將各個權重引數的梯度與對應的權重引數繫結到一起,然後交給optimizer完成最終的修改,此時模型已經得到了一輪最佳化,理論上更加擬合訓練資料,為此我們重新進行一次預測:

pred_y = model(data) # 使用學習後的模型再預測  print(pred_y)tf.Tensor(  [[ 1.4320835 ]  [ 0.53110886]  [-0.9584385 ]他們說是  [ 2.1270993 ]  [-1.888143 ]  [-1.8089024 ]  [ 2.3986485 ]  [ 0.03384402]  [-0.5806757 ]  [ 0.22927463]], shape=(10, 1), dtype=float32)

看一下現在的loss是否更小了:

loss = loss_f(y, pred_y)  print(loss)tf.Tensor(1.6360016, shape=(), dtype=float32)

最佳化過一次的模型loss為1.6360016,比之前的1.6826286要小,說明最佳化過程有效。

出處:https://yuerblog.cc/2020/12/14/tensorflow-%E9%80%8F%E8%BF%87%E6%95%B0%E5%AD%A6%E7%90%86%E8%A7%A3model/

14
  • 「完整」2022年中級註冊安全工程師《化工安全實務》真題解析
  • ED悲劇了···怎麼辦