邏輯回歸(LogisticRegression)
Logistic regression(邏輯回歸)是當前業界比較常用的機器學習方法,用于估計某種事物的可能性。類似某用戶購買某商品的可能性,某病人患有某種疾病的可能性啊等等。這個世界是隨機的(當然了,人為的確定性系統除外,但也有可能有噪聲或產生錯誤的結果,只是這個錯誤發生的可能性太小了,小到千萬年不遇,小到忽略不計而已),所以萬物的發生都可以用可能性或者幾率(Odds)來表達。“幾率”指的是某事物發生的可能性與不發生的可能性的比值。
Logistic regression可以用來回歸,也可以用來分類,主要是二分類。它給我們提供的就是你的這個樣本屬于正類的可能性是多少。
假設我們的樣本是{x, y},y是0或者1,表示正類或者負類,x是我們的m維的樣本特征向量。那么這個樣本x屬于正類,也就是y=1的“概率”可以通過下面的邏輯函數來表示:
這里θ是模型參數,也就是回歸系數,σ是sigmoid函數。實際上這個函數是由下面的對數幾率(也就是x屬于正類的可能性和負類的可能性的比值的對數)變換得到的:
換句話說,y也就是我們關系的變量,我們將這些對應x1,x2,…, xm的權值叫做回歸系數,表達為θ1, θ2,…, θm。他們的加權和就是你的總得分了。
所以說上面的logistic回歸就是一個線性分類模型,它與線性回歸的不同點在于:為了將線性回歸輸出的很大范圍的數,例如從負無窮到正無窮,壓縮到0和1之間,這樣的輸出值表達為“可能性”才能說服廣大民眾。當然了,把大值壓縮到這個范圍還有個很好的好處,就是可以消除特別冒尖的變量的影響。而實現這個偉大的功能其實就只需要平凡一舉,也就是在輸出加一個logistic函數。另外,對于二分類來說,可以簡單的認為:如果樣本x屬于正類的概率大于0.5,那么就判定它是正類,否則就是負類。
所以說,LogisticRegression 就是一個被logistic方程歸一化后的線性回歸,僅此而已。
歸入到正統的機器學習框架下,模型選好了,只是模型的參數θ還是未知的,我們需要用我們收集到的數據來訓練求解得到它。那我們下一步要做的事情就是建立代價函數了。
LogisticRegression最基本的學習算法是最大似然。
假設我們有n個獨立的訓練樣本{(x1, y1) ,(x2, y2),…, (xn, yn)},y={0, 1}。那每一個觀察到的樣本(xi, yi)出現的概率是:
上面為什么是這樣呢?當y=1的時候,后面那一項是不是沒有了,那就只剩下x屬于1類的概率,當y=0的時候,第一項是不是沒有了,那就只剩下后面那個x屬于0的概率(1減去x屬于1的概率)。所以不管y是0還是1,上面得到的數,都是(x,y)出現的概率。那我們的整個樣本集,也就是n個獨立的樣本出現的似然函數為(因為每個樣本都是獨立的,所以n個樣本出現的概率就是他們各自出現的概率相乘):
那最大似然法就是求模型中使得似然函數最大的系數取值θ*。這個最大似然就是我們的代價函數(cost function)了。
OK,那代價函數有了,我們下一步要做的就是優化求解了。我們先嘗試對上面的代價函數求導,看導數為0的時候可不可以解出來,也就是有沒有解析解,有這個解的時候,就皆大歡喜了,一步到位。如果沒有就需要通過迭代了,耗時耗力。
我們先變換下L(θ):取自然對數,然后化簡(不要看到一堆公式就害怕哦,很簡單的哦,只需要耐心一點點,自己動手推推就知道了。注:有xi的時候,表示它是第i個樣本,下面沒有做區分了,相信你的眼睛是雪亮的),得到:
這時候,用L(θ)對θ求導,得到:
然后我們令該導數為0,你會很失望的發現,它無法解析求解。不信你就去嘗試一下。所以沒辦法了,只能借助高大上的迭代來搞定了。這里選用了經典的梯度下降算法。
優化求解
梯度下降(gradient descent)
Gradient descent 又叫 steepest descent,是利用一階的梯度信息找到函數局部最優解的一種方法,也是機器學習里面最簡單最常用的一種優化方法。它的思想很簡單,和我開篇說的那樣,要找最小值,我只需要每一步都往下走(也就是每一步都可以讓代價函數小一點),然后不斷的走,那肯定能走到最小值的地方,例如下圖所示:
但,我同時也需要更快的到達最小值啊,怎么辦呢?我們需要每一步都找下坡最快的地方,也就是每一步我走某個方向,都比走其他方法,要離最小值更近。而這個下坡最快的方向,就是梯度的負方向了。
對logistic Regression來說,梯度下降算法新鮮出爐,如下:
其中,參數α叫學習率,就是每一步走多遠,這個參數蠻關鍵的,該參數前的正負號分別表示梯度上升或梯度下降。
梯度上升、隨機梯度上升算法的python2實現:
from numpy import *
def loadDataSet():#打開文本文件并逐行讀取
dataMat = []; labelMat = []
fr = open('C:/Users/HZF/Desktop/machinelearninginaction/Ch05/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):#sigmoid函數
return 1.0/(1+exp(-inX))
def gradAscent(dataMatIn, classLabels):#梯度上升函數,dataMatIn 為特征列
dataMatrix = mat(dataMatIn) ? ? ? #convert to NumPy matrix,X0假定為1.0,x1,x2為testSet.txt的前兩列數據
labelMat = mat(classLabels).transpose() #convert to NumPy matrix,將類標簽的矩陣行向量轉置成列向量
m,n = shape(dataMatrix)#計算矩陣大小
alpha = 0.001
maxCycles = 500#maxCycles是迭代次數
weights = ones((n,1))
for k in range(maxCycles): ? ? ? #heavy on matrix operations
h = sigmoid(dataMatrix*weights) ? #matrix mult,計算了m*n次,因為計算量大,所以若應用在真實數據時,需要對該方法改進
error = (labelMat - h) ? ? ? #vector subtraction
weights = weights + alpha * dataMatrix.transpose()* error #matrix mult,梯度上升算法推導
return weights
def plotBestFit(weights):#畫出數據集和logistic回歸最佳擬合直線的函數,此處的weights=weights(調用函數梯度上升里的weights).getA(),
import matplotlib.pyplot as plt
dataArr = array(dataMat)
n = 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, s=30, c='red', marker='s')
ax.scatter(xcord2, ycord2, s=30, c='green')
x = arange(-3.0, 3.0, 0.1)
y = (-weights[0]-weights[1]*x)/weights[2]#最佳擬合直線,y=0,因為0是兩個分類0/1的分界處
ax.plot(x, y)
plt.xlabel('X1'); plt.ylabel('X2');
plt.show()#畫出x1與x2特征數據散點圖的分界線,x0默認是1.0
#梯度上升算法的改進算法——隨機梯度上升算法(一次僅用一個樣本點更新回歸系數,減少計算復雜度)
def stocGradAscent0(dataMatrix, classLabels):#隨機梯度上升算法,該算法是一個在線學習算法。
m,n = shape(dataMatrix)
alpha = 0.01
weights = ones(n) ?#initialize to all ones
maxCycles = 200
for k in range(maxCycles):#此行為自己實驗添加代碼,經實驗,隨機梯度上升算法迭代了200次后效果才可與梯度上升算法效果相近且都比較好
for i in range(m):
h = sigmoid(sum(dataMatrix[i]*weights))#h為具體值
error = classLabels[i] - h#error為具體值
weights = weights + alpha * error * dataMatrix[i]
return weights
def classifyVector(inX, weights):#分類函數
prob = sigmoid(sum(inX*weights))#weights取自前面幾種最優化的weights,InX為測試集上的特征向量
if prob > 0.5: return 1.0
else: return 0.0
if __name__=="__main__":
#dataMat,labelMat=loadDataSet()
#weights=gradAscent(dataMat, labelMat)
#print weights
#wei=weights.getA()
dataMat,labelMat=loadDataSet()
dataArr = array(dataMat)
#weights=gradAscent(dataMat, labelMat)#迭代500次
#plotBestFit(weights.getA())
weights=stocGradAscent0(array(dataMat), labelMat)#迭代200次,讀者可以不用迭代看看效果
plotBestFit(weights)
classifyVector(inX, weights)
上篇就寫邏輯回歸算法的基本理論、梯度上升,隨機梯度上升的代碼實現這么多!
參考文獻:
1、機器學習-邏輯回歸理論簡介 - xietingcandice的專欄 - 博客頻道 - CSDN.NET
2、《機器學習》(書)