Tensorflow深度學習-第二天

上面講到了我們通過採樣多個數據樣本及,然後找到一條最好的直線,使其儘可能地讓所有采集點到該直線到誤差之和最小。

但是由於觀測誤差的存在,當我們採集多個數據點時,可能不存在一條直線完美的穿過所有采樣點。退而求其次,我們希望能找到一條比較“好”的位於採樣點中間的直線。

那麼怎麼衡量“好”與“不好”呢?一個很自然的想法就是,求出當前模型的 所有采樣點上的預測值𝑤𝑥(𝑖) + 𝑏與真實值𝑦(𝑖)之間的差的平方和作爲總誤差L:

L = \frac{1}{n} \sum_{i=1}^{n}\left (W ^{x\left ( i \right )} + b - y^{\left ( i \right )}\right)^{2}

latex公式的寫法
L = \frac{1}{n} \sum_{i=1}^{n}\left (W ^{x\left ( i \right )} + b - y^{\left ( i \right )}\right)^{2}

然後搜索一組參數w,b使得L最小,對應的直線就是我們要尋找的最優直線

w, b = arg_{w,b} min\frac{1}{n} \sum_{i=1}^{n}\left (W ^{x\left ( i \right )} + b - y^{\left ( i \right )}\right)^{2}

w, b = arg_{w,b} min\frac{1}{n} \sum_{i=1}^{n}\left (W ^{x\left ( i \right )} + b - y^{\left ( i \right )}\right)^{2}

𝑛表示採樣點的個數。這種誤差計算方法稱爲均方誤差MSE。

優化:

最簡單的優化方法就是暴力搜索或隨機試驗,比如要找出最合適的𝑤∗和𝑏∗,我們就可以從 (部分)實數空間中隨機採樣任意的𝑤和𝑏,並計算出對應模型的誤差值L,然後從測試過的 {L}中挑出最好的L∗,它所對應的𝑤和𝑏就可以作爲我們要找的最優𝑤∗和𝑏∗。

這種算法固然簡單直接,但是面對大規模、高維度數據的優化問題時計算效率極低, 基本不可行。梯度下降算法(Gradient Descent)是神經網絡訓練中最常用的優化算法,配合 強大的圖形處理芯片 GPU(Graphics Processing Unit)的並行加速能力,非常適合優化海量數 據的神經網絡模型,自然也適合優化我們這裏的神經元線性模型。

我們在高中時代學過導數(Derivative)的概念,如果要求解一個函數的極大、極小值, 可以簡單地令導數函數爲 0,求出對應的自變量點(稱爲駐點),再檢驗駐點類型即可。

函數導數(虛線)爲 0 的點即爲𝑓(𝑥)的駐點,函數的極大值和極小值點均出現在駐點中。導數大於0,單調遞增,導數小於0,單調遞減。

箭頭的方向總是指向當前位置函數值增速最 大的方向,函數曲面越陡峭,箭頭的長度也就越長,梯度的模也越大。

我們能直觀地感受到,函數在各處的梯度方向∇𝑓總是指向函數值增 大的方向,那麼梯度的反方向−∇𝑓應指向函數值減少的方向。利用這一性質,我們只需要 按照

x^{'} = x - \eta \cdot \Delta f

沒有在希臘字母中找到∇。

來迭代更新𝒙′,就能獲得越來越小的函數值,其中𝜂用來縮放梯度向量,一般設置爲某較小

的值,如 0.01、0.001 等。特別地,對於一維函數,上述向量形式可以退化成標量形式:

x^{'} = x - \eta\cdot \frac {dy}{dx}

通過上式迭代更新𝑥′若干次,這樣得到的𝑥′處的函數值𝑦′,總是更有可能比在𝑥處的函數值𝑦小。

通過優化參數的方法稱爲梯度下降算法,它通過循環計算函數的梯度∇𝑓並 更新待優化參數𝜃,從而得到函數𝑓獲得極小值時參數𝜃的最優數值解。需要注意的是,在深度學習中,一般𝒙表示模型輸入,模型的待優化參數一般用𝜃、𝑤、𝑏等符號表示。

w^{'} = w- \eta\cdot \frac {dL}{w}

b^{'} = b - \eta\cdot \frac {dL}{db}

在計算出誤差函數在𝑤和𝑏處的梯度後,我們可以根據式(2.1)來更新𝑤和𝑏的值。我們把 對數據集的所有樣本訓練一次稱爲一個 Epoch,共循環迭代 num_iterations 個 Epoch。

# #我在這個簡單實驗中遇到的問題,
# 1. 是複製代碼的時候,計算梯度的for語句被註釋,
# 2.是初始賦值順序錯誤
# 3. 還是對於原理理解不透徹
# 這個原理是使loss最小,所以需要計算梯度來使得w,b對應的函數損失值更小。

# #我在這個簡單實驗中遇到的問題,
# 1. 是複製代碼的時候,計算梯度的for語句被註釋,
# 2. 是初始賦值順序錯誤
# 3. 還是對於原理理解不透徹
# 4. 損失函數是用來衡量預測值與真實值之間的差距,所以當該損失函數越小時,說明有越多的點時和實際值相符合的,所以需要反向梯度傳播算法。
# 5. 梯度方向總是指向函數增大的方向,我們需要減小,才能實現。所以該算法才叫反向梯度傳播算法。
#    這個原理是使loss最小,所以需要計算梯度來使得w,b對應的函數損失值更小。

線性迴歸問題

jupyter notebook代碼:

import numpy as np
data = []# 保存樣本集的列表
for i in range(100): # 循環採樣 100 個點
    x = np.random.uniform(-10., 10.) # 隨機採樣輸入 x # 採樣高斯噪聲
    eps = np.random.normal(0., 0.01)
    # 得到模型的輸出
    y = 1.477 * x + 0.089 + eps
    data.append([x, y]) # 保存樣本點
data = np.array(data) # 轉換爲 2D Numpy 數組
import matplotlib.pyplot as plt#約定俗成的寫法plt

#plt.figure()

x = data[:,0]
y = data[:,1]

fig, ax1 = plt.subplots()
ax1.set_xlabel('Time (s)')
ax1.set_ylabel('Throughput (Gbps)', color='black')
ax1.set_ylim(-10,10,1)
ax1.set_xlim(-10,10, 1)
l1, = ax1.plot(x, y,color='red',label='first_flow')
plt.show()
def mse(b, w, points):
# 根據當前的 w,b 參數計算均方差損失
    totalError = 0
    for i in range(0, len(points)): # 循環迭代所有點
        x = points[i, 0] # 獲得i號點的輸入x 
        y = points[i, 1] # 獲得i號點的輸出y # 計算差的平方,並累加
        totalError += (y - (w * x + b)) ** 2 # 將累加的誤差求平均,得到均方差
        #print(totalError / float(len(points)))
    return totalError / float(len(points))
def step_gradient(b_current, w_current, points, lr): # 計算誤差函數在所有點上的導數,並更新 w,b
    b_gradient = 0
    w_gradient = 0
    M = float(len(points)) # 總樣本數 
    for i in range(0, len(points)):
        x = points[i, 0]
        y = points[i, 1]
# 誤差函數對 b 的導數:grad_b = 2(wx+b-y),參考公式(2.3) 

        b_gradient += (2/M) * ( (w_current * x + b_current) - y) # 誤差函數對 w 的導數:grad_w = 2(wx+b-y)*x,參考公式(2.2)
        w_gradient += (2/M) * x * ((w_current * x + b_current) - y ) # 根據梯度下降算法更新 w',b',其中 lr 爲學習率
   
    #print(b_gradient,w_gradient)

    new_b = b_current - (lr * b_gradient)
    new_w = w_current - (lr * w_gradient)
    
    return [new_b, new_w]
def gradient_descent(points, starting_b, starting_w, lr, num_iterations): # 循環更新 w,b 多次
    b = starting_b # b的初始值
    w = starting_w # w的初始值
    #print(b,w)
# 根據梯度下降算法更新多次
    for step in range(num_iterations):
# 計算梯度並更新一次
        loss = mse(b, w, points) # 計算當前的均方差,用於監控訓練進度 
        b, w = step_gradient(b, w, np.array(points), lr)
        if step%50 == 0: # 打印誤差和實時的 w,b 值
            print(f"iteration:{step}, loss:{loss}, w:{w}, b:{b}") 
    return [b, w] # 返回最後一次的 w,b
def main():
    lr = 0.001 # 學習率 
    initial_b =  0# 初始化b爲0
    initial_w = 0 # 初始化w爲0
    num_iterations = 1000
# 訓練優化1000次,返回最優w*,b*和訓練Loss的下降過程
    [b, w]= gradient_descent(data, initial_b, initial_w, lr, num_iterations) 
    loss = mse(b, w, data) # 計算最優數值解w,b上的均方差
    print(f'Final loss:{loss}, w:{w}, b:{b}')


if __name__ == '__main__':
    main()
fig, ax1 = plt.subplots()
ax1.set_xlabel('Time (s)')
ax1.set_ylabel('Throughput (Gbps)', color='black')
ax1.set_ylim(-10,10,1)
ax1.set_xlim(-10,10, 1)
l2, = ax1.plot(x, 1.4768604696062602 * x + 0.07821812190464268,color='red',label='first_flow')
put.show()

線性迴歸問題其實可以理解爲一組連續值(向量)的預測問題。給定數據集𝔻,我們需要從𝔻中學習到數據的真實模型,從而預測未見過的樣本的輸出值。在假定模型的類型後,學習過程就變成了搜索模型參數的問題,比如我們假設神經元爲線性模型,那麼訓練過程即爲搜索線性模型的𝒘和𝑏參數的過程。訓練完成後,利用學到的模型,對於任意的新輸入𝒙,我們就可以使用學習模型輸出值作爲真實值的近似。從 這個角度來看,它就是一個連續值的預測問題。

在現實生活中,連續值預測問題是非常常見的,比如股價的走勢預測、天氣預報中溫 度和溼度等的預測、年齡的預測、交通流量的預測等。對於預測值是連續的實數範圍,或 者屬於某一段連續的實數區間,我們把這種問題稱爲迴歸(Regression)問題。特別地,如果 使用線性模型去逼近真實模型,那麼我們把這一類方法叫做線性迴歸(Linear Regression, 簡稱 LR),線性迴歸是迴歸問題中的一種具體的實現。

分類問題:

在機器學習中間, 一般希望數據的範圍在 0 周圍的小範圍內分佈。通過預處理步驟,我們把[0,255]像素範圍 歸一化(Normalize)到[0,1. ]區間,再縮放到[−1,1]區間,從而有利於模型的訓練。

每一張圖片的計算流程是通用的,我們在計算的過程中可以一次進行多張圖片的計 算,充分利用 CPU 或 GPU 的並行計算能力。我們用形狀爲[h, 𝑤]的矩陣來表示一張圖片, 對於多張圖片來說,我們在前面添加一個數量維度(Dimension),使用形狀爲[𝑏, h, 𝑤]的張量 來表示,其中𝑏代表了批量(Batch Size);多張彩色圖片可以使用形狀爲[𝑏, h, 𝑤, 𝑐]的張量來 表示,其中𝑐表示通道數量(Channel),彩色圖片𝑐 = 3。通過 TensorFlow 的 Dataset 對象可 以方便完成模型的批量訓練,只需要調用 batch()函數即可構建帶 batch 功能的數據集對 象。

考慮輸入格式,一張灰度圖片𝒙使用矩陣方式存儲,形狀爲:[h, 𝑤],𝑏張圖片使用形狀 爲[𝑏, h, 𝑤]的張量𝑿存儲。而我們模型只能接受向量形式的輸入特徵向量,因此需要將[h, 𝑤] 的矩陣形式圖片特徵打平成[h ∙ 𝑤]長度的向量,如圖 3.5 所示,其中輸入特徵的長度𝑑in = h ∙ 𝑤。

 

矩陣打平操作示意圖

對於輸出標籤𝑦,前面已經介紹了數字編碼,它可以用一個數字來表示標籤信息,此 時輸出只需要一個節點即可表示網絡的預測類別值,例如數字 1 表示貓,數字 3 表示魚等 (編程實現時一般從 0 開始編號)。但是數字編碼一個最大的問題是,數字之間存在天然的 大小關係,例如1 < 2 < 3,如果 1、2、3 分別對應的標籤是貓、狗、魚,他們之間並沒有 大小關係,所以採用數字編碼的時候會迫使模型去學習這種不必要的約束。

那麼怎麼解決這個問題呢?可以將輸出設置爲𝑑out個輸出節點的向量,𝑑out與類別數相 同,讓第𝑖 ∈ [1, 𝑑out]個輸出節點的值表示當前樣本屬於類別𝑖的概率𝑃(𝒙屬於類別𝑖|𝒙)。我 們只考慮輸入圖片只輸入一個類別的情況,此時輸入圖片的真實標籤已經唯一確定:如果 物體屬於第𝑖類的話,那麼索引爲𝑖的位置上設置爲 1,其他位置設置爲 0,我們把這種編碼 方式叫作 one-hot 編碼(獨熱編碼)。

One-hot 編碼是非常稀疏 (Sparse)的,相對於數字編碼來說,佔用較多的存儲空間,所以一般在存儲時還是採用數字

編碼,在計算時,根據需要來把數字編碼轉換成 One-hot 編碼,通過 tf.one_hot 函數即可實現。

舉例說明:
 

tf.constant([0,1,2,3]) # 數字編碼的4個樣本標籤
y = tf.one_hot(y, depth=10) # one-hot編碼,指定類別總數爲10
y
<tf.Tensor: id=21, shape=(4, 10), dtype=float32, numpy=
array([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.]], dtype=float32)>

對於分類問題來說,我們的目標是最大化某個性能指標,比如準確度𝑎𝑐𝑐,但是把準確度當作損失函數去優化時,會發現𝜕𝑎𝑐𝑐其實是不可導的,無法利用梯度下降算法優化網絡參數𝜃。一般的做法是,設立一個平滑可導的代理目標函數,比如優化模型的輸出 與 One-hot 編碼後的真實標籤𝒚之間的距離(Distance),通過優化代理目標函數得到的模型,一般在測試性能上也能有良好的表現。因此,相對迴歸問題而言,分類問題的優化目標函數和評價目標函數是不一致的。模型的訓練目標是通過優化損失函數L來找到最優數值解𝑾 , 𝒃 :

w, b = \underbrace{arg min}_{w, b}L(o, y)

w, b = \underbrace{arg min}_{w, b}L(o, y)

對於分類問題的誤差計算來說,更常見的是採用交叉熵(Cross Entropy)損失函數,較少採用 迴歸問題中介紹的均方差損失函數。我們將在後續章節介紹交叉熵損失函數,這裏仍然使 用均方差損失函數來求解手寫數字識別問題。

既然線性模型不可行,我們可以給線性模型嵌套一個非線性函數,即可將其轉換爲非線性模型。我們把這個非線性函數稱爲激活函數(Activation Function),用𝜎表示:

在 TensorFlow 中間,爲了表達方便,一般把標量、向量、矩陣也統稱爲張量,不作區 分,需要根據張量的維度數或形狀自行判斷,本書也沿用此方式。

tf.constant([1,2.,3.3])
<tf.Tensor: id=23, shape=(3,), dtype=float32, numpy=array([1. , 2. , 3.3], dtype=float32)>

id 是 TensorFlow 中內部索引對象的編號,shape 表示張量的形狀,dtype 表示張量的數 值精度,張量 numpy()方法可以返回 Numpy.array 類型的數據。

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章