代碼可在Github上下載:(https://github.com/q13030880/MachineLearning)
上個博客提到了感知機的原始形式,接下來要給大家介紹的是感知機的對偶形式。
在對偶形式中,感知機的模型是
\[f = sign\left( {\sum\limits_{j = 1}^N {{\alpha _j}{y_j}{x_j} \cdot x + b} } \right)\],策略依舊是誤分類點的數目來作爲損失函數,算法也依舊是隨機梯度下降算法。
感知機的原始形式提到的參數更新:
\[\begin{array}{l}w \leftarrow w + \eta {y_i}{x_i}\\
b \leftarrow b + \eta {y_i}
\end{array}\]
而對偶形式的一個直觀解釋就是,之前原始形式修改了n次的,現在直接將n次修改的量給計算出來,直接一次性進行參數更新。
對偶形式的參數更新:
\[\begin{array}{l}
w = \sum\limits_{i = 1}^N {{\alpha _i}{y_i}{x_i}} \\
b = \sum\limits_{i = 1}^N {{\alpha _i}{y_i}}
\end{array}\]
從對偶形式的模型中可以看出,需要進行內積的計算,所以可以先創建一個Gram矩陣來存儲實例的內積以便於計算。
\[G = {\left[ {{x_i} \cdot {x_j}} \right]_{N \times N}}\]
下面對偶形式的實現。注意:對偶形式的代碼實現與原始形式的代碼實現就相差訓練函數上,所以如果看過原始形式的代碼,主要看看以下這段代碼。
def train(self):
m, n = np.shape(self.dataSet) #m是行和n是列
weights = np.zeros(n)
bias = 0
flag = False
Gram = np.zeros((m , m))
for i in range(m): #計算Gram矩陣
for j in range(m):
Gram[i][j] = dataSet[i] * np.mat(dataSet[j]).transpose()
print(Gram)
a = np.zeros(m)
while flag != True:
flag = True
for i in range(m): #遍歷樣本
sum = 0
for j in range(m): #求誤分條件
sum += a[j] * self.labels[j] * Gram[j][i]
# print a
# print self.labels
# print Gram[0]
# sum += np.sum(np.dot(np.dot(a, self.labels), np.mat(Gram).T[i]))
sum += bias
print(sum)
if (sum * self.labels[i] <= 0):
a[i] += 1
bias += self.labels[i]
flag = False
for i in range(m):
weights += a[i] * self.dataSet[i] * self.labels[i]
return weights, bias
先按慣例求出行和列,然後按書上說的求出Gram矩陣,用於後面的內積計算。
其次設置個flag標記,這個標記是用來標記訓練出來的參數w和b 是否符合當前的訓練樣本了(進入循環時先設置爲True,遍歷訓練樣本時一旦發現有不符合的就設置爲False)。
然後就是誤分條件。
\[{y_i}\left( {\sum\limits_{j = 1}^N {{\alpha _j}{y_j}{x_j} \cdot {x_i} + b} } \right) \le 0\]
接下來就是a和bias的更新了
\[\begin{array}{l}
\alpha \leftarrow \alpha + 1\\
b \leftarrow b + {y_i}
\end{array}\]
最後是根據a求出權重,返回。
最終代碼如下:
# --*-- coding:utf:8 --*--
import numpy as np
class Perceptron: #感知機
def __init__(self, dataSet, labels): #初始化數據集和標籤
self.dataSet = np.array(dataSet)
self.labels = np.array(labels).transpose()
def train(self):
m, n = np.shape(self.dataSet) #m是行和n是列
weights = np.zeros(n)
bias = 0
flag = False
Gram = np.zeros((m , m))
for i in range(m): #計算Gram矩陣
for j in range(m):
Gram[i][j] = dataSet[i] * np.mat(dataSet[j]).transpose()
print(Gram)
a = np.zeros(m)
while flag != True:
flag = True
for i in range(m): #遍歷樣本
sum = 0
for j in range(m): #求誤分條件
sum += a[j] * self.labels[j] * Gram[j][i]
# print a
# print self.labels
# print Gram[0]
# sum += np.sum(np.dot(np.dot(a, self.labels), np.mat(Gram).T[i]))
sum += bias
print(sum)
if (sum * self.labels[i] <= 0):
a[i] += 1
bias += self.labels[i]
flag = False
for i in range(m):
weights += a[i] * self.dataSet[i] * self.labels[i]
return weights, bias
def sign(self, y): #符號函數
# print "y = %d" % y
if (y > 0):
return 1
else:
return -1
dataSet = [[3, 3],
[4, 3],
[1, 1]]
labels = [1, 1, -1]
perceptron = Perceptron(dataSet, labels) #創建一個感知機對象
weights, bias = perceptron.train() #訓練
print("結果是:%s, %s" % (weights, bias))