机器学习基石:PLA算法代码实现

一、前言

本篇是面向机器学习基石第一次作业而言。

15-20题都是需要编程实现才能正确做出选择。

前面14个选择题,我觉得题目出得并不好。这里就不再多说。主要面向最后的LPA和pocket算法的实现。

代码对应的gitee地址

二、PLA算法实现

数据集中每个样本都是的 X X X都是四维向量 [ x 1 , x 2 , x 3 , x 4 ] [x_1,x_2,x_3,x_4] [x1,x2,x3,x4],对应y = {1, -1}

1. 按照数据集给定的顺序更新算法

def pla(datas):
    size = len(datas)
    if size <= 1:
        return;
    err_i = -1  # 标记当前用于更新的data行
    dms = len(datas[0])
    if dms == 0:
        return;
    w = [0 for x in range(0, dms)]
    run_times = 0
    last_pause = size
    now = 0
    while True:
        run_times = run_times + 1 # 整个数据循环的圈数
        while now != last_pause:    # 转一圈之后,两个碰在一起
            p = 0
            now %= size # 当前在size中的位置
            for x in range(0, dms-1):
                p += w[x] * datas[now][x]
            p += w[-1]
            if p <= 0 and datas[now][-1] > 0 or p >0 and datas[now][-1] < 0:
                err_i = now
                last_pause = err_i
                if last_pause == 0:
                    last_pause == size
                now += 1
                break
            now += 1
                
        # 更新w(w_0放在末尾)
        if err_i != -1:
            for x in range(0, dms - 1):
                w[x] += datas[err_i][-1]*datas[err_i][x]
            w[-1] += datas[err_i][-1]
            err_i = -1
        else:break;
        
    return [w, run_times]

其中需要是last_pause是当前最后错误的位置,如果从当前错误的位置转了一圈又回到这里而没有遇到其他错误的更新点的时候,说明已经更新完毕。

2. 随机访问数据的顺序更新算法

# 永远保证当前时刻是[0,n)
# 每次交换当前i的随机的数
def randomIndex(n):
    index = [i for i in range(0,n)]
    def swap(l,x,y):
        l[x] = l[x]+l[y]
        l[y] = l[x] - l[y]
        l[x] = l[x] - l[y]
    for i in range(0,n):
        swap(index,i,int(random.random()*n))
    return index

  
def plaImproved(datas,n = 1):
    size = len(datas)
    if size<=1:
        return;
    err_i = -1

    dms = len(datas[0])
    if dms == 0:
        return;
    para = [0 for x in range(0,dms)]
    run_times = 0
    index = randomIndex(size)
    last_pause = size
    i = 0
    while True:
        #if run_times>=50:
            #break
        run_times+=1

        #for i in range(0, size):
        while i != last_pause:
            p = 0
            i %= size

            for x in range(0, dms - 1):
                p += para[x] * datas[index[i]][x]
            p += para[-1]
            if p <= 0 and datas[index[i]][-1] > 0 or p > 0 and datas[index[i]][-1] < 0:#ignore datas[i][-1] == 0
                err_i = index[i]
                break; #遇到错误推出循环
            i+=1
        if err_i != -1:
            for x in range(0, dms - 1): #用这个错误来更新参数
                para[x] = para[x]+ n* datas[err_i][-1] * datas[err_i][x]  # update the parameters
            para[-1] += n * datas[err_i][-1]
            last_pause = i
            if last_pause == 0:
                last_pause = size
            i+=1
            err_i = -1;
        else:break;

    return [para,run_times]

注意

  1. 所谓improved版本的PLA,主要是实现利用打乱的后的index来作为更新顺序。
  2. 更改权重w:会影响到最终的效果。但是从最终结果来看,平均更新次数差不多。
    W t + 1 = W t + w ∗ y n ( t ) X n ( t ) W_{t+1} = W_t+w*y_n(t)X_n(t) Wt+1=Wt+wyn(t)Xn(t)

三、Pocket算法实现

def pocket(datas, max_time=50, greedy=1):
    size=len(datas)
    if size <= 1:
        return
    err_i = -1
    dms = len(datas[0])
    if dms == 0:
        return
    
    w = [0 for x in range(0,dms)]
    new_w = [0 for x in range(0,dms)]
    new_error = 0
    last_error = size
    run_times = 0
    
    while True:
        index = randomIndex(size)
        if run_times>max_time:
            break
        run_times += 1
        
        for i in range(0, size):
            p = 0
            for x in range(0, dms-1):
                p += new_w[x]*datas[index[i]][x]
            p += new_w[-1]
            if p <= 0 and datas[index[i]][-1] > 0 or p > 0 and datas[index[i]][-1] < 0:#ignore datas[i][-1] == 0
                err_i = index[i]
                break
        if err_i != -1:
            for x in range(0, dms - 1): #用这个错误来更新参数
                new_w[x] += datas[err_i][-1] * datas[err_i][x]  # update the parameters
            new_w[-1] += datas[err_i][-1]
            
            if greedy == 1:           
                for i in range(0, size):
                    p = 0
                    for x in range(0, dms-1):
                        p += new_w[x]*datas[index[i]][x]
                    p += new_w[-1]
                    if p <= 0 and datas[index[i]][-1] > 0 or p > 0 and datas[index[i]][-1] < 0:#ignore datas[i][-1] == 0
                        new_error += 1
                        
                if (new_error < last_error):
                    w = copy.deepcopy(new_w)    # 如果不是deepcopy,就等于只是引用
                    last_error = new_error
                new_error = 0
            
            err_i = -1
        else: break
    
    if greedy == 0:
        return [new_w, run_times]
    else:
        return [w, run_times]

注意事项

  1. python中的拷贝:使用deepcopy才能真正实现我们想要的拷贝
  2. 更新次数的提升,会降低错误分类的比例。100次更新time比50次要好1%左右
  3. 就50次更新而言,使用pocket比直接使用更新后的w效果好,大约1%左右

四、算法可视化

随机生成二维平面[0-20]的点:

def random2DDatas(num):
    result = []
    g1 = [random.random()*20,random.random()*20]
    g2 = [random.random()*20,random.random()*20]
    # 由数据范围内的两个点来确定分割线,保证划分线一定会经过生成的点的范围
    w = [(g1[1] - g2[1])/(g1[0] -g2[0]),-1,g1[1] - (g1[1] - g2[1])/(g1[0] -g2[0])*g1[0]]

    result.append(w) # 完美分割线
    for i in range(num):
        x = [random.random()*20,random.random()*20]
        y = w[0]*x[0]+w[1]*x[1]+w[2]
        if y<0:
            x.append(-1)
        elif y>0:
            x.append(1)
        else:continue
        #print(x,y)
        result.append(x)
    return result

调用PLA算法,并做可视化:

def visualizePLA(all,w = []):
    x = np.linspace(0,20,50)  # 在1到10之间产生50组数据(数据之间呈等差数列)
    y = - all[0][2]/all[0][1]  - all[0][0]/all[0][1]*x  # 最开始的线

    plt.figure()
    plt.plot(x,y,color="black")
    if len(w)!=0:
        z = - w[2] / w[1] - w[0] / w[1] * x
        plt.plot(x,z,color="orange",linestyle="--")
    posx = []
    posy = []
    negx = []
    negy = []
    for i in  range(1,len(all)):
        if all[i][-1] == -1:
            negx.append(all[i][0])
            negy.append(all[i][1])
        else:
            posx.append(all[i][0])
            posy.append(all[i][1])
    plt.scatter(negx,negy,marker='x',c='r')
    plt.scatter(posx,posy,marker='o',c='g')
    plt.show()

最终效果:

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值