【李航统计学】Chap2:感知机及代码实现

统计学笔记 专栏收录该内容
3 篇文章 0 订阅

1. 感知机模型

在这里插入图片描述

  • 感知机 是二类分类的线性分类模型,其输入为实例的特征向量,输出为实例的类别,取+1和-1二值。感知机对应于输入空间(特征空间)中将实例划分为正负两类的分离超平面,属于判别模型
  • 感知机模型的假设空间是定义在特征空间中的所有线性分类模型(linear classification medel)或线性分类器(linear classifier),即函数集合 { f ∣ f ( x ) = w ⋅ x + b } \{f|f(x) = w·x+b\} {ff(x)=wx+b}

在这里插入图片描述

  • 分离超平面(separating hyperplane):线性方程 w ⋅ x + b = 0 w·x+b=0 wx+b=0对应于特征空间 R n R^n Rn中的一个超平面,其中 w w w是超平面的法向量, b b b是超平面的截距,这个特征空间因此被划分为两个部分,两部分的点分别分为正负两类,则超平面S则被称为分离超平面。

2. 感知机学习策略

  • 线性可分数据集(linearly separable dataset): 即存在某个超平面能够将数据集二点正实例点和复实例点完全正确的划分到超平面的两侧,即对所有 y i = + 1 y_i=+1 yi=+1的实例 i i i,有 w ⋅ x i + b > 0 w·x_i+b>0 wxi+b>0,对于所有 y i = − 1 y_i=-1 yi=1的实例 i i i,有 w ⋅ x i + b < 0 w·x_i+b<0 wxi+b<0

==> 确定学习策略,即定义(经验)损失函数并将损失函数极小化

2.1 损失函数

损失函数的一个自然选择是误分类点的综述,但这样的损失函数不是参数 w , b w,b w,b的连续可导函数不易优化。故感知机选择误分类点到超平面S的总距离,作为其损失函数。
输入空间 R n R^n Rn中任一点 x 0 x_0 x0到超平面 S S S的距离( ∣ ∣ w ∣ ∣ ||w|| w w w w L 2 L_2 L2范数,2.2中补充):
1 ∣ ∣ w ∣ ∣ ∣ w ⋅ x 0 + b ∣ \frac{1}{||w||}|w·x_0+b| w1wx0+b
w ⋅ x i + b > 0 w·x_i+b>0 wxi+b>0时, y i = + 1 y_i=+1 yi=+1 w ⋅ x i + b < 0 w·x_i+b<0 wxi+b<0时, y i = − 1 y_i=-1 yi=1的实例 i i i,所以呢~距离应该表示为: − 1 ∣ ∣ w ∣ ∣ y i ∣ w ⋅ x 0 + b ∣ -\frac{1}{||w||}y_i|w·x_0+b| w1yiwx0+b
假设所有误分类点集合为 M M M,那么误分类点到超平面 S S S的总距离为:
− 1 ∣ ∣ w ∣ ∣ ∑ x i ∈ M y i ( w ⋅ x i + b ) -\frac{1}{||w||}\sum\limits_{x_i\in M}y_i(w·x_i+b) w1xiMyi(wxi+b)
不考虑 1 ∣ ∣ w ∣ ∣ \frac{1}{||w||} w1,即为感知机 s i g n ( w ⋅ x + b ) sign(w·x+b) sign(wx+b)的损失函数: L ( w , b ) = − ∑ x i ∈ M y i ( w ⋅ x i + b ) L(w,b)=-\sum\limits_{x_i\in M}y_i(w·x_i+b) L(w,b)=xiMyi(wxi+b)

寻找上式中最小的模型参数 w , b w,b w,b,即感知机模型

2.2 范数

范数是具有“长度”概念的函数。在向量空间内,为所有的向量的赋予非零的增长度或者大小。不同的范数,所求的向量的长度或者大小是不同的。

严谨一点的概念如下图(矩阵论 P 109 P_{109} P109
在这里插入图片描述

在这里插入图片描述

  • L1范数是指向量中各个元素绝对值之和
  • L2范数是指向量各元素的平方和然后求平方根
  • 特别的
    • L0范数:指向量中非零元素的个数
    • 无穷范数:指向量中所有元素的最大绝对值。

其在监督学习中的应用区别可具体参考这篇文章,在此就不展开了。

3. 感知机学习算法

利用随机梯度下降法(stochastic gradient descent)求解损失函数极小化问题

3.1 原始形式

3.1.1 算法实现

  • 首先任意选取一个超平面 w 0 , b 0 w_0,b_0 w0,b0,SGD不断极小化下式目标函数。极小化过程是每次随机选取一个误分类点使其梯度下降,而不是一次性完成M中所有点。

L ( w , b ) = − ∑ x i ∈ M y i ( w ⋅ x i + b ) L(w,b)=-\sum\limits_{x_i\in M}y_i(w·x_i+b) L(w,b)=xiMyi(wxi+b)

  • 损失函数 L ( w , b ) L(w,b) L(w,b)的梯度:
    在这里插入图片描述

  • 随机选取一个误分类点 ( x i , y i ) (x_i,y_i) (xi,yi) w , b w,b w,b进行更新( η ( 0 < η ⩽ 1 ) \eta(0<\eta\leqslant1) η(0<η1)为学习率):
    在这里插入图片描述

  • 总结
    在这里插入图片描述

  • 直观理解这个过程:
    当一个实例点被错误分类时,即位于超平面的错误一侧,通过调整 w , b w,b w,b的值使得超平面向误分类点的一侧移动,以减少该误分类点和超平面之间的距离,直至该超平面超过该误分类点使其被正确分类。

3.1.2 举个栗子

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 然鹅,误分类点的选取顺序是我们人为选择的,不同的选择方式可能得到不同的部分超平面。可见,感知机学习算法由于采用不同的初值或选取不同的误分类点,解可以不同

3.1.3 算法的收敛性

结论:
对于线性可分数据集感知机学习算法原始形式是收敛的,即经过有限次迭代可以得到一个将训练数据集完全正确划分的分离超平面以及感知机模型。

证明:
pass(李航统计学习方法 第二版 P 41 P_{41} P41

3.2 对偶形式

3.2.1算法实现

  • 对偶形式的基本思路:将 w , b w,b w,b表示为实例 x i x_i xi和它的标记 y i y_i yi的线性组合的形式,通过求解其系数而求得 w , b w,b w,b

  • 随机选取一个误分类点 ( x i , y i ) (x_i,y_i) (xi,yi) w , b w,b w,b进行更新( η ( 0 < η ⩽ 1 ) \eta(0<\eta\leqslant1) η(0<η1)为学习率):
    在这里插入图片描述

  • 逐步修改 w , b w,b w,b,容易得到最终学习到的 w , b w,b w,b可以分别表示为:
    在这里插入图片描述

  • α i = n i η \alpha_i=n_i\eta αi=niη n i n_i ni表示修改次数, α i ≥ 0 , i = 1 , 2 , … , N \alpha_i \geq0,i =1,2,…,N αi0,i=1,2,,N,当 η = 1 \eta=1 η=1时,表示第i个实例点由于误分类而进行更新的次数。更新次数越多意味着他距离分类超平面越接近,也就越难分类,换言之,这样的实例对学习结果影响最大。

  • 总结
    在这里插入图片描述

  • 参数更新时对 w w w没有做累加是因为采用的随机梯度下降的方式。

3.2.2 对偶形式的理解

  • 结论写在前面:
    对偶形式是为了降低运算量,且在特征空间维度很高时才起到作用
    对偶形式的感知机,把每轮迭代的时间复杂度的数据规模从特征空间维度 n n n 转移到了训练集大小 N N N上,对于维度非常高的空间能提升性能。

  • 具体分析一下:
    特征空间为 R n , n R^n,n Rnn很大,共有 N N N个训练数据,且 N < < n N<<n N<<n

    • 原始形式:每一轮迭代中对输入实例进行误判点判断,即对于 x i , y i x_i,y_i xi,yi,是否有 y i ( w x i + b ) ≤ 0 y_i(wx_i+b)\leq0 yi(wxi+b)0. 时间复杂度 O ( n ) O(n) O(n)主要集中在求输入实例 x i x_i xi和权值 w w w的内积,由于特征空间维度很高,所以计算很慢

    • 对偶形式:对于输入实例 x i , y i x_i,y_i xi,yi的判断变为 y i ( ∑ j = 1 N α j y j x j x i + b ) ≤ 0 y_{i}\left(\sum_{j=1}^{N} \alpha_{j} y_{j} x_{j} x_{i}+b\right) \leq 0 yi(j=1Nαjyjxjxi+b)0。在这里我们可以预先计算出输入实例两两之间的内积,得到Gram矩阵,即 G = [ x j x i ] N ∗ N G=[x_jx_i]_{N*N} G=[xjxi]NN.在做误差点判断时直接在Gram矩阵中查表得到 x j x i x_jx_i xjxi,时间复杂度变为 O ( N ) O(N) O(N)

  • 总结一下下

    • 简单来说就是原始形式中,感知机所有参数一共需要更新n次,每次参数改变,所有的矩阵都要重新计算。
    • 对偶形式就是将这n次分摊到i个样本中去,最终的参数可以展开使用每个样本点进行表示,即在判断误分类时都可以展开成样本之间的点乘形式,这样就可以利用提前计算好的Gram矩阵来大大降低运算量

3.2.3 举个栗子

好乱凑合看
在这里插入图片描述
在这里插入图片描述

4. 总结

  • 感知机是根据输入实例的特征向量 x x x对其进行二类分类的线性分类模型: f ( x ) = s i g n ( w ⋅ x + b ) f(x)=sign(w·x+b) f(x)=sign(wx+b)
    感知机模型对应于输入空间(特征空间)中的分离超平面 w ⋅ x + b = 0 w·x+b=0 wx+b=0

  • 感知机模型的学习策略为极小化损失函数,损失函数定义为误分类点到分离超平面的总距离:
    min ⁡ w , b L ( w , b ) = − ∑ x i ∈ M y i ( w ⋅ x i + b ) \min _{w, b} L(w, b)=-\sum_{x_{i} \in M} y_{i}\left(w \cdot x_{i}+b\right) w,bminL(w,b)=xiMyi(wxi+b)

  • 感知机学习算法基于随机梯度下降对损失函数进行优化,分为原始形式和对偶形式,算法简单易实现。

  • 当训练集数据线性可分时,感知机学习算法是收敛的。

  • 当训练数据集线性可分时,感知机学习算法存在无穷多个解,其解由于其参数不同的初值和不同的迭代顺序而有所不同。

5. 代码实现

5.1 数据准备

利用iris数据集中两个分类的数据和[sepal length,sepal width]作为特征

import pandas as pd
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt

iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)

df['label'] = iris.target
print(df)
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
print(df)
print(df.label.value_counts())

plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], labels='0') #数据集每50行分别对应一个标签:0,1,2
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1')
plt.scatter(df[100:]['sepal length'], df[50:100]['sepal width'], label='2')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()  # 给图片加上图例
plt.show()

在这里插入图片描述

import numpy as np
data = np.array(df.iloc[:100, [0, 1, -1]])  # data取前100行数据即0,1两个标签,选取两个属性列和最后一列label列
X = data[:, :-1]
y = data[:, -1]
y = np.array([1 if i == 1 else -1 for i in y]) #把标签0,1转换为-1,1,进行分类时使用

在这里插入图片描述

5.2 感知机实现

class Model:
    def __init__(self):
        self.w = np.ones(len(data[0])-1, dtype=np.float32) #w初始化为1,个数为输入特征维度-1 (label那一维)
        self.b = 0
        self.eta = 0.1  # 学习率

    def sign(self, x, w, b):
        y = np.dot(x, w) + b
        return y

    # 随机梯度下降算法
    def fit(self, X_train, y_train):
        classify_wrong = True
        while classify_wrong: #有误分类点
            wrong_count = 0
            for d in range(len(X_train)):
                X = X_train[d]
                y = y_train[d]
                if y * self.sign(X, self.w, self.b) <= 0:  # 当误分类时
                    self.w = self.w + self.eta * np.dot(y, X)
                    self.b = self.b + self.eta * y
                    wrong_count += 1
            if wrong_count == 0:
                classify_wrong = False  # 当没有误分类点时,
        return "原始形式的感知机模型写好啦!~~"


perceptron = Model()
print(perceptron.fit(data_x, data_y))


# 可视化一下
x_points = np.linspace(4, 7, 10)
y_points = - (perceptron.w[0] * x_points + perceptron.b) / perceptron.w[1]
plt.plot(x_points, y_points)
plt.show()
  • y_points = - (perceptron.w[0] * x_points + perceptron.b) / perceptron.w[1] 超平面绘制
    其中w的两维分别对应两个特征,在散点图中分别对应x轴和y轴,所以呢这条线应该:
    perceptron.w[0] * x_points + perceptron.w[1] * y_points + perceptron.b = 0
    y_points 即等于上式~

在这里插入图片描述

5.3 scikit-learn实例

import sklearn
from sklearn.linear_model import Perceptron

clf = Perceptron(fit_intercept=True, max_iter=1000, shuffle=True)
"""
    fit_intercept : bool, default=True
        Whether the intercept should be estimated or not. If False, the
        data is assumed to be already centered.

    max_iter : int, default=1000
        The maximum number of passes over the training data (aka epochs).
        It only impacts the behavior in the ``fit`` method, and not the
        :meth:`partial_fit` method.
"""
clf.fit(data_x,data_y)

# 特征的权重
print(clf.coef_)  # [[ 23.2 -38.7]]
# 截距
print(clf.intercept_)  # [-5.]

# 可视化一下
plt.figure(figsize=(10, 10))
plt.title("鸢尾花线性数据示例")

plt.scatter(data[:50, 0], data[:50, 1], c='b', label='Iris-setosa')
plt.scatter(data[50:100, 0], data[50:100, 1], c='orange', label='Iris-versicolor')

# 绘制感知机的线
x_points = np.arange(4, 8)
y_points = -(clf.coef_[0][0]*x_points + clf.intercept_)/clf.coef_[0][1]
plt.plot(x_points, y_points)
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
plt.show()

在这里插入图片描述
此时可以看到结果图中有两个问题

  • 图片figure title中文没有正常显示
  • 有一个蓝色点分类错误
    • 因为 SKlearn 的 Perceptron 实例中有一个tol参数,tol 参数规定了如果本次迭代的损失和上次迭代的损失之差小于一个特定值时,停止迭代,所以我们需要设置 tol=None 使之可以继续迭代:)

对这两个问题依次进行解决:

#Q1
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.title("鸢尾花线性数据示例")
#Q2
clf = Perceptron(fit_intercept=True, max_iter=1000, tol=None, shuffle=True)

最终得到的结果如下图所示~
在这里插入图片描述


终于
写完了
真好!

  • 1
    点赞
  • 0
    评论
  • 1
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页

打赏作者

baekii

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值