機器學習 (五) Logistic Regression 分類器
前言
上一篇文章我們談了談基於概率論的分類,這篇我們繼續談論分類問題,這篇講述的是一種最優化問題,即通過簡單計算並不能得出來最終結果,需要一步步來優化求最優值,這種分類方法應用廣泛,也是我們必須要熟練掌握的分類演算法,它的地位屬於十大機器學習演算法其中之一,可以說是裡面的老大哥人物,不廢話進入正題。
原理
邏輯迴歸的目的是根據已有的資料訓練出來一個模型,用於對未知資料分類,實現思路是給每一個樣本的每一個特徵賦予一個權重因子,預測結果等於樣本的特徵值乘以每個特徵的影響因子,這個影響因子也就是我們通常方程的係數,因此該問題即轉為了求解最優化係數的問題了,順著這個思路,求解方程最優化問題,一般我們常用的方法有求導讓導數等於0, 可以得到極值點 ,還有一種是逐漸向目標值靠近一點一點迭代出來結果 稱為梯度上升或下降方法,我覺得用這兩種方法是都可以的,根據迴歸場景哪一個更精準了、更適合些我們就選擇哪一個。
求出來最佳迴歸係數之後,我們最終的預測值也就出來了,那如何利用這些預測結果分類呢,這些值得範圍其中並不統一,一般是0到無窮大,當時為了解決這個問題很多人嘗試了各種辦法,嘗試了各種數學上的公式,最後找到了一個叫做sigmoid的函式,試驗之後覺得出其的好,以後邏輯迴歸大家就都使用這個函數了。
解惑
- 邏輯迴歸與KNN演算法關係
這兩種演算法感覺他們處理的樣本特點不同,假如樣本為一個圓形那麼knn演算法是不好處理的,而邏輯迴歸可以處理這種情況,每種演算法都有自身的優點也有不適合之處,具體要根據業務資料自行選擇。 - 線性迴歸與邏輯迴歸關係
想必對於很多剛剛接觸迴歸的人來說經常搞不清楚什麼叫回歸、邏輯迴歸、線性迴歸等概念,開始時我也是經常弄混了,隨著學習的深入對他們之間的區別,才逐漸清晰起來。
首先我們從迴歸說起,迴歸其實就是通過模型擬合樣本資料這一個過程叫做迴歸,生活中有各種樣本資料堆積在我們周圍,其特點各不相同,從最簡單的資料開始利用線性模型來預測樣本點的值分佈,樣本點分佈為線性規律,如下圖:
上圖中表示的是二維平面空間的樣本分佈圖,可見樣本都在圍繞直線周圍,說明樣本的分佈規律是一條直線 ,當然就可以利用直線來模擬,但後來又發現很多實際情況存在分類問題,線性迴歸對於這類問題預測經常出現錯誤,如下圖癌細胞大小與是否是癌症的關係圖:
從圖上可見並不是癌細胞越大與是否得癌症成正相關的線性規律,中間的幾個樣本即不符合的樣本,於是乎開始了尋找其它新模型的道路,這就是經典的邏輯迴歸模型,一般用來解決實際中的二分類問題,這種方法考慮到了每個樣本的特徵,算出來一個綜合評分,評分出來後為了找到一個能將評分對映到最終分類結果上,找到了一個叫做sigimod的函式,發揮出來神奇的作用,解決了二分類問題。
有時我們也把邏輯迴歸歸為廣義的線性迴歸,除了邏輯和線性還有其它幾種迴歸,同屬於一個迴歸家族,他們的自變數基本都相同,不同的是因變數不同,如線性迴歸結果為連續值,邏輯迴歸結果為0或1屬於二分類問題,如下:
- 如果是連續的,即線性迴歸
- 如果是二項分佈,即邏輯迴歸
- 如果是Poisson分佈,即Poisson迴歸
- 如果是負二項分佈,即負二項迴歸
- 損失函式、代價函式、似然函式
- 為什麼要有sigmod函式?
個人感覺sigmod函式並不一定非得有,但是有它能起到很好的效果,假如我們的樣本很好,分出來的類別很容易判斷屬於哪一個類或者有其他型別的函式都可以不適用它,sigmod函式似乎是邏輯迴歸必備函式,把這個函式去掉其它就是標準的線性迴歸方程。
- 判定邊界
這一概念要好理解一些,指分類的時候分好類後類別之間的邊界線,由於戰國時期畫好地界之後你就是屬於那一國家,國與國之間的邊界線將幾個國家分割開來,這裡的分類也是這個意思,判定邊界即是我們要找到分類模型的分類界限,模型的含義其實也是代表著判定邊界,不管在哪個演算法中,模型一直追求的完美分類或迴歸的邊界問題。
概念
- 梯度下降(Gradient Descent, GD)
是一種優化演算法,在機器學習中常用語優化模型係數,優點計算量小,適合大規模資料集 適用場景也比較多,基本思路可以這樣理解,有一座凹凸不平的山,我們要找一個下降最快的路下上,也就是梯度下降最快的方向走,由於山路不平選擇的走動方向可能每次走一步,下一步的方向會放生改變,如下圖所示:
在真實場景中,經常用到梯度法還有幾種變形後的梯度法,如批量梯度下降(BGD)、隨機梯度下降(SGD),它們各有優缺點,SGD每次更新系數時只選取一個樣本,這種方法可能有點極端,實際中也經常使用批量樣本更新模型,這種方式介於全量和單個之間可謂是一種權衡的結果,本篇我們會涉及到隨機梯度法。
實現程式碼
下面採用梯度下降法實現了求最佳係數過程,然後,優化使用了隨機梯度下降。
def loadDataSet():
'''
獲取資料集
:return:測試資料集、分類標籤
'''
# 測試資料集
dataMat = []
# 分類標籤
labelMat = []
# 資料檔案
fr = open('testSet.txt')
# readlines 返回一個字串列表
for line in fr.readlines():
# 去空格和特殊字元
lineArr = line.strip().split()
# 預設設定第一列為1
dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])
labelMat.append(int(lineArr[2]))
return dataMat,labelMat
def sigmoid(intX):
'''
sigmoid階躍函式
:param intX:輸入轉換向量
:return:0-1之間值
'''
return 1.0/(1+np.exp(-intX))
def gradAscent(dataMatIn,classLables):
'''
logistic迴歸梯度上升演算法
:param dataMatIn:資料集
:param classLables:分類標籤
:return: 最佳擬合引數向量
'''
# 將資料集和標籤轉為矩陣型別
print "dataMatIn = %s" % dataMatIn
print "classLables = %s" % classLables
dataMatrix = np.mat(dataMatIn)
labelMat = np.mat(classLables).transpose()
# shape返回矩陣的大小 行列數
m,n = np.shape(dataMatrix)
print "dataMatrix = %s" % dataMatrix
print "labelMat = %s" % labelMat
print "m = %s ,n = %s" % (m,n)
# 向目標移動的步長
alpha = 0.001
# 迭代次數
maxCycles = 500
# 返回一個n行 1 列矩陣
weights = np.ones((n,1))
# 對迴歸係數進行多少次梯度上升
for k in range(maxCycles):
# dataMatrix*weights代表了300次乘機
h = sigmoid(dataMatrix*weights)
print "h = %s " % h
error = (labelMat-h)
print "error = %s " % error
print "dataMatrix.transpose() = %s" % dataMatrix.transpose()
print np.shape(dataMatrix.transpose())
weights = weights + alpha*dataMatrix.transpose()*error
# 返回訓練好的迴歸係數
return weights
def plotBestFit(weights):
'''
畫出資料集和logistic迴歸最佳擬合直線的函式
:param weights:
:return:
'''
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,s=30,c='red',marker='s')
ax.scatter(xcord2,ycord2,s=30,c='green')
x = np.arange(-3.0,3.0,0.1)
y = (-weights[0]-weights[1]*x)/weights[2]
ax.plot(x,y)
plt.xlabel('X1');plt.ylabel('X2')
plt.show()
def stocGradAscent0(dataMatrix,classLabels):
'''
獲取最佳擬合引數向量
:param dataMatrix:資料集
:param classLabels:類別標籤
:return:最佳擬合引數向量
'''
m,n = np.shape(dataMatrix)
print "m = %s,n = %s " % (m,n)
# 步長
alpha = 0.01
# 初始化擬合引數向量
weights = np.ones(n)
print "weights %s = " % weights
# 對迴歸係數進行樣本數量的梯度上升,每次僅僅使用一個樣本
for i in range(m):
print "dataMatrix[i] = %s" % dataMatrix[i]
# h為單一樣本的預測結果
h = sigmoid(sum(dataMatrix[i] * weights))
# error為單一樣本的誤差
error = classLabels[i] - h
# 根據單一樣本結果來更新迴歸係數
weights = weights +alpha * error * dataMatrix[i]
return weights
def stocGradAscent1(dataMatrix,classLabels,numIter=150):
'''
基於隨機梯度上升法的logistic迴歸分類器
:param dataMatrix: 資料集
:param classLabels: 類別標籤
:return:
'''
m,n = np.shape(dataMatrix)
alpha = 0.01
weights = np.ones(n)
# 迭代次數控制
for j in range(numIter):
# 樣本選擇集
dataIndex = range(m)
# 隨機選取樣本遍歷一輪
for i in range(m):
# 每次迭代時調整alpha的值
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
總結
邏輯迴歸是比較常用的分析資料方法,需要知道其原理是如何過濾資料求最佳係數的,有時覺得演算法就是一種思路一種想法,其它沒有什麼難不難之分,演算法都是在常規想法之上優化出來的思路,堪稱某某演算法,要想掌握某個演算法需要知道其解決的什麼問題,優勢與劣勢,才能在真實場景中靈活應用,達到學以致用的目的。
題外思考
2選一思維
在生活中這種想法還是比較常見的,我們做每一件事情的時候都存在二選一思維,認為非黑即白,對或者錯,其實除了二選一都是兩種極端的兩發,生活中不存在完全正確與錯誤的答案或事實,需要開闊思維多角度和維度去想問題,思考問題。