線性迴歸 (Linear Regression)
如果你開始學習深度學習的相關知識,通常第一個學到的方法是線性回歸 ,為什麼要談到線性回歸?線性回歸可以被用來預測連續數值,例如:房價預測、股票預測、薪水預測等,前提是我們想要解決的問題是線性的。
何謂線性(Linear)?
簡單來說只要函數的樣子長得像下面的數學式就稱做線性:
(1)沒有變數擁有 1 以上的指數
(2)沒有變數在絕對值中
(3)沒有變數在符號函數(Sign function)中
何謂回歸(Regression)?
回歸是一種統計學上分析數據的方法,其目的在於找出一個最能夠代表觀測資料關係的函數,它所得出的結論是連續的。例如:想找出年齡對應薪資的關係,隨著時間的股票預測。
在只有兩個變數的平面上,也就是二維空間,找出一條能夠代表資料的線,我們稱之為簡單線性回歸(Simple Linear Regression)。
三維以上的空間中,利用線性回歸找出一能夠代表資料的超平面(Hyperplane),我們稱之為多元線性回歸 (Multiple Linear Regression)。
實作線性回歸
先前我們已經了解線性回歸的用途與常見的模型,你現在一定滿肚子疑惑,實際上該怎麼利用線性回歸的找出能夠代表資料模型呢?
用實際的問題舉例,假設今天有一位老闆想雇用新人,想找到薪資與年資的對應的關係,而那位老闆在公司中已經有許多在職多年的員工,所以他想要利用公司中舊有的員工資料,藉此決定一條能夠代表薪資與年資關係的直線 y=wx+b,用這條直線訂定該給新進員工多少薪資。
線性回歸從零開始,使用 TensorFlow
首先,我們要先下載資料(檔案連結),這是一個虛構的薪資與年資對應資料,它總共有兩個欄位,YearsExperience(年資) 與 Salary(薪資)。
導入所需套件
在線性回歸這個範例中,我們總共會用到五個套件:
Numpy: Numpy 處理多維度資料比起 Python 中的 List 有速度快且方便的優勢。
Pandas: 它是一個存取、處理表格資料非常方便的套件,它將協助我們讀取和處理檔案資料。
random: 在這個例子中,將被作為打亂資料的工具。
TensorFlow: 深度學習的框架。
Matplotlib: 資料視覺化的套件。
導入資料,對資料做特徵標準化
使用 Pandas 讀取 csv 檔時,會自動將表格資料轉換成 DataFrame
,使用 DataFrame
可以非常便利地操縱表格中的資料,且經常會搭配 Numpy 一起使用,這也是在作資料處理時經常會用到的套件。
在這個例子中,我們會將年資當作 features,薪水當作 labels,但是這時候會發現一個問題,即是資料級距過大,薪水的標準差高達 26953,因此在做線性回歸時,容易造成梯度爆炸(Exploding Gradients)的問題。
線性回歸的目標是讓均方誤差 (MSE) 的值趨近於 0,如果參數的初始值設定得不好,均方誤差的結果將會高達 2695³²,因此要讓模型收斂是一件很困難的事情。
解決梯度爆炸的問題,其中一個解決的辦法是特徵標準化,什麼是特徵標準化呢?特徵標準化是讓特徵的值縮放到極小的範圍中,例如在這裡就將特徵縮放到 0 ~ 1 之間,因此均方誤差的值很容易就能落在 0 ~ 1 之間,藉此預防梯度爆炸。
定義超參數
你一定很好奇超參數 (hyperparameters) 與參數 (parameters) 有什麼不同呢?
超參數指的是在訓練模型前預先定義的變數,例如:批量大小 (batch size)、學習速率 (learning rate),迭代次數 (epochs),以及類神經網路的層數與節點數等。
參數指的是類神經網路中經由學習會改變的變數,例如:權重 (weights) 、偏差值 (bias) 等。
定義模型
接著,我們要定義線性回歸的模型,資料有 YearsExperience(年資) 與 Salary(薪資),分別作為特徵 (features) 與標記 (labels),在「實作線性回歸」中有提到欲用 y=wx+b 代表薪資與年資關係的直線,也就是輸入年資(x) 後,會輸出一個相對應的薪資 (y)。
如果將 y = wx+b 轉換成類神經網路圖,則會看到 2 個輸入節點與 1 個輸出節點,所以線性回歸可以說是最簡單的類神經網路模型。
tf.placeholder()
placeholder
是 TensorFlow 中一個很有趣的用法,可以預先在神經網路中 佔位,而不用事先定義好輸入的資料,這也是 TensorFlow 中計算圖(Computing Graph)的概念,之後再使用 Session 將資料傳入計算圖中。
在 placeholder
中定義節點的形狀 shape=(None, num_inputs)
,None
的意思是二維矩陣的 row 事先尚未定義,也就是「不定義輸入資料的筆數」,隨著透過 Session 將資料傳入計算圖時,動態符合輸入資料的筆數。
tf.Variable()
在我們談完 placeholder
後,接著談到 TensorFlow 另一個重要的基石Variable
。placeholder
用於在計算圖中傳入 feature 與 label,Variable
則是在訓練神經網路時,儲存可更新參數的容器。
權重 (weight) 和偏差值 (bias) 皆是我們在訓練神經網路時需要優化的對象,經由優化權重和偏差值,讓神經網路可以擬合 features 與 labels 之間的對應。
損失函數 (Loss Function)
在尋找回歸模型時,經常會用到均方誤差 (Mean Squeared Error, MSE),通過均方誤差,尋找模型預測值與資料實際值的誤差為最小。
若用一張圖來解釋為何要使用均方誤差,可以想像是線性回歸最終的結果要讓每一點至線上的距離最小化,但是由於現實的資料不太可能呈現線性分佈,所以均方誤差幾乎不可能收斂至零。
優化算法 (Optimizer)
在線性回歸中,最終的目的是最小化均方誤差的,也就是要讓預測值愈來愈接近真實值。
在訓練模型時,會透過多次迭代,利用梯度下降優化參數,實際上梯度下降會怎麼做呢?你可以想現在站在山上的某個位置 (預測值),目標是以最快的速度下山,所以你目測 (損失函數) 看距離山谷 (真實值) 多遠,你發現某一個方向 (梯度) 是能用最快的速度抵達山谷,所以就往那個方向走了一步 (學習)。
你會不斷重複上述步驟 (迭代),直到你走到沒力了 (迭代次數),屆時再看距離山谷多遠 (損失函數),若與山谷相距甚微,訓練就到此結束。
在線性回歸的例子中,實際上要優化的是 w 與 b,所以會利用偏導數求得個別梯度,再利用梯度乘以學習速率更新參數。
訓練線性回歸模型
在定義好 Variable、placeholder、loss function、opimizer 後,我們可以將資料丟進類神經網路中開始訓練模型。
由於訓練是透過 mini-batch SGD 優化參數,所以我們先定義能夠批量取得資料的函式 data_iter()
。首先,取得 features
的 index,再將 index 用 random.shuffle
打亂,然後按照設定的 batch_size
每次給定固定批量的資料,若資料無法被 batch_size
除盡時,則回傳剩下的資料。
訓練時,透過 Session 傳遞資料至計算圖中,讓資料流至 y_hat
這個節點,接著透過 MSE 比較預測值與真實值的差異,然後再使用透過梯度下降優化模型中的權重與偏差值,以上就是訓練的過程。
模型將會不斷的重複經過上述的過程訓練,直到迭代次數到達設定的 epochs
為止。
每經過 100 次 epoch 將 loss 儲存至 losses
,在訓練結束後將 losses
繪製成折線圖,觀察是否隨著訓練而讓預測值愈接近真實值,最後 MSE 等於 0.03597400709986687,是一個非常小的數字,也就顯示著訓練相當成功。
使用線性回歸模型
還記得我們一開始說要用 y = wx + b 代表年資與薪資的關係,現在將訓練出的 true_w
與 true_b
帶回 y = wx + b,但是你看到結果可能會有些疑惑,為何 y 的結果皆趨近於 0 ~ 1 之間的值?
這是因為我們訓練出的參數是擬合標準化後介於 0 ~ 1 之間的資料,所以當我們用標準化後的年資去預測薪水時,其結果同樣也是介於 0 ~ 1 之間的值。
所以,在得出 pred_salary
後,必須將它反標準化回原始薪資的資料區間。
同樣地,我們也要將 features
與 labels
反標準化回原始值。
畫出線性回歸模型
最後,我們使用 Matplotlib 將原始資料年資與薪資以點陣圖的形式顯示,並將線性回歸模型以紅色的線顯示。
結語
在線性回歸的這個例子中,我們用 TensorFlow 輕易地實現一個模型,但事實上 TensorFlow 可以寫出更簡潔的程式碼,甚至是使用 Scikit-Learn
與 Keras
幾乎只要透過短短幾行程式碼便能夠實現線性回歸,但是我們選擇從零開始實現線性回歸,這樣更能夠透過程式碼了解線性回歸運行的原理。