原文連接:www.cnblogs.com/fydeblog/p/…html
這篇notebook是關於機器學習中logistic迴歸,內容包括基於logistic迴歸和sigmoid分類,基於最優化方法的最佳係數肯定,從疝氣病症預測病馬的死亡率。 操做系統:ubuntu14.04 運行環境:anaconda-python2.7-jupyter notebook 參考書籍:機器學習實戰和源碼 notebook writer ----方陽python
注意事項:在這裏說一句,默認環境python2.7的notebook,用python3.6的會出問題,還有個人目錄可能跟大家的不同,大家本身跑的時候記得改目錄,我會把notebook和代碼以及數據集放到結尾的百度雲盤,方便大家下載!算法
首先說說sigmoid函數吧。ubuntu
它的表達式是 g(z) = 1/(1+exp(-x)) ,爲直觀看出,咱們畫畫這個函數的曲線。數組
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-5,5,200)
y = 1./(1+np.exp(-x))
plt.figure()
plt.plot(x,y)
plt.xlabel('x')
plt.ylabel('sigmiod(x)')
plt.show()
複製代碼
上面就是sigmiod函數的圖形,那麼咱們怎麼用sigmiod函數進行邏輯迴歸判決呢?bash
首先觀察函數圖形,sigmiod函數的y軸被限制在區間(0,1)上,這有利於咱們判決,將線性的無窮範圍壓縮到這個小範圍,當x=0的時候,sigmiod(0) = 0.5, 因而咱們就將0.5看成界限,特徵值乘以一個迴歸係數,而後結果相加,代入到這個sigmiod函數當中,將函數值大於0.5分爲1類,小於0.5的分爲0類,至此,logistic分類完成。app
這裏說一下,sigmiod函數是爲了幫助咱們來判斷分類類別,而後與真實類別相比較,算出偏差,而後用梯度上升最小化偏差,獲得最佳係數。dom
sigmiod函數的輸入記爲z,公式:z = w0x0+w1x1+w2x2+...+wnxn (這裏0,1,2,...,n都表明下標係數),簡單寫就是 z = wTx (T表明轉置)python2.7
梯度上升法的思想:要找到某函數的最大值 ,最好的方法是沿着該函數的梯度方向探尋。機器學習
這是函數f(x,y)的梯度表達式,當要沿x方向移動時,就是對x求偏導;當要沿y方向移動時,就是對y求偏導。其中,函數f(x,y)必需要在待計算的點上可微。
梯度上升算法的迭代公式:w := w+a dw(f(w)) (dw是關於係數w的梯度,a是學習率)
梯度上升法的具體上升過程,如圖所示
該公式將一直被迭代執行,直至達到某個中止條件爲止,好比迭代次數達到某個指定值或算法達到某個能夠容許的偏差範圍。
梯度上升算法與梯度降低算法的區別:就是梯度上升的學習率前面的加號變爲減號就是梯度降低算法
梯度上升法的僞代碼以下:
每一個迴歸係數初始化爲1
重複R次:
計算整個數據集的梯度
使用alpha X gradient更新迴歸係數
返回迴歸係數
下面進入梯度上升法的具體實現
複製代碼
下面進入梯度上升法的具體實現
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+exp(-inX))
def gradAscent(dataMatIn, classLabels):
dataMatrix = mat(dataMatIn) #convert to NumPy matrix
labelMat = mat(classLabels).transpose() #convert to NumPy matrix
m,n = shape(dataMatrix)
alpha = 0.001
maxCycles = 500
weights = ones((n,1))
for k in range(maxCycles): #heavy on matrix operations
h = sigmoid(dataMatrix*weights) #matrix mult
error = (labelMat - h) #vector subtraction
weights = weights + alpha * dataMatrix.transpose()* error #matrix mult
return weights
複製代碼
第一個函數loadDataSet函數就是導入數據集,並進行封裝,將特徵值封裝成三列的多維列表dataMat,label放在labelMat裏面 第二個函數不用多說,就是sigmiod函數 第三個函數就是梯度上升的具體算法,dataMatIn就是上面的多維列表dataMat,classlabels就是labelMat,函數首先將輸入的數據集和標籤所有轉化成的numpy矩陣,是爲了可以進行矩陣運算和向量運算,maxCycles表明迭代的最大次數,至於參數的迭代還能夠看看如下圖片,我選的是吳恩達的ppt上,這寫的是梯度降低的迭代過程(梯度上升就是反過來,原理同樣),可見損失函數的梯度會有逐漸趨於0,這樣迭代公式的梯度那項也會趨於0,參數不會有太大的浮動,趨於穩定,偏差最小。
測試一下吧
cd 桌面/machinelearninginaction/Ch05
/home/fangyang/桌面/machinelearninginaction/Ch05 import logRegres
dataMat , labelMat = logRegres.loadDataSet()
logRegres.gradAscent(dataMat,labelMat)
咱們獲得了最佳係數,便於理解,咱們要畫出分隔線,便於咱們觀察
代碼以下:
def plotBestFit(weights):
import matplotlib.pyplot as plt
dataMat,labelMat=loadDataSet()
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]
ax.plot(x, y)
plt.xlabel('X1'); plt.ylabel('X2');
plt.show()
複製代碼
這個函數先是調用loadDataSet函數將數據集和標籤賦給dataMat,labelMat,而後對不一樣類別進行不一樣的分組,類別1的數據放在xcord1和ycord1,類別2的數據放在xcord2和ycord2,而後分別顯示,最後畫出輸入的權重對應的分隔線,y的求解你可能有疑問,這裏說一下,具體表達式是wTx=0,wT是輸入權重,x=[x0,x1,x2],其中x0爲了方便表示所創建的,值爲1,x1就是上述函數的x,x2就是y,這下你就知道爲何是那個表達式了吧。
from numpy import *
weights = logRegres.gradAscent(dataMat,labelMat)
logRegres.plotBestFit(weights.getA()) # the function of getA is used to transform matrix into array
能夠看出圖中只錯分了兩到四個點,效果不錯。
梯度上升算法在每次更新迴歸係數時都須要遍歷整個數據集,當特徵的數目很是多的時候,計算量會很是巨大。 一種改進方法是一次僅用一個樣本點來更新迴歸係數, 該方法稱爲隨機梯度上升算法。 因爲能夠在新樣本到來時對分類器進行增量式更新,於是隨機梯度上升算法是一個在線學習算法。與在線學習相對應 ,一次處理全部數據被稱做是 「批處理」 。
隨機梯度上升算法的僞代碼以下
全部迴歸係數初始化爲 1
對數據集中每一個樣本
計算該樣本的梯度
使用 alpha x gradient更新迴歸係數值
返回迴歸係數值
複製代碼
代碼以下:
def stocGradAscent0(dataMatrix, classLabels):
m,n = shape(dataMatrix)
alpha = 0.01
weights = ones(n) #initialize to all ones
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都是向量 ,而前者則全是數值; 第二 ,前者沒有矩陣的轉換過程,全部變量的數據類型都是NumPy數組。 這是由於梯度上升算法是遍歷全部數據,造成的是全部數據的向量,而隨機梯度上升算法每次只用一個樣本,因此是單一數值。其餘相似
來測試一下效果吧
weights = logRegres.stocGradAscent0(array(dataMat),labelMat)
logRegres.plotBestFit(weights)
可見擬合的直線不完美,錯分了三分之一的樣本,直接比較結果是不公平的,梯度上升算法是在整個數據集中迭代了500次,而隨機梯度算法只在整個數據集中迭代了1次,計算量相差不少倍。因此這裏還需對隨機梯度上升算法進行優化,代碼以下
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
m,n = shape(dataMatrix)
weights = ones(n) #initialize to all ones
for j in range(numIter):
dataIndex = range(m)
for i in range(m):
alpha = 4/(1.0+j+i)+0.0001 #apha decreases with iteration, does not
randIndex = int(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動態減小的機制,這樣作的緣由是爲了保證在屢次迭代以後新數據仍然具備必定的影響。 第二點是經過隨機選取樣原本更新迴歸係數,這種方法減小週期性的波動。每次隨機從列表中選出一個值,而後從列表中刪掉該值,從新迭代 須要注意的是: 若是要處理的問題是動態變化的,那麼能夠適當加大上述常數項,來確保新的值得到更大的迴歸係數。
再次運行出來看看
weights = logRegres.stocGradAscent1(array(dataMat),labelMat)
logRegres.plotBestFit(weights)
可見分類效果與梯度上升算法差很少,比較一下計算量,梯度上升算法迭代了500次的整個數據集,而隨機梯度上升算法迭代了150次就達到相似的效果。
這個例子是經過馬疝病的一些指標,使用logistic迴歸和隨機梯度上升算法來預測病馬的生死。
馬疝病的數據集中有30%的值是缺失的,咱們怎樣來解決這個問題呢?
首先咱們要知道,有時候數據是很是昂貴的,扔掉缺失數據和從新獲取新的數據都是不可取的,因此咱們採用一些方法來解決這個問題,方法以下:
下面給出了一些可選的作法:
預處理階段的兩件事:
第一件事,全部的缺失值必須用一個實數值來替換,由於咱們使用的numpy數據類型不容許包含缺失值。
第二件事,若是在測試數據集中發現了一條數據的類別標籤已經缺失,那麼咱們的簡單作法是將該條數據丟棄。這是由於類別標籤與特徵不一樣,很難肯定採用某個合適的值來替換。
這個例子選實數0來替換全部缺失值,不影響特徵係數,若是等於0,對應的參數也被置0,不會更新,還有就是sigmiod(0)=0.5,即對結果的預測不具備任何傾向性,全部用0代替缺失值
使用Logistic迴歸方法進行分類並不須要作不少工做,所需作的只是把測試集上每一個特徵向量乘以最優化方法得來的迴歸係數,再將該乘積結果求和,最後輸人到sigmiod函數中便可。若是對應的sigmiod值大於0.5就預測類別標籤爲1 , 不然爲0 。
例子的代碼以下
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(array(trainingSet), trainingLabels, 1000)
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(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))
複製代碼
第一個函數classifyVector,它以迴歸係數和特徵向量做爲輸入來計算對應的sigmiod值,若是值大於0.5返回1,不然返回0 第二個函數colicTest,先是打開訓練集和測試集,而後使用改善後的隨機梯度上升算法對訓練集進行訓練,獲得訓練參數,而後使用這個訓練參數帶入到測試集當中,比較這個測試集使用該參數獲得的類別和實際類別,算出錯誤的個數,最終程序是返回錯誤率 第三個函數就是屢次使用第二個函數,最後打印出平均錯誤率
logRegres.multiTest()
平均錯誤率34.7761%,這個錯誤率還好,畢竟咱們有30%的數據缺失嘛,做者書上調整迭代次數和步長,能夠改善錯誤率,大家能夠試一試。
logistic迴歸的目的是尋找一個非線性函數sigmiod的最佳擬合參數,求解過程能夠由最優化算法來完成。在最優化算法中,最經常使用的就是梯度上升算法, 而梯度上升算法又能夠簡化爲隨機梯度上升算法。
隨機梯度上升算法與梯度上升算法的效果至關, 但佔用更少的計算資源。此 外 ,隨機梯度上升是一個在線算法, 它能夠在新數據到來時就完成參數更新, 而不須要從新讀取整個數據集來進行批處理運算。
百度雲連接:pan.baidu.com/s/1cSRMHwvi…
圓方圓學院聚集 Python + AI 名師,打造精品的 Python + AI 技術課程。 在各大平臺都長期有優質免費公開課,歡迎報名收看。
公開課地址:ke.qq.com/course/3627…
加入python學習討論羣 78486745 ,獲取資料,和廣大羣友一塊兒學習。