前言
近年來AI人工智能成為社會發展趨勢,在IT行業引起一波熱潮,有關機器學習、深度學習、神經網路等文章多不勝數,從智能家居、自動駕駛、無人機、智能機器人到人造衛星、安防軍備,無論是國家級軍事設備還是廣泛的民用設施,都充斥著AI應用的身影,接下來的一系列文章將會由淺入深從不同角度分別介紹機器學習、深度學習之間的關系與區別,通過一系統的常用案例講述它們的應用場景,
本文將會從最常見的機器學習開始介紹相關的知識應用與開發流程,
目錄
一、淺談機器學習
二、基本概念
三、常用方法介紹
四、線性模型
五、支持向量機
六、k近鄰
七、樸素貝葉斯分類
八、決策樹
一、淺談機器學習
1.1 機器學習簡介
其實AI人工智能并非近代的產物,早在19世紀的50到80年代,科學家們就有著讓計算機演算法代替人腦思考的想法,通過幾十年的努力,到了90年代成為了機器學習蓬勃發展期,很多科技公司在不同領域提供了相關的技術支持,在最初,機器學習只用于垃圾郵件清理,數學公式分析等簡單領域,然而后來其應用場景越來越多,無論是圖片過濾,語音分析,資料清洗等領域都能看到機器學習的身影,到如今無論是智能手機,航空運輸,智能駕駛等方方面面都可以看到 AI 的身影,
從不同領域分析,AI人工智能包含了機器學習與深度學習,

機器學習主要應用資料科學領域,它與普通程式開發的主要區別在于一般程式,資料往往來源于不同的資料庫,通過對資料進行復雜轉化,運算得到最后的結果,
而機器學習目的并不是為了得到最后的運算結果,而是對計算程序進行分析,總結出一套運算的規則,只要資料量足夠多,運算規則就越準確,最后可以根據這套規則對沒有通過驗證的資料進行預算,得到預算后的值,只要使用的規則正確,預算的結果的正確率往往可以達到95%以上,
而深度學習開始只是機器學習的一分支領域,它更強調從連續的層中進行學習,這種層級結構中的每一層代表不同程式的抽象,層級越高,抽象程度越大,這些層主要通過神經網路的模型學習得到的,最大的模型會有上百層之多,而最簡單的神經網路分為輸入層,中間層(中間層往往會包含多個隱藏層),輸出層,

經過多年的發展,深度學習的應用層面越來越廣,從而誕生出 Tensorflow、Keras、Theano、CNTK 等框架的支持,逐漸發展形成獨立的技術領域,根據不同的應用場景,深度學習又分為多層感知器MLP、深度神經網路DNN、卷積神經網路CNN、遞回神經網路RNN、生成對抗網路BNN等多個方面,
由于本系統文章主要是對機器學習進行介紹,對深度學習有興趣有朋友可以留意后面的文章,將會對神經網路進行更深入全面的講解,
1.2 機器學習分類
機器學習可分為監督學習與無監督學習兩種
1.2.1 監督學習
從輸入 / 輸出中總結運算規律進行機械學習的演算法叫做監督學習,在監督學習中,每個實體都是由一個輸入物件(通常為矢量)和一個期望的輸出值(也稱為監督信號)組成,監督學習演算法是分析該資料的輸入物件與輸出值 ,總結當中的運算規律并產生一個推斷的邏輯,并可以把此邏輯用于映射出的新實體,對沒有運算過的資料結果進行預測,
其程序就像讀小學時候的數學應用題,課堂上老師會先講解應用題的公式與計算方法,然后學生可以在做作業時根據此計算規律完成類似的題目,
常見的監督學習包含了線性回歸、k近鄰、樸素貝葉斯分類 、決策樹、隨機森林與梯度提升決策樹、支持向量機等多種演算法,在下面將用不同實體介紹其應用場景,
1.2..2 無監督學習
相比之下無監督學習只有輸入資料,沒有提供輸出結果,只能通過計算評估其中的運行規律,就好比在考試中遇到作業上沒有做過的應用題,只能通過思考總結才能得出答案,它的最大挑戰在于實體沒有包含最終結果,所以無法在學習中評估演算法是否學到了有用的東西,
常見的無監督學習分為聚類、降維兩大類,包含了PCA(主成分分析)、NMF(非負矩陣分解)、t-SNE(流形學習)、k均值聚類、DBSCAN 等多種演算法,將在下一篇文章 《 Python 機器學習實戰 —— 無監督學習》中詳細介紹,敬請留意,
回到目錄
二、基本概念
在機器學習里,每一行的資料實體被看作是一個樣本,每一列的資料被看作是一個特征,學過面向物件開發的朋友應該好理解,這相當于一個類與其屬性的關系,而監督學習是通過一系列樣本計算,總結出特征與計算結果之間的關系,這被稱為特征提取,
2.1 分類與回歸的區別
監督學習分成分類與回歸兩大類:分類與回歸
分類的目標是預測類別標簽,類似于通過人臉識別分出黃種人、白種人、黑種人,
回歸的目標則是預測一個連續值,相當于數學里面的坐標圖,它可以通過已有的資料計算出坐標的走向,從而對末知值進行預測,類似于氣溫預計,GDP增速預算,
其實區分分類與回歸很簡單,只要判斷輸出值是否具備連續性,具有連續性的就是回歸,只有若干個固定值的就是分類,
2.2 資料劃分
從監督學習的概念可以了解,其學習目標是要從已有的資料中總結出各個特征與結果之間的關系,然而這個計算模型的泛化能力如何,我們無法從已通過運算過的資料中得知,所以在運算前,一般會先對資料進行劃分,一部分是訓練資料,專門用作模型的學習,另一部分是測驗資料,用作模型的測驗,其比例一般是75%對25%,
為了方便資料劃分,sklearn 提供 train_test_split 函式方便日常使用,下一節將有介紹,
2.3 過擬合與欠擬合
對于訓練資料最重要的目標是將其泛化,總結出每個特征的比重,因此并非正確率越高越好,如果在訓練程序中,得到的正確率是100%,然而在測驗時候,正常率卻很低,不能泛化到測驗資料當中,這往往是因為訓練程序中存在過似合,過度重視某些特征,而忽略某些關鍵因素,
相反,如果模型過分簡單,考慮的因素太少,甚至導致在訓練程序中的正確率就很低,這可能就是因為模型的欠擬合造成,
所以在選擇模型的時候,需要在過擬合與欠擬合之間作出權衡(如圖),
2.4 損失函式
損失函式一方面跟數學計算原理的應用關系比較密切,另一個方面它也是機器學習的基層原理,此章節想做一個簡單的介紹,由于網上關于這方面的學習材料很多,所以此章節主要是想通過最容易理解的方式去講述計算的原理,至于復雜的計算流程可以在網上查找,
在寫這篇文章的時候,恰好是高考的開考日,在這里預祝師弟師妹能順利闖關,金榜題名,畢竟最近疫情有復發的情況,學習實在不容易,通過此章節想告訴理工科的學子們,辛勤學習并非一無所用,公式里所蘊藏的原理,他日參加作業時候還是有很廣的應用場景,
在日常對資料模型的測驗中,預測值與真實值總會存在一定的偏差,以直線方程 y=a*x+b 為例,如下圖,在預測值 [x1,y1] [x2,y2] [x3,y3] ....... [x(i),y(i)] 中,并非每個點與方程匹配,而是存在一定的誤差,所以在測驗中,當誤差值最小時,可以看作這條直線的正確答案,

損失函式就是用來評價模型的預測值和真實值不一樣的程度,當損失值越小,證明預計值越接近真實值,模型的訓練程度就越好,為了讓預測值 y^ (i) 盡量接近于真實值 y(i) ,學者提出了多個方法進行計算損失值,最常見的損失函式有均方誤差(MSE)、交叉熵誤差(CEE)等,
均方誤差(MSE)中,因為每個預測值與真實值大小不同,為了解決差值的正負值之分,所以誤差大小就用差值平方來表示,差值越小,平方值小,誤差越小,
均方誤差計算公式就是求預測值與真實值之間的方差平均值,如果均方誤差越小,那就證明預測值越接近于真實值,

交叉熵誤差(CEE)的公式如下,其中 為實際的分類結果,
為預測的結果

把它用于分類模型時有一個很好的特性,可以真實的反應出真實分類結果和預測結果的誤差,假設 , 即真實的分類結果是
, 則 交叉熵誤差可以簡化為
,那函式影像如下,可以看到,
越接近
,即預測結果和真實分類結果越接近,誤差越接近
, 即誤差越小,

關于損失函式有多種演算法,下面介紹兩種最常用也是最容易理解的演算法:最小二乘法(OLS)與梯度下降法(GD)
2.4.1 最小二乘法
最小二乘法是基于 MSE 均方誤差而來,它的目標就是求出 n 個 y^ (i) 與 y(i) 誤差值平方和最小時各常量引數的值,還是以上面提到的直線方程 y=a*x+b 為例,均方誤差越小,證明預測值越接近于真實值,這時候只要求出最小均方誤差時 a,b的值,也就可以得出此直線的方程,因為 n 是個常數,所以求最小均方誤差的公式可以轉化為求預測值 y^ (i) 與真實值之 y(i) 間的最小方差,這就是最小二乘法的由來,

代入 y=a*x+b 后,計算公式可得到轉換成下圖,由于計算的是平方和,所以運算式必然存在最小值,通過偏導數為 0 時得極值的原理,可得到計算公式:

此時好辦,最小二乘法已經變成高考的導數題,要了解求解程序的程式猿建議去復習一下高考時偏導數求解的方法(若怕麻煩通過幾條簡單的語法計算也可得到最終結果),根據以下得出的公式,代入已知的資料點 [x1,y1] [x2,y2] [x3,y3] ....... [x(i),y(i)] 便可得到最后的 a、b 引數值,最后把 a、b 值代入直線方程就是最終的答案,

第四節介紹的線性回歸模型中,有部分就是使用最小二乘法來實作的,敬請留意,
當然,現實情形下資料模型是復雜的,不可能完全按照直線模型來走,所以復雜的線性模型還可以通過多次方程式,基函式,線性分割等多種方法來處理,在后面章節將會詳細講述,
2.4.2 梯度下降法
上面提到損失函式就是用來評價模型的預測值和真實值不一樣的程度,損失值越小證明引數的預測值越接近于真實值,梯度下降法就是用于求損失值最小時的引數值,相對于最小二乘法復雜的數學公式,它可以通過偏導數斜率的變化更形象地描述解決的程序,
先介紹梯度下降法用到的基本概念:
切線斜率:從數學的角度可知,一維函式在點 x 的導數叫做函式在點 x 的切線斜率,二維函式在點(x0,x1) 的偏導數稱為點(x0,x1) 的切線斜率,如此類推,
鞍點:函式的極小值(最小值)被稱為鞍點,從數學原理可知當到達鞍點時,切線斜率接近于 0,
梯度:以常用的二元方程 f(x0,x1)=x02+x12 為例子,把全部變數的偏導數(切線斜率)匯總成的向量稱為梯度

下面以一個二次方程 f(x)=x2+1 為例,介紹一下如何通過梯度下降法求極值
首先,切線的斜率可以通過導數原理求得,例如 f(x) 分別經過點 A(- 4,17) ,B(- 5,26),此時會發現,兩點之間的距離越小,它們所組成的直線就越接近于該點的切線,兩點距離無限接近于0時,此時可認為這條直線就是該點的切線,這也是微積分的基本原理,

下面的例子就是實作上述的原理,畫出與點(- 4,17) 間 x 軸的距離分別為 0.3、0.2、0.001,0.0001 的直線,呼叫 tangent()可以回傳該直線的斜率,當 h 使用默認值 0.0001 值,該直線斜率已經無限接近于導數值(即切線斜率),
1 class gradinet_drop(): 2 def __init__(self): 3 return 4 5 def f(self, x): 6 return x*x+1 7 8 def polt_line(self): 9 # 畫出 y=x*x+1 圖 10 x = np.linspace(-5, 5, 100) 11 plt.plot(x.reshape(-1, 1), self.f(x)) 12 plt.xlabel('x') 13 plt.ylabel('f(x)') 14 15 def tangent(seft,x,h=0.0001): 16 #根據 y=ax+b 直線公式 17 #求導數,即切線斜率 18 a=(seft.f(x+h)-seft.f(x))/h 19 #求截距 20 b=seft.f(x+h)-a*x+h 21 #劃出切線 22 seft.polt_tangent(a,b) 23 return a 24 25 def polt_tangent(self,a,b): 26 #劃出直線 y=ax+b 27 x = np.linspace(-5, -1, 100) 28 y=a*x+b 29 plt.plot(x.reshape(-1,1),y,'--') 30 31 gradientdrop=gradinet_drop() 32 gradientdrop.polt_line() 33 # 分別以 h 等于 -0.3,-0.2,-0.001 ,0.0001求斜率 34 # 將發現 h 越小,所得斜率越接于近切線斜率 35 for h in [-0.3,-0.2,0.001,0.0001]: 36 print('h={0}, tangent={1}'.format(h,gradientdrop.tangent(-4,h))) 37 plt.show()
運行結果


梯度下降法最終的目標是求得切線斜率接近無限于0時的資料點(鞍點), 所以函式可以使用最簡單的方法,使取值沿著當前梯度方向不斷前進,然后重復計算梯度,通過此遞回方式,切線斜率就是無限接近于0,
用數學公式表達如下圖,η 表示學習率,學習率可以根據實際需要而改變,將學習率乘以該點的切線斜率代表學習量,

根據此公式,可以在上面的代碼中加入一個簡單的方法gradient(),以 0.01 的學習率,重復1000次進行計算,隨著梯度不斷地下降,x 坐標就會根據學習量不斷接近鞍點,在重復1000后可以看到在資料點(0,1)處的切線斜率 tangent 為 -1.0098588631990424e-08,已無限接近于 0 ,此時鞍點的函式值 f(x)=1 就是此函式的最小值,
1 class gradinet_drop(): 2 def __init__(self): 3 return 4 5 def f(self, x): 6 return x*x+1 7 8 def polt_line(self): 9 # 畫出 y=x*x+1 圖 10 x = np.linspace(-5, 5, 100) 11 plt.plot(x.reshape(-1, 1), self.f(x)) 12 plt.xlabel('x') 13 plt.ylabel('f(x)') 14 15 def tangent(seft,x,h=0.0001): 16 #根據 y=ax+b 直線公式 17 #求導數(切線斜率) 18 a=(seft.f(x+h)-seft.f(x))/h 19 #求截距 20 b=seft.f(x+h)-a*x+h 21 #隨機畫出切線 22 n=np.random.randint(100) 23 if(n==5): 24 seft.polt_tangent(a,b) 25 return a 26 27 def polt_tangent(self,a,b): 28 #劃出切線 y=ax+b 29 x = np.linspace(-5, 1, 100) 30 y=a*x+b 31 plt.plot(x.reshape(-1,1),y,'--') 32 33 def gradinet(seft,x,rate=0.01,n=1000): 34 #學習率默認為0.01,默認重復1000次 35 for i in range(n): 36 x-=seft.tangent(x)*rate 37 print('x={0},f(x)={1},tangent slope={2}'.format(x,seft.f(x),seft.tangent(x))) 38 39 gradientdrop=gradinet_drop() 40 gradientdrop.polt_line() 41 #使用學習率默認0.01,默認重復1000次求出鞍點 42 gradientdrop.gradinet(-3) 43 plt.show()
運行結果


到此不防思考一下使用梯度下降法找到鞍點的目標是什么,其實只要把簡單的二次方程 f(x) 替換成為損失函式便會豁然開朗,以均方誤差 MSE 為例,當找到鞍點,意味著找到函式最小值,即在該點時均方誤差最小,在這點得到的引數值就是該函式的常量,
同樣以最小二乘法中的直線 y=a*x+b 為例,把代碼稍微修改一下,首先把方程改為均方誤差公式,通過 sympy 包中的函式 diff 分別對 a,b 求偏導數,把學習率設定為0.01,訓練1000次,a,b的默認初始值均為 1,使用 sklearn 中的測驗資料 make_regression 進行訓練,最后畫出該直線并輸出斜率和截距,
1 class gradinet_drop(): 2 def __init__(self,train_x,train_y): 3 #定義測驗資料 4 self.x=train_x 5 self.y=train_y 6 return 7 8 def mes(self,a,b): 9 #均方誤差計算公式 10 return mean((self.y-a*self.x-b)**2) 11 12 def partial_derivative(seft): 13 #求 mes(a,b)對 a,b 的偏導數 14 partial_derivative_a=sp.diff(seft.mes(a,b), a) 15 partial_derivative_b=sp.diff(seft.mes(a,b),b) 16 return [partial_derivative_a,partial_derivative_b] 17 18 def gradinet(seft,rate=0.01,n=1000): 19 #學習率默認為0.01,默認重復1000次 20 #把 y=a*x+b 引數 a,b 的初始值設為 1,1 21 a1=1 22 b1=1 23 #默認訓練1000次,找到最小均方誤差時的 a,b 值 24 for i in range(n): 25 deri=seft.partial_derivative() 26 a1-=deri[0].subs({a:a1,b:b1})*rate 27 b1-=deri[1].subs({a:a1,b:b1})*rate 28 #輸出直線引數 a,b 值 29 print('y=a*x=b\n a={0},b={1}'.format(a1,b1)) 30 return [a1,b1] 31 32 def polt_line(self,param): 33 #根據a,b引數值劃出直線 y=a*x+b 34 x = np.linspace(-3, 3, 100) 35 y=param[0]*x+param[1] 36 plt.plot(x,y,20) 37 plt.legend(['train data','line']) 38 39 #輸入測驗資料 40 X,y=dataset.make_regression(n_features=1,noise=5) 41 gradient_drop=gradinet_drop(np.squeeze(X),y) 42 #畫出資料點 43 plt.plot(X,y,'.') 44 #訓練資料找出最小均方誤差時的引數 a,b 值 45 param=gradient_drop.gradinet() 46 #畫出訓練后的直線 47 gradient_drop.polt_line(param) 48 plt.show()
運行結果
![]()

可見計算后的直線與訓練庫中的點已相當接近,使用梯度下降法只需要牢記一點:計算目標是求出鞍點,在損失值最低時的引數值就是該函式的常量,下面介紹到的 SGD 模型正是使用梯度下降法進行計算,后面將有詳細說明,
對損失函式就介紹到這里,希望對各位的理解有所幫助,
回到目錄
三、常用方法介紹
Scikit-Learn 是目錄最常用的機器學習庫,所以在本文當中大部實體都是運用當中的方法完成,
在 sklearn.datasets 中包含了大量的資料集,可為運算提供測驗案例,例如鳶尾花資料集 load_iris()、癌癥資料集 load_breast_cancer()、波士頓放假資料集 load_boston() 這些都是各大技術文章里常用的資料集,為了方便閱讀下面大部分的例子中都會用到這些資料作為例子進行解說,
3.1 train_test_split 方法
上面曾經提起,為了方便區分訓練資料與測驗資料,sklearn 提供了 train_test_split 方法去劃分訓練資料與測驗資料
train_test_split( * arrays, test_size=None, train_size=None, random_state=None, shuffle=True,stratify=None)
- *arrays:可以是串列、numpy陣列、scipy稀疏矩陣或pandas的資料框
- test_size:可以為浮點、整數或None,默認為None
①若為浮點時,表示測驗集占總樣本的百分比
②若為整數時,表示測驗樣本樣本數
③若為None時,test size自動設定成0.25
- train_size:可以為浮點、整數或None,默認為None
①若為浮點時,表示訓練集占總樣本的百分比
②若為整數時,表示訓練樣本的樣本數
③若為None時,train_size自動被設定成0.75
- random_state:可以為整數、RandomState實體或None,默認為None
①若為None時,每次生成的資料都是隨機,可能不一樣
②若為整數時,每次生成的資料都相同
- stratify:可以為類似陣列或None
①若為None時,劃分出來的測驗集或訓練集中,其類標簽的比例也是隨機的
②若不為None時,劃分出來的測驗集或訓練集中,其類標簽的比例同輸入的陣列中類標簽的比例相同,用于處理不均衡資料集
常用例子:
把1000個資料集按 75% 與 25% 的比例劃分為訓練資料與測驗資料
1 X,y=make_wave(1000) 2 X_train,X_test,y_train,y_test=train_test_split(X,y)
train_size 與 test_size 默認值為75% 與 25% ,所以上面的例子與下面例子得出結果相同
1 X,y=make_wave(1000) 2 X_train,X_test,y_train,y_test=train_test_split(X,y,train_size=0.75,test_size=0.25)
為了每次測驗得到相同的資料集,可以把 RandomState 設定為相同值,只要RandomState相同,則產生的資料集相同,
因為 RandomState 默認為空,因此在不填寫的情況下,每次產生的資料集都是隨機資料
1 X,y=make_wave(1000) 2 X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=0)
3.2 predict 方法與 accuracy_score 方法
在分類模型中,通常使用 predict(selt, X:any)方法預測新資料的標簽,通常在完成訓練后,就是通過此方法輸入測驗資料,把計算結果與測驗結果進行對比,
accuracy_score 主要用于對比預測結果的準備率
下面的代碼就是最簡單的流程,先劃分訓練資料與測驗資料,然后選擇模型,輸入訓練資料進行泛化,輸入測驗資料求出計算結果,最后把計算結果與已有的測驗結果進行對比,查看其正確率,
1 X,y=make_blobs(n_samples=150,n_features=2) 2 #劃分訓練資料與測驗資料 3 X_train,X_test,y_train,y_test=train_test_split(X,y) 4 #系結模型 5 knn_classifier=KNeighborsClassifier(n_neighbors=7) 6 #輸入訓練資料 7 knn_classifier.fit(X_train,y_train) 8 #運行測驗資料 9 y_model=knn_classifier.predict(X_test) 10 #把運行結果與測驗結果進行對比 11 print(accuracy_score(y_test,y_model))
3.3 score 方法
在回歸模型當中,由于測驗結果并非固定值,所以一般通過使用 score(self, X, y, sample_weight=None) 方法,通過對 R^2(判定系數)來衡量與目標均值的對比結果,
R^2=1,則表示模型與資料完成吻合,如果R^2為負值,側表示模型性能非常差,
1 X,y=make_wave(1000) 2 X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=0) 3 linearReg=LinearRegression() 4 linearReg.fit(X_train,y_train) 5 print(linearReg.score(X_train,y_train))
3.4 validation_curve 驗證曲線
上一節曾經講過,要提高模型的質量,應該在過擬合跟欠擬合當中,選擇一個平衡點,
有見及此,為了更直觀地了解模型的質量,sklearn 特意準備了 validation_curve 方法 ,可以更直觀與觀察到驗證曲線的示意圖,
validation_curve(estimator, X, y, *, param_name, param_range, groups=None,
cv=None, scoring=None, n_jobs=None, pre_dispatch="all",
verbose=0, error_score=np.nan, fit_params=None)
- estimator:實作了fit 和 predict 方法的物件
- X : 訓練的向量
- y : 目標相對于X分類或回歸
- param_name:將被改變的變數名稱
- param_range:param_name對應的變數的取值
- cv:如果傳入整數,測驗資料將分成對應的分數,其中一份作為cv集,其余n-1作為traning(默認為3份)
只需要簡單地系結模型,輸入資料,便可得到模型的準確率變化曲線
1 # validation_curve 系結 SVC 2 X, y = load_digits(return_X_y=True) 3 param_range = np.logspace(-6, -1, 5) 4 train_scores, test_scores = validation_curve( 5 SVC(), X, y, param_name="gamma", param_range=param_range, 6 scoring="accuracy", n_jobs=1) 7 #訓練資料與測驗資料的平均得分 8 train_scores_mean = np.mean(train_scores, axis=1) 9 test_scores_mean = np.mean(test_scores, axis=1) 10 #顯示資料 11 plt.title("Validation Curve with SVM") 12 plt.xlabel(r"$\gamma$") 13 plt.ylabel("Score") 14 plt.ylim(0.0, 1.1) 15 lw = 2 16 plt.semilogx(param_range, train_scores_mean, label="Training score", 17 color="darkorange", lw=lw) 18 plt.semilogx(param_range, test_scores_mean, label="Cross-validation score", 19 color="navy", lw=lw) 20 plt.legend(loc="best") 21 plt.show()
運行結果

下面的章節開始介紹 sklearn 中監督學習的一些常用模型,可能模型的使用方法基本一致,看起來似乎千篇一律,實則不然,因為機器學習與一般的開發不一樣,主要是了解不同模型的運算規則和適用場景,從原理中理解實質,希望讀者能夠明白,
回到目錄
四、線性模型
線性模型是實踐中應用最為廣泛的模型之一,它支持分類與回歸,最常用的線性分類演算法有 LogisticRegression , LinearSVC、SGDClassifier,常用的線性回歸演算法有 LinearRegression、Ridge、Lasso 、SGDRegressor 等,后面將一一講解,前幾節,將從最常用的線性回歸開始入手,介紹幾個最常用的線性模型,
從最簡單的直線函式 y = w * x + k 開始介紹,這就是線性模型當中最簡單的單一特征模型,模型訓練的目標是通過資料計算出最接近資料點的模型引數 w 與 k,
其中 x 是輸入變數,也是唯一的特征;
w 為斜率,也被稱為權重被保存在 coef_ 屬性當中;
k 為載矩也稱偏移量,被保存于 intercept_ 屬性當中,
線性函式的運算結果類似于下圖,是一條 斜率為 w,偏移量為 k 的直線

模型看似簡單,然而在機器學習中模型往往是由淺入深,當輸入特征由一個變為兩個時,模型變會從一條直線變為一個平面,當特征變為三個時,模型將變成一個立體三維空間 ......
由此可得,線性模型的最終公式如下,模型會有 n+1 個特征
y = w[0] * x[0] + w[1] * x[1] + w[2] * x[2] + w[3] * x[3] + ...... + w[n] * x[n] + k
為了更好地理解線性模型,下面先由最簡單的 “線性回歸 LinearRegression” 開始講起
4.1 線性回歸 LinearRegression
LinearRegression 是最簡單的的線性模型,此模型就是通過第二節介紹的最小二乘法,找出引數 w 與 k,使得訓練集與預測集的均方誤差 MSE 最小,最終確定 w 與 k 值,
下面就是一個單一特征的資料集進行測驗,用 100 條資料計算出斜率和偏移量,再劃出此直線,
1 def linear_regression_test(): 2 #測驗資料 3 line=np.linspace(-3,3,50) 4 datasets.make_regression(n_features=1,noise=35,random_state=1) 5 X_train,X_test,y_train,y_test=train_test_split(X,y) 6 #線性回歸模型訓練 7 linear=LinearRegression() 8 linear.fit(X_train,y_train) 9 #準確率 10 print('train data prec:'+str(linear.score(X_train,y_train))) 11 print('test data prec:'+str(linear.score(X_test,y_test))) 12 #斜率與截距 13 print('coef:'+str(linear.coef_)) 14 print('intercept:'+str(linear.intercept_)) 15 #圖形顯示 16 plt.plot(X_train,y_train,'*','') 17 result=linear.predict(line.reshape(-1,1)) 18 plt.plot(line,result,'r') 19 plt.legend(['training data','model']) 20 plt.show()
輸出結果

線性模型
LinearRegression 模型比較簡單,不需要任何引數,但因此也無法調節模型的復雜程度,可以看到訓練資料與測驗資料的準確都不高,在實際應用中,LinearRegression 模型的應用場景并不多,
4.2 Ridge 嶺回歸
為了提高準確率,sklearn 設計了 Ridge 嶺回歸模型來代替 LinearRegression,嶺回歸也是使用最小二乘法進行計算,然而與 LinearRegression 不同的是嶺回歸使用了正則化 L2,它會讓 w 的元素盡量偏向于0,
Ridge 建構式
1 class Ridge(MultiOutputMixin, RegressorMixin, _BaseRidge): 2 @_deprecate_positional_args 3 def __init__(self, alpha=1.0, *, fit_intercept=True, normalize=False, 4 copy_X=True, max_iter=None, tol=1e-3, solver="auto", 5 random_state=None): 6 ......
引數說明
- alpha 是正則項系數,初始值為1,數值越大,則對復雜模型的懲罰力度越大,
- fit_intercept:bool型別,默認為True,表示是否計算截距 ( 即 y=wx+k 中的 k ),
- normalize:bool型別,默認為False,表示是否對各個特征進行標準化(默認方法是:減去均值并除以L2范數),推薦設定為True,如果設定為False,則建議在輸入模型之前,手動進行標準化,當fit_intercept設定為False時,將忽略此引數,
- copy_X:默認值為True,代表 x 將被復制,為 False 時,則 x 有可能被覆寫,
- max_iter:默認值為None,部分求解器需要通過迭代實作,這個引數指定了模型優化的最大迭代次數,
- tol:默認為小數點后3位,代表求解方法精度
- solver:求解優化問題的演算法,默認值 auto,可以根據資料型別選擇最合適的演算法,可選的演算法有:
1).svd:采用奇異值分解的方法來計算,
2).cholesky:采用scipy.linalg.solve函式求得閉式解,
3).sparse_cg:采用scipy.sparse.linalg.cg函式來求取最優解,
4).lsqr:使用scipy.sparse.linalg.lsqr求解,它是最快的,
5).sag:使用隨機平均梯度下降,當n_samples和n_features都較大時,通常比其他求解器更快,
- random_state:亂數種子,推薦設定一個任意整數,同一個隨機值,模型可以復現,
Ridge 使用了 L2 正則化規范,對模型系數 w 進行約束,使每個特征的 w 盡可能的小,避免過度擬合, 相比 LinearRegression ,可見 Ridge 使用 L2 正則化規范后,分數有進一步的提升,
1 def ridge_test(): 2 #測驗資料 3 line=np.linspace(-3,3,100) 4 X,y=datasets.make_regression(n_features=1,noise=35,random_state=1) 5 X_train,X_test,y_train,y_test=train_test_split(X,y) 6 #嶺回歸模型 7 ridge=Ridge(alpha=1) 8 ridge.fit(X_train,y_train) 9 #計算準確率 10 print('train data prec:'+str(ridge.score(X_train,y_train))) 11 print('test data prec:'+str(ridge.score(X_test,y_test))) 12 #斜率與截距 13 print('coef:'+str(ridge.coef_)) 14 print('intercept:'+str(ridge.intercept_)) 15 # 圖形顯示 16 plt.plot(X_train, y_train, '*', '') 17 plt.legend(['training data', 'model']) 18 result = ridge.predict(line.reshape(-1, 1)) 19 plt.plot(line, result, 'r') 20 plt.show()
運算結果

Ridge 模型

除此以外,Ridge 模型還提供了一個引數 alpha 用于控制其泛化性能,alpha 默認值為 1,
alpha 越大,w 會更趨向于零,因此泛化性能越高,但訓練集上的性能會降低,在資料量較大的時候需要權衡利弊,
alpha 越小,w 的限制就會越小,訓練集上的性能就會越高,要注意的是如果把 alpha 調到接近于 0,那 L2 正則化基本不起作用,Ridge 的計算結果將接近于 LinearRegression,
下面試著把 alpha 值調為 0.2 ,可以看到測驗集上的分數有一定提升,

4.3 Lasso 模型
Lasso 跟 Ridge 一樣都是線性回歸模型,兩者的主要區別在 Ridge 默認是使用 L2 正則化,而 Lasso 使用的是 L1 正則化規范,L1 會把引數控制趨向于0,從而抽取更有關鍵代表性的幾個特征,
Lasso的建構式
1 class Lasso(ElasticNet): 2 @_deprecate_positional_args 3 def __init__(self, alpha=1.0, *, fit_intercept=True, normalize=False, 4 precompute=False, copy_X=True, max_iter=1000, 5 tol=1e-4, warm_start=False, positive=False, 6 random_state=None, selection='cyclic'): ......
引數說明
- alpha 是正則項系數,初始值為1,數值越大,則對復雜模型的懲罰力度越大,
- fit_intercept:bool型別,默認為True,表示是否計算截距(即y=wx+k中的k),
- normalize:bool型別,默認為False,表示是否對各個特征進行標準化(默認方法是:減去均值并除以L2范數),推薦設定為True,如果設定為False,則建議在輸入模型之前,手動進行標準化,當fit_intercept設定為False時,將忽略此引數,
- precompute:默認值為 Falise,表示是否使用預計算的 Gram 矩陣來加速計算,如果設定為 auto 代表讓計算機來決定,Gram 矩陣也可以作為引數傳遞,對于稀疏輸入這個選項總是正確的,用于保持稀疏性,
- copy_X:默認值為True,代表 x 將被復制,為 False 時,則 x 有可能被覆寫,
- max_iter:默認值為10000,部分求解器需要通過迭代實作,這個引數指定了模型優化的最大迭代次數,
- tol:默認為小數點后 4 位,代表求解方法精度,
- warm_start:默認值為 False,當設定為True時,重用之前呼叫的解決方案作為初始化,否則,只需要洗掉前面的解決方案,
- positive:默認值為 False,當設定為 True 時,則系數總是為正數,
- random_state:亂數種子,推薦設定一個任意整數,同一個隨機值,模型可以復現,
- selection:默認值為 cyclic ,如果設定為 random,則每一次迭代都會更新一個隨機系數,而不是在默認情況下按順序回圈,這樣做通常會導致更快的收斂速度,尤其是當tol大于1e-4時,
Lasso 模型當中包含兩個主要引數:
alpha 默認值為 1 ,按照 L1的正則化規范,alpha 值越大,特征提取數越少,更多的權重會接近于0,alpha 越小,特征提取越多,當alpha 接近于0時,Lasso 模型與 Ridge 類似,所有特征權重都不為 0
max_iter 默認值為10000, 它標志著運行迭代的最大次數,有需要時可以增加 max_iter 提高準確率,
下面嘗試對 100 特征的資料進行測驗,把 alpha 值設定為 0.2,可以看到測驗資料的準確率比較低,只有 0.82,有效的特征有72個,
1 def lasso_test(): 2 # 測驗資料 3 X,y=datasets.make_regression(n_features=100,noise=40,random_state=1) 4 X_train,X_test,y_train,y_test=train_test_split(X,y,train_size=0.75,test_size=0.25) 5 # 把alpha設定為30 6 lasso=Lasso(alpha=0.2,max_iter=10000) 7 lasso.fit(X_train,y_train) 8 #輸出正確率 9 print('lasso\n train data:{0}'.format(lasso.score(X_train,y_train))) 10 print(' test data:{0}'.format(lasso.score(X_test,y_test))) 11 #有效特征數與總數 12 print(' used features: {0}'.format(sum(lasso.coef_!=0))) 13 print(' total features: {0}'.format(lasso.n_features_in_)) 14 plt.plot(lasso.coef_,'o',color='red') 15 plt.legend(['feature']) 16 plt.show()
運行結果


試著把 alpha 調為2,此時有效特征下降到 45,而測驗資料的分數達到 0.94


4.4 淺談正則化概念
通過 Ridge 與 Lasso 的例子,相信大家對正則化概念會有一定的了解,在這里簡單介紹一下L1、L2 正則化的區別,
正則化(Regularization)是機器學習中一種常用的技術,其主要目的是控制模型復雜度,減小過擬合,最基本的正則化方法是在原目標函式 中添加懲罰項,對復雜度高的模型進行“懲罰”,其數學表達形式為:
式中 、
為訓練樣本和相應標簽,
為權重系數向量;
為目標函式,
即為懲罰項,可理解為模型“規模”的某種度量;引數
控制控制正則化強弱,不同的
函式對權重
的最優解有不同的偏好,因而會產生不同的正則化效果,最常用的
函式有兩種,即
范數和
范數,相應稱之為
正則化和
正則化,
由運算式可以看出,L1 正則化則是以累加絕對值來計算懲罰項,因此使用 L1 會讓 W(i) 元素產生不同量的偏移,使某些元素為0,從而產生稀疏性,提取最有效的特征進行計算,
L2 正則化則是使用累加 W 平方值計算懲罰項,使用 L2 時 W(i) 的權重都不會為0,而是對每個元素進行不同比例的放縮,
通過 Ridge 與 Lasso 對比 L1 與 L2 的區別
1 def ridge_test(): 2 #測驗資料 3 line=np.linspace(-3,3,100) 4 X,y=datasets.make_regression(n_features=100,noise=40,random_state=1) 5 X_train,X_test,y_train,y_test=train_test_split(X,y) 6 #嶺回歸模型 7 ridge=Ridge() 8 ridge.fit(X_train,y_train) 9 #計算準確率 10 print('ridge\n train data:{0}'.format(ridge.score(X_train,y_train))) 11 print(' test data:'.format(ridge.score(X_test,y_test))) 12 print(' used features: {0}'.format(sum(ridge.coef_!=0))) 13 print(' total features: {0}'.format(ridge.n_features_in_)) 14 plt.plot(ridge.coef_,'^',color='g') 15 16 17 def lasso_test(): 18 # 測驗資料 19 X,y=datasets.make_regression(n_features=100,noise=40,random_state=1) 20 X_train,X_test,y_train,y_test=train_test_split(X,y) 21 # 把alpha設定為2 22 lasso=Lasso(alpha=2,max_iter=10000) 23 lasso.fit(X_train,y_train) 24 #輸出正確率 25 print('lasso\n train data:{0}'.format(lasso.score(X_train,y_train))) 26 print(' test data:{0}'.format(lasso.score(X_test,y_test))) 27 #斜率與截距 28 print(' used features: {0}'.format(sum(lasso.coef_!=0))) 29 print(' total features: {0}'.format(lasso.n_features_in_)) 30 plt.plot(lasso.coef_,'o',color='red') 31 plt.legend(['feature']) 32 33 ridge_test() 34 lasso_test() 35 plt.show()
運行結果


可見 Ridge 模型中有效特征仍為100,而 Lasso 的有效特征僅為 48,在一般運算中,往往會優先使用 Ridge 模型,但當資料的特征數太多時,Lasso 更顯出其優勢,
4.5 SGDRegressor 模型
前面介紹的 LinearRegression、Ridge、Lasso 等幾個模型,都是使用最小二乘法和求逆運算來計算引數的,下面介紹的 SGDRegressor 模型是使用梯度下降法進行計算的,
在第二節已經使用基礎的 Python 代碼實作最簡單的梯度下降法演算法,但其實在 sklearn 模型中早已準備了 SGDRegressor 模型支持梯度下降計算,運算時它比最小二乘法和求逆運算更加快節,當模型比較復雜,測驗資料較大時,可以考慮使用 SGDRegressor 模型,
SGDRegressor 建構式
1 class SGDRegressor(BaseSGDRegressor): 2 @_deprecate_positional_args 3 def __init__(self, loss="squared_loss", *, penalty="l2", alpha=0.0001, 4 l1_ratio=0.15, fit_intercept=True, max_iter=1000, tol=1e-3, 5 shuffle=True, verbose=0, epsilon=DEFAULT_EPSILON, 6 random_state=None, learning_rate="invscaling", eta0=0.01, 7 power_t=0.25, early_stopping=False, validation_fraction=0.1, 8 n_iter_no_change=5, warm_start=False, average=False): 9 ......
- loss:默認為“squared_loss”, 選擇要使用的損失函式,可用的回歸損失函式有:'squared_loss'、'huber'、epsilon_unsensitive'或'squared_epsilon_unsensitive',
- penalty:默認為 L2,用于指定懲罰項中使用的規范,可選引數為 L1 、 L2、elasticnet
- alpha:默認值為 0.0001 乘以正則項的常數,值越大,正則化越強,當學習率設為“optimal”時,用于計算學習率,
- l1_ratio:默認值為 0.15 彈性凈混合引數,0 <= l1_ratio <= 1. l1_ratio=0對應于L2懲罰,l1_ratio=1到 l1,僅當 penalty 為 elasticnet 時使用,
- fit_intercept:bool型別,默認為True,表示是否計算截距 ( 即 y=wx+k 中的 k ),
- max_iter:默認值為 1000,部分求解器需要通過迭代實作,這個引數指定了模型優化的最大迭代次數,
- tol:默認值為1e-3,默認為小數點后 3 位,代表求解方法精度
- shuffle:默認值為 True ,是否在每個epoch之后對訓練資料進行洗牌,
- verbose:默認值為 0 詳細程度,
- epsilon:當 loss 選擇 “huber” 時,它決定了一個閾值,在這個閾值下,預測值將被忽略,若選擇 “epsilon-insensitive” 表示若當前預測和正確標簽之間的差異小于此閾值,將被忽略,
- random_state:默認值為None 亂數種子,推薦設定一個任意整數,同一個隨機值,模型可以復現,
- learning_rate:學習率,默認值為 ’invscaling’ ,可選 constant、optimal、invscaling、adaptive
1)‘constant’: eta = eta0;
2)‘optimal: eta = 1.0 / (alpha * (t + t0)) ;
3)‘invscaling’: eta = eta0 / pow(t, power_t);
4)‘adaptive’: eta = eta0
- eta0:默認值為0.01,初始學習速率,
- power_t:默認值為0.25 反向縮放學習速率的指數
- early_stopping:默認值為 False 驗證分數沒有提高時,是否使用提前停止終止培訓,如果設定為True,它將自動將訓練資料的分層部分作為驗證,并且當分數方法回傳的驗證分數對 n_iter_no_change 連續時間段沒有至少提高tol時終止訓練,
- validation_fraction:默認值為 0.1 作為早期停機驗證設定的培訓資料的比例,必須介于0和1之間,僅在“早停”為真時使用,
- n_iter_no_change:默認值為 5 在提前停止之前沒有改進的迭代次數,
- warm_start:bool, 默認值為 False 當設定為True時,將上一個呼叫的解決方案重用為fit作為初始化,否則,只需洗掉以前的解決方案,
- average:默認值為 False 當設定為True時,計算所有更新的 averaged SGD權重,并將結果存盤在coef_ 屬性中,如果設定為大于1的整數,則當看到的樣本總數達到平均值時,將開始平均,所以average=10將在看到10個樣本后開始平均,
SGDRegressor 模型與 Ridge 類似,默認使用 L2 正則化,所有特征權重都不為 0 而是對每個元素進行不同比例的放縮,
max_iter 默認值為1000, 它標志著運行迭代的最大次數,有需要時可以增加 max_iter 提高準確率,
從第二節的例子可以理解,SGD默認使用 squared_loss 均方誤差作為損失函式,引數 eta0 =0.01 是初始的學習速率,當 learning_rate 為 constant 時,學習速率則恒定為 0.01,若使用默認值 invscaling,學習速率則通過公式 eta = eta0 / pow(t, power_t) 計算,
在下面的例子,把學習率設為恒定的 0.001,然后使用二個特征的資料進行測驗,最后使用三維圖形把計算出來的結果進行顯示
1 def sgd_regressor_test(): 2 # 測驗資料 3 X,y=dataset.make_regression(n_samples=100,n_features=2) 4 X_train, X_test, y_train, y_test = train_test_split(X, y) 5 # SGD 模型 6 sgd = SGDRegressor(learning_rate='constant',eta0=0.001,average=True) 7 sgd.fit(X_train,y_train) 8 # 準確率 9 print('SGD:\n train data:{0}\n test data:{1}' 10 .format(sgd.score(X_train,y_train),sgd.score(X_test,y_test))) 11 ax=plt.axes(projection='3d') 12 print(' coef:{0} intercept:{1}'.format(sgd.coef_,sgd.intercept_)) 13 # 生成3維圖 14 ax.scatter3D(X[:,0],X[:,1],y,color='red') 15 # 生成格網矩陣 16 x0, x1 = np.meshgrid(X[:,0], X[:,1]) 17 z = sgd.coef_[0] * x0 + sgd.coef_[1] * x1+sgd.intercept_ 18 # 繪制3d 19 ax.plot_surface(x0, x1, z,color='white',alpha=0.01) 20 plt.show()
運行結果


對大資料進行測驗時,相比起最小二乘法,使用梯度下降法效率會更高,下面的例子就是分別使用 LinearRegrssion 與 SGDRegressor 對 100 個特征的 100000 條資料進行測驗,結果 SGD 節省了大約 30%的時間,
1 def linear_regression_test(): 2 # 測驗資料 3 X, y = dataset.make_regression(n_samples=100000,n_features=100, random_state=2) 4 X_train, X_test, y_train, y_test = train_test_split(X, y) 5 # 線性回歸模型訓練 6 linear = LinearRegression() 7 linear.fit(X_train, y_train) 8 # 準確率 9 print('Linear:\n train data:{0}\n test data:{1}' 10 .format(linear.score(X_train,y_train),linear.score(X_test,y_test))) 11 12 def sgd_regressor_test(): 13 # 測驗資料 14 X,y=dataset.make_regression(n_samples=100000,n_features=100,random_state=2) 15 X_train, X_test, y_train, y_test = train_test_split(X, y) 16 # SGD 模型 17 sgd = SGDRegressor(learning_rate='constant',eta0=0.01) 18 sgd.fit(X_train,y_train) 19 # 準確率 20 print('SGD:\n train data:{0}\n test data:{1}' 21 .format(sgd.score(X_train,y_train),sgd.score(X_test,y_test))) 22 23 print(' Utime:{0}'.format(timeit.timeit(stmt=linear_regression_test, number=1))) 24 print(' Utime:{0}'.format(timeit.timeit(stmt=sgd_regressor_test, number=1)))
運行結果

4.6 LogisticRegression 模型
上面幾個例子,都是講述線性回歸,下面將開始介紹線性分類的模型,LogisticRegression 模型雖然名稱里包含了 Regression ,但其實它是一個線性分類模型,
LogisticRegression 的建構式
1 class LogisticRegression(LinearClassifierMixin, 2 SparseCoefMixin, 3 BaseEstimator): 4 @_deprecate_positional_args 5 def __init__(self, penalty='l2', *, dual=False, tol=1e-4, C=1.0, 6 fit_intercept=True, intercept_scaling=1, class_weight=None, 7 random_state=None, solver='lbfgs', max_iter=100, 8 multi_class='auto', verbose=0, warm_start=False, n_jobs=None, 9 l1_ratio=None): 10 ......
引數說明
- penalty:默認為 L2,用于指定懲罰項中使用的規范,可選引數為 L1 和 L2,
- dual:默認為False,對偶或原始方法,對偶方法只用在求解線性多核(liblinear)的L2懲罰項上,當樣本數量>樣本特征的時候,dual通常設定為False,
- tol:默認為小數點后 4 位,代表求解方法精度,
- C:正則化系數 λ 的倒數,float型別,默認為1.0,越小的數值表示越強的正則化,
- fit_intercept:bool型別,默認為True,表示是否計算截距 ( 即 y=wx+k 中的 k ),
- intercept_scaling:float型別,默認為1,僅在 solver 為 ”liblinear”,且 fit_intercept設定為True時有用 ,
- class_weight:用于標示分類模型中各種型別的權重,默認值為None,即不考慮權重,也可選擇balanced 讓類別庫自己計算型別權重,此時類別庫會根據訓練樣本量來計算權重,某種型別樣本量越多,則權重越低,樣本量越少,則權重越高,或者輸入型別的權重比,例如 class_weight={0:0.9, 1:0.1},此時型別0的權重為90%,而型別1的權重為10%,
- random_state:亂數種子,推薦設定一個任意整數,同一個隨機值,模型可以復現,
- solver:求解優化演算法,默認值 lbfgs,可以根據資料型別選擇最合適的演算法,可選的演算法有:
1)liblinear:使用了開源的liblinear庫實作,內部使用了坐標軸下降法來迭代優化損失函式,
2)lbfgs:利用損失函式二階導數矩陣即海森矩陣來迭代優化損失函式,
3)newton-cg:利用損失函式二階導數矩陣即海森矩陣來迭代優化損失函式,
4)sag:即隨機平均梯度下降,是梯度下降法的變種,每次迭代僅僅用一部分的樣本來計算梯度,適合資料量較大時使用,
5)saga:線性收斂的隨機優化演算法的的變重,
- max_iter:默認值為 100,部分求解器需要通過迭代實作,這個引數指定了模型優化的最大迭代次數,
- multi_class:分類方式選擇引數,str型別,可選引數為ovr和multinomial,默認為ovr,如果是二元邏輯回歸,ovr和multinomial并沒有任何區別,區別主要在多元邏輯回歸上,
- verbose:日志冗長度,int型別,默認為0,就是不輸出訓練程序,1的時候偶爾輸出結果,大于1,對于每個子模型都輸出,
- warm_start:默認值為 False,當設定為True時,重用之前呼叫的解決方案作為初始化,否則,只需要洗掉前面的解決方案,
- n_jobs:CPU 并行數,默認為None,代表1,若設定為 -1 的時候,則用所有 CPU 的內核運行程式,
- l1_ratios : 默認為None,表示彈性網混合引數串列,
LogisticRegression 包含兩個最常用的引數 penalty 正則化規則、C 正則化程度,由建構式可以看到,一般情況下 LogisticRegression 使用的是 L2 正則規范,此時決定正則化規范強度的引數是由 C 值決定,C 越大,對應的正則化越弱,資料集的擬合度會更高,C 越小,則模型的權重系統 w 更趨向于 0,
最典型的例子就是使用 make_forge 庫里的例子進行測驗
1 def logistic(): 2 #生成資料集 3 X,y=datasets.make_forge() 4 X_train,X_test,y_train,y_test=train_test_split(X,y) 5 #對Logistic模型進行訓練 6 logistic=LogisticRegression(C=1.0,random_state=1) 7 logistic.fit(X_train,y_train) 8 #輸入正確率 9 print('logistic\n train data:{0}'.format(logistic.score(X_train,y_train))) 10 print(' test data:{0}'.format(logistic.score(X_test,y_test))) 11 #輸出模型點 12 plt.scatter(X[:,0], X[:,1],c=y,s=100) 13 plt.legend(['model','data']) 14 #輸出模型決策邊界 15 line = np.linspace(7, 13, 100) 16 y=(-logistic.coef_[0][0]*line-logistic.intercept_)/logistic.coef_[0][1] 17 plt.plot(line,y,'-') 18 plt.show()
運行結果
![]()
此時,雖然測驗資料集比較簡單,但仍可見型別為0和1的資料仍有產生一定的錯位,此時,嘗試將引數 C 調整為 100,得出的結果如下圖,
可見,當C值越大時,正則化規則越弱,資料集的擬合度會更高
![]()

相反,若將 C 調整為 0.01 時,權重系數 w 會越趨向于0,它會讓公式適用于更多的資料點
![]()

LogisticRegression 除了可以通過 C 控制其正則化強度外,還可以通過 penalty 引數選擇正則化規則,penalty 默認值為 L2,但是當資料集的特征較多時,可以嘗試通過設定 penalty 讓其使用 L1 正則化,但需要注意的是,當使用 L1 正則化時 solver 必須使用 liblinear 演算法,否則系統會報錯,
1 def logistic(penalty,c): 2 #生成資料集 3 X,y=dataset.make_classification(n_samples=1000,n_features=100,random_state=1) 4 X_train,X_test,y_train,y_test=train_test_split(X,y) 5 #對Logistic模型進行訓練 6 if(penalty=='l1'): 7 solver='liblinear' 8 else: 9 solver='lbfgs' 10 logistic=LogisticRegression(penalty=penalty,C=c,solver=solver,random_state=1) 11 logistic.fit(X_train,y_train) 12 #輸入正確率 13 print('{0}\n train data:{1}'.format(penalty,logistic.score(X_train,y_train))) 14 print(' test data:{0}'.format(logistic.score(X_test,y_test))) 15 16 #輸出模型決策邊界 17 print(' total features:{0}'.format(logistic.n_features_in_)) 18 print(' used features:{0}'.format(sum(logistic.coef_[0]!=0))) 19 plt.plot(logistic.coef_[0],'^') 20 logistic('l1',0.2) 21 logistic('l2',0.2) 22 plt.show()
運行結果


可以看到當使用 L1 規則時,系統只使用了 4 個特征,此方法更適用于特征數量比較多的資料集,
使用 L2 時 C 值為 0.2,系統使用了100個特征,由特征圖形可見,其權重系數 w 都接近于 0
此時可嘗試調節一下引數,對比一下運行 L2 ,輸入不同 C 值時的變化,
1 logistic("l2", 0.01) 2 logistic('l2',100) 3 plt.legend(['c=0.01','c=100']) 4 plt.show()
運行結果

可見 C 值越小,w 就會越趨向于0,C 值越大,w 就會越分散且受正則化的約束越小
4.7 LinearSVC 線性支持向量機
上面的例子所看到的線性分類模型,大部分的例子都只二分類的,下面介紹一下可用于多分類的線性模型 LinearSVC
LinearSVC 的建構式
1 class LinearSVC(LinearClassifierMixin, 2 SparseCoefMixin, 3 BaseEstimator): 4 @_deprecate_positional_args 5 def __init__(self, penalty='l2', loss='squared_hinge', *, dual=True, 6 tol=1e-4, C=1.0, multi_class='ovr', fit_intercept=True, 7 intercept_scaling=1, class_weight=None, verbose=0, 8 random_state=None, max_iter=1000): 9 ......
引數說明
- penalty:默認為 L2,用于指定懲罰項中使用的規范,可選引數為 L1 和 L2,當使用 L1 準則時,引數 loss 必須為 squared_hinge , dual 必須為 False,
- loss : 指定損失函式,默認值為 squared_hinge,可選擇 ‘hinge’ 或 ‘squared_hinge’ ,
- dual:默認為False,對偶或原始方法,對偶方法只用在求解線性多核(liblinear)的L2懲罰項上,當樣本數量>樣本特征的時候,dual通常設定為False,
- tol:默認為小數點后 4 位,代表求解方法精度,
- C:正則化系數 λ 的倒數,float型別,默認為1.0,越小的數值表示越強的正則化,
- multi_class : 默認值為 ovr,可選擇 ‘ovr’ 或 ‘crammer_singer’ ,用于確定多類策略, “ovr” 訓練n_classes one-vs-rest 分類器,而 “crammer_singer” 優化所有類的聯合目標, 如果選擇“crammer_singer”,則將忽略選項 loss,penalty 和 dual 引數,
- fit_intercept:bool型別,默認為True,表示是否計算截距 ( 即 y=wx+k 中的 k ),
- intercept_scaling:float型別,默認為1,僅在 fit_intercept設定為True時有用 ,
- class_weight:用于標示分類模型中各種型別的權重,默認值為None,即不考慮權重,也可選擇balanced 讓類別庫自己計算型別權重,此時類別庫會根據訓練樣本量來計算權重,某種型別樣本量越多,則權重越低,樣本量越少,則權重越高,或者輸入型別的權重比,例如 class_weight={0:0.9, 1:0.1},此時型別0的權重為90%,而型別1的權重為10%,
- verbose:日志冗長度,int型別,默認為0,就是不輸出訓練程序,1的時候偶爾輸出結果,大于1,對于每個子模型都輸出
- random_state:亂數種子,推薦設定一個任意整數,同一個隨機值,模型可以復現,
- max_iter:默認值為 10000,部分求解器需要通過迭代實作,這個引數指定了模型優化的最大迭代次數,
與LogisticRegression相似,LinearSVC 默認也是使用 L2 準則,同樣可以通過 C 控制其正則化強度,C 越大,對應的正則化越弱,資料集的擬合度會更高,C 越小,則模型的權重系統 w 更趨向于 0,
還可以通過 penalty 引數選擇正則化規則,當使用 L1 準則時,引數 loss 必須為 squared_hinge , dual 必須為 False,
當用于多個類別時,LinearSVC 會使用一對其余的方式,每次學習都會使用一個二分類模型,然后把余下的資料集再次進行二分類,不斷回圈最后得出預測結果,
1 def linearSVC(): 2 #生成資料集 3 X,y=mglearn.datasets.make_blobs(n_samples=200,random_state=23,centers=3) 4 X_train,X_test,y_train,y_test=train_test_split(X,y) 5 #使用 LinearSVC 模型,使用 L1 準則 6 linearSVC=LinearSVC(penalty='l1',loss='squared_hinge', dual=False) 7 linearSVC.fit(X_train,y_train) 8 #輸出準確率 9 print('LinearSVC\n train data:{0}'.format(linearSVC.score(X_train,y_train))) 10 print(' test data:{0}'.format(linearSVC.score(X_test,y_test))) 11 #劃出圖形分隔線 12 plt.scatter(X[:,0], X[:,1],c=y,s=100,cmap='autumn',marker='*') 13 n=np.linspace(-8,8,100) 14 value0=(-n*linearSVC.coef_[0][0] - linearSVC.intercept_[0]) / linearSVC.coef_[0][1] 15 value1=(-n*linearSVC.coef_[1][0] - linearSVC.intercept_[1]) / linearSVC.coef_[1][1] 16 value2=(-n*linearSVC.coef_[2][0] - linearSVC.intercept_[2]) / linearSVC.coef_[2][1] 17 plt.plot(n.reshape(-1, 1), value0,'-') 18 plt.plot(n.reshape(-1, 1), value1,'--') 19 plt.plot(n.reshape(-1, 1), value2,'+') 20 plt.legend(['class0','class1','class2']) 21 plt.show()
運行結果

4.8 SGDClassifier 分類模型
SGDClassifier 與 SGDRegressor 類似,都是使用梯度下降法進行分類計算,由于SGD是以一次一個的方式獨立處理訓練實體,所以它能夠有效處理大型的資料集,SDGClassifier 默認也是使用 L2 準則,注意與SGDRegressor不同的是它的學習率 learning_rate 默認使用 optimal,此時 eta0 無效,若要使用 eta 0 需要提前修改 learning_rate 引數,
建構式
1 class SGDClassifier(BaseSGDClassifier): 2 @_deprecate_positional_args 3 def __init__(self, loss="hinge", *, penalty='l2', alpha=0.0001, 4 l1_ratio=0.15, 5 fit_intercept=True, max_iter=1000, tol=1e-3, shuffle=True, 6 verbose=0, epsilon=DEFAULT_EPSILON, n_jobs=None, 7 random_state=None, learning_rate="optimal", eta0=0.0, 8 power_t=0.5, early_stopping=False, validation_fraction=0.1, 9 n_iter_no_change=5, class_weight=None, warm_start=False, 10 average=False): 11 ......
- loss:默認為“hinge”, 選擇要使用的損失函式,可用的損失函式有:'hinge', 'log', 'modified_huber','squared_hinge', 'perceptron',log 損失使邏輯回歸成為概率分類器, 'modified_huber'是另一個平滑的損失,它使例外值和概率估計具有一定的容忍度,“ squared_hinge”與hinge類似,但會受到二次懲罰,“perceptron”是感知器演算法使用的線性損失,
- penalty:默認為 L2,用于指定懲罰項中使用的規范,可選引數為 L1 、 L2、elasticnet
- alpha:默認值為 0.0001 乘以正則項的常數,值越大,正則化越強,當學習率設為“optimal”時,用于計算學習率,
- l1_ratio:默認值為 0.15 彈性凈混合引數,0 <= l1_ratio <= 1. l1_ratio=0對應于L2懲罰,l1_ratio=1到 l1,僅當 penalty 為 elasticnet 時使用,
- fit_intercept:bool型別,默認為True,表示是否計算截距 ( 即 y=wx+k 中的 k ),
- max_iter:默認值為 1000,部分求解器需要通過迭代實作,這個引數指定了模型優化的最大迭代次數,
- tol:默認值為1e-3,默認為小數點后 3 位,代表求解方法精度
- shuffle:默認值為 True ,是否在每個epoch之后對訓練資料進行洗牌,
- verbose:默認值為 0 詳細程度,
- epsilon:默認值為0.1 loss 選擇 “huber” 時,它決定了一個閾值,在這個閾值下,預測值將被忽略,若選擇 “epsilon-insensitive” 表示若當前預測和正確標簽之間的差異小于此閾值,將被忽略,
- n_job:CPU 并行數,默認為None,代表1,若設定為 -1 的時候,則用所有 CPU 的內核運行程式,
- random_state:默認值為None 亂數種子,推薦設定一個任意整數,同一個隨機值,模型可以復現,
- learning_rate:學習率,默認值為 ’optimal’ ,可選 constant、optimal、invscaling、adaptive
1)‘constant’: eta = eta0;
2)‘optimal: eta = 1.0 / (alpha * (t + t0)) ;
3)‘invscaling’: eta = eta0 / pow(t, power_t);
4)‘adaptive’: eta = eta0
- eta0:默認值為0.0,初始學習速率,當 learning_rate 為 optimal 時,此值無效,
- power_t:默認值為0.5 反向縮放學習速率的指數
- early_stopping:默認值為 False 驗證分數沒有提高時,是否使用提前停止終止培訓,如果設定為True,它將自動將訓練資料的分層部分作為驗證,并且當分數方法回傳的驗證分數對 n_iter_no_change 連續時間段沒有至少提高tol時終止訓練,
- validation_fraction:默認值為 0.1 作為早期停機驗證設定的培訓資料的比例,必須介于0和1之間,僅在“早停”為真時使用,
- n_iter_no_change:默認值為 5 在提前停止之前沒有改進的迭代次數,
- class_weight: 類別關聯的權重,使用字典格式,默認值 {class_label: None} 也可選擇balanced 讓類別庫自己計算型別權重,此時類別庫會根據訓練樣本量來計算權重,某種型別樣本量越多,則權重越低,樣本量越少,則權重越高,或者輸入型別的權重比,例如 class_weight={0:0.9, 1:0.1},此時型別0的權重為90%,而型別1的權重為10%,
- warm_start:bool, 默認值為 False 當設定為True時,將上一個呼叫的解決方案重用為fit作為初始化,否則,只需洗掉以前的解決方案,
- average:默認值為 False 當設定為True時,計算所有更新的 averaged SGD權重,并將結果存盤在coef_ 屬性中,如果設定為大于1的整數,則當看到的樣本總數達到平均值時,將開始平均,所以average=10將在看到10個樣本后開始平均,
下面嘗試用 SDGClassifier 區分MNIST的數字圖片,由于圖片有70000張,運行可能較慢,嘗試使用多核運算,把 n_job 設定為 -1,把學習率設定為固定值 0.01,可以看到準確率可以達到將近 90%
1 def sgd_classifier_test(): 2 # 輸入入資料 3 (X_train, y_train), (X_test, y_test)=keras.datasets.mnist.load_data() 4 # 把28*28影像資料進行轉換 5 X_train=X_train.reshape(-1,784) 6 X_test=X_test.reshape(-1,784) 7 #使用SGDClassfier模式,使用多核計算,學習率為0.01 8 sgd_classifier=SGDClassifier(learning_rate='constant',eta0=0.01,n_jobs=-1) 9 sgd_classifier.fit(X_train,y_train) 10 #查看準確率 11 print('SGDClassfier\n train data:{0}\n test data:{1}'.format( 12 sgd_classifier.score(X_train,y_train) 13 ,sgd_classifier.score(X_test,y_test))) 14 #查看測驗數量第256幅圖 15 data=https://www.cnblogs.com/leslies2/p/X_test[256].reshape(28,28) 16 plt.imshow(data,cmap='binary') 17 plt.show() 18 print(' test number is:{0}'.format(y_test[256]))
運行結果


4.9 多項式轉換器與管道
4.9.1 PolynomialFeatures 轉換器
到此以上所有的例子用的都是純線性的實體,然而現實場景中并非如此,比如說一個簡單的二元一次的方程 y = a*x*x+b*x+c 所構成的資料(如下圖),就不可能通過直線進行連接,

為此,sklearn 準備了多項式轉換器 PolynomialFeatures 來解決此問題,前面提到普通的線性模型每個特征都是符合單次方規則:y = w[0] * x[0] + w[1] * x[1] + w[2] * x[2] + w[3] * x[3] + ...... + w[n] * x[n] + k,每個模型會有 n+1 個特征,而 PolynomialFeatures 轉換器可以把單個特征轉換成多次方關系: 當 degree=n 時,每個特征都會符合關系式 y = w[0]+w[1]*x+w[2]*x2+w[3]*x3+....+w[n]*xn,如此類推如果有 m 個特征且degree = m 時,則![]()
建構式
1 class PolynomialFeatures(): 2 @_deprecate_positional_args 3 def __init__(self, degree=2, *, interaction_only=False, include_bias=True, 4 order='C'): 5 self.degree = degree 6 self.interaction_only = interaction_only 7 self.include_bias = include_bias 8 self.order = order 9 ......
- degree:默認值為2,控制多項式的次數;
- interaction_only:默認為 False,如果指定為 True,那么就不會有特征自己和自己結合的項,組合的特征中沒有 X12或 X1 * X23
- include_bias:默認為 True ,如果為 True 的話,那么結果中就會有 0 次冪項,即全為 1 這一列,
- order: 默認為"C" ,可選擇 "F" ,“C” 表示是在密集情況(dense case)下的輸出array的順序,“F” 可以加快操作但可能使得subsequent estimators變慢,
用一個二元一次的方程 y = a*x*x+b*x+c 作為例子,首先生成100 個點的測驗資料畫在圖上,然后使用多項式轉換器 PolynomialFeatures 把 degree 設定為默認值 2,相當于使用 x 的最高次為 2 的多次項作為特征,最后使用 LinearRegression 模型根據斜率和變數畫出曲線,
1 # 測驗資料,根據 y=3*x*x+2*x+1 生成 2 def getData(): 3 x=np.linspace(-3.5,3,100) 4 y=3*x*x+2*x+1 5 d=np.random.random(100)*2 6 y=y-d 7 plt.plot(x,y,'.') 8 return [x,y] 9 10 def polynomial_test(): 11 # 獲取測驗資料 12 data=https://www.cnblogs.com/leslies2/p/getData() 13 X=data[0].reshape(-1,1) 14 y=data[1] 15 # 生成多項式回歸模型 16 polynomial=PolynomialFeatures(degree=2) 17 X_poly=polynomial.fit_transform(X) 18 # 把運算過的資料放到 LinearRegression 進行運算 19 linearRegression=LinearRegression() 20 21 linearRegression.fit(X_poly,y) 22 # 列印資料 23 print('PolynomialFeature:\n coef:{0}\n intercept:{1}\n score:{2}' 24 .format(linearRegression.coef_,linearRegression.intercept_, 25 linearRegression.score(X_poly,y))) 26 # 根據斜率和截距畫出圖 27 x=np.linspace(-3.5,3,100) 28 y=linearRegression.coef_[2]*x*x+linearRegression.coef_[1]*x+linearRegression.intercept_ 29 plt.plot(x,y) 30 plt.legend(['data','model']) 31 plt.show()
運行結果


可見使用 PolynomialFeatures 多項式轉換器可以向資料中加入非線性特征,讓線性模型變得更加強大,
4.9.2 Pipeline 管道
正如上一章節的例子,如果繁雜的模型每次都需要經過多個步驟運算,那將是一個耗時費力的操作,有見及此,sklearn 中有一個 Pipeline 類可以按作業流程分步驟執行模型訓練,在上一章節資料先經過 PolynomialFeatures 模型訓練再進行 LinearRegression 訓練可寫為 pipe=Pipeline([('polynomial',PolynomialFeatures()),('linearRegression',LinearRegression())]) ,在 Pipeline 引數是以字典的形式輸入,先輸入名稱,再輸入型別,如果覺得每次都要為模型物件定義引數名稱比較麻煩,sklearn 還有一個更簡單的方法 make_pipeline ,使用此方法只需要直接把模型的類按順序輸入即可 pipe=make_pipeline(PolynomialFeatures(),LinearRegression()) ,事實上這種寫法也是管道最常用的方法,
使用 Pipeline 管道,可以把上一節的例子簡化成下面的代碼,輸出完全一樣的結果,
1 # 測驗資料根據 y=3*x*x+2*x+1 生成 2 def getData(): 3 x=np.linspace(-3.5,3,100) 4 y=3*x*x+2*x+1 5 d=np.random.random(100)*2 6 y=y-d 7 plt.plot(x,y,'.') 8 return [x,y] 9 10 def polynomial_test(): 11 # 獲取測驗資料 12 data=https://www.cnblogs.com/leslies2/p/getData() 13 X=data[0].reshape(-1,1) 14 y=data[1] 15 # 生成管道先執行 PolynomialFeatures 再執行 LinearRegression 16 pipe=make_pipeline(PolynomialFeatures(degree=2),LinearRegression()) 17 # 訓練資料 18 pipe.fit(X,y) 19 # 獲取執行物件 20 linearRegression=pipe.steps[1][1] 21 # 列印資料 22 print('PolynomialFeature:\n coef:{0}\n intercept:{1}\n score:{2}' 23 .format(linearRegression.coef_,linearRegression.intercept_, 24 pipe.score(X,y))) 25 # 根據斜率和截距畫出圖 26 x=np.linspace(-3.5,3,100) 27 y=linearRegression.coef_[2]*x*x+linearRegression.coef_[1]*x+linearRegression.intercept_ 28 plt.plot(x,y) 29 plt.legend(['data','model']) 30 plt.show()
由于篇幅關系,線性模型的使用就先介紹到這里,關于 支持向量機、k近鄰、樸素貝葉斯分類 、決策樹 等章節將在 《 Python 機器學習實戰 —— 監督學習(下)》中詳細講述,敬請留意,
回到目錄
本篇總結
本文主要講述了機械學習的相關概念與基礎知識,監督學習的主要流程,對損失函式進行了基礎的介紹,并對常用的均方誤差與遞度下降法的計算程序進行演示,希望能幫助大家更好地理解,
在線性模型方法,對常用的 LogisticRegression , LinearSVC、SGDClassifier、 LinearRegression、Ridge、Lasso 、SGDRegressor 等線性模型進行了介紹,最后對非線性的 PolynomialFeatures 多項式轉換器進行介紹,講解管道 Pipe 的基本用法,
希望本篇文章對相關的開發人員有所幫助,由于時間倉促,錯漏之處敬請點評,
對 .Python 開發有興趣的朋友歡迎加入QQ群:790518786 共同探討 !
對 JAVA 開發有興趣的朋友歡迎加入QQ群:174850571 共同探討!
對 .NET 開發有興趣的朋友歡迎加入QQ群:162338858 共同探討 !
作者:風塵浪子
https://www.cnblogs.com/leslies2/p/14832685.html
原創作品,轉載時請注明作者及出處
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/288039.html
標籤:Python
