第2章 感知机

目录

  • 第2章 感知机
    • 前言
    • 1 理论部分
      • 1.1 感知机模型
      • 1.2 感知机学习策略
      • 1.3 感知机算法
        • 1.3.1 原始形式算法
        • 1.3.2 对偶形式算法
    • 2 代码部分
      • 2.1 Perceptron
      • 2.2 scikit-learn实例

前言

  • 本文主要参考资料:

    • 《统计学习方法》第二版 李航
    • 《统计学习方法》第二版PPT 袁春

【导读】本章的结构按着统计学习方法三要素——模型、策略、算法来进行安排。感知机是我们正式学习的第一个模型,算是一个比较简单基础的的二分类的线性分类模型,但是感知机是神经网络和支持向量机的基础,我们一定要把握好。主要内容依然是用思维导图展示,强调知识体系结构。此外,由于我们后面学习的模型很多,我们要重点弄清每个模型的具体应用场景和优缺点。

1 理论部分

链接:https://pan.baidu.com/s/1XhWfHDiedlSDWUucgfsPHA
提取码:z0rr

1.1 感知机模型

应用 简单的线性分类

优点

  • 自然是简单易于实现咯

缺点

  • 无法很好处理线性不可分数据
  • 最终迭代代数受结果超平面及训练集的数据影响大
  • 损失函数的目标只是减小所有误分类点与超平面,最终很有可能会导致部分样本点距离超平面很近

定义 感知机,就是二类分类的线性分类模型,其输入为样本的特征向量,输出为样本的类别,取+1和-1二值,即通过某样本的特征,就可以准确判断该样本属于哪一类。顾名思义,感知机能够解决的问题首先要求特征空间是线性可分的,再者是二类分类,即将样本分为{+1, -1}两类。从比较学术的层面来说,由输入空间到输出空间的函数:

f(x)=sign⁡(w⋅x+b)f(x)=\operatorname{sign}(w \cdot x+b)f(x)=sign(w⋅x+b)

称为感知机,w和b为感知机参数,w为权值(weight),b为偏置(bias)。sign为符号函数:

sign⁡(x)={+1,x⩾0−1,x<0\operatorname{sign}(x)=\left\{\begin{array}{ll}+1, & x \geqslant 0 \\ -1, & x<0\end{array}\right. sign(x)={+1,−1,​x⩾0x<0​

感知机模型的假设空间是定义在特征空间中的线性分类模型,即函数集合:{f∣f(x)=w⋅x+b}\{f \mid f(x)=w \cdot x+b\}{f∣f(x)=w⋅x+b}

1.2 感知机学习策略

感知机学习的策略是极小化损失函数:

min⁡w,bL(w,b)=−∑xi∈Myi(w⋅xi+b)\min _{w, b} L(w, b)=-\sum_{x_{i} \in M} y_{i}\left(w \cdot x_{i}+b\right) w,bmin​L(w,b)=−xi​∈M∑​yi​(w⋅xi​+b)

损失函数对应于误分类点到分离超平面的总距离。

【思考】样本到超平面距离为什么是1∥w∥∣w⋅x0+b∣\frac{1}{\|w\|}\left|w \cdot x_{0}+b\right|∥w∥1​∣w⋅x0​+b∣?

【回答】我们知道点(x0,y0)\left(x_{0}, y_{0}\right)(x0​,y0​)到直线Ax+By+C=0的距离是:

d=Ax0+By0+CA2+B2d=\frac{A x_{0}+B y_{0}+C}{\sqrt{A^{2}+B^{2}}}d=A2+B2​Ax0​+By0​+C​

将公式推广,把样本点带入公式即可求得。

【思考】为什么可以不考虑1∥w∥\frac{1}{\|w\|}∥w∥1​?

【回答】

  1. 感知机处理线性可分数据集,二分类,yi∈{−1,+1}y_i∈\{−1,+1\}yi​∈{−1,+1},所以涉及到的乘以 yiy_iyi​ 的操作实际贡献的是符号;

  2. 损失函数 L(w,b)=−∑xi∈Myi(w⋅xi+b)L(w,b)=-\sum_{x_i\in M}y_i(w\cdot x_i+b)L(w,b)=−∑xi​∈M​yi​(w⋅xi​+b),其中 MMM 是错分的点集合,线性可分的数据集肯定能找到超平面 SSS, 所以这个损失函数最值是0。

  3. 如果正确分类, yi(w⋅xi+b)=∣w⋅xi+b∣y_i(w\cdot x_i+b)=|w\cdot x_i+b|yi​(w⋅xi​+b)=∣w⋅xi​+b∣ ,错误分类的话,为了保证正数就加个负号,这就是损失函数里面那个负号,这个就是函数间隔;

  4. 1∣∣w∣∣\frac{1}{||w||}∣∣w∣∣1​ 用来归一化超平面法向量,得到几何间隔,也就是点到超平面的距离, 函数间隔和几何间隔的差异在于同一个超平面 (w,b)(w,b)(w,b) 参数等比例放大成 (kw,kb)(kw,kb)(kw,kb) 之后,虽然表示的同一个超平面,但是点到超平面的函数间隔也放大了,但是几何间隔是不变的;

  5. 具体算法实现的时候, www要初始化,然后每次迭代针对错分点进行调整,既然要初始化,那如果初始化个 ∣∣w∣∣=1||w||=1∣∣w∣∣=1 的情况也就不用纠结了,和不考虑 1∣∣w∣∣\frac{1}{||w||}∣∣w∣∣1​ 是一样的了;

  6. 针对错分点是这么调整的

    w←w+ηyixib←b+ηyi\begin{aligned} w&\leftarrow w+\eta y_ix_i\\ b&\leftarrow b+\eta y_i \end{aligned}wb​←w+ηyi​xi​←b+ηyi​​

    前面说了 yiy_iyi​ 就是个符号,那么感知机就可以解释为针对误分类点,通过调整 w,bw,bw,b 使得超平面向该误分类点一侧移动,迭代这个过程最后全分类正确;

  7. 感知机的解不唯一,和初值有关系,和误分类点调整顺序也有关系;

  8. 这么调整就能找到感知机的解?能,Novikoff还证明了,通过有限次搜索能找到将训练数据完全正确分开的分离超平面。

所以,

如果只考虑损失函数的最值,那没啥影响,线性可分数据集,最后这个损失就是0; 那个分母用来归一化法向量,不归一化也一样用,感知机的解不唯一;说正数不影响的应该考虑的是不影响超平面调整方向吧

1.3 感知机算法

感知机学习算法是对上述损失函数进行极小化,求得w和b。但是用普通的基于所有样本的梯度和的均值的批量梯度下降法(BGD)是行不通的,原因在于我们的损失函数里面有限定,只有误分类的M集合里面的样本才能参与损失函数的优化。所以我们不能用最普通的批量梯度下降,只能采用随机梯度下降(SGD)
随机抽取一个误分类点使其梯度下降。

w=w+ηyixiw = w + \eta y_{i}x_{i}w=w+ηyi​xi​

b=b+ηyib = b + \eta y_{i}b=b+ηyi​

当实例点被误分类,即位于分离超平面的错误侧,则调整www, bbb的值,使分离超平面向该无分类点的一侧移动,直至误分类点被正确分类

目标函数如下:

L(w,b)=arg⁡min⁡w,b(−∑xi∈Myi(w⋅xi+b))L(w, b)=\arg \min _{w, b}\left(-\sum_{x_{i} \in M} y_{i}\left(w \cdot x_{i}+b\right)\right)L(w,b)=argw,bmin​(−xi​∈M∑​yi​(w⋅xi​+b))

【墙裂推荐阅读】为什么说随机最速下降法 (SGD) 是一个很好的方法?

1.3.1 原始形式算法

输入:训练数据集T={(x1,y1),(x2,y2),⋯,(xN,yN)}T=\left\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \cdots,\left(x_{N}, y_{N}\right)\right\}T={(x1​,y1​),(x2​,y2​),⋯,(xN​,yN​)},yi∈{−1,+1}y_i∈\{−1,+1\}yi​∈{−1,+1},学习率η(0<η<1)

输出:w,bw, bw,b;感知机模型f(x)=sign⁡(w⋅x+b)f(x)=\operatorname{sign}(w \cdot x+b)f(x)=sign(w⋅x+b)

  1. 赋初值 w0,b0w_{0}, b_{0}w0​,b0​
  2. 选取数据点(xi,yi)\left(x_{i}, y_{i}\right)(xi​,yi​)
  3. 判断该数据点是否为当前模型的误分类点,即判断若yi(w⋅xi+b)⩽0y_{i}\left(w \cdot x_{i}+b\right) \leqslant 0yi​(w⋅xi​+b)⩽0则更新

w=w+ηyixiw=w+\eta y_{i} x_{i}w=w+ηyi​xi​
b=b+ηyib=b+\eta y_{i}b=b+ηyi​

  1. 转到2,直到训练集中没有误分类点

1.3.2 对偶形式算法

由于w,bw, bw,b的梯度更新公式:

w=w+ηyixiw=w+\eta y_{i} x_{i}w=w+ηyi​xi​
b=b+ηyib=b+\eta y_{i}b=b+ηyi​

我们的w,bw, bw,b经过了n次修改后的,参数可以变化为下公式,其中αi=niη\alpha_{i}=n_{i} \etaαi​=ni​η:

w=∑xi∈Mηyixi=∑i=1nαiyixiw=\sum_{x_{i} \in M} \eta y_{i} x_{i}=\sum_{i=1}^{n} \alpha_{i} y_{i} x_{i}w=xi​∈M∑​ηyi​xi​=i=1∑n​αi​yi​xi​
b=∑xi∈Mηyi=∑i=1nαiyib=\sum_{x_{i} \in M} \eta y_{i}=\sum_{i=1}^{n} \alpha_{i} y_{i}b=xi​∈M∑​ηyi​=i=1∑n​αi​yi​
这样我们就得出了感知机的对偶算法。

输入:训练数据集T={(x1,y1),(x2,y2),⋯,(xN,yN)}T=\left\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \cdots,\left(x_{N}, y_{N}\right)\right\}T={(x1​,y1​),(x2​,y2​),⋯,(xN​,yN​)},yi∈{−1,+1}y_i∈\{−1,+1\}yi​∈{−1,+1},学习率η(0<η<1)

输出:α,b\alpha, bα,b;感知机模型f(x)=sign⁡(∑j=1nαjyjxj⋅x+b)f(x)=\operatorname{sign}\left(\sum_{j=1}^{n} \alpha_{j} y_{j} x_{j} \cdot x+b\right)f(x)=sign(∑j=1n​αj​yj​xj​⋅x+b)

其中α=(α1,α2,…,αn)T\alpha=\left(\alpha_{1}, \alpha_{2}, \ldots, \alpha_{n}\right)^{T}α=(α1​,α2​,…,αn​)T

赋初值 α0,b0\alpha_{0}, b_{0}α0​,b0​
选取数据点(xi,yi)\left(x_{i}, y_{i}\right)(xi​,yi​)
判断该数据点是否为当前模型的误分类点,即判断若yi(∑j=1Nαjyjxj⋅xi+b)⩽0y_{i}\left(\sum_{j=1}^{N} \alpha_{j} y_{j} x_{j} \cdot x_{i}+b\right) \leqslant 0yi​(∑j=1N​αj​yj​xj​⋅xi​+b)⩽0则更新

αi=αi+η\alpha_{i}=\alpha_{i}+\etaαi​=αi​+η
b=b+ηyib=b+\eta y_{i}b=b+ηyi​

转到2,直到训练集中没有误分类点
为了减少计算量,我们可以预先计算式中的内积,得到Gram矩阵

G=[xi,xj]N×NG=\left[x_{i}, x_{j}\right] N \times NG=[xi​,xj​]N×N

【思考】原始形式和对偶形式的如何选择?

【回答】

  • 在向量维数(特征数)过高时,计算内积非常耗时,应选择对偶形式算法加速。
  • 在向量个数(样本数)过多时,每次计算累计和就没有必要,应选择原始算法

【墙裂推荐阅读】如何理解感知机学习算法的对偶形式?

2 代码部分

import pandas as pd
import numpy as np
from sklearn.datasets import load_iris #sklearn库里面自带的鸢尾花数据集
import matplotlib.pyplot as plt
%matplotlib inline
# load data
iris = load_iris() #iris包括2个数组data和target,里面还有不少属性,感兴趣可以输出看看,加深对代码的直观理解
df = pd.DataFrame(iris.data, columns=iris.feature_names) #将数组data转成DataFrame
df['label'] = iris.target
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label'
] #对列名重命名
df.label.value_counts()
2    50
1    50
0    50
Name: label, dtype: int64

iris数据集前50个标签为0,51-100标签为1,101-150标签为2

#考虑两个属性sepal length和sepal width的影响
plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label='0')
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
<matplotlib.legend.Legend at 0x249ec7ab388>

data = np.array(df.iloc[:100, [0, 1, -1]]) #将df中前100行的第一第二列和最后一列(label)抽出来转成数组
X, y = data[:,:-1], data[:,-1] #元组赋值,前两列赋值给X,最后一列赋值给y
y = np.array([1 if i == 1 else -1 for i in y]) #把label为0的换成1
y
array([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1,1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1])

2.1 Perceptron

# 数据线性可分,二分类数据
# 此处为一元一次线性方程
class Model: #定义一个类Modeldef __init__(self): #初始化w、b、l_rate(学习率),理论部分用η表示self.w = np.ones(len(data[0]) - 1, dtype=np.float32)self.b = 0self.l_rate = 0.1# self.data = datadef sign(self, x, w, b):y = np.dot(x, w) + breturn y# 随机梯度下降法def fit(self, X_train, y_train):is_wrong = Falsewhile not is_wrong: #找茬,看有没有误分类wrong_count = 0for 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.l_rate * np.dot(y, X)self.b = self.b + self.l_rate * ywrong_count += 1if wrong_count == 0:is_wrong = Truereturn 'Perceptron Model!'def score(self):pass
perceptron = Model()
perceptron.fit(X, y)
'Perceptron Model!'
x_points = np.linspace(4, 7, 10)
y_ = -(perceptron.w[0] * x_points + perceptron.b) / perceptron.w[1]
plt.plot(x_points, y_) #画蓝色那条直线plt.plot(data[:50, 0], data[:50, 1], 'bo', color='blue', label='0')
plt.plot(data[50:100, 0], data[50:100, 1], 'bo', color='orange', label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
<matplotlib.legend.Legend at 0x249ed1d97c8>

2.2 scikit-learn实例

【参考】https://scikit-learn.org/0.19/modules/generated/sklearn.linear_model.Perceptron.html

import sklearn
from sklearn.linear_model import Perceptron
sklearn.__version__
'0.21.3'
clf = Perceptron(fit_intercept=True, max_iter=1000, shuffle=True)
clf.fit(X, y)
Perceptron(alpha=0.0001, class_weight=None, early_stopping=False, eta0=1.0,fit_intercept=True, max_iter=1000, n_iter_no_change=5, n_jobs=None,penalty=None, random_state=0, shuffle=True, tol=0.001,validation_fraction=0.1, verbose=0, warm_start=False)
# Weights w assigned to the features. w
print(clf.coef_)
[[ 23.2 -38.7]]
# 截距 Constants b in decision function.
print(clf.intercept_)
[-5.]
# 画布大小
plt.figure(figsize=(10,10))# 中文标题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
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_ponits = np.arange(4, 8)
y_ = -(clf.coef_[0][0]*x_ponits + clf.intercept_)/clf.coef_[0][1]
plt.plot(x_ponits, y_)# 其他部分
plt.legend()  # 显示图例
plt.grid(False)  # 不显示网格
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
<matplotlib.legend.Legend at 0x249ecd14d08>

注意 !

在上图中,有一个位于左下角的蓝点没有被正确分类,这是因为 SKlearn 的 Perceptron 实例中有一个tol参数。

tol 参数规定了如果本次迭代的损失和上次迭代的损失之差小于一个特定值时,停止迭代。所以我们需要设置 tol=None 使之可以继续迭代:

clf = Perceptron(fit_intercept=True, max_iter=1000,tol=None,shuffle=True)
clf.fit(X, y)# 画布大小
plt.figure(figsize=(10,10))# 中文标题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
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_ponits = np.arange(4, 8)
y_ = -(clf.coef_[0][0]*x_ponits + clf.intercept_)/clf.coef_[0][1]
plt.plot(x_ponits, y_)# 其他部分
plt.legend()  # 显示图例
plt.grid(False)  # 不显示网格
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
<matplotlib.legend.Legend at 0x249ecda4a88>

现在可以看到,所有的两种鸢尾花都被正确分类了。


参考代码:https://github.com/wzyonggege/statistical-learning-method

《统计学习方法第二版》学习笔记2——感知机相关推荐

  1. DirectX 9.0c游戏开发手记之“龙书”第二版学习笔记之1: 开场白

    在开场白之前的说明: 这是"DirectX 9.0c游戏开发手记"的第一部分,叫做"'龙书'第二版学习笔记",讲的是我做"龙书"第二版(原名 ...

  2. 《统计学习方法第二版》学习笔记1——概论

    第1章 统计学习及监督学习概率论 目录 第1章 统计学习及监督学习概率论 前言 1 理论部分 1.1 统计学习 1.2 统计学习的分类 1.3 统计学习方法三要素 1.4 模型评估与模型选择 1.5 ...

  3. 统计学习方法第二版知识点合集 - 自用笔记

    第一篇 监督学习 第一章 统计学习及监督学习概论 1.1 统计学习 统计学习是关于计算机基于数据构建概率统计模型并运用模型对数据进行预测与分析的一门学科,也称统计机器学习,也就是我们俗称的机器学习. ...

  4. 《统计学习方法》--李航 学习笔记

    <统计学习方法>--李航 第一章 统计学习方法三要素: 模型 模型评估与模型选择 正则化与交叉验证 正则化 交叉验证 泛化能力 生成模型与判别模型 生成方法 判别方法 分类问题.标注问题. ...

  5. 李航 统计学习方法 第二版 课后习题答案 机器学习

    李航<统计学习方法>课后习题答案(第2版)

  6. 《统计学习方法(第二版)》学习笔记 第五章 决策树

    决策树 - ID3.C4.5.CART 对于决策树的学习,无论是ID3.C4.5和CART分类树还是CART回归树,其实都需要重点关注几个点: 数据集确定性的评价指标(信息增益.信息增益比.基尼指数. ...

  7. 利用python进行数据分析第二版学习笔记

    行话: 数据规整(Munge/Munging/Wrangling) 指的是将非结构化和(或)散乱数据处理为结构化或整洁形式的整个过程.这几个词已经悄悄成为当今数据黑客们的行话了.Munge这个词跟Lu ...

  8. 统计学习方法 第一章 学习笔记

    假设空间:P5,介绍:监督学习的目的在于找到最好的模型,这个模型即是由输入空间到输出空间的映射的集合,这个集合就是假设空间(hypothesis space).假设空间的确定意味着学习范围的确定. 监 ...

  9. Docker技术入门与实战 第二版-学习笔记-3-Dockerfile 指令详解

    前面已经讲解了FROM.RUN指令,还提及了COPY.ADD,接下来学习其他的指令 5.Dockerfile 指令详解 1> COPY 复制文件 格式: COPY  <源路径> .. ...

最新文章

  1. 小白都能看懂最小生成树prime算法
  2. 第十五周程序阅读-范型程序设计(1)
  3. 关于installshield安装界面上installshield字样的删除问题
  4. 【学习笔记】智能制造问与答
  5. Linux 小知识翻译 - 「端口和端口号」
  6. 微信能远程控制电脑吗_神器分享:用微信就能远程控制电脑,这款神器有些厉害...
  7. ZZULIOJ 1098: 复合函数求值(函数专题)
  8. 饿了么口碑实现超30亿美元融资,引领本地生活数字化升级...
  9. 问村民一个什么问题就能决定走哪条路?
  10. LeetCode 49. Group Anagrams
  11. 搞清字符集和字符编码
  12. java poi doc转docx_POI动态插入数据到Word文档
  13. Delphi调用C#类库.doc
  14. 能否用计算机发短信,电脑发手机短信要怎么操作 用电脑发短信是免费的吗
  15. sublime text3设置快捷键在浏览器中打开
  16. android模拟器游戏大全,安卓模拟器游戏大全_小鸡模拟器
  17. 下载数据库名前加“#”方法
  18. 高速公路二维码定位报警系统
  19. 为什么要做高新?高新技术企业和科技型企业的区别?
  20. 李一男:别了港湾 被逼上绝路的对手

热门文章

  1. 10款经典的web前端特效的预览、源码及效果图
  2. Linux 内核调试 二:ubuntu20.04安装qemu
  3. 讲解虚拟服务器的书_“红平果”虚拟仿真实训平台在医学教育中的实践与应用...
  4. 苹果CEO库克怒怼媒体:Ive离职与我无关,华尔街不懂苹果
  5. docker、docker-compose 下安装kibana
  6. i18n实现SpringBoot后端多语言化(前后端分离)
  7. 数据结构:顺序栈基本操作(入栈和出栈)C语言详解(转载)by解学武
  8. poj 1149 PIG
  9. xpath获取指定的非连续索引的子元素的文本数据
  10. 【Python数据分析与处理 实训03】 --- 酒类消费信息分析(数据分组聚合 group().agg()应用)