【火爐煉AI】機器學習003-簡單線性迴歸器的建立,測試,模型儲存和載入
(本文所使用的Python庫和版本號: Python 3.5, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )
迴歸分析是一種基於現有資料集,從現有資料集中尋找資料規律的一種建模技術,主要研究的是因變數(輸出y,或標記,或目標,它的別名比較多)和自變數(輸入x,或特徵,或預測器)之間的關係。通常用於預測分析,從現有資料集中找到資料規律,用該資料規律來預測未來的,或新輸入的自變數,從而計算出因變數的過程。
簡單線性迴歸是最基本,最簡單的一種迴歸分析模型,也是最被人熟知,最被人首選的迴歸模型。簡單線性迴歸模型的因變數y是連續的,自變數X可以是連續的也可以是離散的,其基本假設是因變數與自變數之間成直線的變化關係,可以通過一條簡單的擬合直線(y=ax+b)來表示兩者之間的關係。所以線性迴歸模型的訓練目標就是獲取這兩直線的斜率a和截距b。如下圖所示為簡單的線性迴歸器的模型得到的線性圖。
剩下的問題就是,怎麼從資料集中得迴歸直線y=ax+b中的a和b,答案是最小二乘法。最小二乘法是擬合直線最常用的方法,其本質是通過最小化每個資料點到直線的垂直偏差的平方和來計算最佳擬合直線。最小二乘法的計算公式為:
下面使用線性迴歸來擬合資料集,得到線性方程。
1. 準備資料集
對於機器學習而言,第一部分往往是準備資料集。一般的,資料集是存放在txt,或csv,或excel,或圖片檔案中,但此處我們沒有這些資料集,故而我自己用隨機數生成x,y組成資料集。如下程式碼:
# ***************使用隨機資料來構建簡單線性迴歸器**********************************
import numpy as np
import matplotlib.pyplot as plt
# %matplotlib inline
np.random.seed(37) # 使得每次執行得到的隨機數都一樣
# 第一部分:準備資料集
# 一般的,資料集可以放置在一個專門的txt,或csv檔案中,此處我自己創造一些資料
x=np.arange(10,110)
x_shift=np.random.normal(size=x.shape)
x=x+x_shift # 構建的x含有100個數,通過在整數點引入偏差得到
print('x first 5: {},\nx last 5: {}'.format(x[:5],x[-5:]))
error=np.random.normal(size=x.shape)*15 # 構建誤差作為噪音,*15是擴大噪音的影響
y=1.8*x+5.9+error
plt.scatter(x,y) # 可以檢視生成的資料集的分佈情況
# 要從這100個隨機選擇80個點來train,剩下的20個點來test
# 最簡單的方法是呼叫下面的train_test_split函式
dataset=[(i,j) for i,j in zip(x,y)]
from sklearn.model_selection import train_test_split
train_set,test_set=train_test_split(dataset,test_size=0.2,random_state=37)
print('train set first 3: {}, last 3: {}'.format(train_set[:3],train_set[-3:]))
print('test set first 3: {}, last 3: {}'.format(test_set[:3],test_set[-3:]))
# 第二種方法:也可以自己先shuffle,再隨機選取
# np.random.shuffle(dataset) # 亂序排列
# train_set,test_set=dataset[:80],dataset[80:]
# print('train set first 3: {}, last 3: {}'.format(train_set[:3],train_set[-3:]))
# print('test set first 3: {}, last 3: {}'.format(test_set[:3],test_set[-3:]))
X_train=np.array([i for (i,j) in train_set]).reshape(-1,1) # 後面的fit需要先reshape
y_train=np.array([j for (i,j) in train_set]).reshape(-1,1)
X_test= np.array([i for (i,j) in test_set]).reshape(-1,1)
y_test= np.array([j for (i,j) in test_set]).reshape(-1,1)
print('X_train first 3: {}'.format(X_train[:3]))
print('y_train first 3: {}'.format(y_train[:3]))
複製程式碼
-------------------------------------輸---------出--------------------------------
x first 5: [ 9.94553639 11.67430807 12.34664703 11.69965383 15.51851188], x last 5: [104.96860647 105.5657788 107.39973957 109.13188006 107.11399729] train set first 3: [(87.14423520405674, 167.36573263695115), (55.67781082162659, 99.22823777086437), (66.59394694856373, 132.76688712312875)], last 3: [(101.17645506359409, 192.5625947080063), (84.26081861492051, 140.24466883845), (25.045565547096164, 30.8008361424697)] test set first 3: [(70.59406981304555, 119.31318224915739), (57.91939805734182, 92.13834259220599), (96.47990086803438, 179.5012882066724)], last 3: [(102.0058216600241, 201.04210881463908), (18.172421360793678, 47.04372291312748), (104.96860647152728, 222.13041948920244)] X_train first 3: [[87.1442352 ] [55.67781082] [66.59394695]] y_train first 3: [[167.36573264] [ 99.22823777] [132.76688712]]
--------------------------------------------完-------------------------------------
########################小**********結###############################
1,資料集的準備通常需要對資料進行處理,比如,異常點,缺失值的處理等。
2,從資料集中劃分一部分作為train set,一部分作為test set是很有必要的,也是機器學習中經常做的一部分工作,可以採用sklearn的train_test_split函式來實現,簡單有效。也可以自己寫函式先shuffle,再取一部分資料。
3,為了配合後面的線性迴歸器進行fit,需要對向量資料進行reshape,轉變為M行N列的形式(此處只有一個特徵向量,即X,故而N=1)。
#################################################################
2. 構建,訓練線性迴歸器模型
使用sklearn可以非常簡單的建立和訓練迴歸器模型,只需一兩行程式碼就搞定,如下程式碼:
# 第二部分:使用train set進行模型訓練
from sklearn import linear_model
linear_regressor=linear_model.LinearRegression() # 建立線性迴歸器物件
linear_regressor.fit(X_train,y_train) # 使用訓練資料集訓練該回歸器物件
# 檢視擬合結果
y_predict=linear_regressor.predict(X_train) # 使用訓練後的迴歸器物件來擬合訓練資料
plt.figure()
plt.scatter(X_train,y_train)
plt.plot(X_train,y_predict,'-b',linewidth=3)
複製程式碼
通過上圖,可以看出該線性迴歸模型貌似能夠很好地擬合這些資料點,但是這是將模型用於預測訓練集得到的結果,如果用該模型預測它從來沒見過的測試集,會得到什麼結果了?
# 用訓練好的模型計算測試集的資料,看是否能得到準確值
y_predict_test=linear_regressor.predict(X_test)
plt.figure()
plt.scatter(X_test,y_test)
plt.plot(X_test,y_predict_test,'-b',linewidth=3)
plt.scatter(X_test,y_predict_test,color='black',marker='x')
複製程式碼
########################小**********結###############################
1,sklearn模組種類的linear_model包中有很多線性模型,其中的LinearRegression 專門用於線性迴歸模型。
2,這個線性迴歸模型的訓練很簡單,fit()函式一下子搞定。
3,一旦模型訓練好,可以用predict()函式來預測新的沒有見過的X資料。
#################################################################
3. 評價模型的準確性
雖然上面我們把模型擬合後的直線繪製出來,從肉眼看上去,貌似這個線性迴歸模型可以得到比較好的擬合結果,但是怎麼樣通過定量的數值來評價這個模型的好壞了?評價指標。
有很多評價指標可以用來衡量回歸器的擬合效果,最常用的是:均方誤差(Mean Squared Error, MSE)。
均方誤差MSE是應用的最廣泛的一個評價指標,其定義為:給定資料集的所有點的誤差的平方的平均值,是衡量模型的預測值和真實值之間誤差的常用指標。這個值越小,表明模型越好。計算公式為:
還有一些其他的評價指標,比如中位數絕對誤差,解釋方差分,R方得分等。如下是本模型的評價指標結果。
# 使用評價指標來評估模型的好壞
import sklearn.metrics as metrics
print('平均絕對誤差:{}'.format(
round(metrics.mean_absolute_error(y_predict_test,y_test),2)))
print('均方誤差MSE:{}'.format(
round(metrics.mean_squared_error(y_predict_test,y_test),2)))
print('中位數絕對誤差:{}'.format(
round(metrics.median_absolute_error(y_predict_test,y_test),2)))
print('解釋方差分:{}'.format(
round(metrics.explained_variance_score(y_predict_test,y_test),2)))
print('R方得分:{}'.format(
round(metrics.r2_score(y_predict_test,y_test),2)))
複製程式碼
-------------------------------------輸---------出--------------------------------
平均絕對誤差:11.98 均方誤差MSE:211.52 中位數絕對誤差:12.35 解釋方差分:0.93 R方得分:0.92
--------------------------------------------完-------------------------------------
########################小**********結###############################
1,對一個模型的評價指標非常多,而對線性迴歸模型,可以只看均方誤差和解釋方差分,均方誤差越小越好,而解釋方差分越大越好。
2,本模型的均方誤差比較大的原因,可能在於準備資料集時引入的誤差太大,導致了資料太過分散,得到的模型擬合的直線與隨機點的誤差較大。
#################################################################
4. 模型的儲存和載入
模型一般得來不易,有時候需要耗費幾個小時,乃至幾個月的訓練才能得到,故而在訓練過程中,或者訓練結束後儲存模型是非常有必要的,這樣的預測新資料時只需簡單載入模型就可以直接使用。
關於sklearn模型的儲存和載入,sklearn官網給出了兩種建議的方式,第一種是使用pickle,第二種使用joblib,但我建議使用joblib,因為簡單好用。
# 迴歸模型的儲存和載入
# 在儲存之前先看一下模型的內部引數,主要為兩項,截距和係數
print('直線的截距: {}'.format(linear_regressor.intercept_)) #這是截距
print('直線的係數(斜率): {}'.format(linear_regressor.coef_)) #這係數,對應於本專案的直線斜率
y_value=linear_regressor.predict([[120]])
print('用線性模型計算的值:{}'
.format(y_value)) # 這兩個print的結果應該是一樣的
print('直線方程計算的值:{}'
.format(linear_regressor.coef_*120+linear_regressor.intercept_))
save_path='d:/Models/LinearRegressor_v1.txt'
# 第一種方法,pickle
import pickle
s=pickle.dumps(linear_regressor) # 模型儲存
loaded_classifier=pickle.loads(s) # 模型載入
y_value=loaded_classifier.predict([[120]])
print('pickle載入後的模型計算結果:{}'.format(y_value))
# 第二種方法:joblib
from sklearn.externals import joblib
joblib.dump(linear_regressor,save_path) # 模型儲存到檔案中
loaded_classifier2=joblib.load(save_path)
y_value=loaded_classifier2.predict([[120]])
print('joblib載入後的模型計算結果:{}'.format(y_value))
複製程式碼
-------------------------------------輸---------出--------------------------------
直線的截距: [9.50824666] 直線的係數(斜率): [[1.71905515]] 用線性模型計算的值:[[215.79486427]] 直線方程計算的值:[[215.79486427]] pickle載入後的模型計算結果:[[215.79486427]] joblib載入後的模型計算結果:[[215.79486427]]
--------------------------------------------完-------------------------------------
########################小**********結###############################
1,對sklearn模型的儲存和載入有兩種方式,通過pickle和joblib兩種方式,但joblib是sklearn自帶的模型儲存函式,而且可以很明確的指定儲存到哪一個檔案中,故而推薦用這種方式。
2,雖然儲存的方式不一樣,但是都可以正常的儲存和載入,得到的計算結果也一樣。
3,模型得到的直線方程是y=1.719x+9.508,和最開始的y資料產生時所用的直線方程y=1.8x+5.9+error有很大偏差,這是因為error噪音資料太大所導致的,故而模型得到的均方誤差也比較大一些。
#################################################################
注:本部分程式碼已經全部上傳到(我的github)上,歡迎下載。
參考資料:
1, Python機器學習經典例項,Prateek Joshi著,陶俊傑,陳小莉譯