LESSON 3 线性回归的手动实现
Lesson 3.線性回歸的手動建模實驗
在此前的兩節課程中,我們已經介紹了關于線性回歸模型的基本概念,并且介紹了一個多元線性回歸的損失函數求解方法——最小二乘法。在有了這一些列理論推導之后,本節我們將結合Lesson 1中所介紹的機器學習一般建模流程,并首先嘗試在一個手動構建的數據集上進行完整的線性回歸模型建模。
Lesson 3.1 變量相關性基礎理論
在創建好了數據生成器之后,接下來即可進行手動線性回歸建模實驗。
# 科學計算模塊 import numpy as np import pandas as pd# 繪圖模塊 import matplotlib as mpl import matplotlib.pyplot as plt機器學習的“學習”目標,其實就是數據集中隱藏的數字規律,而又由于這些規律背后代表的是某些事物的真實屬性或者運行狀態,因此這些規律是具備指導生產生活事件意義的有價值的規律,這也是機器學習算法價值的根本。
- 相關系基本解釋與相關系數計算公式
當然,對于不同數據集來說,是否具備規律、以及規律隱藏的深淺都不一樣。對于模型來說,擅長挖掘的規律、以及規律挖掘的能力也都各不相同。而對于線性回歸來說,捕捉的實際上是數據集的線性相關的規律。所謂線性相關,簡單來說就是數據的同步變化特性。例如此前數據集:
特征和標簽就存在著非常明顯的同步變化的特性:第二條樣本特征增加2、對應標簽也增加2,當然,這也就是線性模型本身可解釋性的來源——體重越大的鮑魚、年齡越大,并且體重每增加2、年齡也增加2。這種同步變化特性用更加專業的角度來描述就是變量之間的相關性。這種相關性可以用一個計算公式算得,也就是相關性系數計算公式:
Correlation=Cov(X,Y)Var(X)?Var(Y)Correlation = \frac{Cov(X, Y)}{\sqrt {Var(X) * Var(Y)}}Correlation=Var(X)?Var(Y)?Cov(X,Y)?
其中,XXX和YYY是兩個隨機變量(對應數據集也就代表兩個字段),Var(X)、Var(Y)Var(X)、Var(Y)Var(X)、Var(Y)為X、YX、YX、Y的方差,Cov(X,Y)Cov(X,Y)Cov(X,Y)為XXX和YYY這兩個變量的協方差,具體計算公式為:
Cov?(X,Y)=E(X?E(X))E(Y?E(Y))=E(XY)?E(X)E(Y)\begin{aligned} \operatorname{Cov}(X, Y) &=E(X-E(X)) E(Y-E(Y)) \\ &=E(X Y)-E(X) E(Y) \end{aligned} Cov(X,Y)?=E(X?E(X))E(Y?E(Y))=E(XY)?E(X)E(Y)?
其中E(X)、E(Y)E(X)、E(Y)E(X)、E(Y)為X、YX、YX、Y期望計算結果。
關于相關系數的計算有很多種方法,此處介紹的相關系數計算也被稱為皮爾遜相關系數,最早由統計學家卡爾·皮爾遜提出,是目前最為通用的相關系數計算方法。
- 相關系數計算在NumPy中的實現
當然,在NumPy中也提供了相關系數計算函數corrcoef可用于快速計算兩個數組之間的相關系數
??該函數最終返回的是相關系數矩陣A2?2A_{2*2}A2?2?,其中ai,ja_{i,j}ai,j?表示第i、j兩個變量之間的相關系數。很明顯,相關系數矩陣是一個對角線元素全是1的矩陣,并且上三角和下三角元素對應位置元素相等。當然,對于A中的兩個數組相關系數計算結果為0.933。
- 相關系數計算結果解讀
相關系數的計算結果取值為[-1,1]之內,取值為負時代表兩個變量同步變化關系為負,也就是其中一個數值增加、另一個數值減少。例如:
總體來說,相關系數絕對值越大兩個變量的相關性越強,絕對值為1時候代表完全相關,兩個變量完全線性同步變化,其中一個變量可以由另一個變量線性表出。而絕對值為0時,則表示完全線性無關,兩個變量沒有線性同步變化規律,這兩個變量沒有線性關系。當絕對值介于0和1之間時候,相關性強弱可以表示如下:
如果是雙變量的相關性,我們也可以用一組函數關系及圖像來進行表示
當然,如果我們想在y基礎上創建一個稍微弱化線性相關關系的數組,可以在y基礎上加入一個隨機數作為擾動項。例如:
接下來,創建一個控制擾動項大小的變量delta
delta = 0.5 #因此,擾動項最終計算過程為: r = ran * delta r #array([-0.70326304, 0.39202727, 0.63362961, -0.28215417, -0.30074036, # -0.59024454, -0.33660292, -0.10770454, 0.1688505 , 1.13219149, # 0.2886254 , 0.19396348, 0.08446266, -0.05326953, -0.45031793, # -0.59683277, 0.28079264, 0.62728816, 0.24544957, -0.46301168]) y1 = y + r y1 #array([-1.68723915, 0.76532326, 2.045763 , -0.74109006, 0.32887279, # 0.35244733, -2.68058767, -0.67867186, 1.29879589, 1.50933817, # 0.27243497, 1.19472997, 2.79165527, 2.9877919 , -0.92177274, # -0.66149061, -0.69079609, 1.26800102, 2.62706381, 0.32901834])此處,y1就是在y基礎上加入擾動項之后的標簽。由于有一個隨性擾動項的存在,會使得y1和X的線性關系被削弱。
從更根本的角度來說,加入擾動項削弱線性相關性,也是因為擾動項本身無規律可循,加入數據之后只會掩蓋原始規律。類似擾動項我們也可稱其為白噪聲。白噪聲其實也有一定的實際背景,在很多情況下,我們采集的數據本身就是包含一定隨機誤差的,或者說是包含了無法幫助提取有效規律的信息。
plt.subplot(121) plt.plot(X, y, 'o') plt.title('y=x+1') plt.subplot(122) plt.plot(X, y1, 'o') plt.title('y=x+1+r')
由此可見,在加入了擾動項之后,模型線性相關性明顯變弱。據此,當然,伴隨delta的增加,噪聲數據的絕對值會越來越大,掩蓋原始數據線性相關規律的趨勢會更加明顯。我們可以簡單用一組圖像來展示不同相關性時雙變量呈現的分布情況:
能夠明顯看出,伴隨delta取值越來越大,數據相關性越來越弱,當然,我們也能通過觀察cl的取值來查看各組變量的相關系數
根據Lesson 2補充材料所介紹,簡單線性回歸就是在二維平面中通過構建一條直線來試圖捕捉平面當中的點,線性相關性越弱、線性模型越難捕捉到這些所有點,模型效果就越差。換而言之,就是數據集之間線性相關性越明顯,數據規律越明顯,模型越容易捕捉到這些規律。
在這個基礎理論之上,我們有以下兩方面衍生的應用:
其一:我們可以在構建線性模型之前先探查數據本身的線性相關性,如果自變量和因變量存在很好的相關性,那就一定可以順利的構建線性回歸模型對數據進行擬合。而如果線性相關性不強,則說明當前數據并不適合構建線性回歸模型,或者在構建模型之前我們需要對數據進行一些“不影響真實規律”的轉化,令其表現出線性的分布趨勢。當然,如果需要更換模型,就需要在我們學習了更多其他模型之后來進行討論,而如果是圍繞數據進行修改,則會涉及特征工程相關理論;
??
其二:上述幾組不同的數據,實際上就代表著對線性回歸模型建模難度各不相同的幾組數據,delta越大對線性回歸模型來說建模就更加困難。據此,我們可以生成一個手動創建數據集的函數,該函數可以輸出不同建模難度(規律深淺)的數據集,來輔助我們測試模型和優化算法的性能。
當然,線性相關性的減弱,不僅是對于線性回歸模型,對于很多回歸類問題都會造成更大的建模困難。
Lesson 3.2 數據生成器與Python模塊編寫
在有了相關性理論基礎之后,我們即可創建一個可用于回歸模型實驗的數據生成器。
# 科學計算模塊 import numpy as np import pandas as pd# 繪圖模塊 import matplotlib as mpl import matplotlib.pyplot as plt一、自定義數據生成器
為了方便后續練習的展開,我們嘗試自己創建一個數據生成器,用于自主生成一些符合某些條件、難度可控、具備某些特性的數據集。機器學習發展至今,在追求模型效果提升的過程中,模型本身可解釋性逐漸變弱,對于很多集成類算法,很多模型構建思路也在朝向深度學習靠攏。這使得很多模型內部逐漸呈現灰箱甚至是黑箱的狀態。但是,在初學過程中,我們仍然需要通過類似控制變量的方法、通過設計一些實驗,去深入理解算法運行原理及一些優化方法的實際作用,這就需要我們自己動手,創建一些數據用于實驗的原材料,通過一些實驗深入了解模型原理,從“煉丹師”朝著“化學家”更進一步。
和Lesson 1中介紹的一樣,課程中案例分為三類,分別是基礎階段的手動創建數據集、實戰階段的競賽數據集和企業應用實戰階段的企業數據集。
1.手動生成數據
我們先嘗試生成兩個特征、存在偏差,自變量和因變量存在線性關系的數據集
num_inputs = 2 # 兩個特征 num_examples = 1000 # 總共一千條數據 #然后嘗試通過線性方程,確定自變量和因變量的真實關系 np.random.seed(24) # 設置隨機數種子 np.random.randn(2, 2) #array([[ 1.32921217, -0.77003345], # [-0.31628036, -0.99081039]]) # 線性方程系數 w_true = np.array([2, -1]).reshape(-1, 1) b_true = np.array(1) # 擾動項相關 delta = 0.01 # 創建數據集的特征和標簽取值 features = np.random.randn(num_examples, num_inputs) labels_true = features.dot(w_true) + b_true labels = labels_true + np.random.normal(size = labels_true.shape) * delta注意,此時labels_true和features滿足嚴格意義上的線性方程關系
y=2x1?x2+1y = 2x_1-x_2+1y=2x1??x2?+1
但我們實際使用的標簽labels,則是在labels_true的基礎上增添了一個擾動項,np.random.normal(size = labels_true.shape) * delta,這其實也符合我們一般獲取數據的情況:真實客觀世界或許存在某個規律,但我們搜集到的數據往往會因為各種原因存在一定的誤差,無法完全描述真實世界的客觀規律,這其實也是模型誤差的來源之一(另一個誤差來源是模型本身捕獲規律的能力)。這其中,y=2x1?x2+1y=2x_1-x_2+1y=2x1??x2?+1相當于我們從上帝視角創建的數據真實服從的規律,而擾動項,則相當于人為創造的獲取數據時的誤差。
簡單查看我們創建的數據集
不難看出,兩個特征和標簽都存在一定的線性關系,并且跟特征的系數絕對值有很大關系。當然,若要增加線性模型的建模難度,可以增加擾動項的數值比例,從而削弱線性關系。
當然,我們也能生成非線性關系的數據集,此處我們創建滿足y=2x2+1y=2x^2+1y=2x2+1規律的數據集。
關于曲線相關,其實也可將其轉化為線性相關,例如上例中,我們只需要新增一列x2=xx_2=xx2?=x即可構建一個線性模型y=x2+1y=x_2+1y=x2?+1,該方法也是特征工程中的一種特征衍生的方法。
此時需要注意的是,無論是創建了曲線規律的數據集,還是增加了擾動項絕對數值,都會對建模造成困難。但二者的造成困難的方式是不同的。如果是曲線規律的數據集,則代表規律本身更加復雜,此時需要使用更加復雜的模型來進行規律提取,該部分屬于模型選取和參數調優的技術內容;而如果是擾動項比較大,則代表規律被掩蓋的比較深,此時需要采用其他技術手段進行白噪聲的處理,該部分屬于特征工程技術內容。但本節內,無論是曲線規律還是白噪聲數值過大,都會對線性方程建模造成困難。
這也就是控制數據集建模難度的最基礎的抓手。
2.創建生成回歸類數據的函數
為了方便后續使用,我們將上述過程封裝在一個函數內
- 定義創建函數
注:上述函數默認創建的是一個滿足y=2x1?x2+1y=2x_1-x_2+1y=2x1??x2?+1的數據集。
- 測試函數性能
首先查看擾動項較小的時候的數據情況
# 設置隨機數種子 np.random.seed(24) # 擾動項取值為0.01 f, l = arrayGenReg(delta=0.01)f #array([[ 1.32921217, -0.77003345, 1. ], # [-0.31628036, -0.99081039, 1. ], # [-1.07081626, -1.43871328, 1. ], # ..., # [ 1.5507578 , -0.35986144, 1. ], # [-1.36267161, -0.61353562, 1. ], # [-1.44029131, 0.50439425, 1. ]]) # 繪制圖像查看結果 plt.subplot(121) plt.scatter(f[:, 0], l) # 第一個特征和標簽的關系 plt.subplot(122) plt.scatter(f[:, 1], l) # 第二個特征和標簽的關系
然后查看擾動項較大時數據情況
在定義好數據創建函數之后,即可開始嘗試手動實現線性回歸模型。
該數據生成器并非為線性回歸模型量身定制,而是通用與此后我們學習的所有回歸類模型。關于分類模型數據集的創建,我們將在介紹邏輯回歸時講解。
此外,對于機器學習模型,我們將更多的從數據規律和模型提取規律角度出發,并不會太多顧及模型可解釋性等問題。上述數據本身也是從規律角度出發構建的數據,并不具備“解釋性”。
隨機數種子的使用
??此處我們使用了一個隨機數種子,以確保隨機結果的可重復性。當我們設置某個隨機數種子之后,每次隨機過程都是可重復的:我們只需要再次調用相同隨機種子,就可以重復此前隨機過程。
當然,不同隨機數種子所誕生的隨機過程是不一樣的,這也是“種子”的由來。此外,不同庫中的隨機過程需要用的不同隨機種子也是不同的,比如上述我們用np.random.seed來規定numpy中相關隨機過程,但如果是其他第三方庫,如random庫所定義的隨機過程,就需要使用random庫中的隨機種子來確定。
import random l = list(range(5)) l #[0, 1, 2, 3, 4] random.seed(24) random.shuffle(l) l #[4, 2, 0, 1, 3] random.shuffle(l) l #[1, 4, 0, 3, 2] l = list(range(5)) l #[0, 1, 2, 3, 4] random.seed(24) random.shuffle(l) l #[4, 2, 0, 1, 3] random.shuffle(l) l #[1, 4, 0, 3, 2] l = list(range(5)) l #[0, 1, 2, 3, 4] np.random.seed(24) random.shuffle(l) l #[4, 0, 3, 2, 1] random.shuffle(l) l #[0, 4, 3, 1, 2] l = list(range(5)) l #[0, 1, 2, 3, 4] np.random.seed(24) random.shuffle(l) l #[4, 2, 3, 1, 0]二、Python模塊的編寫與調用
根據課程安排,本節定義的函數將后續課程中將經常使用,因此需要將其封裝為一個模塊方便后續調用。封裝為模塊有以下幾種基本方法:
- 打開文本編輯器,將寫好并測試完成的函數寫入其中,并將文本的拓展名改寫為.py;
- 在spyder或者pycharm中復制相關函數,并保存為.py文件;
然后將文件保存在jupyter主目錄下,并取名為ML_basic_function,后續即可通過import ML_basic_function進行調用。如果是jupyterlab用戶,也可按照如下方式進行編寫:
Step 1.打開左側文件管理欄頁,點擊新建
Step 2.在新建目錄中,選擇Test File,并通過重命名將其命名為ML_basic_function.py
Step 3.在打開的文本編輯器中輸入代碼
并且需要注意,需要在py文件內導入相關的包。
Step 4.測試能否調用
首先重啟當前jupyter
然后嘗試導入自定義模塊
至此就完成了自定義模塊的編寫與保存,后續我們還將陸續寫入其他函數。
Lesson 3.3 線性回歸手動實現與模型局限
在創建好了數據生成器之后,接下來即可進行手動線性回歸建模實驗。
一、線性回歸的手動實現
接下來,我們嘗試進行線性回歸模型的手動建模實驗。建模過程將遵照機器學習的一般建模流程,并且借助NumPy所提供的相關工具來進行實現。通過本次實驗,我們將進一步深化對機器學習建模流程的理解,并且也將進一步熟悉對編程基礎工具的掌握。
1.根據機器學習建模流程構建線性回歸模型
Step 1.數據準備
??首先,是準備數據集。我們利用數據生成器創建一個擾動項不太大的數據集:
其中,features也被稱為特征矩陣、labels也被稱為標簽數組
- Step 2.模型選取
接下來,選取模型對上述回歸類問題數據進行建模。此處我們選取帶有截距項的多元線性回歸方程進行建模,基本模型為:
f(x)=w1x1+w2x2+bf(x) = w_1x_1+w_2x_2+b f(x)=w1?x1?+w2?x2?+b
令w^=[w1,w2,b]T\hat w = [w_1,w_2,b]^Tw^=[w1?,w2?,b]T,x^=[x1,x2,1]T\hat x = [x_1,x_2, 1]^Tx^=[x1?,x2?,1]T,則上式可寫為
f(x)=w^Tx^f(x) = \hat w^T\hat x f(x)=w^Tx^
注,此處如果要構建一個不帶截距項的模型,則可另X為原始特征矩陣帶入進行建模。
- Step 3.構造損失函數
對于線性回歸來說,我們可以參照SSE、MSE或者RMSE的計算過程構造損失函數。由于目前模型參數還只是隱式的值(在代碼中并不顯示),我們可以簡單嘗試,通過人工設置一組𝑤? 來計算SSE。
- 令 𝑤? 為一組隨機值,計算SSE
此時模型輸出結果為:
y_hat = features.dot(w) y_hat[:10] #array([[ 2.04347616], # [ 0.02627308], # [-0.63176501], # [ 0.20623364], # [-2.64718921], # [-0.86880796], # [ 0.88171608], # [-1.61055557], # [ 0.80113619], # [-0.49279524]])??據此,根據公式SSE=∣∣y?Xw^∣∣22=(y?y^)T(y?y^)SSE= ||y - X\hat w||_2^2 = (y - \hat y)^T(y - \hat y)SSE=∣∣y?Xw^∣∣22?=(y?y^?)T(y?y^?),SSE計算結果為:
(labels - y_hat).T.dot(labels - y_hat) #array([[2093.52940481]]) # 計算MSE (labels - y_hat).T.dot(labels - y_hat) / len(labels) #array([[2.0935294]]) labels[:10] #array([[ 4.43811826], # [ 1.375912 ], # [ 0.30286597], # [ 1.81970897], # [-2.47783626], # [ 0.47374318], # [ 2.83085905], # [-0.83695165], # [ 2.84344069], # [ 0.8176895 ]])能夠看出,在當前參數取值下,模型輸出結果和真實結果相距甚遠。
不過,為了后續快速計算SSE,我們可以將上述SSE計算過程封裝為一個函數,令其在輸入特征矩陣、標簽數組和真實參數情況下即可輸出SSE計算結果:
def SSELoss(X, w, y):"""SSE計算函數:param X:輸入數據的特征矩陣:param w:線性方程參數:param y:輸入數據的標簽數組:return SSE:返回對應數據集預測結果和真實結果的誤差平方和 """y_hat = X.dot(w)SSE = (y - y_hat).T.dot(y - y_hat)return SSE # 簡單測試函數性能 SSELoss(features, w, labels) #array([[2093.52940481]])實驗結束后,需要將上述SSELoss函數寫入ML_basic_function.py中。
- Step 4.利用最小二乘法求解損失函數
接下來,我們需要在SSELoss中找到一組最佳的參數取值,另模型預測結果和真實結果盡可能接近。此處我們利用Lesson 2中介紹的最小二乘法來進行求解,最小二乘法求解模型參數公式為:w^=(XTX)?1XTy\hat w = (X^TX)^{-1}X^Tyw^=(XTX)?1XTy
值得注意的是,最小二乘法在進行求解過程中,需要特征矩陣的交叉乘積可逆,也就是XTXX^TXXTX必須存在逆矩陣。我們可以通過計算其行列式來判斷該條件是否滿足:
np.linalg.det(features.T.dot(features)) #967456500.1798325行列式不為0,因此XTXX^TXXTX逆矩陣存在,可以通過最小二乘法求解。具體求解方法分為兩種,其一是使用NumPy中線性代數基本方法,根據上述公式進行求解,同時也可以直接使用lstsq函數進行求解。
- 基礎方法求解
即可算出模型最優參數w。所謂模型最優參數,指的是參數取得任何其他數值,模型評估結果都不如該組參數時計算結果更好。首先,我們也可以計算此時模型SSE指標:
SSELoss(features, w, labels) #array([[0.09300731]])明顯小于此前所采用的隨機w取值時SSE結果。此外,我們還可以計算模型MSE
SSELoss(features, w, labels) / len(y) #array([[9.30073138e-05]])當然,由于數據集本身是依據𝑦=2𝑥1𝑥_{1}x1?+𝑥2𝑥_{2}x2??1規律構建的,因此從模型參數也能夠看出模型預測效果較好。
模型評估指標中SSE、MSE、RMSE三者反應的是一個事實,我們根據SSE構建損失函數只是因為SSE計算函數能夠非常方便的進行最小值推導,SSE取得最小值時MSE、RMSE也取得最小值。
- lstsq函數求解
當然,我們也可以利用lstsq函數進行最小二乘法結果求解。二者結果一致。
最終w參數取值為:
np.linalg.lstsq(features, labels, rcond=-1)[0] #array([[ 1.99961892], # [-0.99985281], # [ 0.99970541]])至此,我們即完成了整個線性回歸的機器學習建模流程。
二、線性回歸模型局限
盡管上述建模過程能夠發現,面對白噪聲不是很大、并且線性相關性非常明顯的數據集,模型整體表現較好,但在實際應用中,大多數數據集可能都不具備明顯的線性相關性,并且存在一定的白噪聲(數據誤差)。此時多元線性回歸模型效果會受到極大影響。
plt.plot(features[:, 0], labels, 'o')- 非線性相關規律
例如,此處創建一個滿足y=x3+1y=x^3+1y=x3+1基本規律,并且白噪聲很小的數據集進行建模測試
從模型結果能夠看出,模型和數據集分布規律相差較大
- 噪聲增加
此外,我們稍微增加模型白噪聲,測試線性回歸模型效果
# 設置隨機數種子 np.random.seed(24) # 擾動項取值為2 features, labels = arrayGenReg(w=[2,1], delta=2) features #array([[ 1.32921217, 1. ], # [-0.77003345, 1. ], # [-0.31628036, 1. ], # ..., # [ 0.84682091, 1. ], # [ 1.73889649, 1. ], # [ 1.93991673, 1. ]]) plt.plot(features[:, 0], labels, 'o') np.linalg.lstsq(features, labels, rcond=-1) #(array([[1.91605821], # [0.90602215]]), # array([3767.12804359]), # 2, # array([32.13187164, 31.53261742])) w = np.linalg.lstsq(features, labels, rcond=-1)[0] w #array([[1.91605821], # [0.90602215]]) SSELoss(features, w, labels) #array([[3767.12804359]]) X = np.linspace(-5, 5, 1000) y = w[0] * X + w[1] plt.plot(features[:, 0], labels, 'o') plt.plot(X, y, 'r-')
能夠發現,模型誤差較大。
- 最小二乘法條件限制
并且,除此之外,線性回歸模型還面臨這一個重大問題就是,如果特征矩陣的交叉乘積不可逆,則最小二乘法求解過程就不成立了。
w^=(XTX)?1XTy\hat w = (X^TX)^{-1}X^Tyw^=(XTX)?1XTy
當然,此時也代表著數據集存在著較為嚴重的多重共線性,換而言之就是數據集的特征矩陣可能可以相互線性表出。這個時候矩陣方程XTXw^=XTyX^TX\hat w=X^TyXTXw^=XTy并不一定存在唯一解。解決該問題的方法有很多種,從數學角度出發,我們可以從以下三個方面入手:
- 其一,對數據進行降維處理:
??首先,可考慮進一步對數據集進行SVD分解或PCA主成分分析,在SVD或PCA執行的過程中會對數據集進行正交變換,最終所得數據集各列將不存在任何相關性。當然此舉會對數據集的結構進行改變,且各列特征變得不可解釋。 - 其二,修改求解損失函數的方法:
??我們可以試圖求解原方程的廣義逆矩陣,對于某些矩陣方程來說,通過求解廣義逆矩陣,也可以得到近似最優解;此外,我們還可以通過使用其他最優化求解方法,如梯度下降算法等來進行求解; - 其三,修改損失函數:
??其實可以修改原損失函數,令其滿足最小二乘法求解條件即可。如果XTXX^TXXTX不可逆,那么我們可以通過試圖在損失函數中加入一個正則化項,從而令損失函數可解。根據Lesson 2中的公式推導,目前根據SSE所構建的損失函數如下:
??SSELoss(w^)=∣∣y?Xw^∣∣22=(y?Xw^)T(y?Xw^)SSELoss(\hat w) = ||y - X\hat w||_2^2 = (y - X\hat w)^T(y - X\hat w)SSELoss(w^)=∣∣y?Xw^∣∣22?=(y?Xw^)T(y?Xw^)
通過數學過程可以證明,此時如果我們在原有損失函數基礎上添加一個關于參數www的1-范數(∣∣w^∣∣1||\hat w||_1∣∣w^∣∣1?)或者2-范數(∣∣w^∣∣2||\hat w||_2∣∣w^∣∣2?)的某個計算結果,則可令最小二乘法條件得到滿足。此時最小二乘法計算結果由無偏估計變為有偏估計。例如,當我們在損失函數中加上λ∣∣w^∣∣22\lambda ||\hat w||_2^2λ∣∣w^∣∣22?(其中λ\lambdaλ為參數)時,模型損失函數為:
Loss(w^)=∣∣y?Xw^∣∣22+λ∣∣w^∣∣22Loss(\hat w) = ||y - X\hat w||_2^2 +\lambda ||\hat w||_2^2Loss(w^)=∣∣y?Xw^∣∣22?+λ∣∣w^∣∣22?
經過數學轉化,上述矩陣表達式對w^\hat ww^求導后令其為零,則可解出:
(XTX+λI)w^=XTy(X^TX+\lambda I) \hat w = X^Ty(XTX+λI)w^=XTy
其中III為單位矩陣。此時由于(XTX+λI)(X^TX+\lambda I)(XTX+λI)肯定是可逆矩陣,因此可以順利求解出w^\hat ww^:
w^=(XTX+λI)?1XTy\hat w = (X^TX+\lambda I)^{-1}X^Tyw^=(XTX+λI)?1XTy
該過程也被稱為嶺回歸。而類似的,如果是通過添加了w^\hat ww^的1-范數的某個表達式,從而構造損失函數如下:
Loss(w^)=∣∣y?Xw^∣∣22+λ∣∣w^∣∣1Loss(\hat w) = ||y - X\hat w||_2^2 +\lambda ||\hat w||_1Loss(w^)=∣∣y?Xw^∣∣22?+λ∣∣w^∣∣1?
則該過程被稱為Lasso。而更進一步,如果構建的損失函數同時包含w^\hat ww^的1-范數和2-范數,形如如下形式:
Loss(w^)=12n∣∣y?Xw^∣∣22+λα∣∣w^∣∣1+λ(1?α)2∣∣w^∣∣22Loss(\hat w) = \frac{1}{2n}||y - X\hat w||_2^2 + \lambda \alpha ||\hat w||_1 +\frac{\lambda(1-\alpha)}{2} ||\hat w||_2 ^ 2Loss(w^)=2n1?∣∣y?Xw^∣∣22?+λα∣∣w^∣∣1?+2λ(1?α)?∣∣w^∣∣22?
則構建的是彈性網模型(Elastic-Net),其中λ、α\lambda、\alphaλ、α都是參數,n是樣本個數。不難發現,嶺回歸和Lasso其實都是彈性網的一種特殊形式。更多關于線性模型的相關方法,我們將在后續逐漸介紹。
1-范數也被稱為L1范數,將參數的1-范數添加入損失函數的做法,也被稱為損失函數的L1正則化,L2正則化也類似。在大多數情況下,添加正則化項也可稱為添加懲罰函數p(w)p(w)p(w),核心作用是緩解模型過擬合傾向。
三、線性回歸的決定系數
對于線性回歸模型來說,除了SSE以外,我們還可使用決定系數(R-square,也被稱為擬合優度檢驗)作為其模型評估指標。決定系數的計算需要使用之前介紹的組間誤差平方和和離差平方和的概念。在回歸分析中,SSR表示聚類中類似的組間平方和概念,表意為Sum of squares of the regression,由預測數據與標簽均值之間差值的平方和計算的出:
SSR=∑i=1n(yiˉ?yi^)2SSR =\sum^{n}_{i=1}(\bar{y_i}-\hat{y_i})^2SSR=i=1∑n?(yi?ˉ??yi?^?)2
而SST(Total sum of squares)則是實際值和均值之間的差值的平方和計算得到:
SST=∑i=1n(yiˉ?yi)2SST =\sum^{n}_{i=1}(\bar{y_i}-y_i)^2SST=i=1∑n?(yi?ˉ??yi?)2
并且,SSTSSTSST可由SSR+SSESSR+SSESSR+SSE計算得出。而決定系數,則由SSRSSRSSR和SSTSSTSST共同決定:
R?square=SSRSST=SST?SSESSE=1?SSESSTR-square=\frac{SSR}{SST}=\frac{SST-SSE}{SSE}=1-\frac{SSE}{SST}R?square=SSTSSR?=SSESST?SSE?=1?SSTSSE?
很明顯,決定系數是一個鑒于[0,1]之間的值,并且約趨近于1,模型擬合效果越好。我們可以通過如下過程,進行決定系數的計算:
Lesson 3.4 機器學習模型結果可信度理論與交叉驗證基礎
“農場里有群火雞,農場主每天中午十一點來喂食?;痣u中有位科學家觀察了近一年無例外后宣布發現了宇宙一個偉大定律:“每天上午十一點,會有食物降臨?!备卸鞴澰绯?#xff0c;它向火雞們公布了這個定律,但這天上午十一點食物沒有降臨,農場主將它們捉去殺掉,把它們變成了食物?!?????????????????????????????--羅素的火雞
# 科學計算模塊 import numpy as np import pandas as pd# 繪圖模塊 import matplotlib as mpl import matplotlib.pyplot as plt# 自定義模塊 from ML_basic_function import *一、機器學習模型結果可信度理論基礎與數據集劃分
1.機器學習模型結果可信度基礎理論
在Lesson 0中我們曾說,模型評估指標是用于評估模型效果好壞的數值指標,例如SSE就是評估回歸類模型擬合效果的指標。但是否是評估指標好的模型就一定能用呢?其實并不一定。這里會涉及到一個關于評估指標可信度、或者說了解模型真實性能的重要命題。
其實,要了解模型的性能其實并不簡單,固然我們會使用某些指標去進行模型評估,但其實指標也只是我們了解模型性能的途徑而不是模型性能本身。而要真實、深刻的評判模型性能,就必須首先了解機器學習的建模目標,并在此基礎之上熟悉我們判斷模型是否能夠完成目標的一些方法,當然,只有真實了解的模型性能,我們才能進一步考慮如何提升模型性能。因此,在正式講解模型優化方法之前,我們需要花些時間討論機器學習算法的建模目標、機器學習算法為了能夠達到目標的一般思路,以及評估模型性能的手段,也就是模型評估指標。
無論是機器學習還是傳統的統計分析模型,核心使命就是探索數字規律,而有監督學習則是希望在探索數字規律的基礎上進一步對未來進行預測,當然,在數字的世界,這個預測未來,也就是預測未來某項事件的某項數值指標,如某地區未來患病人次、具備某種數字特征的圖片上的動物是哪一類,此處的未來也并非指絕對意義上的以后的時間,而是在模型訓練階段暫時未接觸到的數據。正是因為模型有了在未知標簽情況下進行預判的能力,有監督學習才有了存在的價值,但我們知道,基本上所有的模型,都只能從以往的歷史經驗當中進行學習,也就是在以往的、已經知道的數據集上進行訓練(如上述利用已知數據集進行模型訓練,如利用過往股票數據訓練時間序列模型),這里的核心矛盾在于,在以往的數據中提取出來的經驗(也就是模型),怎么證明能夠在接下來的數據中也具備一定的預測能力呢?或者說,要怎么訓練模型,才能讓模型在未知的數據集上也擁有良好的表現呢?
目的相同,但在具體的實現方法上,傳統的數理統計分析建模和機器學習采用了不同的解決方案。
首先,在統計分析領域,我們會假設現在的數據和未來的數據其實都屬于某個存在但不可獲得的總體,也就是說,現在和未來的數據都是從某個總體中抽樣而來的,都是這個總體的樣本。而正式因為這些數據屬于同一個總體,因此具備某些相同的規律,而現在挖掘到的數據規律也就在某些程度上可以應用到未來的數據當中去,不過呢,不同抽樣的樣本之間也會有個體之間的區別,另外模型本身也無法完全捕獲規律,而這些就是誤差的來源。
雖然樣本和總體的概念是統計學概念,但樣本和總體的概念所假設的前后數據的“局部規律一致性”,卻是所有機器學習建模的基礎。試想一下,如果獲取到的數據前后描繪的不是一件事情,那么模型訓練也就毫無價值(比如拿著A股走勢預測的時間序列預測某地區下個季度患病人次)。因此,無論是機器學習所強調的從業務角度出發,要確保前后數據描述的一致性,還是統計分析所強調的樣本和總體的概念,都是建模的基礎。
在有了假設基礎之后,統計分析就會利用一系列的數學方法和數理統計工具去推導總體的基本規律,也就是變量的分布規律和一些統計量的取值,由于這個過程是通過已知的樣本去推斷未知的總體,因此會有大量的“估計”和“檢驗”,在確定了總體的基本分布規律之后,才能夠進一步使用統計分析模型構建模型(這也就是為什么在數理統計分析領域,構建線性回歸模型需要先進行一系列的檢驗和變換的原因),當然,這些模型都是在總體規律基礎之上、根據樣本具體的數值進行的建模,我們自然有理由相信這些模型對接下來仍然是從總體中抽樣而來的樣本還是會具備一定的預測能力,這也就是我們對統計分析模型“信心”的來源。簡單來說,就是我們通過樣本推斷總體的規律,然后結合總體的規律和樣本的數值構建模型,由于模型也描繪了總體規律,所以模型對接下來從總體當中抽樣而來的數據也會有不錯的預測效果,這個過程我們可以通過下圖來進行表示。
而對于機器學習來說,并沒有借助“樣本-總體”的基本理論,而是簡單的采用了一種后驗的方法來判別模型有效性,前面說到,我們假設前后獲取的數據擁有規律一致性,但數據彼此之間又略有不同,為了能夠在捕捉規律的同時又能考慮到“略有不同”所帶來的誤差,機器學習會把當前能獲取到的數據劃分成訓練集(trainSet)和測試集(testSet),在訓練集上構建模型,然后帶入測試集的數據,觀測在測試集上模型預測結果和真實結果之間的差異。這個過程其實就是在模擬獲取到真實數據之后模型預測的情況,此前說到,模型能夠在未知標簽的數據集上進行預測,就是模型的核心價值,此時的測試集就是用于模擬未來的未知標簽的數據集。如果模型能夠在測試集上有不錯的預測效果,我們就“簡單粗暴”的認為模型可以在真實的未來獲取的未知數據集上有不錯的表現。其一般過程可以由下圖表示。
雖然對比起數理統計分析,機器學習的證明模型有效性的過程更加“簡單”,畢竟只要一次“模擬”成功,我們就認為模型對未來的數據也擁有判別效力,但這種“簡單”的處理方式卻非常實用,可以說,這是一種經過長期實踐被證明的行之有效的方法。這也是為什么機器學習很多時候也被認為是實證類的方法,而在以后的學習中,我們也將了解到,機器學習有很多方法都是“經驗總結的結果”。相比數理統計分析,確實沒有“那么嚴謹”,但更易于理解的理論和更通用的方法,卻使得機器學習可以在更為廣泛的應用場景中發揮作用。(當然,負面影響卻是,機器學習在曾經的很長一段時間內并不是主流的算法。)
據此,我們稱模型在訓練集上誤差稱為訓練誤差,在測試集上的誤差稱為泛化誤差,不過畢竟在測試集上進行測試還只是模擬演習,我們采用模型的泛化能力來描述模型在未知數據上的判別能力,當然泛化能力無法準確衡量(未知的數據還未到來,到來的數據都變成了已知數據),我們只能通過模型在訓練集和測試集上的表現,判別模型泛化能力,當然,就像此前說的一樣,最基本的,我們會通過模型在測試集上的表現來判斷模型的泛化能力。
2.數據集切分方法
接下來,我們就嘗試通過數據集切分來執行更加可信的機器學習建模流程。首先是對數據集進行切分。一般來說,為了避免數據集上下順序對數據規律的影響,我們會考慮對數據集進行隨機切分,其中70%-80%作為訓練集、20%-30%作為測試集。此處我們先考慮構建一個數據切分函數,用于訓練集和測試集的切分:
np.random.shuffle函數
在NumPy中,我們可以非常便捷的通過調用np.random.shuffle函數來進行二維數組按行重排的相關操作,而二維數組也就是結構化數據的一般表示形式,因此我們可以使用該函數進行數據集亂序排列。并且我們可以通過設置隨機數種子,來復現這個亂序的過程,這將極大程度有助于當需要對多個序列進行亂序排列時的代碼簡化。
- np.vsplit切分函數
此外,我們可以通過vsplit函數進行數組的按行切分,也就相當于split(axis=0)。
- 數據集切分函數
基于上述函數,我們可以非常簡單的構建一個數據集切分函數:
一般來說,訓練集和測試集可以按照8:2或7:3比例進行劃分。在進行數據劃分的過程中,如果測試集劃分數據過多,參與模型訓練的數據就會相應減少,而訓練數據不足則會導致模型無法正常訓練、損失函數無法收斂、模型過擬合等問題,但如果反過來測試集劃分數據過少,則無法代表一般數據情況測試模型是否對未知數據也有很好的預測作用。因此,根據經驗,我們一般來說會按照8:2或7:3比例進行劃分。
??看到這里,相信肯定有小伙伴覺得根據所謂的“經驗”來定數據集劃分比例不太嚴謹,有沒有一種方法能夠“精準”的確定什么劃分比例最佳呢?例如通過類似最小二乘法來計算劃分比例?
??值得一提的是,在機器學習領域,充斥著大量的“經驗之談”或者“約定俗成”的規則,一方面這些經驗為建模提供了諸多便捷、也節省了很多算力,但另一方面,通過經驗來決定影響模型效果的一些“超參數”取值的不嚴謹的做法,也被數理統計分析流派所詬病。
接下來,測試函數性能
f = np.arange(10).reshape(-1, 1) # 創建特征0-9 f #array([[0], # [1], # [2], # [3], # [4], # [5], # [6], # [7], # [8], # [9]]) l = np.arange(1, 11).reshape(-1, 1) # 創建標簽1-10,保持和特征+1的關系 l #array([[ 1], # [ 2], # [ 3], # [ 4], # [ 5], # [ 6], # [ 7], # [ 8], # [ 9], # [10]]) array_split(f, l) #(array([[9], # [4], # [8], # [7], # [5], # [6], # [1]]), # array([[0], # [3], # [2]]), # array([[10], # [ 5], # [ 9], # [ 8], # [ 6], # [ 7], # [ 2]]), # array([[1], # [4], # [3]]))3.線性回歸手動實現
根據機器學習結果可信度理論,我們構建一個在訓練集上訓練、在測試集上測試的完整的線性回歸實現流程。
數據準備
# 設置隨機數種子 np.random.seed(24) # 擾動項取值為0.01 features, labels = arrayGenReg(delta=0.01)# 數據切分 Xtrain, Xtest, ytrain, ytest = array_split(features, labels)- 在訓練集上訓練
最小二乘法求解公式:w^=(XTX)?1XTy\hat w = (X^TX)^{-1}X^Tyw^=(XTX)?1XTy
在大多數情況下,所謂訓練模型,都是訓練得出模型的一組參數。
- 在測試集上測試
然后即可在測試集上計算模型評估指標:
至此,我們即完成了模型在訓練集上訓練得出參數,然后運行測試集觀察結果的全過程。由于數據情況較為簡單,因此模型效果較好。
但如果測試集效果不好,我們能否因此對模型進行調整的呢?如果測試集真的完全不參與建模,那么根據測試集反饋結果調整模型是否算測試集間接參與建模?如果測試集不能間接參與建模,那測試集的提供的模型結果反饋又有什么作用呢?這就是所謂的,測試集悖論。
4.測試集的“不可知”悖論
我們已經知道,機器學習模型主要通過模型在測試集上的運行效果來判斷模型好壞,測試集相當于是“高考”,而此前的模型訓練都相當于是在練習,但怎么樣的練習才能有效的提高高考成績,這里就存在一個“悖論”,那就是練習是為了高考,而在高考前我們永遠不知道練習是否有效,那高考對于練習的核心指導意義何在?在機器學習領域,嚴格意義上的測試集是不能參與建模的,此處不能參與建模,不僅是指在訓練模型時不能帶入測試集進行訓練,更是指當模型訓練完成之后、觀察模型在測試集上的運行結果后,也不能據此再進行模型修改(比如增加神經網絡層數),后面我們會提到,把數據帶入模型訓練是影響模型參數,而根據模型運行結果再進行模型結構調整,實際上是修改了模型超參數,不管是修改參數還是超參數,都是影響了模型建模過程,都相當于是帶入進行了建模。是的,如果通過觀察測試集結果再調整模型結構,也相當于是帶入測試集數據進行訓練,而嚴格意義上的測試集,是不能帶入模型訓練的。(這是一個有點繞的“悖論”…)
但是,還記得我們此前說的,機器學習建模的核心目標就是提升模型的泛化能力么?而泛化能力指的是在模型未知數據集(沒帶入進行訓練的數據集)上的表現,雖然測試集只能測一次,但我們還是希望有機會能把模型帶入未知數據集進行測試,此時我們就需要一類新的數據集——驗證集。驗證集在模型訓練階段不會帶入模型進行訓練,但當模型訓練結束之后,我們會把模型帶入驗證集進行計算,通過觀測驗證集上模型運行結果,判斷模型是否要進行調整,驗證集也會模型訓練,只不過驗證集訓練的不是模型參數,而是模型超參數,關于模型參數和超參數的概念后面還會再詳細討論,當然,我們也可以把驗證集看成是應對高考的“模擬考試”,通過“模擬考試”的考試結果來調整復習策略,從而更好的應對“高考”??偟膩碚f,測試集是嚴格不能帶入訓練的數據集,在實際建模過程中我們可以先把測試集切分出來,然后“假裝這個數據集不存在”,在剩余的數據集中劃分訓練集和驗證集,把訓練集帶入模型進行運算,再把驗證集放在訓練好的模型中進行運行,觀測運行結果,再進行模型調整。
總的來說,在模型訓練和觀測模型運行結果的過程總共涉及三類數據集,分別是訓練集、驗證集和測試集。不過由于測試集定位特殊,在一些不需要太嚴謹的場景下,有時也會混用驗證集和測試集的概念,我們常常聽到“測試集效果不好、重新調整模型”等等,都是混用了二者概念,由于以下是模擬練習過程,暫時不做測試集和驗證集的區分。在不區分驗證集和測試集的情況下,當數據集切分完成后,對于一個模型來說,我們能夠獲得兩套模型運行結果,一個是訓練集上模型效果,一個是測試集上模型效果,而這組結果,就將是整個模型優化的基礎數據。
在某些場景下,測試集確實是嚴格不可知的,比如在線提交結果的數據競賽。
二、交叉驗證基本思想
除了訓練集-測試集劃分理論之外,和模型結果可信度相關的,還有一個基本理論——交叉驗證。
盡管通過訓練集和測試集的劃分,我們可以以不參與建模的測試集的結論來證明模型結果的可信度,但在很多實際場景中,數據集的隨機切分本身也是影響模型泛化能力、影響測試集結果可信度的重要因素。此時,我們可以采用一種名為交叉驗證的技術手段來進一步提升模型最終輸出結果的可信度。交叉驗證的基本思想非常簡單,在我們不嚴格區分測試集的情況下,我們可以將數據集整體按照某個比例切分,然后進行循環驗證。在所有的切分方法中,最基礎也最常用的一種就是所謂的K-fold(K折)驗證,也就是將數據集進行K份等比例劃分,然后依次取出其中一份進行驗證(測試)、剩下幾份進行訓練。例如,當我們使用10折驗證時,數據集劃分情況如下所示:
假設仍然是此前我們創建的數據集并且仍然采用SSE作為模型評估指標,則在進行十折驗證時可以計算出十組SSE取值,最終我們可以對這十組結果進行均值計算,求得這組參數最終所對應的模型評估指標結果。不過此時我們需要在所有的數據集上進行訓練,然后再進行交叉驗證。
不得不說,對于線性回歸的交叉驗證過程其實和訓練集測試集劃分理論略顯沖突,如果缺少了測試集的后驗過程,再訓練集上再怎么訓練,得出的結果都沒有反饋調整的機會。其實從更加根本的角度來說,在機器學習理論體系中,一個更加嚴謹的做法,是先劃分訓練集和測試集,然后再在訓練集上劃分測試集,并且“訓練集-測試集”劃分方法用于進行模型參數訓練,而“訓練集-驗證集”的劃分方法主要用于進行模型超參數選取。此處由于我們尚未接觸超參數相關概念,因此目前暫時先只介紹其基本思想。
總結
以上是生活随笔為你收集整理的LESSON 3 线性回归的手动实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lesson 2.矩阵运算基础、矩阵求导
- 下一篇: Lesson 4.1-4.2 逻辑回归模