假設輸入空間(特徵空間)是 ,輸出空間是html
稱爲 感知機。python
假設訓練數據集是線性可分的 感知機學習的目標是求得一個可以將訓練集正實例點和負實例點徹底正確分開的分離超平面。爲了找出這樣的超平面,即肯定感知機模型參數 和 ,須要肯定一個學習策略,即定義(經驗)損失函數並將損失函數極小化。git
損失函數的一個天然選擇是誤分類點的總數。 可是,這樣的損失函數不是參數w,b的連續可導函數,不易優化。 損失函數的另外一個選擇是誤分類點到超平面S的總距離,這是感知機所採用的。github
全部誤分類點到超平面S的總距離爲算法
不考慮,就獲得感知機學習的損失函數。api
輸入:訓練數據集 ,其中 ;學習率;bash
輸出:;感知機模型 。app
# 原始
def _fit(self):
n_samples, n_features = self.X.shape
# 選取初值 w0,b0
self.w = np.zeros(n_features, dtype=np.float64)
self.b = 0.0
is_finished = False
# 一直循環
while not is_finished:
count = 0 # 記錄誤分類點的數目
for i in range(n_samples):
# 若是 yi(w·xi+b)≤0
if self.y[i] * self.sign(self.w, self.X[i], self.b) <= 0:
self.w += self.l_rate * np.dot(self.y[i], self.X[i])
self.b += self.l_rate * self.y[i]
self._wbs.append((i, self.w, self.b))
count += 1
# 直至訓練集中沒有誤分類點
if count == 0:
is_finished = True
複製代碼
這種學習算法直觀上有以下解釋:dom
當一個實例點被誤分類,即位於分離超平面的錯誤一側時,則調整w,b的值,使分離超平面向該誤分類點的一側移動,以減小該誤分類點與超平面間的距離,直至超平面越過該誤分類點使其被正確分類。函數
算法是感知機學習的基本算法,對應於後面的對偶形式,稱爲原始形式。
感知機學習算法簡單且易於實現。
# 對偶形式
def _fit_dual(self):
""" 對偶形式中訓練實例僅之內積的形式出現 先求出 Gram 矩陣,能大大減小計算量 """
n_samples, n_features = self.X.shape
self._alpha = np.zeros(n_samples, dtype=np.float64)
self.w = np.zeros(n_features, dtype=np.float64)
self.b = 0.0
self._cal_gram_matrix()
i = 0
while i < n_samples:
if self._dual_judge(i) <= 0:
self._alpha[i] += self.l_rate
self.b += self.l_rate * self.y[i]
i = 0
else:
i += 1
for i in range(n_samples):
self.w += self._alpha[i] * self.X[i] * self.y[i]
複製代碼
對偶形式中訓練實例僅之內積的形式出現。爲了方便,能夠預先將訓練集中實例間的內積計算出來並以矩陣的形式存儲,這個矩陣就是所謂的Gram矩陣(Gram matrix)。
pytest fixture
中的 pytest.mark.parametrize
裝飾器能夠實現用例參數化。 這裏直接傳進 5 組參數。
import pytest
from slmethod.perceptron import Perceptron
import numpy as np
#
@pytest.mark.parametrize("dual, l_rate", [(True, None), (False, None),
(False, 1), (False, 0.1),
(False, 0.01)])
def test_perceptron(dual, l_rate):
train_X = np.array([ ... ])
train_y = np.array([ ... ])
clf = Perceptron(dual=dual, l_rate=l_rate)
clf.fit(train_X, train_y)
test_X = np.array([[10, 3], [-29, 5]])
test_y = np.array([1, -1])
predict_y = clf.predict(test_X)
assert np.array_equal(test_y, predict_y)
複製代碼
具體代碼可查看 GitHub: github.com/iOSDevLog/s…
def show2d(self, name=None):
if (self.X.shape[1] != 2):
raise ValueError("X must have 2d array.")
複製代碼
minX = np.min(self.X[:, 0])
maxX = np.max(self.X[:, 0])
x_points = np.array([minX, maxX])
複製代碼
from matplotlib import pyplot as plt
from matplotlib import animation
import numpy as np
複製代碼
fig, ax = plt.subplots()
ax.scatter(self.X[:, 0], self.X[:, 1], c=self.y, s=1, marker="o")
line, = ax.plot(x_points,
np.zeros(len(x_points)),
"r-",
linewidth=2,
label="slmethod perceptron")
複製代碼
接着,構造自定義動畫函數 update
,用來更新每一幀上各個 x 對應的 y 座標值,參數表示第 i 幀:
def update(iter):
(index, w, b) = self._wbs[iter]
# title
title = "iter: {}, index: {}".format(iter, index)
plt.title(title)
# show w and b
wb = "w0: {}, w1: {}, b: {}".format(w[0], w[1], b)
ax.set_xlabel(wb)
# update y
y_points = -(w[0] * x_points + b) / w[1]
line.set_ydata(y_points)
return line, ax
複製代碼
而後,構造開始幀函數 init
:
def init():
line.set_ydata(np.zeros(len(x_points)))
return line,
複製代碼
接下來,咱們調用FuncAnimation函數生成動畫。
參數說明:
anim = FuncAnimation(fig,
update,
init_func=init,
frames=len(self._wbs),
interval=200)
複製代碼
plt.show()
複製代碼
gif
anim.save(name, writer="imagemagick")
複製代碼
保存和顯示不能同時,不知道爲何?
具體代碼請查看:github.com/iOSDevLog/s…
pip
安裝
pip install slmethod
複製代碼
使用方法和 sklearn
很是類似,如下步驟可省略部分。
import numpy as np
from sklearn.datasets import make_blobs
from slmethod.perceptron import Perceptron
X, y = make_blobs(n_samples=500,
n_features=2,
centers=2,
cluster_std=0.2,
random_state=59)
y = np.where(y == 1, 1, -1)
# 原始感知機
origin_cls = Perceptron(dual=False)
origin_cls.fit(X, y)
origin_cls.show_anim()
# 對偶形式
dual_cls = Perceptron(dual=True)
dual_cls.fit(X, y)
dual_cls.show2d()
複製代碼