用python实现PLA算法和POCKET_PLA算法并分析执行效果

1. PLA算法python实现

pic1
为便于在二维空间中进行画图分析,构建二维感知机模型,即wx均为二维向量,根据学习算法的原始形式,可以得出PLA算法的代码如下:

def PLA():
    W = np.zeros(2)
    b = 0
    count = 0
    while True:
        count += 1
        cease = True
        for i in range(0,len(dataset)):
            x = dataset[i][:-1]
            X = np.array(x)
            Y = np.dot(W,X) + b
            if sign(Y) == sign(dataset[i][-1]):
                continue
            else:
                cease = False
                W = W + (dataset[i][-1]) * X
                b = b + dataset[i][-1]
        
        if cease:
            break
            
    print("W:",W)
    print("count:",count)
    Display(dataset,W[0],W[1],b)
    return W

观察PLA可知,在数据集线性可分的情况下,PLA一定能停机;
在数据集线性不可分的情况下,对任意由w和b决定的线性划分,总存在某一点x使得wx+b != sign(y),故PLA将不会停机。

2. POCKET_PLA算法python实现

为使PLA算法可用于线性不可分的情况,引入容忍噪声的PLA算法,即POCKET_PLA算法,其主要思想如下:

初始化最优划分[w_best,b_best]
对整个数据集:
1 (随机)找到一个被分错的点(x , y)
2 尝试使用该点来修正当前划分
wt+1 = wt + y*x
bt+1 = bt + y
3 如果修正后的划分 [wt+1,bt+1] 优于 [w_best,b_best] ,则将 [w_best,b_best] 更新为 [wt+1,bt+1]

循环直至 限定的循环次数(POCKET_PLA算法不会自行停机)
最终得到的[wt+1,bt+1] 即为最好的线性分类器

一言以蔽之,在POCKET_PLA算法中,保证每次 w_best 中存放的都是最优划分。

由以上思想可得 POCKET_PLA 的代码实现如下:

def POCKET_PLA():
    count = 0       #count记录继上次找到一个更好的划分开始,已经走过的循环次数
    W = np.ones(2)
    b = 0
    best_W = W      
    best_b = b      #best_W和best_b共同记录当前最好的划分
    min_num = 15    #min_num记录最好划分错误点的个数,初始化为全部点的个数
    while True:
        count += 1
        cease = True
        faultset = []
        for i in range(0,len(dataset)):
            x = dataset[i][:-1]
            X = np.array(x)
            Y = np.dot(W,X) + b
            if sign(Y) == sign(dataset[i][-1]):
                continue
            else:
                cease = False
                faultset.append(dataset[i]) #该点分错,加入错误点集
        if cease == False:
            j = random.randint(0,len(faultset)-1) #从被分错的点中随机选取一个并尝试修正
            W = W + (faultset[j][-1]) * faultset[j][:-1]
            b = b + faultset[j][-1]
            num = num_fault(W,b,dataset)
            if num < min_num: #找到一个j更好的划分,记录下来
                count = 0     #将count归零
                min_num = num
                best_W = W
                best_b = b
        if (cease or count==100):  #限制迭代次数,上限100break
            
    print("best_W:",best_W)
    print("best_b:",best_b)
    print("count:",count)
    print("min_fault_point:",min_num)
    return best_W

容易看出,每修正一次划分,POCKET_PLA 算法都会遍历一次所有的点来判断新划分的好坏(num_fault函数见文末代码),因此在计算同一线性可分的数据集时,POCKET_PLA 算法应该慢于PLA算法。

3. 数据集选取

自行构建一个简单的线性可分数据集:

dataset = [[0.10 ,-0.10, 1],
           [0.30 , 0.60, 1],
           [0.50 ,-0.20, 1],
           [0.60 ,-0.25, 1],
           [-0.10,-0.25, 1],
           [-0.42,-0.30, 1],
           [-0.50,-0.15,-1],
           [-0.55,-0.12,-1],
           [-0.70,-0.28,-1],
           [-0.51, 0.22,-1],
           [-0.48, 0.48,-1],
           [-0.52, 0.47,-1],
           [ 0.15, 0.63,-1],
           [ 0.09, 0.81,-1],
           [-0.68, 0.58,-1]]

使用matplotlib库绘制出其散点图:
在这里插入图片描述
自行构建一个简单的线性不可分数据集:

dataset = [[ 0.10,-0.10, 1],
           [ 0.00, 0.75, 1],
           [ 0.50,-0.20, 1],
           [ 0.60,-0.25, 1],
           [-0.10,-0.25, 1],
           [-0.55, 0.30, 1],
           [-0.50,-0.15,-1],
           [-0.55,-0.12,-1],
           [ 0.53,-0.28,-1],
           [-0.51, 0.22,-1],
           [-0.48, 0.48,-1],
           [-0.52, 0.47,-1],
           [ 0.15, 0.63,-1],
           [ 0.09, 0.81,-1],
           [-0.68, 0.58,-1]]

在这里插入图片描述

4. 验证算法执行的时间和效果

4.1 比较同一线性可分数据集下PLA和POCKET_PLA算法的执行时间和效果

(1)执行时间比较

计算程序执行时间的代码:

import time

starttime = time.time()
endtime = time.time()
dtime = endtime - starttime

在程序主体中加入以上代码,使用构建的线性可分数据集分别执行10次PLA和POCKET_PLA算法并记录执行次数和执行时间(精确到小数点后6位)

NO.PLA执行时间(s)PLA循环次数POCKET_PLA执行时间(s)P_P循环次数
10.001993220.00598836
20.001995220.00501830
30.002030220.00694942
40.001962220.00698134
50.001991220.00398828
60.001994220.00498628
70.001993220.00498734
80.001981220.00794942
90.001993220.00598436
100.002000220.00698048
平均0.001993220.00548236

可见PLA算法在执行速度上明显比POCKET_PLA算法要快。

(2)执行效果比较

由于POCKET_PLA每次从错误点中随机选取一个进行修正,而PLA中无随机选取的过程,故 POCKET_PLA算法每次运行结果不同,而PLA算法每次运行结果相同。

PLA运行结果:

在这里插入图片描述

POCKET_PLA运行结果:

第一次运行
在这里插入图片描述
第二次运行
在这里插入图片描述
第三次运行
在这里插入图片描述

4.2 验证线性不可分情况下POCKET_PLA算法的执行效果

同理,每次运行的执行效果不相同:
第一次运行:
在这里插入图片描述
第二次运行:
在这里插入图片描述
第三次运行:
在这里插入图片描述

5. 附完整代码:POCKET_PLA(Divisible and Indivisible)

import numpy as np
import time
import matplotlib.pyplot as plt
from numpy import *
import random

"""dataset = [[0.10 ,-0.10, 1],
           [0.30 , 0.60, 1],
           [0.50 ,-0.20, 1],
           [0.60 ,-0.25, 1],
           [-0.10,-0.25, 1],
           [-0.42,-0.30, 1],
           [-0.50,-0.15,-1],
           [-0.55,-0.12,-1],
           [-0.70,-0.28,-1],
           [-0.51, 0.22,-1],
           [-0.48, 0.48,-1],
           [-0.52, 0.47,-1],
           [ 0.15, 0.63,-1],
           [ 0.09, 0.81,-1],
           [-0.68, 0.58,-1]]"""

dataset = [[ 0.10,-0.10, 1],
           [ 0.00, 0.75, 1],
           [ 0.50,-0.20, 1],
           [ 0.60,-0.25, 1],
           [-0.10,-0.25, 1],
           [-0.55, 0.30, 1],
           [-0.50,-0.15,-1],
           [-0.55,-0.12,-1],
           [ 0.53,-0.28,-1],
           [-0.51, 0.22,-1],
           [-0.48, 0.48,-1],
           [-0.52, 0.47,-1],
           [ 0.15, 0.63,-1],
           [ 0.09, 0.81,-1],
           [-0.68, 0.58,-1]]

dataset = np.array(dataset) #变为数组,不然无法进行分片操作

def Display(dataset,w0,w1,b): #结果图
    plt.scatter(dataset[0:6,0], dataset[0:6,1], color='blue', marker='o', label='Positive') 
    plt.scatter(dataset[6:,0], dataset[6:,1], color='red', marker='x', label='Negative')
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend(loc='upper left')
    plt.title('Scatter')
    plt.plot([-1,1],[(w0-b)/w1,-1*(w0+b)/w1],'g')#画直线
    plt.show()

def num_fault(W,b,dataset): #当前划分下错误点的个数
    count = 0
    for i in range(0,len(dataset)):
        X = np.array(dataset[i][:-1])    
        Y = np.dot(W,X) + b
        if sign(Y) == sign(dataset[i][-1]):
            continue
        else:
            count += 1
    return count

def POCKET_PLA():
    starttime = time.time()
    count = 0       #count记录继上次找到一个更好的划分开始,已经走过的循环次数
    W = np.ones(2)
    b = 0
    best_W = W      
    best_b = b      #best_W和best_b共同记录当前最好的划分
    min_num = 15    #min_num记录最好划分错误点的个数,初始化为全部点的个数
    while True:
        count += 1
        cease = True
        faultset = [] #错误点集
        for i in range(0,len(dataset)):
            x = dataset[i][:-1]
            X = np.array(x)
            Y = np.dot(W,X) + b
            if sign(Y) == sign(dataset[i][-1]):
                continue
            else:
                cease = False
                faultset.append(dataset[i]) #该点分错,加入错误点集
        if cease == False:
            j = random.randint(0,len(faultset)-1) #从被分错的点中随机选取一个并尝试修正
            W = W + (faultset[j][-1]) * faultset[j][:-1]
            b = b + faultset[j][-1]
            num = num_fault(W,b,dataset)
            if num < min_num: #找到一个更好的划分,记录下来
                count = 0     #将count归零
                min_num = num
                best_W = W
                best_b = b
        if (cease or count==100):  #限制迭代次数,上限100break
    endtime = time.time()
    dtime = endtime - starttime
    print("time: %.8s s" % dtime)
    print("best_W:",best_W)
    print("best_b:",best_b)
    print("count:",count)
    print("min_fault_point:",min_num)
    Display(dataset,best_W[0],best_W[1],best_b)
    return best_W

def main():
    
    W = POCKET_PLA()
    
    
if __name__ == '__main__':
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值