感知机(Perceptron)及python实现

感知机(Perceptron)及python实现

专栏文章汇总


文章结构如下:

1: 感知机模型

2: 感知机学习策略

  • 2.1 数据集的线性可分
  • 2.2 感知机学习策略

3: 感知机学习算法

  • 3.1 感知机学习算法的原始形式
  • 3.2 算法的收敛性
  • 3.3 感知机python实现
  • 3.4 感知机学习算法的对偶形式

4:参考文献


感知机:
输入为实例的特征向量,输出为实例的类别,取+1和-1;
感知机对应于输入空间中将实例划分为正负两类的分离超平面,属于判别模型;
导入基于误分类的损失函数,利用梯度下降法对损失函数进行极小化;
感知机学习算法具有简单而易于实现的优点,分为原始形式和对偶形式。

1: 感知机模型

定义.感知机:假设输入空间 \mathcal{X} \subseteq R^{n} ,输出空间 \mathcal{Y} = \left\{+1, -1 \right\} 。输入 x \in \mathcal{X} 表示实例的特征向量,对应于输入空间的点;输出 y \in \mathcal{Y} 表示实例的类别。由输入空间到输出空间的函数

\begin{align*} \\& f \left( x \right) = sign \left( w \cdot x + b \right) \end{align*} \\

称为感知机。其中, wb 为感知机模型参数, w \in R^{n} 叫做权值或权值向量, b \in R 叫偏置, w \cdot x 表示 wb 的内积。 sign 是符号函数,即

\begin{align*} sign \left( x \right) = \left\{ \begin{aligned} \ & +1, x \geq 0 \\ & -1, x<0 \end{aligned} \right.\end{align*} \\

感知机是一种线性分类模型,属于判别模型。感知机模型的假设空间是定义在特征空间中的所有线性分类模型或线性分类器,即函数集合 \left\{ f | f \left( x \right) = w \cdot x + b \right\}

线性方程

\begin{align*} \\& w \cdot x + b = 0 \end{align*} \\

对应于特征空间 R^{n} 中的一个超平面 S ,其中 w 是超平面的法向量, b 是超平面的截距。超平面 S 将特征空间划分为两部分,位于其中的点被分为正、负两类,超平面 S 称为分离超平面。


2: 感知机学习策略

2.1 数据集的线性可分

给定数据集

\begin{align*} \\& T = \left\{ \left( x_{1}, y_{1} \right), \left( x_{2}, y_{2} \right), \cdots, \left( x_{N}, y_{N} \right) \right\} \end{align*} \\

其中, x_{i} \in \mathcal{X} = R^{n}, y_{i} \in \mathcal{Y} = \left\{ +1, -1 \right\}, i = 1, 2, \cdots, N ,如果存在某个超平面 S

\begin{align*} \\& w \cdot x + b = 0 \end{align*} \\

能够将数据集的正实例和负实例完全正确地划分到超平面的两侧,即对所有 y_{i}=+1 的实例 x_{i} ,有 w \cdot x_{i} + b > 0 ,对所有 y_{i}=-1 的实例 x_{i} ,有 w \cdot x_{i} + b < 0 ,则称数据集 T 为线性可分数据集 linearly separable data set ;否则,称数据集 T 线性不可分。


2.2 感知机学习策略

输入空间 R^{n} 中的任一点 x_{0} 到超平面 S 的距离:

\begin{align*} \\& \dfrac{1}{\| w \|} \left| w \cdot x_{0} + b \right| \end{align*} \\

其中 \| w \|wL_{2} 范数。

对于误分类数据 \left( x_{i}, y_{i} \right) ,当 w \cdot x + b > 0 时, y_{i}=-1 ,当 w \cdot x + b < 0 时, y_{i}=+1 ,有

\begin{align*} \\& -y_{i} \left( w \cdot x_{i} + b \right) > 0 \end{align*} \\

误分类点 x_{i} 到分离超平面的距离:

\begin{align*} \\& -\dfrac{1}{\| w \|} y_{i}\left( w \cdot x_{i} + b \right) \end{align*}\\
假设超平面 S 的误分类点集合为 M ,则所有误分类点到超平面 S 的总距离:
\begin{align*} \\& -\dfrac{1}{\| w \|} \sum_{x_{i} \in M} y_{i} \left( w \cdot x_{i} + b \right) \end{align*}\\

给定训练数据集

\begin{align*} \\& T = \left\{ \left( x_{1}, y_{1} \right), \left( x_{2}, y_{2} \right), \cdots, \left( x_{N}, y_{N} \right) \right\} \end{align*} \\

其中, x_{i} \in \mathcal{X} = R^{n}, y_{i} \in \mathcal{Y} = \left\{ +1, -1 \right\}, i = 1, 2, \cdots, N 。感知机 sign \left( w \cdot x + b \right) 的损失函数定义为

\begin{align*} \\& L \left( w, b \right) = -\sum_{x_{i} \in M} y_{i} \left( w \cdot x_{i} + b \right) \end{align*} \\
其中, M 为误分类点的集合。

注:对于这里损失函数少了 \dfrac{1}{\| w \|} 可以先这么理解:我们目的是要找个超平面 w \cdot x + b = 0 ,可以增加一个特征: x_0=1 ,因此超平面可以简化为 \sum_{i=0}^nw_ix_i=0 ,用向量表示为 w \cdot x = 0 ,则所有误分类点到超平面 S 的总距离: -\dfrac{1}{\| w \|} \sum_{x_{i} \in M} y_{i} w \cdot x_{i} 。这样可以发现,分子和分母都含有 w ,当分子的 w 扩大 N 倍时,分母的 L2 范数也会扩大 N 倍。也就是说,分子和分母有固定的倍数关系。那么我们可以固定分子或者分母为1,然后求另一个即分子自己或者分母的倒数的最小化作为损失函数,这样可以简化我们的损失函数。在感知机模型中,我们采用的是保留分子,即最终感知机模型的损失函数简化为:  L \left( w, b \right) = -\sum_{x_{i} \in M} y_{i}w \cdot x_{i}


3: 感知机学习算法

3.1 感知机学习算法的原始形式

给定训练数据集

\begin{align*} \\& T = \left\{ \left( x_{1}, y_{1} \right), \left( x_{2}, y_{2} \right), \cdots, \left( x_{N}, y_{N} \right) \right\} \end{align*} \\
其中, x_{i} \in \mathcal{X} = R^{n}, y_{i} \in \mathcal{Y} = \left\{ +1, -1 \right\}, i = 1, 2, \cdots, N 。求参数 wb ,使其为以下损失函数极小化问题的解

\begin{align*} \\& \min_{w,b} L \left( w, b \right) = -\sum_{x_{i} \in M} y_{i} \left( w \cdot x_{i} + b \right) \end{align*} \\
其中, M 为误分类点的集合。

感知机学习算法是误分类驱动的,采用随机梯度下降法 stochastic gradient descent。极小化过程中不是一次使用$M$中所有误分类点的梯度下降,而是一次随机选取一个误分类点使其梯度下降。

假设误分类点集合 M 是固定的,则损失函数 L \left( w, b \right) 的梯度

\begin{align*} \\& \nabla _{w} L \left( w, b \right) = -\sum_{x_{i} \in M} y_{i} x_{i} \\ & \nabla _{b} L \left( w, b \right) = -\sum_{x_{i} \in M} y_{i} \end{align*} \\
随机选取一个误分类点 \left( x_{i}, y_{i} \right) ,对 w, b 进行更新:

\begin{align*} \\& w \leftarrow w + \eta y_{i} x_{i} \\ & b \leftarrow b + \eta y_{i} \end{align*} \\
其中, \eta \left( 0 < \eta \leq 1 \right) 是步长,称为学习率。

感知机算法(原始形式)
输入:训练数据集 T = \left\{ \left( x_{1}, y_{1} \right), \left( x_{2}, y_{2} \right), \cdots, \left( x_{N}, y_{N} \right) \right\} ,其中 x_{i} \in \mathcal{X} = R^{n}, y_{i} \in \mathcal{Y} = \left\{ +1, -1 \right\}, i = 1, 2, \cdots, N ;学习率 \eta \left( 0 < \eta \leq 1 \right)
输出: w,b ;感知机模型 f \left( x \right) = sign \left( w \cdot x + b \right)
1. 选取初值 w_{0},b_{0}
2. 在训练集中选取数据 \left( x_{i}, y_{i} \right)
3. 如果 y_{i} \left( w \cdot x_{i} + b \right) \leq 0
\begin{align*} \\& w \leftarrow w + \eta y_{i} x_{i} \\ & b \leftarrow b + \eta y_{i} \end{align*} \\
4. 转至2,直至训练集中没有误分类点。

直观解释:当一个实例点被误分类,即位于分离超平面的错误一侧,则调整 w,b 的值,使分离超平面向该误分类点的一侧移动,以减少该误分类点与超平面的距离,直至超平面越过该分类点使其被分类正确。


3.2 算法的收敛性

定理.Novikoff 设训练数据集 T = \left\{ \left( x_{1}, y_{1} \right), \left( x_{2}, y_{2} \right), \cdots, \left( x_{N}, y_{N} \right) \right\} 是线性可分的,其中, x_{i} \in \mathcal{X} = R^{n}, y_{i} \in \mathcal{Y} = \left\{ +1, -1 \right\}, i = 1, 2, \cdots, N ,则:

(1)存在满足条件 \left \| \hat{w}_{opt} \right \|=1 的超平面 \hat{w}_{opt}\cdot \hat{x}=w_{opt}\cdot x+b_{opt}=0 将训练数据集完整正确分开;并存在 \gamma >0 ,对所有 i=1,2,...,N

y_{i}(\hat{w}_{opt}\cdot \hat{x}_{i})=y_{i}(w_{opt}\cdot x_{i}+b_{opt})\geqslant \gamma \\

(2)令 R=\underset{1\leqslant i\leqslant N}{max}\left \| \hat{x}_{i} \right \| ,则感知机算法在训练集上的误分类次数 k 满足不等式:

k\leqslant (\frac{R}{\gamma })^{2}\\


3.3 感知机python实现

import pandas as pd
import numpy as np
import cv2
import time
from sklearn.cross_validation import train_test_split
from sklearn.metrics import accuracy_score


class Perceptron(object):
    def __init__(self):
        self.learning_step = 0.00001
        self.max_iteration = 5000

    def predict_(self, x):
        wx = sum([self.w[j] * x[j] for j in xrange(len(self.w))])
        return int(wx > 0)

    def train(self, features, labels):
        self.w = [0.0] * (len(features[0]) + 1)  #权重+偏置
        correct_count = 0
        time = 0

        while time < self.max_iteration:
            index = np.random.randint(0, len(labels) - 1) #随机找个样本
            x = list(features[index])
            x.append(1.0)  #偏置
            y = 2 * labels[index] - 1
            wx = sum([self.w[j] * x[j] for j in range(len(self.w))])

            if wx * y > 0:
                correct_count += 1
                if correct_count > self.max_iteration:
                    break
                continue

            #错误就更新参数
            for i in range(len(self.w)):
                self.w[i] += self.learning_step * (y * x[i])

    def predict(self,features):
        labels = []
        for feature in features:
            x = list(feature)
            x.append(1)
            labels.append(self.predict_(x))
        return labels

if __name__ == '__main__':
    print('Start read data')

    time_1 = time.time()
    raw_data = pd.read_csv('../data/train_binary.csv', header=0)
    data = raw_data.values

    imgs = data[0::, 1::]
    labels = data[::, 0]

    # 选取 2/3 数据作为训练集, 1/3 数据作为测试集
    train_features, test_features, train_labels, test_labels = train_test_split(
        imgs, labels, test_size=0.33, random_state=23323)

    time_2 = time.time()
    print('read data cost ', time_2 - time_1, ' second', '\n')

    print('Start training')
    p = Perceptron()
    p.train(train_features, train_labels)

    time_3 = time.time()
    print('training cost ', time_3 - time_2, ' second', '\n')

    print('Start predicting')
    test_predict = p.predict(test_features)
    time_4 = time.time()
    print('predicting cost ', time_4 - time_3, ' second', '\n')

    score = accuracy_score(test_labels, test_predict)
    print("The accruacy socre is ", score)


3.4 感机学习算法的对偶形式

上面的感知机模型的算法形式我们一般称为感知机模型的算法原始形式。对偶形式是对算法执行速度的优化。

通过上一节感知机模型的算法原始形式 w = w + \eta y_{i}x_{i},b=b+\eta y_{i} 可以看出,我们每次梯度的迭代都是选择的一个样本来更新 w,b 向量。最终经过若干次的迭代得到最终的结果。对于从来都没有误分类过的样本,他被选择参与 w,b 迭代的次数是0,对于被多次误分类而更新的样本 j ,它参与 w,b 迭代的次数我们设置为 n_i 。如果令 w,b 向量初始值为0向量, w,b 修改n次,则 w,b 关于 \left( x_{i}, y_{i} \right) 的增量分别是 \alpha_{i} y_{i} x_{i}\alpha_{i} y_{i} ,其中 \alpha_{i} = n_{i} \etaw,b 可表示为

\begin{align*} \\& w = \sum_{i=1}^{N} \alpha_{i} y_{i} x_{i} \\ & b = \sum_{i=1}^{N} \alpha_{i} y_{i} \end{align*} \\
其中, \alpha_{i} \geq 0, i=1,2, \cdots, N

感知机算法(对偶形式):
输入:训练数据集 T = \left\{ \left( x_{1}, y_{1} \right), \left( x_{2}, y_{2} \right), \cdots, \left( x_{N}, y_{N} \right) \right\} ,其中 x_{i} \in \mathcal{X} = R^{n}, y_{i} \in \mathcal{Y} = \left\{ +1, -1 \right\}, i = 1, 2, \cdots, N ;学习率 \eta \left( 0 < \eta \leq 1 \right)
输出: \alpha,b ;感知机模型 f \left( x \right) = sign \left( \sum_{j=1}^{N} \alpha_{j} y_{j} x_{j} \cdot x + b \right) ,其中 \alpha = \left( \alpha_{1}, \alpha_{2}, \cdots, \alpha_{N} \right)^{T}
1. \alpha \leftarrow 0, b \leftarrow 0
2. 在训练集中选取数据 \left( x_{i}, y_{i} \right)
3. 如果 y_{i} \left( \sum_{j=1}^{N} \alpha_{j} y_{j} x_{j} \cdot x_{i} + b \right) \leq 0
\begin{align*} \\& \alpha_{i} \leftarrow \alpha_{i} + \eta \\ & b \leftarrow b + \eta y_{i} \end{align*} \\

4. 转至2,直至训练集中没有误分类点。

注意:第三个判断误分类的形式里面是计算两个样本 x_ix_j 的内积,而且这个内积计算的结果在下面的迭代次数中可以重用。如果我们事先用矩阵运算计算出所有的样本之间的内积,即用 Gram 矩阵存储实例间内积 G = \left[ x_{i} \cdot x_{j} \right]_{N \times N} ,那么在算法运行时,仅仅一次的矩阵内积运算比多次的循环计算省时。计算量最大的判断误分类这儿就省下了很多的时间,这也是对偶形式的感知机模型比原始形式优的原因。


4:参考文献

李航. 统计学习方法[M]. 北京:清华大学出版社,2012

感知机原理小结 - 刘建平Pinard - 博客园

编辑于 2018-06-14

文章被以下专栏收录