1. PLA算法python实现

plpython3u如何使用_感知机


为便于在二维空间中进行画图分析,构建二维感知机模型,即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):  #限制迭代次数,上限100次
            break
            
    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库绘制出其散点图:

plpython3u如何使用_机器学习_02


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

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]]

plpython3u如何使用_POCKET_PLA算法_03

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循环次数

1

0.001993

22

0.005988

36

2

0.001995

22

0.005018

30

3

0.002030

22

0.006949

42

4

0.001962

22

0.006981

34

5

0.001991

22

0.003988

28

6

0.001994

22

0.004986

28

7

0.001993

22

0.004987

34

8

0.001981

22

0.007949

42

9

0.001993

22

0.005984

36

10

0.002000

22

0.006980

48

平均

0.001993

22

0.005482

36

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

(2)执行效果比较

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

PLA运行结果:

plpython3u如何使用_感知机_04

POCKET_PLA运行结果:

第一次运行

plpython3u如何使用_感知机_05


第二次运行

plpython3u如何使用_机器学习_06


第三次运行

plpython3u如何使用_感知机_07

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

同理,每次运行的执行效果不相同:

第一次运行:

plpython3u如何使用_机器学习_08


第二次运行:

plpython3u如何使用_POCKET_PLA算法_09


第三次运行:

plpython3u如何使用_机器学习_10

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):  #限制迭代次数,上限100次
            break
    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()