假设输入空间(特征空间)是 ,输出空间是
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()
复制代码