机器学习

机器学习:感知机算法(PLA)

本文主要是介绍机器学习:感知机算法(PLA),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1,概述

1.1,定义

感知机(Perceptron):

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

感知机的缺点:我们在不知道数据的情况下(是否线性可分)PLA就不会停下来,这个时候我们会使用Pocket演算法来找到一根不错的直线。Pocket演算法的基本思想就是找到一根犯错误更小的直线。实务上我们一般会执行到一定的时间或者一定的步数就会停下来。结果是它是一条不错的线,但是可能不是一个最优解。

1.2,感知机模型

假设输入空间(特征空间)是 X\subseteq R^n ,输出空间是 y=\left \{ +1,-1 \right \} 。输入 x\in X 表示实例的特征向量,对应输入空间(特征空间)的点,输出 y\in Y 表示实例的类别。由输入空间到输出空间的函数:

感知机: f(x)=sign(w*x+b)

w,b 为模型参数,w\in R^n 叫做权值或权值向量,b\in R 叫作偏执。

符号函数: sign(x)=\begin{Bmatrix} +1,x\geqslant 0\\-1,x< 0 \end{Bmatrix}

1.3,几何解释

感知机可以理解为几何中的线性方程:w*x+b=0 对应于特征空间 R^n 中的一个超平面 S ,其中 w 是超平面法向量,b 是超平面的截距。这个超平面将特征空间划分为两个部分。位于两部分的点(特征向量)分别被分为正、负两类。

2,学习策略

2.1,损失函数

虑训练集T,若存在 w\in R^{n} ,b\in R 和 \varepsilon >0 使得:对所有的 y_{i} = 1 的指标i,有w^{T}x_{i} + b \geqslant \varepsilon ;而对所有的 y_{i} = -1 的指标j,w^{T}x_{i} + b \leqslant \varepsilon 则称训练集T线性可分。

假设训练集是线性可分的,感知机学习的目标是求得一个能够将训练集正实例点和负实例点完全正确分开的分离超平面。为了找出这样的超平面,即确定感知机模型参数 w,b ,需要确定一个学习策略,即定义(经验)损失函数并将损失函数极小化。

损失函数的一个自然选择是误分类点的总数。但是,这样的损失函数不是参数 w,b 的连续可导函数,不易优化。损失函数的另外一个选择是误分类点到超平面 S 的总距离,这是感知机所采用的。为此,首先定义输入空间 R^n 中任一点 x_0 到超平面 S 的距离:

\frac{|w*x_0+b|}{||w||}

其次,对于误分类的数据 (x_i,y_i) 来说:

y_i(w*x_i+b)<0

因为:y_i(w*x_i+b)>0,\begin{Bmatrix} y_i=+1,w*x+b>0\\ y_i=-1,w*x+b<0 \end{Bmatrix}

因此,误分类点到平面 S 的距离:

-\frac{y_i(w*x_i+b)}{||w||}

这样,假设超平面 S 的误分类点集合为 M ,那么所有误分类点到超平面 S 的总距离为:

-\frac{1}{||w||}\sum_{x_i\in M}^{}y_i(w*x_i+b)

不考虑 \frac{1}{||w||},就得到感知机学习的损失函数:

L(w,b)=-\sum_{x_i\in M}^{}y_i(w*x_i+b)

由于 y_i(w*x_i+b)<0 所以 L(w,b) 是非负的。如果没有误分类点,损失函数值为0。而且由于误分类点越少,误分类点里超平面越近,损失函数值就越小(求和数目减少)。一个特定的样本点的损失函数:在误分类时是参数 w,b 的线性函数,在正确分类时是0。因此,给定训练数据集 T ,损失函数 L(w,b) 是 w,b 的连续可导函数。

因此感知学习的策略是在假设空间中选取使损失函数 L(w,b) 最小的模型参数 w,b,即感知机模型:

\underset{min}{L(w,b)}=min(-\sum_{x_i\in M}^{}y_i(w*x_i+b))

2.2,优化函数

首先,任意选取一个超平面 w_0, b_0,然后用梯度下降法不断极小化目标函数 \underset{min}{L(w,b)}。极小化的过程不是一次使 M 中所有误分类点的梯度下降,而是一次又一次随机选取一个误分类点使其梯度下降。

假设误分类点集合 M 是固定的,那么损失函数 L(w,b) 的梯度:

\Delta_wL(w,b) = -\sum_{x_i\in M}y_ix_i

\Delta_bL(w,b) = -\sum_{x_i\in M}y_i

随机采取一个误分类点 (x_i, y_i) ,对 w,b 进行更新:

w_{t+1}=w_{t}+y_n(t)x_n(t)

采用这种方法的原因:简单。但不限于这一种方法。

修正:

整体修正过程:

3,理论支持

3.1,向量持续修正

每次修改后的 w 向量逐渐趋向于最终的完美的 w_f(向量的乘积不断变大):

更新:w_{t+1}=w_{t}+y_n(t)x_n(t)

缩放:y_{n(t)}w_f^Tx_{n(t)}\geqslant \underset{n}{min}y_nw_f^Tx_n

完美的w_f下不会分类错误:\underset{n}{min}y_nw_f^Tx_n> 0,等于0,分类点在线上,需要详细讨论。

w_f^Tw_{t+1}=w_f^T(w_t+y_{n(t)}x_{n(t)})

\geqslant w_f^Tw_t+\underset{n}{min}y_nw_f^Tx_n

>w_f^Tw_t+0 =w_f^Tw_t

我们确实发现它们的内积在不断地增大,内积的增大原因有向量的模长在增大或者是夹角在变小。但是需要证明两个向量的相关性增大需要证明的是两个向量的夹角在变小,所以我们需要排除另一个模长的不确定性。

3.2,修正幅度上限

在这个例子中,我们更加关心的是 w 与 w_f(最终的 w )之间的角度,我们观察分类错误的点:

||w_t+1||^2 = ||w_t+y_{n(t)}x_{n(t)}||^2

=||w_t||^2+2y_{n(t)}w_t^Tx_{n(t)}+||y_{n(t)}x_{n(t)}||^2

\leqslant ||w_t||^2+0+||y_{n(t)}x_{n(t)}||

\leqslant ||w_t||^2+\underset{n}{max}||y_nx_n||^2

即:

||w_t+1||^2 \leqslant ||w_t||^2+\underset{n}{max}||y_nx_n||^2

||w_{t+1}||^2 - ||w_{t}||^2 \leqslant\underset{n}{max}||y_nx_n||^2

修正之后权向量的长度,相较于修正之前的增加有一个上限,或者说它的长度增长是较慢的。这个上限由 S 中距离坐标原点最远的那个点决定,所以 w_t 和 w_{t+1} 差别不会太大,所以现在还不能证明夹角在变小。

3.3,修正次数上限 

用wf与w的模向量直接相乘(乘出来的结果即为两个向量的角度),过程如下:

更新:w_{t+1}=w_{t}+y_n(t)x_n(t)

w_f^Tw_t=w_f^T(w_{t-1}+y_n(t-1)x_n(t-1))

\geqslant w_f^Tw_{t-1}+\underset{n}{min}\, y_nw_f^Tx_n

\geqslant w_0+T*\underset{n}{min}\, y_nw_f^Tx_n

\geqslant T*\underset{n}{min}\, y_nw_f^Tx_n

即:w_f^Tw_t \geqslant T*\underset{n}{min}\, y_nw_f^Tx_n

-----------------------------------------------------------------------------------

||w_t||^2=||w_{t-1}+y_{n(t-1)}x_{n(t-1)}||^2

=||w_{t-1}||^2+2y_{n(t-1)}w_t^Tx_{n(t-1)}+||y_{n(t-1)}x_{n(t-1)}||^2

\leqslant ||w_{t-1}||^2+0+||y_{n(t-1)}x_{n(t-1)}||^2

\leqslant ||w_{t-1}||^2+\underset{n}{max}||x_{n}||^2

\leqslant ||w_{0}||^2+T*\underset{n}{max}||x_{n}||^2=T*\underset{n}{max}||x_{n}||^2

即:||w_t||\leqslant \sqrt{T}*\underset{n}{max}||x_n||

-----------------------------------------------------------------------------------

\frac{w_f^T}{||w_f||}\frac{w_t}{||w_t||}\geqslant \frac{T*\underset{n}{min}\, y_nw_f^Tx_n}{||w_f^T||||w_t||}

\geqslant \frac{T*\underset{n}{min}\, y_nw_f^Tx_n}{||w_f^T||*\sqrt{T}*\underset{n}{max}||x_n||} = \frac{\sqrt{T}*\underset{n}{min}\, y_nw_f^Tx_n}{||w_f^T||*\underset{n}{max}||x_n||}=\sqrt{T}*constant

即:\frac{w_f^T}{||w_f||}\frac{w_t}{||w_t||}\geqslant \frac{\sqrt{T}*\underset{n}{min}\, y_nw_f^Tx_n}{||w_f^T||*\underset{n}{max}||x_n||}=\sqrt{T}*constant

因为  \frac{w_f^T}{||w_f||}\frac{w_t}{||w_t||} \leqslant 1,可得:

\frac{\sqrt{T}*\underset{n}{min}\, y_nw_f^Tx_n}{||w_f^T||*\underset{n}{max}||x_n||}\leqslant 1

即:T\leqslant \frac{\underset{n}{max}||x_n||^2*||w_f^T||^2}{\underset{n}{min}^2\, y_nw_f^Tx_n}=\frac{R^2}{\rho ^2}

3.4,线性不可分(Pocket PLA)

如果数据集是线性不可分的,那么对PLA进行改进,类似贪心算法:在pocket中保留一条当前最好的线,如果改进后的线更好(错分的样本数少),则更新最好的线,这样的缺点是需要遍历完空间内所有点的才能得到最好的结果。

4,Sklearn实现PLA

4.1,数据集

线性可分:data1.cvs

2.6487,4.5192,1
1.5438,2.4443,1
1.899,4.2409,1
2.4711,5.8097,1
3.359,6.4423,1
3.2406,5.8097,1
3.8128,6.3917,1
4.4441,6.8725,1
3.6747,6.7966,1
4.7401,8.163,1
3.8917,7.4038,1
4.602,7.6316,1
5.7265,7.7581,1
4.9571,6.5688,1
3.9903,5.3543,1
3.0236,4.4686,1
2.0568,2.9757,1
1.2676,2.4443,1
1.169,0.9008,1
1.7411,2.1154,1
1.386,3.2794,1
1.5636,4.165,1
1.8793,4.8482,1
2.7868,3.33,1
3.5563,5.1518,1
4.0693,6.2652,1
4.3849,6.2652,1
1.5438,7.2014,1
2.412,7.6569,1
1.7806,6.1387,1
1.4057,4.4939,1
2.6093,4.8735,1
3.0828,5.5314,1
3.9311,6.0121,1
4.7598,7.1508,1
5.3122,7.7075,1
5.7068,8.3148,1
5.1149,8.5172,1
5.4109,8.7449,1
3.8128,7.8593,1
3.2406,6.999,1
2.9052,5.5061,1
2.6882,4.9241,1
3.8325,6.6447,1
4.5428,7.6822,1
5.7857,8.0364,1
6.5552,8.9221,1
5.253,7.8593,1
5.2333,6.5941,1
4.7598,6.0374,1
4.5822,2.7227,-1
3.6549,1.9383,-1
2.9841,1.6852,-1
4.4244,4.3168,-1
3.7536,3.4312,-1
5.2728,5.4808,-1
4.8387,4.1144,-1
4.4244,3.2034,-1
5.3911,4.1144,-1
6.0817,5.1012,-1
5.5687,4.8988,-1
6.4565,5.9615,-1
6.0028,5.7591,-1
6.7722,6.6953,-1
6.6538,5.7338,-1
7.1471,6.6194,-1
7.5219,7.2014,-1
6.8314,7.2014,-1
7.6206,8.5931,-1
7.1865,7.7581,-1
7.7784,7.7581,-1
7.6009,5.1012,-1
6.496,4.2156,-1
5.8055,3.4818,-1
5.0163,2.3684,-1
4.1876,1.7864,-1
3.4379,0.9008,-1
5.7857,0.9008,-1
6.3382,1.9636,-1
4.9571,1.4069,-1
6.8511,2.419,-1
6.0817,2.8745,-1
7.1668,4.0132,-1
7.226,4.6711,-1
8.1533,5.1771,-1
7.4825,6.2146,-1
7.0484,5.4555,-1
8.5084,5.9868,-1
7.5417,4.0891,-1
7.2063,2.3937,-1
6.5355,1.331,-1
5.4503,1.7358,-1
5.8449,2.4443,-1
4.8979,3.1781,-1
5.8055,4.6711,-1
7.3641,5.9868,-1
6.2592,4.6711,-1
8.3703,7.581,-1
8.5676,4.6457,-1
8.1676,4.6457,-1

线性不可分:data2.cvs

机器学习:支持向量机(SVM)_燕双嘤-CSDN博客1,算法描述支持向量机(SVM)是用来解决分类问题的。作为数据挖掘领域中一项非常重要的任务,分类目前在商业上应用最多(比如分析型CRM里面的客户分类模型、客户流失模型、客户盈利等,其本质上都属于分类问题)。而分类的目的则是构造一个分类函数或分类模型,该模型能吧数据库中的数据项映射到给定类别中的某一个,从而可以用来预测未知类别。先考虑最简单的情况,比如豌豆和米粒,用筛子很快可以分离它们,小颗粒漏下去,大颗粒保留。用函数来表示就是当直径d大于某个值D,就判定其为豌豆,小于D就是米粒。在数轴上就是D左边https://shao12138.blog.csdn.net/article/details/121164645#t14

4.2,经典PLA

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import Perceptron

sample = np.loadtxt('data1.csv', delimiter=',')
sample1 = np.hsplit(sample, [2])[0]
sample2 = np.hsplit(sample, [2])[1]
clf = Perceptron(fit_intercept=True, shuffle=False)
# 训练感知机
clf.fit(sample1, sample2)
# 得到权重矩阵
weigths = clf.coef_
# 得到截距bisa
bias = clf.intercept_
print(weigths,bias)

5,Python实现PLA

5.1,经典PLA

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split


def createdata():
    sample = np.loadtxt('data1.csv', delimiter=',')
    sample1 = np.hsplit(sample, [2])[0]
    sample2 = np.hsplit(sample, [2])[1]
    X_train, X_test, y_train, y_test = train_test_split(sample1, sample2, test_size=0.25)
    return X_train, X_test, y_train, y_test

# 训练感知机模型
class Perceptron:
    def __init__(self, x, y, a=1):
        self.x = x
        self.y = y
        self.w = np.zeros((x.shape[1], 1))  # 初始化权重,w1,w2均为0
        self.b = 0
        self.a = 1  # 学习率
        self.numsamples = self.x.shape[0]
        self.numfeatures = self.x.shape[1]
        self.predict2 = []

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

    def update(self, label_i, data_i):
        tmp = label_i * self.a * data_i
        tmp = tmp.reshape(self.w.shape)
        # 更新w和b
        self.w = tmp + self.w
        self.b = self.b + label_i * self.a

    def train(self):
        isFind = False
        while not isFind:
            count = 0
            for i in range(self.numsamples):
                tmpY = self.sign(self.w, self.b, self.x[i, :])
                if tmpY * self.y[i] <= 0:  # 如果是一个误分类实例点
                    print('误分类点为:', self.x[i, :], '此时的w和b为:', self.w, self.b)
                    count += 1
                    self.update(self.y[i], self.x[i, :])
            if count == 0:
                print('最终训练得到的w和b为:', self.w, self.b)
                isFind = True
        return self.w, self.b

    # 评价
    def predict(self, X_test, y_test):
        count = 0
        for i in range(25):
            y = self.sign(self.w, self.b, X_test[i, :])
            if y >= 0:
                y = 1
            else:
                y = -1
            self.predict2.append(y)     #为了画出坐标,需要统计结果
            if (y == y_test[i]):
                count += 1
        print('平均精确度为:%.2f' % (count / 25 * 100.0))
# 画图描绘
class Picture:
    def __init__(self, data, label,data2, label2, w, b):
        self.b = b
        self.w = w
        plt.figure(1)
        plt.title('Perceptron Learning Algorithm', size=14)
        plt.xlabel('x0-axis', size=14)
        plt.ylabel('x1-axis', size=14)

        xData = np.linspace(0, 10, 100)
        yData = self.expression(xData)
        plt.plot(xData, yData,  label='sample data')

        for i, j in zip(data, label):
            if j == 1:
                plt.scatter(i[0], i[1], s=50, color='blue', marker='+')
            if j == -1:
                plt.scatter(i[0], i[1], s=50, color='green', marker='+')
        for i, j in zip(data2, label2):
            if j == 1:
                plt.scatter(i[0], i[1], s=50, color='black', marker='*')
            if j == -1:
                plt.scatter(i[0], i[1], s=50, color='red', marker='*')
        plt.show()

    def expression(self, x):
        y = (-self.b - self.w[0] * x) / self.w[1]  # 注意在此,把x0,x1当做两个坐标轴,把x1当做自变量,x2为因变量
        return y
if __name__ == '__main__':
    X_train, X_test, y_train, y_test = createdata()
    myperceptron = Perceptron(x=X_train, y=y_train)
    weights, bias = myperceptron.train()
    myperceptron.predict(X_test=X_test, y_test=y_test)
    Picture = Picture(X_train, y_train, X_test, myperceptron.predict2, weights, bias)

5.2,PLA-Pocket

import numpy as np
import random
import time
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

def createdata():
    sample = np.loadtxt('data1.csv', delimiter=',')
    sample1 = np.hsplit(sample, [2])[0]
    sample2 = np.hsplit(sample, [2])[1]
    X_train, X_test, y_train, y_test = train_test_split(sample1, sample2, test_size=0.25)
    return X_train, X_test, y_train, y_test


# 训练感知机模型
class Perceptron:
    def __init__(self, x, y, a=1):
        self.x = x
        self.y = y
        self.w = np.zeros((x.shape[1], 1))  # 初始化权重,w1,w2均为0
        self.b = 0
        self.best_w = np.zeros((x.shape[1], 1))  # 最好
        self.best_b = 0
        self.a = 1  # 学习率
        self.numsamples = self.x.shape[0]
        self.numfeatures = self.x.shape[1]
        self.predict2 = []

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

    def update(self, label_i, data_i):
        tmp = label_i * self.a * data_i
        tmp = tmp.reshape(self.w.shape)
        # 更新w和b
        tmpw = tmp + self.w
        tmpb = self.b + label_i * self.a
        # print(len(self.classify(self.w,self.b)),',',len(self.classify(tmpw,tmpb)))
        if (len(self.classify(self.best_w, self.best_b)) >= (len(self.classify(tmpw, tmpb)))):  #如果分错的更少则更新最优解
            self.best_w = tmp + self.w
            self.best_b = self.b + label_i * self.a
        self.w = tmp + self.w
        self.b = self.b + label_i * self.a

    def classify(self, w1, b1): #统计分类错误的点,为后续统计长度
        mistkaes = []
        for i in range(self.numsamples):
            tmpY = self.sign(w1, b1, self.x[i, :])
            if tmpY * self.y[i] <= 0:  # 如果是一个误分类实例点
                mistkaes.append(i)
        return mistkaes

    def train(self, num):
        count = 0
        isFind = False
        while not isFind:
            mistakes = self.classify(self.w, self.b)
            if (len(mistakes) == 0):
                print('最终训练得到的w和b为:', self.best_w, self.best_b)
                break
            n = mistakes[random.randint(0, len(mistakes) - 1)]  #随机获取误分类点
            self.update(self.y[n], self.x[n, :])
            print('第', count, '次迭代误分类点为:', self.x[n, :], '此时的w和b为:', self.w, self.b)
            count += 1
            if count == num:
                print('最终训练得到的w和b为:', self.best_w, self.best_b)
                isFind = True
        return self.best_w, self.best_b

    # 评价
    def predict(self, X_test, y_test):
        count = 0
        for i in range(25):
            y = self.sign(self.w, self.b, X_test[i, :])
            if y >= 0:
                y = 1
            else:
                y = -1
            self.predict2.append(y)
            if (y == y_test[i]):
                count += 1
        print('平均精确度为:%.2f' % (count / 25 * 100.0))


# 画图描绘
class Picture:
    def __init__(self, data, label, data2, label2, w, b):
        self.b = b
        self.w = w
        plt.figure(1)
        plt.title('Perceptron Learning Algorithm', size=14)
        plt.xlabel('x0-axis', size=14)
        plt.ylabel('x1-axis', size=14)

        xData = np.linspace(0, 10, 100)
        yData = self.expression(xData)
        plt.plot(xData, yData, label='sample data')

        for i, j in zip(data, label):   #训练集
            if j == 1:
                plt.scatter(i[0], i[1], s=50, color='blue', marker='+')
            if j == -1:
                plt.scatter(i[0], i[1], s=50, color='green', marker='+')
        for i, j in zip(data2, label2): #测试集预测
            if j == 1:
                plt.scatter(i[0], i[1], s=50, color='black', marker='*')
            if j == -1:
                plt.scatter(i[0], i[1], s=50, color='red', marker='*')
        plt.savefig('2d.png', dpi=75)

    def expression(self, x):
        y = (-self.b - self.w[0] * x) / self.w[1]  # 注意在此,把x0,x1当做两个坐标轴,把x1当做自变量,x2为因变量
        return y

    def Show(self):
        plt.show()


if __name__ == '__main__':
    start = time.time()
    X_train, X_test, y_train, y_test = createdata()
    myperceptron = Perceptron(x=X_train, y=y_train)
    weights, bias = myperceptron.train(100) #设置阀值,不一定能收敛
    myperceptron.predict(X_test=X_test, y_test=y_test)
    Picture = Picture(X_train, y_train, X_test, myperceptron.predict2, weights, bias)
    Picture.Show()
这篇关于机器学习:感知机算法(PLA)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!