统计学习——感知机

一、什么是感知机

  感知机1957年由Rosenblatt提出,是神经网络的起源算法。
  什么是感知机呢?对神经网络来说,感知机就是最基础,最普遍的一种逻辑模型,为什么说是最基础的逻辑模型呢,就是得解决网络中基本逻辑,就比如说与或这种,感知机刚好就能解决与或这样基本问题,但唯独不能解决异或问题,也算是很大缺陷吧。用书上话来讲感知机(perceptron)是一种解决二分类的线性分类模型,将特征空间的实例特征向量划分为正负两类,本质是一种分离超平面,是一种判别模型。

二、感知机详解

1.函数模型

  假设输入空间(特征空间) 是xRn,输出空间是y={+1,1}.由输入空间到输出空间的如下函数:

f(x)=sign(wx+b)

  其中,wb为感知机模型参数,wRn叫作权值(weight)或权值向量(weight vector),bR叫作偏置(bias),wx表示wx的内积。sign是符号函数,即

sign(x)={+1x01x<0

  感知机模型的假设空间是定义在特征空间中的所有线性分类模型(linear classificatio model)或线性分类器(linear classifier),即函数集合{f|f(x)=wx+b}.

2.几何解释

  感知机有如下几何解释:线性方程

wx+b=0

  对应于特征空间Rn中的一个超平面S,其中w是超平面的法向量,b是超平面的截距。这个超平面将特征空间划分为两个部分。位于两部分的点(特征向量)分别被分为正、负两类。因此,超平面S称为分离超平面(separating hyperplane).


  感知机学习,由训练数据集(实例的特征向量及类别)

T={(x1,y1),(x2,y2),···,(xn,yn)}

  感知机预测,通过学习得到的感知机模型,对于新的输入实例给出其对应的输出类别。

3.感知机学习策略

3.1数据集的线性可分性

  定义:给定一个数据集T={(x1,y1),(x2,y2),···,(xn,yn)},如果存在某个超平面S

wx+b=0

  能够将数据集的正实例点和负实例点完全正确地划分到超平面的两侧,即对所有yi=+1的实例i,有wxi+b0,对所有yi=1的实例i,有wxi+b<0,则称数据集T为线性可分数据集(linear separab data set);否则,为线性不可分。

3.2感知机学习策略

  假设训练数据集是线性可分的,感知机学习的目标是求得一个能够将训练集正实例点和负实例点完全正确分开的分离超平面。为了找出这样的超平面,即确定感知机模型参数w,b,需要确定一个学习策略,即定义(经验)损失函数并将损失函数极小化。
  首先损失函数容易想到采用误分类点的数量作为损失函数,但是是离散的,w,b不是连续可导的,所以我们采取误分类点到超平面的总距离作为损失函数,我们希望总距离越小越好,当损失函数为0时,不存在误分类点。

3.2.1损失函数

  首先引入输入空间Rn中任一点xi到超平面S的距离:

di=|wxi+b|||w||

  这里,||w||wL2范数。
  对于误分类的数据(xi,yi)来说,

|wxi+b|=yi(wxi+b)>0

  显然成立。
  因此,一个误分类点到超平面S的距离为:

di=|wxi+b|||w||=yi(wxi+b)||w||

  假设误分类点集合为M,那么总距离为:

D=1||w||xiMyi(wxi+b)

  最后,不考虑1||w||,求得最终的感知机损失函数即经验风险函数:

L(w,b)=xiMyi(wxi+b)

  思考:为什么不考虑1||w||?
  1.1||w||不影响正负判断。
  2.感知机算法的终止条件是所有输入输出都被正确分类,即不存在误分类点,最终损失为0,让分子为0即可。

4.感知机学习算法

  采用随机梯度下降(SGD),目标函数如下:

minw,bL(w,b)=xiMyi(wxi+b)

4.1原始形式算法

  求函数极小化问题的解,在随机梯度下降过程中,每一次仅用一个误分类点样本来使其梯度下降。
  首先,求解梯度,分别对w,b求偏导:

wL(w,b)=xiMyixi(n1)bL(w,b)=xiMyi

  然后,随机选取一个误分类点对w,b进行更新:

ww+ηyixi

bb+ηyi

  其中,η 为学习率,通过这样迭代使损失函数L(w,b)不断减小,直至最小化。
  算法步骤
  输入:训练数据T={(x1,y1),(x2,y2),···,(xn,yn)},yi{1,+1},学习率η(0<η<1)
  输出:w,b;感知机模型f(x)=sign(wx+b)
  步骤:
  (1)赋初值w0,b0
  (2)在训练集中选取数据点(xi,yi)
  (3)判断该数据点是否为当前模型的误分类点,即若yi(wxi+b)<0,则更新

w=w+ηyixi

b=b+ηyi

   (4) 转到(2),直到训练集中没有误分类点
  这种学习算法直观上有如下解释:当一个实例点被误分类,即位于分离超平面的错误一侧时,则调整w,b的值,使分离超平面向该误分类点的一侧移动,以减少该误分类点与超平面间的距离,直至超平面越过该误分类点使其被正确分类。
  代码实战:
  拿出iris数据集中两个数据[sepal length,sepal width]作为输入特征,进行分类:

import pandas as pd
import numpy as np
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
df.columns = [
    'sepal length', 'sepal width', 'petal length', 'petal width', 'label'
]
df.label.value_counts()
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()
plt.show()
data = np.array(df.iloc[:100, [0, 1, -1]])
X, y = data[:,:-1], data[:,-1]
y = np.array([1 if i == 1 else -1 for i in y])
# 数据线性可分,二分类数据
# 此处为一元一次线性方程
class Model:
    def __init__(self):
        self.w = np.ones(len(data[0]) - 1, dtype=np.float32)
        self.b = 0
        self.l_rate = 0.1
        # self.data = data

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

    # 随机梯度下降法
    def fit(self, X_train, y_train):
        is_wrong = False
        while not is_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.l_rate * np.dot(y, X)
                    self.b = self.b + self.l_rate * y
                    wrong_count += 1
            if wrong_count == 0:
                is_wrong = True
        return 'Perceptron Model!'

perceptron = Model()
perceptron.fit(X, y)
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()
plt.show()


  将蓝点,橙点进行分类

  可见感知机模型完美地将两类实例点进行二分。

4.2算法的收敛性

  现在证明,对于线性可分数据集感知机学习算法原始形式收敛,即经过有限次迭代可以得到一个将训练数据集完全正确划分的分离超平面及感知机模型。
  为了方便叙述与推导,将偏置b并入权重向量w,记作w^ = (wT,b)T,同样也将输入向量加以扩充,加进常数1,记作x^ = (xT,1)Tw^x^ = wx+b.
  Novikoff定理:设训练数据集T={(x1,y1),(x2,y2),···,(xn,yn)}是线性可分的,其中xiX=Rn,yiY={1,+1},i=1,2,3,···n,则
  (1) 存在满足条件||w^opt||=1的超平面w^optx^=0将训练数据集完全正确分开;且存在 γ0,对所有i=1,2,···,n

yi(w^optx^i)=yi(woptxi+bopt)γ

  (2) 令R=max1in||x^i||,则感知机算法在训练数据集上的误分类次数k满足不等式

k(Rγ)2

  Proof:
  (1)由于训练数据集是线性可分的,按照定义,存在超平面可将训练数据集完全正确分开,取此超平面为w^optx^ =woptx+bopt=0,因为wopt是该超平面的法向量,是可以任意伸缩的,我们让这个法向量除以它的范数,就可以使||w^opt||=1,就像2x+2y+1=023x+23y+13=0表示同一条直线。接下来又因为所有分类点都是正确分类的,所以对于所有分类点来说,存在

yi(w^optx^i)=yi(woptxi+bopt)0

  那么自然能在上述非负数集合中找到一个最小的非负数γ0

γ=min{yi(woptxi+bopt)}

  使

yi(w^optx^i)=yi(woptxi+bopt)γ

  (2)感知机算法从w^0=0开始,如果实例被误分类,则更新权重,令w^k1是第k个误分类实例之前的扩充权重向量,即w^k1=(wk1T,bk1)T,则第k个误分类实例的条件是

yi(w^k1x^i)=yi(wk1xi+bk1)0

  若(xi,yi)是被w^k1误分类的数据,则wb的更新是

wkwk1+ηyixi

bkbk1+ηyi

  即

w^k=w^k1+ηyix^i

  下面推导两个不等式:
  ①

w^kw^optkηγ

  证明:

w^kw^opt=w^k1w^opt+ηyiw^optx^iw^k1w^opt+ηγ

  由此递推可得不等式

w^kw^optw^k1w^opt+ηγw^k2w^opt+2ηγ···kηγ

  ②

||w^k||kη2R2

  证明:

||w^k||=||w^k1||2+2ηyiw^k1x^i+η2||x^i||2||w^k1||2+η2||x^i||2||w^k1||2+η2R2||w^k2||+2η2R2···kη2R2

  所以有不等式:

kηγw^kw^opt≤∥w^k∥∥w^opt∥≤kηRk2γ2kR2

   于是

k(Rγ)2

  定理表明,误分类的次数k是有上界的,经过有限次搜索可以找到将训练数据完全正确分开的分离超平面。也就是说,感知机学习算法原始形式是收敛的。

4.3感知机算法的对偶形式

  原始形式感知机每次梯度下降都是随机选取一个误分类点数据来更新w,b,最终迭代若干次得到最终结果。对于从来都没有误分类的点来说,它被选择参与迭代的次数为0,假设样本点(xi,yi)T在迭代更新w,b时被使用了ki次,令αi=ηki,因此在原始感知机算法中,算法最后收敛时的w,b为:

w=ηxiTkiyixi=i=1nαiyixib=ηxiTkiyi=i=1nαiyi

  注意,为什么是xiT,而不是xiM,因为此时对于非误分类点来说αi=0,对权重w不会造成影响,把上述w,b回代到原始感知机算法的假设函数中可得

f(x)=sign(xiTαiyixix+xiTαiyi)

  此时的参数不再是w,b,而是αi。因此原始问题由求解参数w,b转换为如何求解参数αi。综上可以给出对偶形式下的参数迭代格式:

{ki=ki+1×ηαi=αi+ηifyi(ηj=1jkjyj(xj,xi)+ηj=1nkjyj)0i=i+1else

  这很容易理解,当该数据点误分类,那么让它继续分类次数ki加1。
  其中xj,xi表示做内积运算,引入Gram矩阵来存储内积。在迭代更新时可以发现当某实例数据点在算法更新时使用次数越多,意味着它距离分离超平面越近,也就越难以正确分类,换言之,这样的数据点对感知机算法学习的效果影响最大。
  Gram矩阵定义如下:

(3)G=[x1x1x1x2···x1xnx2x1x2x2···x2xn············xnx1xnx2···xnxn]

  其实就是对偶形式算法本质就是将w表示成数据点yixi的线性组合,b表示成yi的线性组合。
  算法步骤
  输入:训练数据T={(x1,y1),(x2,y2),···,(xn,yn)},yi{1,+1},学习率η(0<η<1)
  输出:α,b;感知机模型f(x)=sign(j=1nαjyjxjx+b),其中α=(α1,α2,···αn)T
  (1) 赋初值α0,b0
  (2) 选取数据点(xi,yi)
  (3) 判断该数据点是否为当前模型的误分类点,即判断若yi(j=1nαjyjxjxi+b)0则更新

αi=αi+η

b=b+ηyi

  (4) 转(2),直到训练集中没有误分类点

def fit( self,X,y):
    #权重和偏置初始化
    self.alpha = np.zeros ( X.shape[0])
    self.b =0
    train_complete_flag = False
    Gram = np.dot(X, X.T) #存放样本两两内积的Gram矩阵(特点)
    while not train_complete_flag:
        error_count = 0
        for i in range ( X.shape[0]):
            x_, y_ = X[i],y[i]
            #有—个点当分类错误时,更新alpha_i和偏置
            tmp= np.dot(np.multiply(self.alpha,y),Gram[ :, i]) +self.b
            #进行误分类点判断
            if y_* tmp <= 0:
                self.alpha[i] += self.lr
                self.b += self.lr * y_
                error_count += 1
        if not error_count:
            train_complete_flag = True#训练完成后计算权重
            self.w = np.dot(np.multiply (self.alpha,y),X)

4.4原始形式与对偶形式

  在向量维数(特征数)过高时(特征数n >> 样本数N),需要计算输入特征xi与权值向量w的内积,效率低,应选择对偶算法
  在向量个数(样本数)过多时(特征数n << 样本数N),需要计算每次累加和,效率低,应选择原始算法

5.例子

  如图所示,正实例点是x1=(3,3)T,x2=(4,3)T,负实例点是x3=(1,1)T,使用感知机模型求解f(x)=sign(wx+b)。这里w=(w(1),w(2)),x=(x(1),x(2))


  这里我们取初值w0=0,b0=0,取η=1
  原始形式代码如下:

train = [[(3, 3), 1], [(4, 3), 1], [(1, 1), -1]]

w = [0, 0]
b = 0

# 使用梯度下降法更新权重
def update(data):
    global w, b
    w[0] = w[0] + 1 * data[1] * data[0][0]
    w[1] = w[1] + 1 * data[1] * data[0][1]
    b = b + 1 * data[1]
    print(w, b)

# 计算到超平面的距离
def cal(data):
    global w, b
    res = 0
    for i in range(len(data[0])):
        res += data[0][i] * w[i]
    res += b
    res *= data[1]
    return res

# 检查是否可以正确分类
def check():
    flag = False
    for data in train:
        if cal(data) <= 0:
            flag = True
            update(data)
    if not flag:
        print("The result: w: " + str(w) + ", b: "+ str(b))
        return False
    flag = False

for i in range(1000):
    check()
    if check() == False:
        break
[3, 3] 1
[2, 2] 0
[1, 1] -1
[0, 0] -2
[3, 3] -1
[2, 2] -2
[1, 1] -3
The result: w: [1, 1], b: -3

  对偶形式代码如下:

import numpy as np

train = np.array([[[3, 3], 1], [[4, 3], 1], [[1, 1], -1]])

a = np.array([0, 0, 0])##为什么是三维的,三维对应三个数据点,这里代表每一个数据点误分类次数,实际是ki*学习率
b = 0
lr = 1
Gram = np.array([])
y = np.array(range(len(train))).reshape(1, 3)# 标签
x = np.array(range(len(train) * 2)).reshape(3, 2)# 特征
# 计算Gram矩阵
def gram():
    g = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
    for i in range(len(train)):
        for j in range(len(train)):
            g[i][j] = np.dot(train[i][0], train[j][0])
    return g
# 更新权重
def update(i):##更新第i个误分类点
    global a, b
    a[i] = a[i] + lr
    b = b + lr * train[i][1]
    print(a, b)

# 计算到超平面的距离
def cal(key):
    global a, b, x, y
    i = 0
    for data in train:
        y[0][i] = data[1]
        i = i + 1
    temp = a * y
    res = np.dot(temp, Gram[key])
    res = (res + b) * train[key][1]
    return res

# 检查是否可以正确分类
def check():
    global a, b, x, y
    flag = False
    for i in range(len(train)):
        if cal(i) <= 0:
            flag = True
            update(i)
    if not flag:
        i = 0
        for data in train:
            y[0][i] = data[1]
            x[i] = data[0]
            i = i + 1
        temp = a * y
        w = np.dot(temp, x)
        print("The result: w: " + str(w) + ", b: "+ str(b))
        return False
    flag = False


Gram = gram()# 初始化Gram矩阵
for i in range(1000):
    check()
    if check() == False:
        break

[1 0 0] 1
[1 0 1] 0
[1 0 2] -1
[1 0 3] -2
[2 0 3] -1
[2 0 4] -2
[2 0 5] -3
The result: w: [[1 1]], b: -3

6.总结

  本章介绍了统计学习中最简单的一种算法——感知机,对现在的机器学习理论来说,这个算法的确是太简单了,但这样简单的东西却是很多现在流行算法的基础,比如神经网络,比如支持向量机,需要好好掌握。

参考文献

  [1]机器学习入门之《统计学习方法》笔记整理——感知机
  [2]机器学习——感知机

posted @   呵呵哈希  阅读(96)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示