【統計學習方法】感知機Python 對偶形式實現

代碼可在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))

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章