全文共4431字,預計學習時長9分鐘
深度學習是目前在實際數據科學應用中最流行的模型之一。從圖像到文本,再到語音/音樂等領域,它都是一個有效的模型。隨著使用量的上升,快速且可擴展地實現深度學習變得至關重要。Tensorflow等深度學習平臺的興起可以幫助簡化開發人員的實現方式。
在本文中,我們將學習深度學習的工作原理,並熟悉相關術語,例如反向傳播和批量大小。我們將實現一個簡單的深度學習模型 - 從理論到scratch實現 - 使用python預定義輸入和輸出,然後使用Keras和Tensorflow等深度學習平臺實現相同的模型。
我們使用Keras和Tensorflow 1.x和2.0編寫了這個簡單的深度學習模型,該模型具有三種不同級別的複雜性和易編碼性。
Scratch的深度學習實現
一個簡單的多層感知器有4個輸入神經元,隱含層有3個神經元,輸出層有1個神經元。我們有三個輸入數據樣本,用表示,三個期望輸出數據樣本,用表示。因此,每個輸入數據樣本都有4個特徵。
# Inputs and outputs of the neural net:import numpy as npX=np.array([[1.0, 0.0, 1.0, 0.0],[1.0, 0.0, 1.0, 1.0],[0.0, 1.0, 0.0, 1.0]])yt=np.array([[1.0],[1.0],[0.0]])
該神經網絡有4個輸入神經元,1個隱含層有3個神經元,輸出層有1個神經元
圖中 x(m)是X的1個樣本,h(m)是x(m) 輸入的隱含層輸出,Wi和Wh為權重。
神經網絡(NN)的目標是為給定輸入獲得權重和偏差,NN提供期望輸出。但是,我們事先並不知道什麼是適當的權重和偏差,因此我們需要更新權重和偏差,使得NN、和期望的的輸出之間的誤差最小化。這種用迭代的方式最小化誤差過程稱為神經網絡訓練。
假設隱含層和輸出層的激活函數都是sigmoid函數。因此,
神經網絡的權值大小、偏差以及輸入和輸出之間的關係
其中,激活函數是sigmoid函數,m是第m個數據樣本, yp(m) 是NN輸出。
衡量NN輸出與期望輸出之間差異的誤差函數可以用數學方式表示為:
神經網絡所定義的誤差是平方誤差
上述NN的偽代碼總結如下:
用於神經網絡訓練的偽代碼
通過偽代碼得出,我們需要計算誤差(E)相對於參數(權重和偏差)的偏導數。利用微積分中的鏈式法則,表示如下:
誤差函數對權重的導數
在此我們有兩種方式可以用來更新反向路徑中的權重和偏差(反向路徑指更新權重和偏差,從而最小化誤差):
1. 使用訓練數據的全部個樣本
2. 使用一個樣本或一簇樣本
第一種方式的批量大小是。如果第二種方式使用一個樣本來更新參數,則其批量大小是1。因此批量大小代表用於更新權重和偏差的數據樣本數量。
從上述神經網絡的實現可以得出,關於參數的誤差梯度是按符號計算的,其中具有不同的批量大小。
如上述例子所示,基於scratch創建簡單的深度學習模型,方法十分複雜。下一節將介紹如何通過深度學習框架實現可擴展且簡單的模型。
Keras、Tensorflow 1.x及2.0的深度學習實現
在上一節中,我們運用鏈式法則計算了誤差梯度參數。這不是一種簡單或可擴展的方法。同時,我們會在每次迭代時評估偏導數,因此,儘管偏導數的值很重要,但不需要梯度符號。此時Keras和Tensorflow等深度學習框架可以發揮其作用。深度學習框架使用AutoDiff方法對部分梯度進行數值計算。如果你對AutoDiff不熟悉,那麼StackExchange就是一個很好的例子。AutoDiff將複雜表達式分解為一組原始表達式,即最多由一個函數調用組成的表達式。由於已知每個單獨表達式的區分規則,因此可以以有效的方式計算最終結果。
我們在Keras、Tensorflow 1.x和Tensorflow 2.0中實現了三個不同級別的NN模型:
1. 高級(Keras和Tensorflow 2.0):
高級Tensorflow 2.0,批量大小1
https://github.com/miladtoutounchian/Deep-Learning-/blob/master/TF_v2_HighLevel_batchsize1_train_on_batch.py
2. 中級(Tensorflow 1.x和2.0):
中級Tensorflow 1.x,批量大小1,
https://github.com/miladtoutounchian/Deep-Learning-/blob/master/TF_v1_MediumLevel_batchsize1.py
中級Tensorflow 1.x,批量大小N,
https://github.com/miladtoutounchian/Deep-Learning-/blob/master/TF_v1_MediumLevel_batchsizeN.py
中級Tensorflow 2.0,批量大小1,
https://github.com/miladtoutounchian/Deep-Learning-/blob/master/TF_v2_MediumLevel_batchsize1.py
中級Tensorflow 2.0,批量大小N
https://github.com/miladtoutounchian/Deep-Learning-/blob/master/TF_v2_MediumLevel_batchsizeN.py
3. 初級(Tensorflow 1.x):
初級Tensorflow 1.x,批量大小N
https://github.com/miladtoutounchian/Deep-Learning-/blob/master/TF_v1_LowLevel_batchsizeN.py
代碼片段:
在高級實現中,基於model.train_on_batch運用keras和Tensorflow v 2.0實現模型:
# High-Level implementation of the neural net in Tensorflow:model.compile(loss=mse,optimizer=optimizer)for _ in range(2000):for step, (x, y) in enumerate(zip(X_data, y_data)):model.train_on_batch(np.array([x]), np.array([y]))
在Tensorflow 1.x的中級實現中,定義如下:
E = tf.reduce_sum(tf.pow(ypred - Y, 2))optimizer = tf.train.GradientDescentOptimizer(0.1)grads = optimizer.compute_gradients(E, [W_h, b_h, W_o, b_o])updates = optimizer.apply_gradients(grads)
以此確保在for循環中更新需要更新的變量。在中級實現中,梯度及其更新在for循環外部進行定義,內部則為迭代更新。在Tensorflow v 2.x的中級實現中,使用如下:
# Medium-Level implementation of the neural net in Tensorflow#
In for_loop
with tf.GradientTape() as tape: x = tf.convert_to_tensor(np.array([x]), dtype=tf.float64) y = tf.convert_to_tensor(np.array([y]), dtype=tf.float64) ypred = model(x) loss = mse(y, ypred)gradients = tape.gradient(loss, model.trainable_weights)optimizer.apply_gradients(zip(gradients,model.trainable_weights))
在初級實現中,分別更新每個權重和偏差。在Tensorflow v 1.x初級實現中,定義如下:
# Low-Level implementation of the neural net in Tensorflow:E = tf.reduce_sum(tf.pow(ypred - Y, 2))dE_dW_h = tf.gradients(E, [W_h])[0]dE_db_h = tf.gradients(E, [b_h])[0]dE_dW_o = tf.gradients(E, [W_o])[0]dE_db_o = tf.gradients(E, [b_o])[0]# In for_loop:evaluated_dE_dW_h = sess.run(dE_dW_h, feed_dict={W_h: W_h_i, b_h: b_h_i, W_o: W_o_i, b_o: b_o_i, X: X_data.T, Y: y_data.T}) W_h_i = W_h_i - 0.1 * evaluated_dE_dW_h evaluated_dE_db_h = sess.run(dE_db_h, feed_dict={W_h: W_h_i, b_h: b_h_i, W_o: W_o_i, b_o: b_o_i, X: X_data.T, Y: y_data.T}) b_h_i = b_h_i - 0.1 * evaluated_dE_db_h evaluated_dE_dW_o = sess.run(dE_dW_o, feed_dict={W_h: W_h_i, b_h: b_h_i, W_o: W_o_i, b_o: b_o_i, X: X_data.T, Y: y_data.T}) W_o_i = W_o_i - 0.1 * evaluated_dE_dW_o evaluated_dE_db_o = sess.run(dE_db_o, feed_dict={W_h: W_h_i, b_h: b_h_i, W_o: W_o_i, b_o: b_o_i, X: X_data.T, Y: y_data.T}) b_o_i = b_o_i - 0.1 * evaluated_dE_db_o
如初級實現所述,開發人員可以更好地控制數值運算和計算的每一步。
上述所示,在Scratch的深度學習實現中,即便只是通過使用梯度符號計算建立一個簡單的深度學習模型來更新權重和偏差並不是一種簡單或可擴展的方法。而使用用於更新權重和偏差的穩定數值梯度計算AutoDiff,深度學習框架可以加速此進程。
留言 點讚 關注
我們一起分享AI學習與發展的乾貨
編譯組:張淑霏、王書晗
相關連結:
https://towardsdatascience.com/deep-learning-from-scratch-and-using-tensorflow-in-python-34aad75f939
如需轉載,請後臺留言,遵守轉載規範