1、基本形式
定義:最簡單的線性函數爲:
將其擴展爲矩陣形式,其中表示對於單個樣本xi,它擁有j個不同的特徵,而針對每個特徵的重要度不同,自然有權重矩陣:
因此 wx 結果爲一個數,再加上b得到f(xi)的評判結果。
2、線性迴歸
目的是通過線性模型將離散屬性通過連續化轉化爲連續值,在線性模型中確定w和b的值是關鍵,因此需要衡量f(x)的預估結果於y真實值之間的差別。均方誤差法通常被用來進行性能度量,試圖讓均方誤差最小化來求解最合適的w和b值。
實爲歐式距離計算公式或“最小二乘法”。幾何意義爲找一條直線使所有樣本到直線上的歐式距離之和最小。
令
因此,求解w和b使得E最小化的過程,稱爲線性迴歸模型的最小二乘參數估計。可以利用求導的方式解得w和b(結果見書p54頁)。
對於多元線性迴歸,將原式改爲矩陣形式可得:
但是現實任務種X^TX往往不是滿秩矩陣,此時可解出多個w,它們都能使均方誤差最小化,因此選擇哪一個解作爲輸出將由學習算法的歸納偏好決定,常見的作法是引用正則化。
2.1 正則化
向模型加入某些規則,加入先驗知識縮小解空間,減小求出錯誤解的可能性。將所用喲䣌數學知識數字化,告訴模型,對代價函數來說就是加入對模型“好壞”的評判標準。
數學解釋
1、通俗定義
就是給平面不可約代數曲線以某種形式的全純參數表示。
即
2、嚴格定義
設C是不可約平面代數曲線,S是C的奇點的集合。
使得
(S)→C\S是一對一的映射
則稱(C*,σ)爲C的正則化。不至於混淆的時候,也可以稱C*爲C的正則化。正則化的做法,實際上是在不可約平面代數曲線的奇點處,把具有不同切線的曲線分支分開,從而消除這種奇異性。
2.2 從狹義模型到廣義
線性模型雖然簡單,但是卻又很多的變換形式。常用的指數尺度變化模型爲:
也可以利用對數的形式將線性迴歸模型的預測值與真是標記聯繫起來:
爲了更一般的考慮,也可以利用單調可微函數g(·):
這個被稱爲廣義線性模型,其中g(·)被稱爲“聯繫函數”,很顯然,ln(·)是g(·)的一個特例。
3、對數機率迴歸
在處理分類任務的情況時,只需找一個單調可微函數將分類任務的真實標記y與線性迴歸模型的預測值聯繫起來。
3.1 單位階躍函數
考慮二分類任務,其輸出標記y∈{0, 1},而線性迴歸模型產生的預測值z=wx+b是實值,於時,我們需要將實值z轉換爲0/1值。最理想的方式就是單位階躍函數。
若預測值z大於0則判斷爲正例,小於零則爲反例,預測值爲臨界值則可以任意判別,這也被稱爲Heaviside函數。
但是階躍函數不連續,我們希望找到一個一定程度上近似單位階躍函數的替代函數:
並希望它可以微調,這便是更多情況下選用的對數機率函數。
3.2對數機率函數
對數機率函數就是我們常說的Sigmoid函數種的一種,它將z值轉化爲一個接近0或1的y值,並且其輸出值在z=0附近變化很顯著。
Logistic迴歸
邏輯迴歸的主要作用是分類,其思想是:根據現有數據對分類邊界線建立迴歸共識,以此進行分類。旨在找到最佳擬合參數集,所運用的方法便是常見的一些最優化算法。
邏輯迴歸的一般過程:
1、收集數據:採用任意方法收集數據;
2、準備數據:因算法涉及歐氏距離計算,故要求數據類型爲數值型(結構化數據格式最好);
3、分析數據:採用任意方法分析數據;
4、訓練數據:目的是找到最佳擬合參數集(迴歸係數);
5、測試算法:訓練完成後對算法的泛化性能做評估;
6、使用算法:對任意新的待分類數據集進行實際應用。注意對數據進行結構化處理。
此部分將對邏輯迴歸的基本原理做出解釋說明,並介紹相關的最優化算法如梯度上升和隨機梯度上升。最後將附上測試代碼。
1、基於邏輯迴歸和Sigmoid函數的分類
邏輯迴歸的特點:
【優點】:計算代價不高,容易理解;
【缺點】:容易出現欠擬合,分類精度不高;
【數據類型】:數值型和標稱型。
常用的用於進行二分類的函數有海維賽德階躍函數和單位階躍函數,都是用於將輸入結果映射爲0或1的輸出結果。而上述兩個函數有各自的缺點,例如瞬間跳躍無法處理、對整個輸入數據集的數據不具備普遍操作性等。因此,爲了數學上便與處理,Sigmoid函數的出現起到了很重要的作用。
【*注:雖然在神經網絡中Sigmoid函數存在梯度彌散的問題。但是此處不對雙曲正切Tanh和ReLU做過多說明】
Sigmoid函數的特點在於其在x取0時,S(x)=0.5。因此,以0.5作爲閾值出現了x<0 → 0而x>0 → 1的情況。
爲了實現Logistic迴歸,我們可以在每個特徵上乘上一個迴歸係數,然後把所有的結果值相加,將總和代入Sigmoid函數中,從而得到一個範圍在0~1間的數值。這也是邏輯迴歸常被視爲一種概率估計的原因。
顯然,在函數模型確定的情況下,迴歸係數是多少纔是邏輯迴歸的關鍵。
2、基於最優化方法的最佳迴歸係數確定
首先需要明白一點:邏輯迴歸的輸入並不是原始的樣例特徵值輸入。
其中,θ就是迴歸係數矩陣,而X就是原始的樣例輸入集合。爲了得到最佳擬合係數矩陣θ,我們常用梯度上升法這一最優化方法,利用python可以繪製決策邊界圖,用以直觀地秒回梯度與分類精度之間的變化關係。
2.1 梯度上升法
【概念】:要找到某函數的最大值,最好的方法就是沿着該函數的梯度方向探尋。即對函數求導。(沿不同方向便是分別對不同方向的參數求偏導)梯度算子在圖像中(梯度方向上)總是指向數值增長最快的方向,即與該點處的切線始終垂直,而每一次梯度移動的長度被稱爲步長α。
由此概念我們可以得到梯度迭代公式:
對係數函數求導*步長+原係數=新系數值。該公式將一直被迭代直到達到終止條件,例如某一個指定數值或某一個指定誤差範圍。
【*注】梯度上升的公式中是“+”,用於求函數最大值;而梯度下降公式中是“-”,用於求最小值。
2.2 實戰:使用梯度上升找到最佳參數
樣本空間爲100個樣例,有兩個特徵A1和A2。我們將對該數據集進行測試,利用梯度上升法找到最佳迴歸係數,也就是擬合出最佳的Logistic迴歸模型。
僞代碼如下:
每個迴歸係數初始化爲1
重複R次:
計算整個數據集的梯度
使用“步長*梯度”來更新迴歸係數
返回迴歸係數
程序清單
logRegres.py
import numpy as np
def loadDataSet():
dataMat = []; labelMat = []
fr = open('testSet.txt')
for line in fr.readlines():
lineArr = line.strip().split()
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
labelMat.append(int(lineArr[2]))
return dataMat, labelMat
def sigmoid(inX):
return 1.0/(1+np.exp(-inX))
def gradAscent(dataMatIn, classLabels):
dataMatrix = np.mat(dataMatIn)
labelMat = np.mat(classLabels).transpose()
m,n = np.shape(dataMatrix)
alpha = 0.001
maxCycles = 500
weights = np.ones((n,1))
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights)
error = (labelMat - h)
weights = alpha*dataMatrix.transpose() * error + weights
return weights
這裏設置了步長α=0.001,設置了迭代次數maxCycles=500。而梯度上升公式也變成了:
這裏的公式推導如下:
2.3 決策邊界的可視化
程序清單
logRegres.py
def plotBestFit(weights):
import matplotlib.pyplot as plt
dataMat, labelMat = loadDataSet()
dataArr = np.array(dataMat)
n = np.shape(dataArr)[0]
xcord1 = []; ycord1 = []
xcord2 = []; ycord2 = []
for i in range(n):
if int(labelMat[i]) == 1:
xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2])
else:
xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xcord1, ycord1, c='red', s=30, marker = 's')
ax.scatter(xcord2, ycord2, c='green', s=30)
x = np.arange(-3.0, 3.0, 0.1)
y = (-weights[0]-weights[1]*x)/weights[2]
x = x.reshape(np.shape(y))
ax.plot(x, y)
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()
這裏預設了sigmoid的閾值爲0。本代碼經過修改爲python3版本,經過可視化操作發現效果不錯,但是計算成本仍然很高。
2.4 梯度隨機上升法
在樣本空間規模不夠龐大時,使用梯度上升算法是很有效的,但是當有數十億樣本和成千上萬特徵時,該方法的計算複雜度就顯得很高。因爲梯度上升算法每一次更新迴歸係數時都需要遍歷整個數據集。
隨機梯度上升法:一次僅用一個樣本點來更新迴歸係數。同時也是一個在線學習算法(在新樣本到來時對分類器進行增量式更新),僞代碼如下:
所有迴歸係數初始化爲1
對數據集中每個樣本
計算該樣本的梯度
使用α*Δf(x)的方式來更新迴歸係數值
返回迴歸係數值
程序清單
logRegres.py
def stocGradAscent0(dataMatrix, classLabels):
m,n = np.shape(dataMatrix)
alpha = 0.01
weights = np.ones(n)
for i in range(m):
h = sigmoid(sum(dataMatrix[i]*weights))
error = classLabels[i] - h
weights = weights + alpha * error * dataMatrix[i]
return weights
同梯度上升算法相比,隨機梯度上升法的h、error都是由向量形式而非數值形式表現;且在計算權重的同時沒有對dataMatrix做轉置處理。經過可視化處理後發現,該方法在此樣本空間中的分類精度似乎不及梯度上升法。但事實上並非如此,在隨機梯度上升法中,當迭代次數增多時,迴歸係數的迭代也會趨於穩定,這時再將回歸係數帶入待定函數中進行求解效果會更好。
當然,隨機梯度下降法也可以再做一定的改進:
程序清單:
logRegres.py
def stocGradAscent1(dataMatrix, classLabels, numIter = 150):
m, n = np.shape(dataMatrix)
weights = np.ones(n)
for j in range(numIter):
dataIndex = list(range(m))
for i in range(m):
alpha = 4/(1.0 + j + i) + 0.01
randIndex = int(np.random.uniform(0, len(dataIndex)))
h = sigmoid(sum(dataMatrix[randIndex] * weights))
error = classLabels[randIndex] - h
weights = weights + alpha * error * dataMatrix[randIndex]
del(dataIndex[randIndex])
return weights
顯然,上述代碼添加了alpha = 4/(1.0 + j + i) + 0.01的部分來設置步長的自動迭代,同時也避免了步長隨着迭代次數的增多而減少到0(因爲末尾有常數項0.01)。這樣的做法是爲了避免數據陷入梯度中而無法走出的情況,即使梯度達到了最小,函數也能從中”走“出來。關於α,j是迭代次數而i是樣本下標。
同時,randIndex的設置可以避免週期性的波動(因爲一般的隨機梯度上升方法在α穩定前後會經歷或大或小的波動甚至是週期性波動詳見書82-83頁圖5-6進而5-7)。這樣做可以節省計算時間和步驟。
3、實例:疝氣疾病預測病馬死亡率
【X = 368, Attribute = 28】
回顧一下,首要任務是找到合適的迴歸係數,然後來使用算法。
3.1 處理數據中的缺省值
數據的價值往往超乎想象,有時候面對缺省的數據集,用之不妥,棄之可惜,這時候往往就需要對缺省值進行處理,而我們處理缺省值的目的就是充分利用數據,儘可能彌補數據的不足,常用的處理數據缺省的方法有:
1、用缺省值所在特徵的均值填補;
2、用特殊值填補,例如-1;
3、忽略該樣本;
4、用相似樣本的值來填補此處的值;
5、換一種機器學習算法
再用numpy處理時,必須保證填補的缺省值爲實數值,這裏的logistic迴歸公式爲
本實例中採用0來填補缺省值,這是因爲sigmoid函數本身的優點。另外,如果在數據中發現缺省的是標籤值,那麼該樣例就一定要捨棄,因爲無法對利用監督學習模型分析非監督學習的樣例。
3.2 用logistic迴歸進行分類
程序清單
logRegres.py
def classifyVector(inX, weights):
prob = sigmoid(sum(inX * weights))
if prob > 0.5:
return 1.0
else:
return 0.0
def colicTest():
frTrain = open('horseColicTraining.txt')
frTest = open('horseColicTest.txt')
trainingSet = []; trainingLabels = []
for line in frTrain.readlines():
currLine = line.strip().split('\t')
lineArr = []
for i in range(21):
lineArr.append(float(currLine[i]))
trainingSet.append(lineArr)
trainingLabels.append(float(currLine[21]))
trainWeights = stocGradAscent1(np.array(trainingSet), trainingLabels, 500)
errorCount = 0; numTestVec = 0.0
for line in frTest.readlines():
numTestVec += 1.0
currLine = line.strip().split('\t')
lineArr = []
for i in range(21):
lineArr.append(float(currLine[i]))
if int(classifyVector(np.array(lineArr), trainWeights))!=int(currLine[21]):
errorCount += 1
errorRate = (float(errorCount)/numTestVec)
print("the error rate of this test is: %f" % errorRate)
return errorRate
def multiTest():
numTests = 10; errorSum = 0.0
for k in range(numTests):
errorSum += colicTest()
print("after %d iterations the average error rate is: %f" % (numTests, errorSum/float(numTests)))
小結
Logistic迴歸的目的是尋找一個非線性函數Sigmoid的最佳擬合參數,求解過程由最優化方法(梯度)來完成。而適用於本處的梯度上升法又可以改進爲隨機梯度上升法。
而關於數據的預處理階段,此處僅涉及到了對缺省值的處理,有不同的處理方法。同樣在將來的數據預處理實驗中還會涉及到對數據的清洗,包括對噪聲數據的處理,這將在接下來的博客中有所涉及。
【*注:從概率角度解釋梯度下降】
滿足正態分佈:,其中。
約束條件:
分佈滿足:
再對w求導。
滿足拉普拉斯算子: