當前位置:
首頁 > 知識 > 機器學習演算法實踐:Logistic 回歸與梯度上升演算法

機器學習演算法實踐:Logistic 回歸與梯度上升演算法

(點擊

上方藍字

,快速關注我們)




來源:伯樂在線專欄作者 - iPytLab


如有好文章投稿,請點擊 → 這裡了解詳情




前言





關於Logistic回歸分類器主要分兩部分進行總結,第一部分主要介紹Logistic回歸的理論相關的部分,涉及到通過似然函數建立Logistic回歸模型以及使用梯度上升演算法優化參數。第二部分主要使用Python一步步實現一個Logistic回歸分類器,並分別使用梯度上升和隨機梯度上升演算法實現,對二維數據點分類進行可視化,最後使用之前使用過的SMS垃圾簡訊語料庫中的簡訊數據進行模型訓練並對簡訊數據進行分類。



Logistic回歸




Logistic回歸為概率型非線性回歸模型, 是研究二值型輸出分類的一種多變數分析方法。通過logistic回歸我們可以將二分類的觀察結果y與一些影響因素[x1,x2,x3,…]建立起關係從而對某些因素條件下某個結果發生的概率進行估計並分類。




Sigmoid函數




對於二分類問題,我們想要一個函數能夠接受所有輸入然後預測出兩種類別,可以通過輸出0或者1。這個函數就是sigmoid函數,它是一種階躍函數具體的計算公式如下:




Sigmoid函數的性質: 當x為0時,Sigmoid函數值為0.5,隨著x的增大對應的Sigmoid值將逼近於1; 而隨著x的減小, Sigmoid函數會趨近於0。




Logistic回歸分類器(Logistic Regression Classifier)




Logistic回歸分類器是這樣一種分類器:




在分類情形下,經過學習後的LR分類器是一組權值




樣本也可以用一組向量 x 表示:





其中x0=1




將 x 根據 w 線性疊加帶入到Sigmoid函數中便可以得到範圍在 (0,1), 之間的數值,大於0.5被分入1類,小於0.5的被歸入0類:






其中p(y=1|x)就是指在特徵為x屬於類1的條件概率, 當然也可以容易得到屬於類0的概率為:





所以Logistic回歸最關鍵的問題就是研究如何求得 ω 。這個問題就需要用

似然函數

進行

極大似然估計

來處理了。




似然函數(Likelihood function)




In statistics, a likelihood function (often simply the likelihood) is a function of the parameters of a statistical model given data.




從似然函數的英文定義中可以看到,似然函數是與

統計模型中的參數

的函數。雖然似然性和概率的意思差不多,但是在統計學中卻有著明確的區分:




概率(Probability)使我們平時用的最多的,用於在一直某些參數的值的情況下預測某個事件被觀測到的可能性。





  1. 似然性(Likelihood)則是在一直觀測到的結果時,對有關參數進行估計。



  2. 可見這兩個是個概念是個可逆的過程,即似然函數是條件概率的逆反.




對於某個已發生的事件 x, 某個參數或者某個參數向量 θ 的似然函數的值與已知參數 θ 前提下相同事件 x 放生的條件概率(概率密度)的值相等, 即:







似然函數對於離散和連續隨機分布的表示形式是不同的:




離散型




對於具有與參數 θ 相關離散概率分布 p 的變數 X, 對於某個變數 X=x θ 的似然函數表示成:







連續型




連續性的分布我們則用概率密度 f 來表示:


注意似然函數並不是一個條件概率,雖然表達式與條件概率的形式相同。因為 θ 並不是一個隨機變數而是一個參數。




關於對似然性的理解,個人認為,似然性並不是一個概率,而是表示在一些列事件發生時,關於事件相關的參數的可能性信息,一個參數就對應一個似然函數的值,當參數發生變化的時候,似然函數也會隨之變化,似然函數的重要性並不在於他的具體值是多少,而在於他隨參數變化的變化趨勢,是變大還是變小。當我們在取得某個參數的時候,似然函數的值到達了極大值,則說明這個參數具有**最合理性**。




極大似然估計




極大似然估計是似然函數最初也是最然的應用,我們優化Logistic模型就行極大似然估計的過程(求似然函數的極大值),通過極大似然估計,我們可以得到最合理的參數。




Logistic回歸中的極大似然估計




上一部分總結了什麼似然函數和極大似然估計,這裡就總結下Logistic模型的極大似然估計。




在LR分類器部分我們推導了Sigmoid函數計算兩類問題的概率表達式,由於是二分類,分類結果是0和1,我們可以將兩種類別的概率用一個式子表達, 對於一個樣本 xi 得到一個觀測值為 yi 的概率為:





若各個樣本之間是相互獨立的,則聯合概率為各個樣本概率的乘積。於是根據這系列的樣本,我們就能得到關於參數向量 ω 的似然函數 L(ω) :







我們的目的就是要對這個似然函數的極大值進行參數估計,這便是我們訓練Logistic回歸模型的過程。通過極大似然估計我們便可以通過所有樣本得到滿足訓練數據集的最合理的參數 ω




通過梯度上升演算法進行極大似然估計




有了似然函數,我們便可以通過優化演算法來進行優化了。使用梯度上升需要計算目標函數的梯度,下面我簡單對梯度的計算進行一下推導:




為了方便,我們將似然函數取自然對數先,







然後我們對去過對數的函數的梯度進行計算:





通過矩陣乘法直接表示成梯度:







設步長為α, 則迭代得到的新的權重參數為:







這樣我們通過梯度上升法做極大似然估計來做Logistic回歸的過程就很清楚了,剩下的我們就需要通過代碼來實現Logistic回歸吧.




Python實現




前面主要總結了Logistic回歸模型建立的理論基礎,主要包含模型似然函數的建立以及梯度上升演算法的優化推導。下面我們在上文的基礎上使用Python一步步實現一個Logistic回歸分類器。




載入數據




從文件中讀取特徵以及類別標籤用於優化模型參數





def

load_data

(

filename

)

:


    

dataset

,

labels

=

[],

[]


    

with

open

(

filename

,

"r"

)

as

f

:


        

for

line

in

f

:


            

splited_line

=

[

float

(

i

)

for

i

in

line

.

strip

().

split

(

" "

)]


            

data

,

label

=

[

1.0

]

+

splited_line

[

: -

1

],

splited_line

[

-

1

]


            

dataset

.

append

(

data

)


            

labels

.

append

(

label

)


    

dataset

=

np

.

array

(

dataset

)


    

labels

=

np

.

array

(

labels

)


    

return

dataset

,

labels




使用梯度上升演算法




上文對Logistic回歸模型使用梯度上升演算法優化參數進行了理論介紹,這裡就最先使用梯度上升演算法來構建一個分類器.




首先我們是Sigmoid函數:





def

sigmoid

(

x

)

:


    

""" Sigmoid 階躍函數


    """


    

return

1.0

/

(

1

+

np

.

exp

(

-

x

))




然後是梯度上升演算法的實現:





def

gradient_ascent

(

self

,

dataset

,

labels

,

max_iter

=

10000

)

:


    

""" 使用梯度上升優化Logistic回歸模型參數


    :param dataset: 數據特徵矩陣


    :type dataset: MxN numpy matrix


    :param labels: 數據集對應的類型向量


    :type labels: Nx1 numpy matrix


    """


    

dataset

=

np

.

matrix

(

dataset

)


    

vlabels

=

np

.

matrix

(

labels

).

reshape

(

-

1

,

1

)


    

m

,

n

=

dataset

.

shape


    

w

=

np

.

ones

((

n

,

1

))


    

alpha

=

0.001


    

ws

=

[]


    

for

i

in

range

(

max_iter

)

:


        

error

=

vlabels

-

self

.

sigmoid

(

dataset

*

w

)


        

w

+=

alpha

*

dataset

.

T

*

error


        

ws

.

append

(

w

.

reshape

(

1

,

-

1

).

tolist

()[

0

])


    

self

.

w

=

w


    

return

w

,

np

.

array

(

ws

)




在這裡的數據操作都轉換成Numpy矩陣的操作,主要是方便處理避免Python循環處理。同時每次梯度上升迭代過程中都把自變數,也就是Logistic模型參數進行收集,方便最後查看參數收斂情況。




關於梯度上升演算法中,我們每次沿著梯度方向移動的步長 αα 都設的固定距離為0.001,並沒有做一維搜索。




可視化決策邊界




Sigmoid函數的特點就是通過0點來進行分類,xT?ω 的值小於0為一類,大於0位另外一類,因此我們可以通過 xT?ω=0 來獲取分界線或者超平面。在二維平面里,我們可以通過求解 w0x0+w1x1+w2x2(其中 x0=1) 並繪製直線來可視化決策邊界。





def

snapshot

(

w

,

dataset

,

labels

,

pic_name

)

:


    

""" 繪製類型分割線圖


    """


    

if

not

os.path

.

exists

(

"./snapshots"

)

:


        

os

.

mkdir

(

"./snapshots"

)


    

fig

=

plt

.

figure

()


    

ax

=

fig

.

add_subplot

(

111

)


    

pts

=

{}


    

for

data

,

label

in

zip

(

dataset

.

tolist

(),

labels

.

tolist

())

:


        

pts

.

setdefault

(

label

,

[

data

]).

append

(

data

)


    

for

label

,

data

in

pts

.

items

()

:


        

data

=

np

.

array

(

data

)


        

plt

.

scatter

(

data

[

:

,

1

],

data

[

:

,

2

],

label

=

label

,

alpha

=

0.5

)


    

# 分割線繪製


    

def

get_y

(

x

,

w

)

:


        

w0

,

w1

,

w2

=

w


        

return

(

-

w0

-

w1

*

x

)

/

w2


    

x

=

[

-

4.0

,

3.0

]


    

y

=

[

get_y

(

i

,

w

)

for

i

in

x

]


    

plt

.

plot

(

x

,

y

,

linewidth

=

2

,

color

=

"#FB4A42"

)


    

pic_name

=

"./snapshots/{}"

.

format

(

pic_name

)


    

fig

.

savefig

(

pic_name

)


    

plt

.

close

(

fig

)




好了,優化演算法和可視化代碼都具備了,我們便可以擬合我們的數據了,這裡使用兩種類型的二維數據點來訓練模型, 數據見https://github.com/PytLab/MLBox/blob/master/logistic_regression/testSet.txt





if

"__main__"

==

__name__

:


    

clf

=

LogisticRegressionClassifier

()


    

dataset

,

labels

=

load_data

(

"testSet.txt"

)


    

w

,

ws

=

clf

.

gradient_ascent

(

dataset

,

labels

,

max_iter

=

50000

)


    

m

,

n

=

ws

.

shape


    

# 繪製分割線


    

for

i

in

range

(

300

)

:


        

if

i

%

(

30

)

==

0

:


            

print

(

"{}.png saved"

.

format

(

i

))


            

snapshot

(

ws

[

i

].

tolist

(),

dataset

,

labels

,

"{}.png"

.

format

(

i

))


    

fig

=

plt

.

figure

()


    

for

i

in

range

(

n

)

:


        

label

=

"w{}"

.

format

(

i

)


        

ax

=

fig

.

add_subplot

(

n

,

1

,

i

+

1

)


        

ax

.

plot

(

ws

[

:

,

i

],

label

=

label

)


        

ax

.

legend

()


    

fig

.

savefig

(

"w_traj.png"

)




通過將迭代過程中的權重參數輸出,我們可以繪製決策邊界的變化,看到參數的優化過程:



下面我們可視化一下模型參數在梯度上升過程中的收斂情況,我們總共迭代了50000步:



使用隨機隨機梯度上升演算法




從求目標函數梯度的公式





和實現代碼





error

=

vlabels

-

self

.

sigmoid

(

dataset

*

w

)


w

+=

alpha

*

dataset

.

T

*

error




中我們可以看到,在使用梯度上升演算法優化的時候每次迭代都需要使用所有的訓練數據乘上誤差向量,如果樣本只有幾百個那還好,如果有數十億樣本,那這個矩陣乘法將會非常的大,於是我們可以考慮使用隨機梯度上升來更新 ω , 所謂隨機梯度就是指更新 ω 的時候不需要用所有的數據矩陣和誤差矩陣乘積來更新,而是使用樣本中隨機選出的一個數據點來計算梯度並更新。這樣可以在新的樣本到來時對分類器進行

增量式更新

,因而隨機梯度演算法是一個在線學習演算法, 之前的梯度上升演算法是一次性處理所有樣本數據被稱作是

批處理




下面我們就重新寫一個通過隨機梯度上升演算法優化的Logistic回歸分類器





from

logreg_grad_ascent

import

LogisticRegressionClassifier

as

BaseClassifer


from

logreg_grad_ascent

import

load_data

,

snapshot


 


class

LogisticRegressionClassifier

(

BaseClassifer

)

:


 


    

def

stoch_gradient_ascent

(

self

,

dataset

,

labels

,

max_iter

=

150

)

:


        

""" 使用隨機梯度上升演算法優化Logistic回歸模型參數


        """


        

dataset

=

np

.

matrix

(

dataset

)


        

m

,

n

=

dataset

.

shape


        

w

=

np

.

matrix

(

np

.

ones

((

n

,

1

)))


        

ws

=

[]


 


        

for

i

in

range

(

max_iter

)

:


            

data_indices

=

list

(

range

(

m

))


            

random

.

shuffle

(

data_indices

)


            

for

j

,

idx

in

enumerate

(

data_indices

)

:


                

data

,

label

=

dataset

[

idx

],

labels

[

idx

]


                

error

=

label

-

self

.

sigmoid

((

data

*

w

).

tolist

()[

0

][

0

])


                

alpha

=

4

/

(

1

+

j

+

i

)

+

0.01


                

w

+=

alpha

*

data

.

T

*

error


                

ws

.

append

(

w

.

T

.

tolist

()[

0

])


 


        

self

.

w

=

w


 


        

return

w

,

np

.

array

(

ws

)




我們寫一個繼承與剛才實現的分類器的派生類,並實現隨機梯度演算法,這裡沿著梯度方向的步長隨著迭代會逐漸減小來減弱參數的博定。




我們同樣來可視化決策邊界和參數的收斂曲線來看看隨機梯度下降法對模型的優化過程:





if

"__main__"

==

__name__

:


    

clf

=

LogisticRegressionClassifier

()


    

dataset

,

labels

=

load_data

(

"testSet.txt"

)


    

w

,

ws

=

clf

.

stoch_gradient_ascent

(

dataset

,

labels

,

max_iter

=

500

)


    

m

,

n

=

ws

.

shape


 


    

# 繪製分割線


    

for

i

,

w

in

enumerate

(

ws

)

:


        

if

i

%

(

m

//

10

)

==

0

:


            

print

(

"{}.png saved"

.

format

(

i

))


            

snapshot

(

w

.

tolist

(),

dataset

,

labels

,

"{}.png"

.

format

(

i

))


 


    

fig

=

plt

.

figure

()


    

for

i

in

range

(

n

)

:


        

label

=

"w{}"

.

format

(

i

)


        

ax

=

fig

.

add_subplot

(

n

,

1

,

i

+

1

)


        

ax

.

plot

(

ws

[

:

,

i

],

label

=

label

)


        

ax

.

legend

()


 


    

fig

.

savefig

(

"stoch_grad_ascent_params.png"

)




決策線變化:



參數收斂曲線:







使用Logistic回歸分類器分類簡訊




這裡我還是使用了前兩篇決策樹和貝葉斯分類器使用的垃圾簡訊數據集來訓練Logistic回歸分類器,這時候對於Logistic分類器的參數可能會比較多,我們使用隨機梯度上升演算法來優化參數,相對於貝葉斯分類器,基於隨機梯度上升演算法的Logistic回歸分類器對於維數較高的的數據向量和數量較大的數據集的訓練速度還是有待改善的,我們同樣使用留存交叉驗證的方式來訓練和測試模型,測試了三次Logistic回歸模型對於垃圾簡訊的識別錯誤率分別為: 0.0833, 0.038, 0.038.







平均錯誤率為5.3%。可見我們的Logistic回歸分類器也能較好的對垃圾簡訊文本進行識別。




總結




本文總結了Logistic回歸和相關的優化演算法(梯度上升以及隨機梯度上升)的理論和代碼實現,並對實現的模型進行了訓練和測試。




相關閱讀






  • 機器學習演算法實踐-決策樹(Decision Tree)



  • 機器學習演算法實踐-樸素貝葉斯(Naive Bayes)




看完本文有收穫?請轉發分享給更多人


關注「大數據與機器學習文摘」,成為Top 1%


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發者 的精彩文章:

Python 開發者節省時間的 10 個方法
27 個機器學習、數學、Python 速查表
Python Yield Generator 詳解

TAG:Python開發者 |

您可能感興趣

用Python 實現的基礎機器學習演算法
最強神器!用Excel實踐機器學習演算法
用Python實現流行機器學習演算法
使用Caicloud TaaS 平台落地深度學習演算法實戰
Redis Scan演算法設計思想
WeforWe 演算法清單
YouTube視頻機器學習推薦演算法探測
Machine Learning:十大機器學習演算法
MeanShift濾波演算法與實現
Facebook開源「Detectron」,用於AR研究的計算機視覺演算法!
最小生成樹prime演算法、kruskal演算法 最短路徑演算法floyd、dijkstra
Python手寫機器學習最簡單的KNN演算法
從原理到應用:簡述Logistic回歸演算法
K-means演算法優化
以太坊core team正打算研討防asic挖礦演算法
十分鐘演算法小故事之Adaboost演算法
Redis數據淘汰演算法
ClickHouse如何結合自家的GNDT演算法庫CatBoost來做機器學習
Google正式推出「速度升級」演算法
Google Medic全面核心演算法更新