簡介
我看到了有無數個感知機算法的blog,但是很多都是直接把代碼丟上去,即使有講解也都很淺。於是我挑選了一個我認爲最簡潔的python代碼實現,但是同樣博主也沒有很深入的講解。我結合他的代碼來深入的介紹一下這個算法,如果您能認真的看完,那麼這個算法應該不會有大的問題了。
算法簡介
首先我們要知道,感知機算法是神經網絡和支持向量機的基礎,其中的數學思想更是基礎中的基礎。他是二分類的線性分類模型,感知機學習最終目的是將樣本劃分爲正負兩類分離超平面,算法的總體思想是引入基於誤分類的損失函數,利用隨機梯度下降的方法來解決求解這個隨時函數最優化的問題。
優點就是非常的簡單和易於實現。
數學原理
算法的目標是找到一個可以將訓練集的正負點完全分開的分離超平面,
在二維的情況下就是一條直線。那就是需要確定y=wx+b中的w和b。那麼我們需要建立一個學習模型:
- 首先
對於每一個正確分類的樣本點,滿足{wx + b > 0,y = 1}或者{wx + b < 0 ,y = -1},所以對於所有的誤分點一定有y(wx+b) <= 0。
- 其次
我們定義損失函數,這個損失函數計算的是每個誤分點到目前的y = wx + b這條直線的距離之和。公式表達爲:
其中M表示的是誤分點的集合。
那麼這時我們的問題就轉化爲了minL(w,b)的問題。
- 接下來
爲了解決minL(w,b)的問題,使用隨機梯度下降的方法,,也就是說極小化的過程並不是對M中的所有點的梯度下降,而是一次隨機選取一個誤分類點進行梯度下降。
由於在實際的應用中M使固定的,所以隨時函數的梯度就是:
所以我們隨機選取一個誤分點,然後
w->w+nyixi
b->b+nyi
其中n是步長,那麼在下面的代碼中我們叫做學習率。通過迭代使得L不斷減小。
原始形式
代碼分析:
總的來說就是分爲4步
- 隨機選取w0,b0
- 在數據集中選取數據(xi,yi)
- 判斷if y(wx+b) <= 0 then {
w->w+nyixi
b->b+nyi
} - 回到2,當沒有誤分點時結束。
代碼:
def vectorInnerProduct(vector1,vector2): # 實現兩個向量的內積
result = 0
length = len(vector1)
for i in range(length):
result += vector1[i] * vector2[i]
return result
def elementAddition(vector1,vector2): # 實現兩個向量的對應元素相加
for i in range(len(vector1)):
vector1[i] += vector2[i]
return vector1
def numberMultiply(num,vector): # 實現向量的數乘
tempVector = []
for i in range(len(vector)):
tempVector.append(vector[i] * num)
return tempVector
# 不能直接修改原來的vector,要不然帶入到感知機的主函數會一邊修改權重和偏置,
# 一邊修改原來的數據集
"""
上面三個函數,是爲了方便對數據(向量)進行相關的運算,寫的輔助函數。很簡單
"""
def perceptron(bias,dataSet,learnRate):
# 感知機原始算法,需要輸入三個變量,偏置,數據集以及學習率。
weightVector = [0 for i in range(len(dataSet[0][0]))] # 權重向量初始化爲0
while True:
# 因爲要不斷遍歷訓練集,直到沒有誤分類點,因此利用一個while循環,和記錄誤分類點數量的變量errornum
errorNum = 0
for data in dataSet: # 一遍一遍的遍歷數據集,進行迭代
if data[1] * (vectorInnerProduct(weightVector,data[0])+bias) <= 0:
errorNum += 1
weightVector = elementAddition(weightVector,numberMultiply(learnRate * data[1],data[0]))
bias += learnRate * data[1]
if errorNum == 0: # 如果沒有誤分類點,退出循環
break
return weightVector,bias # 返回模型參數
代碼來自https://blog.csdn.net/ggdhs/article/details/92803970
對偶形式
基本思想:
和原始形式稍有不同但是原理是一樣的,將w和b表示爲xi和yi的線性組合形式,然後通過求解其係數來得到w和b。所以w和b我們可以表示爲:
代碼
僞代碼:
主要有四步
- a = 0 ,b = 0
- 在數據集中選取數據(xi,yi)
- 判斷if
then {
ai = ai + n
b = b + nyi
} - 回到2,當沒有誤分點時結束。
注:在下面的代碼實現中爲了提高效率,使用了空間換時間的方法,將內積計算並存儲起來,也就是gramMatrix函數。
def vectorInnerProduct(vector1,vector2): # 向量內積
result = 0
length = len(vector1)
for i in range(length):
result += vector1[i] * vector2[i]
return result
def gramMatrix(dataSet): # 計算gram矩陣
length = len(dataSet)
gramMatrix = [[0 for i in range(length)] for i in range(length)]
for i in range(length):
for j in range(length):
gramMatrix[i][j] = vectorInnerProduct(dataSet[i][0],dataSet[j][0])
return gramMatrix
def elementAddition(vector1,vector2): # 向量元素相加
for i in range(len(vector1)):
vector1[i] += vector2[i]
return vector1
def numberMultiply(num,vector): # 數乘
tempVector = []
for i in range(len(vector)):
tempVector.append(vector[i] * num)
return tempVector
def perceptron(dataSet,learnRate): # 對偶形式的變量爲數據集和學習率,alpha和偏置在函數中設置成了0
n = len(dataSet)
alphaList= [0 for i in range(n)]
bias = 0
gram = gramMatrix(dataSet)
while True: # 具體的思路和原始算法一樣,只是一些細節(判定條件和學習表達式)修改了一下
errorNum = 0
for i in range(n):
tempSum = 0
for j in range(n):
tempSum += alphaList[j] * dataSet[j][1] * gram[j][i]
if dataSet[i][1] * (tempSum + bias) <= 0:
errorNum += 1
alphaList[i] += learnRate
bias += learnRate * dataSet[i][1]
if errorNum == 0:
break
# 在學習過程中學習的是alpha,利用學習的alpha計算最終的權值向量
weightVector = numberMultiply(alphaList[0]*dataSet[0][1],dataSet[0][0])
for i in range(1,n):
weightVector = elementAddition(weightVector,numberMultiply(alphaList[i]*dataSet[i][1],dataSet[i][0]))
return weightVector,bias
有不對的請大家指正
共勉~~
保佑武漢