深度學習-數值計算基礎
數值計算
對於機器學習中的問題,有一部分可以通過數學推導的方式直接得到用公式表達的解析解,但對絕大多數的問題來說,解析解是不存在的,需要使用迭代更新的方法求數值解。然而實數的精度是無限的,計算機能夠表達的精度是有限的,這就涉及到許多數值計算方法的問題。
1. 基本概念
-
上溢和下溢:由於計算機表達實數的精度的有限,在某些情況下許多複雜的複合運算中的四捨五入會導致一個接近0的小數變為0或者一個非常大的數被認為是無窮,這都會導致很嚴重的後果。
-
病態條件:條件數指的是函式相對於輸入的微小變化而變化的快慢程度,輸入被輕微擾動而迅速改變的函式對於科學計算來說可能會出現問題。
-
目標函式,損失函式(Objective function, Loss function):在求解機器學習問題時需要最大化或者最小化的函式目標。
-
導數:對於單變數函式來說,其導數表示在在x的斜率大小。
-
偏導數:多變數函式針對某單一變數的導數,例如相對與的偏導數為。
-
梯度:梯度是一個向量,向量中的元素是函式的偏導數,對於來說,其梯度,梯度表示函式在當前變化最快的方向。
-
雅克比矩陣(Jacobian Matrix):對於函式,其雅克比矩陣被定義為。
-
海森矩陣(Hessian Matrix):對於函式,其海森矩陣是二階導陣列成的矩陣,被定義為。
-
極值點&鞍點:當對於函式來說,其導數等於0的點可能會存在的情況如下圖所示,點對圖中三個函式來說分別是極大值點、極小值點、鞍點(非極值點)。對於多層的神經網路來說,鞍點是非常常見的,在迭代優化的時候需要使用一些方法跳出。
-
全域性最小點:在函式的定義域上取得全域性最小值的點,這通常來說是許多機器學習問題所要尋找的優化目標點。對神經網路中問題的損失函式來說,可能存在很多的的區域性最小點,它們與全域性的最小點差距不大,應此通常也可以作為問題的解(因為要求出全域性最小點是一個幾乎不可能的任務,所以我們一般選擇區域性的最優值即可),下面是一個示例。
2. 基於梯度的優化方法
-
梯度下降法:根據導數的計算公式,可以有,為了求到目標函式的最小值,我們可以將向梯度的相反方向移動一小段距離來進行逼近。
-
一階優化演算法:僅適用梯度資訊的優化演算法,如梯度下降法。
-
二階優化演算法:使用海森矩陣進行優化的演算法,如牛頓法。
-
約束優化:在求解優化目標時,可能需要在函式定義域的某個子集中得到極值,這種條件下的優化問題被稱為約束優化,一般使用構造拉格朗日函式利用KKT條件的方法求解。
3. 利用梯度下降法求解的例項
問題
假設有一些樣本點和對應的目標,已知可以由加上噪音生成,求解函式的引數和。
求解
首先定義相對於的損失函式(這裡一般使用均方誤差,比較預測值和真實值的差距)
那麼損失函式相對於引數和的導數分別為:
根據梯度下降法的公式可以得到引數的更新公式:
下面是利用numpy實現上述過求解的程式碼(可以在jupyter nootbook中直接執行):
import numpy as np
import matplotlib.pyplot as plt
## 定義w和b
#隨機生成10個在[0.0,1.0)之間的數作為初始權值
feature_num = 10
w_real = np.random.random(feature_num)
print(w_real)
b_real = np.random.random()
## 生成訓練資料
instance_num = 1000 #假設實力數目為1000
X = np.random.uniform(-100,100,(feature_num, instance_num))
#y=wx+b
y = np.matmul(w_real, X) + b_real
y = y + np.random.random(y.shape)
## 初始化引數
w = np.random.random(feature_num)
b = np.random.random()
#迭代20次,步長為0.0001
iter_time = 20
step_size = 0.0001
#記錄損失值
loss_value = []
## 迭代求解
for i in range(iter_time):
#誤差
delta = np.matmul(w, X) + b_real - y
#均方誤差
loss_value.append((delta*delta).mean())
#更新系數w,b
w = w - step_size*(np.matmul(delta, X.T))/instance_num
print('w=',w)
b = b - step_size*delta.mean()
print('b=',b)
for t in loss_value:
print(t)
plt.clf
plt.plot(np.array(loss_value[1:]))
plt.title("gradient_descent_example")
plt.show()
執行上述程式碼可以得到是迭代過程中的損失變化情況,如下圖示:
我們還可以打印出w和b在迭代過程中值的變化:
同樣可以打印出每輪迭代中的損失值(可以看出損失值越來越小,說明梯度下降是成功的):
最後可以實際觀察一下w和w_real的值,可以看到經過梯度迭代,確實可以得到引數的值。
上面的示例中,可以根據梯度迭代公式很容易且快速的迭代算出引數值,然而實際的機器學習問題往往比上面的問題複雜的多,因此迭代過程也不像上面的例子一樣簡單,而是涉及到許多技巧。
numpy中random.random()的用法:
numpy中random.uniform()的用法:
在這裡可以查詢numpy函式的相關使用
numpy.matmul