logistic迴歸算法
我們將使用Logistic迴歸分類器來尋找最優值這是一個最優化算法問題。我們在k鄰近算法中通過計算給定點到樣本集合中所有點的距離,來判斷該店所屬於的類別,其實我們可以換一種解法,我們可以通過做一條直線將屬於label1 和 label2 的兩大類的點集進行區分,這樣在給點點(x,y)可以通過判斷(x,y)再直線的上方和下方來判斷(x,y)的類別,那兒我們如何找到最適合的直線呢? 這裏可能有一系列斜率值w直線,我們通過最優算法來確認最優質的w。
梯度下降算法
擬合曲線
對於給定的訓練集我們可以使用曲線去做擬合,如下圖:
我們可以爲每個特徵值賦一個權值w,這樣我們就能得到我們的擬合曲線 hθ(x) = θ0 + θ1x1 + … θnxn = θx,迴歸算法的目的就是求出W的最佳權值。
損失函數
評估我們得到的θ是否合適,我們就需要使用損失算法去計算得到的W帶入訓練集得到的目標值和正確值之間的差異,這裏我們採用平方差來定義損失函數。
要找到損失函數的最小值,我們可以採用梯度下降算法去獲取,每次按照梯度減少的方向去調整我們的θ,最終能夠找到符合損失範圍的一個θ值。
這裏我們就需要J(θ)對θ求偏導,最終得到偏導函數:
偏導函數乘於一個步長序數得到每次迴歸θ的減少值,下面是最終θ的迴歸公式:
在進行梯度下降尋找最小值時由於θ的初始值問題有可能最終得到的最小值時區域最小值而非全局最小值。如果我們最終要預測的值試一個0或1,這種時候我們已經可以使用單位階躍函數Sigmoid f(z) = 1/(1+e-z) 其中-z是冪數,將hθ(x)帶入其中當時f(z)>0.5時屬於分類1,當f(z)<0.5時屬於分類0。
使用梯度算法尋找最佳擬合直線
我們使用在knn裏面的訓練數據爲它們尋找擬合曲線,訓練數據分佈:
首先按照往常使用下面腳本加載數據:
#-*- coding=utf-8 -*-
from numpy import *
from math import *
#加載數據的接口
def loadDataSet():
dataMat = [];labelMat = []
fr = open('testSet.txt')
for line in fr.readlines():
lineArr = line.strip().split("\t")
#這裏的1.0實際定義爲 θ0的默認值
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
labelMat.append(int(lineArr[2]))
return dataMat, labelMat
#定義我們的Sigmoid函數
def sigmiod(z):
return 1.0/(1 + exp(-z))
算法實現:
#訓練算法
def dataTrain(dataMatIn, classLables):
#因爲要使用矩陣方法,都先轉換爲矩陣
dataMatIn = mat(dataMatIn)
classLables = mat(classLables)
m,n = shape(dataMatIn)
#m表示測試數據量,n表示特徵數
#我們爲每個特徵數指定一個默認的權值
weights = ones([n,1])
#設置每次移動的步長,這個步長是可以自己進行調整的
alpha = 0.001
#設置迴歸次數,其實有兩種方法來終止迴歸,一種是指定迴歸次數,一種是指定誤差值範圍
maxCycles = 500
for k in range(maxCycles):
#獲取預測結果 這裏使用的是矩陣乘法 MxN * Nx1 = Mx1
h = sigmiod(dataMatIn*weights)
#獲取誤差值,相當於上面的求導公式裏面的 hθ(x) - y
#當然這裏的結果是一個矩陣向量 Mx1矩陣
error = (h - classLabels)
#一次迴歸θ 按照公式 θ = θ - alpha * error * dataMatIn
weights = weights - alpha * dataMatIn.transpose() * error
return weights
#分類方法
def classify(intX, weights):
value = sigmiod(inX * weights)
if value > 0.5:
return 1
return 0
#weight result:
matrix([[ 4.12414349],
[ 0.48007329],
[-0.6168482 ]])
最終得到的擬合直線:
隨機梯度下降
上面的梯度算法我們的訓練集總共有100個數據,迴歸次數500次,每次迴歸我們都會涉及到對所有訓練集進行計算,計算複雜度太高。因此引出了我們的隨機梯度下降算法,該算法一次僅適用一個樣本點來更新迴歸係數,這個算法能夠實現在線學習算法,當新樣本到達時我們就可以對分類器做增量更新。
算法實現:
def randomDataTrain(dataMatIn, classLabels):
m,n = shape(dataMatIn)
classLabels = mat(classLabels)
weights = ones(n)
alpha = 0.1
for i in range(m):
#這裏是array數組相乘
h = sigmoid(sum(dataMatIn[i]*weights.transpose()))
error = h - classLabels[i] #一次迴歸
weights = weights - alpha * mat(dataMatIn[i]) * error return weights
weights result:
matrix([[ 2.38604812, 0.95218922, -0.72978254]])
擬合曲線綠色的之前表示隨機梯度下降得到的值:
這裏可以看出得到的權值並不是最優權值,原因是因爲我們的訓練集合樣本太少。但這並不能說明隨機算法比上面的循環算法差,判斷一個算法的優劣應該是看它是否收斂,參數是否達到穩定值。
補充
上面的隨機梯度下降算法存在一個挺嚴重的問題是,由於每次使用其中的一條數據進行訓練,會出現由於訓練集合裏面存在異常的數據項,從而導致我們的特徵權值出現比較嚴重的波動,最終影響算法的收斂,針對這個問題我們可以進行改進。
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
#go to 0 because of the constant
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