標籤:

機器學習演算法之手寫邏輯回歸

標題:

  1. 邏輯回歸演算法步驟簡述
  2. 選擇輸入函數:sigmoid函數
  3. 選擇優化演算法:梯度上升法
  4. 觀察數據集
  5. 批梯度上升訓練
  6. 隨機梯度上升訓練

儘管對於機器學習來說,理論是非常重要的內容,但是持續的理論學習多少會有些審美疲勞,如果讀者已經初步學習了之前介紹的關於機器學習的內容的話,那麼到這篇文章出現的時候,也至少已經了解了兩個機器學習中最簡單的模型,「線性回歸」和「邏輯回歸」。因此今天,我們就試著用代碼來簡單實現一下邏輯回歸,也方便大家更好地理解邏輯回歸的原理,以及機器學習模型在實踐中是怎麼運作的。

我們使用的語言是Python2.7,關於語言的介紹這裡就不贅述啦,如果不是很了解的話,可以先通過本公眾號推送的關於Python的一系列文章來了解這門編程語言。

首先,我們來構建一下實現邏輯回歸演算法的步驟:

| 邏輯回歸演算法步驟簡述

構建一個邏輯回歸模型有以下幾步:

  1. 收集數據:採用任意方法收集數據
  2. 準備數據:由於需要進行距離計算,因此我們要求數據類型為數值型。若是結構化數據格式更佳
  3. 分析數據:採用任意方法對數據進行分析
  4. 訓練演算法:大部分時間將用於訓練,訓練的目的是為了找到最佳的分類回歸係數
  5. 測試演算法:訓練步驟完成後將對演算法進行測試
  6. 使用演算法:首先我們需要輸入一些數據,並將其轉換成對應的結構化數值;接著,基於訓練好的回歸係數就可以對這些數值進行簡單的回歸運算,判定它們屬於哪個類別;在這之後,我們就可以在輸出的類別上做一些其它的分析工作。

並且,因為邏輯回歸適用於二元分類,因此,我們這次的這組數據的預測值僅有0和1(其它類型的數值也沒關係,但都以0,1表示會比較方便)分別代表二元分類中的negative class 和 possitive class。

| 選擇輸入函數:sigmoid函數

因為我們已經確定是邏輯回歸模型(若是未知模型的數據我們還需要從頭推導模型),所以作為分類器的輸出函數我們選擇邏輯函數,又稱sigmoid函數:

我們將sigmoid函數的輸入θTx記為z,z由下面這個公式導出:

顯然,x便是我們的輸入變數。

| 選擇優化演算法:梯度上升法

作為第一次訓練,我們選擇比較簡單的參數更新方法:梯度上升法,它細分為兩種,一種是精度比較高但消耗比較大的批梯度上升法:

另一種是精度較低但速度較快的隨機梯度上升法:

| 觀察數據集

在數據集的樣本特徵還處在比較的低維的空間(方便我們觀察的空間,像是二維(兩個特徵)或者三維(三個特徵))的時候,我們可以為數據集繪製坐標圖形從而對坐標進行簡單地觀察。儘管在現實任務中基本上不可能出現特徵空間是二維或者三維的數據集,但在用代碼實現理論模型的時候,這些簡單的數據集能夠讓我們非常直觀的看到實現的模型的效果而不需要去藉助比較複雜的模型評估方法,換句話說,它們能夠很好的輔助我們實現模型。

在現實任務中我們往往也有在處理數據前對數據集進行觀察的必要,不論是制定數據預處理的策略還是選擇模型的策略,觀察並且了解自己將要處理的數據集都是非常非常重要的一項工作。

這裡我用的數據集是來自Peter Harrington《機器學習實戰》第五章上的數據集,github鏈接(github.com/pbharrin/mac

這個數據集的樣本特徵空間只有兩維,分別為x1和x2,標籤僅有正類(1)和負類(0),我們用matplotlib模塊來繪製它在平面直角坐標繫上的數據分布圖像:

數據集導入和輸出代碼:

函數loadDataSet將數據集從testSet.txt逐行讀取並存入兩個矩陣中。testSet中每一行的數據有三個值,分別是X1,X2和數據對應的類別標籤。並且,注意到我們在dataMat中的第一個值設置為1,那其實是X0的值,這在這裡單純的數據集輸出中沒有太大的作用,但是會方便之後我們導入模型時的計算。shape函數讀取矩陣的行數m和列數n。

figure建立繪圖平面,addsubplot表示我們要在平面上建立繪製幾個圖表,111說明我們希望繪製一個占整個平面1?1大小的圖表,然後選取第一個圖表。

scatter表示散點圖,參數marker=s表示點的形狀為方形,它還可以接收一個參數s=<NUMBER>來調整點的大小。

| 批梯度上升訓練

得知數據集的輸出型狀後,我們可以著手構建模型了,這一次我們先使用梯度上升模型。

我們先來構築sigmoid函數:

表示接收一個輸入inX可以認為是我們在sigmoid函數中所說的』z』,用sigmoid函數輸出。

然後構建梯度上升函數:

第一個參數是全部輸入樣本組成的二維數組,每一個樣本包含3個特徵分別是X0,X1,X2,因此,用mat函數轉換後的dataMat是一個3 x 100的輸入矩陣。第二個參數是每一個樣本的標籤組成的矩陣,為了方便計算,我們在轉化它為1 x 100的矩陣後還要用transpose()將其轉置。

變數alpha表示梯度上升中的步長,maxCycles表示我們將要進行的步數,一般來說我們也可以通過設定條件讓程序判斷收斂情況來自行決定合適的步長,但這一步我們暫且先簡化為這樣。weights便是我們希望求得的參數,可以看作是上面給出的梯度上升的數學模型中的θ,這裡我們先將其初始化為一個 3 x 1的矩陣分別對應數據的3個特徵。

然後我們循環更新weights值,以找到最合適的weights。更新方式便是梯度上升更新:

我們可以把它與上面的梯度更新公式對比一下。然後通過500步更新得到目標參數weights

我們可以用print輸出看看我們得到的參數:

然後將其繪製到我們一開始做的數據集表示圖上。

發現效果不錯。其中,函數getA()表示將矩陣weights轉換成數組,如果我們不這麼做的話,輸出一下x和y就會發現這一步的必要性:

我們會發現y的值是被包裹在兩個[]裡面的,實際上可以認為y是一個嵌套了兩層的一維矩陣,這也是為什麼,我們要用getA來將weights從矩陣轉換回數組。

y的計算方式或許也會給人帶來疑問:實際上,我們知道,我們希望得到的是一條將兩個數據集分開的直線。因此,我們在給出一串連續的橫坐標(代碼中就是從-3到3每隔0.1取一個橫坐標)組成的向量後,就可以根據直線的方程y=kx+b(轉換成?w2X2=w1X1+w0)

計算這一連串橫坐標對應的y軸坐標,然後將其繪製到散點圖上。

| 隨機梯度上升訓練

因為我們這裡的樣本比較小,所以我們的批梯度上升可以很快的就得到我們想要的結果,但實際上,很多數據集包含的內容都非常巨大,因此,為了能夠快速執行分類任務,我們有時候會犧牲一些精度來換取運算的速度。這便是我們的隨機梯度上升法,它的原理我在機器學習理論中的筆記有講,這裡就不再贅述:

可以得到我們隨機梯度下降的結果:

發現結果沒有之前的準確,這是當然的,因為我們犧牲了精度,隨機梯度上升對參數weights的每一次更新都只用了一個樣本,因此速度上相較批梯度上升會大幅提升。

下面這張圖表示每一次更新回歸參數X0,X1,X2的值,根據樣本次數,我們總共更新了100次:

繪圖代碼:

可以看出來,回歸參數的上下波動非常巨大,並且時常會往與梯度不同的方向更新,X2在開始的幾次更新之後很快達到了穩定,但是X0和X1則沒有。

並且,我們可以發現,參數在趨於穩定之後依然會有局部波動,這是因為數據集中並非所有的數據都可以確保正確分類(因為數據集並非線性可分)。

本文首發於公眾號「AI遇見機器學習」,更多乾貨可搜索[mltoai]或直接公眾號名,歡迎關注!

推薦閱讀:

《to buy or not to buy》筆記
極簡機器學習入門
局部敏感判別分析
淺識 Batch Normalization
機器學習的平台化發展趨勢

TAG:机器学习 |