機器學習實戰(四)邏輯迴歸LR(Logistic Regression)
目錄
6. 隨機梯度下降/上升法(Stochastic gradient descent/ascent)
學習完機器學習實戰的邏輯迴歸,簡單的做個筆記。文中部分描述屬於個人消化後的理解,僅供參考。
本篇綜合了先前的文章,如有不理解,可參考:
所有程式碼和資料可以訪問 我的 github
如果這篇文章對你有一點小小的幫助,請給個關注喔~我會非常開心的~
0. 前言
邏輯迴歸(Logistic Regression)是一種基於最優化思想的二分類監督學習演算法。
其主要思想是根據現有資料對分類邊界線建立迴歸公式,以此分類。
其目的是尋找一個非線性函式 的最佳擬合引數,求解過程可以通過最優化演算法完成。
- 優點:計算代價不高,易於理解和實現
- 缺點:容易造成欠擬合,分類精度不高
- 適用資料型別:數值型和標稱型資料
1. Sigmoid 函式
在線性迴歸中,對資料進行擬合,是在每個特徵上都乘以一個迴歸係數,然後把所有結果再相加,即 ,向量化即 。
在邏輯迴歸中,再將 帶入 函式,可得到屬於 範圍的一個數值,若大於 則屬於正類,若小於則反之。
函式的定義如下:
函式的影象如下:
綜合上述,整體公式表示為:
2. 梯度上升與梯度下降
梯度:某一函式在該點處的方向導數沿該方向取得最大值,即在該點變化率(斜率)最大。
- 梯度上升法:尋找某函式的最大值,沿著該函式的梯度方向尋找
- 梯度下降法:尋找某函式的最小值,沿著該函式的梯度方向尋找
梯度符號記為 , 的梯度可表示為,對各個方向求偏導數:
如下圖所示,求最小值,可採用梯度下降法:
梯度下降: ,當 在最小值左邊時,導數為負數, 向右移;當 在最小值右邊時,導數為正數, 向左移。
如下圖所示,求最大值,可採用梯度上升法:
梯度上升: ,當 在最小值左邊時,導數為正數, 向右移;當 在最小值右邊時,導數為負數, 向左移。
3. 梯度下降法(Gradient descent)
梯度下降法的代價函式如下定義:
代價函式是指,函式值越大,付出的代價越大,即精度越差。
第二項是正則化項,是為了防止過擬合而設定,先忽略這項。
第一項是所有樣本計算結果的求平均,若 ,則取 ,若 ,則取 。
由上圖易得, 時,若 越接近 代價越大, 時,若 越接近 代價越大。
為了降低代價函式 ,可採取梯度下降法,定義如下:
4. 梯度上升法(Gradient ascent)
梯度上升法求解邏輯迴歸引數,和梯度下降法求解引數原理是一樣的。
只是在梯度上升法中, 定義如下:
與梯度下降法只相差一個負號,所以是求解最大值,採用梯度上升法:
5. 梯度下降/上升法的數學推導
以梯度上升法為例子,不考慮正則化項:
對三項分別計算:
綜合三項可得:
綜上所述,在梯度上升法中:
6. 隨機梯度下降/上升法(Stochastic gradient descent/ascent)
以隨機梯度下降法為例子,隨機梯度下降法是對梯度下降法的改進。
在梯度下降法中,每次更新引數都需要遍歷整個資料集,又稱作 Batch gradient descent 。
在隨機梯度下降法中,每次更新引數只隨機選擇一個樣本點,通過每次更新引數的迭代而選擇不同的樣本點。
演算法流程如下:
隨機梯度下降/上升法是一個線上演算法,可以在新資料來臨的時候直接更新引數,而不用遍歷整個資料集。
7. 實戰案例
以下將展示書中案例的程式碼段,所有程式碼和資料可以在github中下載:
7.1. 簡單案例
# coding:utf-8
from numpy import *
import matplotlib.pyplot as plt
"""
簡單案例
"""
# sigmoid函式
def sigmoid(inX):
return 1.0 / (1 + exp(-inX))
# 梯度上升求解J(\theta)
# Batch gradient ascent
def gradAscent(dataMatIn, classLabels):
# 轉換為矩陣
dataMatrix = mat(dataMatIn)
# 轉換為矩陣後轉置,表示為列向量
labelMat = mat(classLabels).transpose()
m, n = shape(dataMatrix)
alpha = 0.001
maxCycles = 500
# \theta 表示為列向量
weights = ones((n, 1))
# 梯度上升迭代
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights)
error = (labelMat - h)
weights = weights + alpha * dataMatrix.transpose() * error
return weights
# 載入資料集
def loadDataSet():
dataMat = []
labelMat = []
fr = open('testSet.txt')
for line in fr.readlines():
# 去除頭尾空字元,按照空格分割字串
lineArr = line.strip().split()
# 新增偏置位 w_0(\theta_0) 相乘的 x_0 = 1.0
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
labelMat.append(int(lineArr[2]))
return dataMat, labelMat
# 畫出資料的分界線
def plotBestFit(weights):
dataMat, labelMat = loadDataSet()
dataArr = array(dataMat)
n = shape(dataArr)[0]
xcord1 = []
ycord1 = []
xcord2 = []
ycord2 = []
# 遍歷每一條資料,根據類別將x1, x2分別插入不同的List中
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 = array(arange(-3.0, 3.0, 0.1))
# x=x1, y=x2 表示的是 w0x0+w1x1+w2x2 = 0 的直線
y = (-weights[0] - weights[1] * x) / weights[2]
ax.plot(x, y)
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()
if __name__ == '__main__':
dataArr, labelMat = loadDataSet()
weights = gradAscent(dataArr, labelMat)
# getA():
# 將矩陣轉換為ndarray
plotBestFit(weights.getA())
7.2. 病馬死亡率案例
# coding:utf-8
from numpy import *
"""
病馬死亡率案例
"""
# sigmoid函式
def sigmoid(inX):
return 1.0 / (1 + exp(-inX))
# 隨機梯度上升
# stochastic gradient ascent
def stocGradAscent0(dataMatrix, classLabels):
m, n = shape(dataMatrix)
alpha = 0.01
weights = 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
# 改進後的隨機梯度上升
# 學習率隨迭代次數減少
# 隨機選擇樣本
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
m, n = shape(dataMatrix)
weights = ones(n)
for j in range(numIter):
dataIndex = list(range(m))
for i in range(m):
# 學習率降低
alpha = 4 / (1.0 + j + i) + 0.0001
randIndex = int(random.uniform(0, len(dataIndex)))
h = sigmoid(sum(dataMatrix[dataIndex[randIndex]] * weights))
error = classLabels[dataIndex[randIndex]] - h
weights = weights + alpha * error * dataMatrix[dataIndex[randIndex]]
del (dataIndex[randIndex])
return weights
# 分類函式
def classifyVector(inX, weights):
prob = sigmoid(sum(inX * weights))
if prob > 0.5:
return 1
else:
return 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)
correctCount = 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]):
correctCount += 1
accuracy = (float(correctCount) / numTestVec)
print("the accuracy of this test is: %f" % accuracy)
return accuracy
# 多次測試分類器
def multiTest():
numTests = 10
correctSum = 0.0
for k in range(numTests):
correctSum += colicTest()
print("after %d iterations the average accuracy is: %f" % (numTests, correctSum / float(numTests)))
if __name__ == '__main__':
multiTest()
如果這篇文章對你有一點小小的幫助,請給個關注喔~我會非常開心的~