使用 TensorFlow 學習線性回歸 (Linear Regression)

Leo Chiu
手寫筆記
Published in
3 min readJul 24, 2018

線性迴歸 (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 另一個重要的基石Variableplaceholder 用於在計算圖中傳入 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_wtrue_b 帶回 y = wx + b,但是你看到結果可能會有些疑惑,為何 y 的結果皆趨近於 0 ~ 1 之間的值?

這是因為我們訓練出的參數是擬合標準化後介於 0 ~ 1 之間的資料,所以當我們用標準化後的年資去預測薪水時,其結果同樣也是介於 0 ~ 1 之間的值。

所以,在得出 pred_salary 後,必須將它反標準化回原始薪資的資料區間。

同樣地,我們也要將 featureslabels 反標準化回原始值。

畫出線性回歸模型

最後,我們使用 Matplotlib 將原始資料年資與薪資以點陣圖的形式顯示,並將線性回歸模型以紅色的線顯示。

結語

在線性回歸的這個例子中,我們用 TensorFlow 輕易地實現一個模型,但事實上 TensorFlow 可以寫出更簡潔的程式碼,甚至是使用 Scikit-LearnKeras 幾乎只要透過短短幾行程式碼便能夠實現線性回歸,但是我們選擇從零開始實現線性回歸,這樣更能夠透過程式碼了解線性回歸運行的原理。

參考資料

延伸閱讀

--

--

Leo Chiu
手寫筆記

每天進步一點點,在終點遇見更好的自己。 Instragram 小帳:@leo.web.dev