林轩田机器学习PLA算法实现_cifar100

PLA

由于PLA针对线性可分数据集,因此在此选择Pocket PLA算法进行实现,文章记录针对cifar100数据集的实现,由于PLA算法过于简单,因此准确率很低,但仅作为一次尝试。



一、加载数据

1.导入数据

from keras.datasets import cifar100 
(x_train, y_train), (x_test, y_test) = cifar100.load_data(label_mode='fine')

官网介绍:CIFAR100 小图像分类数据集
50,000 张 32x32 彩色训练图像数据,以及 10,000 张测试图像数据,总共分为 100 个类别。

加载过慢

由于数据集较大,加载时速度较慢,可以等待其下载,或者直接点击链接进行下载,将下载好的数据集放在keras数据集的指定位置,即可直接加载数据集。
在这里插入图片描述
注意路径,直接将cifar-100-python.tar.gz文件放进去即可。(下不下来数据集的可以找我~)
在这里插入图片描述
加载成功~

2.探索数据

在这里插入图片描述

3.处理数据

(1)由于数据集过大,我们选取其中的500个样本进行操作,并按照3:1的比例划分数据集。
在这里插入图片描述
(2)将数据打平,方便后续处理
在这里插入图片描述
至此,数据处理基本结束。后续会根据算法进行X和Y的具体调整。整理成函数如下。
参数num:加载数据的个数,例如本次实验以500个数据集为例,调用时load_cifar100(500)即可,想换数据集大小改变参数即可

def load_cifar100(num):
    # 加载数据集
    (x_train, y_train), (x_test, y_test) = cifar100.load_data(label_mode='fine')
    x = x_train[:num]
    y = y_train[:num]
    X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.25, random_state=0)
    X_train = X_train.reshape((X_train.shape[0], -1)).T
    X_test = X_test.reshape((X_test.shape[0], -1)).T
    y_train = y_train.reshape((y_train.shape[0], -1)).T
    y_test = y_test.reshape((y_test.shape[0], -1)).T
    return X_train,X_test,y_train,y_test

二、PLA算法

1.编写代码

在这里插入图片描述
根据算法,进行编写代码。
算法:(pocket PLA)----数据不是线性可分
1.随机初始化参数,计算在初始化的直线中分类错误的个数。
2.随机挑选一个误分类点,进行修正,计算分类错误点数,并与之前错误个数进行比较,取个数较小的直线为当前选择的分类直线
3.经过n次迭代后,选择个数最小的直线对应的w,为我们最终得到的想要的权重值

def model_PLA(X_train, y_train, X_test, y_test, iteration):
    '''
    构建一个PLA模型
    :param X_train: feature   (nx+1,m)
    :param y_train: label     (1,m)
    :param iteration:迭代轮数
    :param w:参数     (nx+1,1)
    :return:
    '''

    #初始化参数
    w = init_para(X_train)
    # 存放E_in和E_out
    E_in = []
    E_out = []

    # 初始值的结果
    y_pred = np.sign(np.dot(w.T, X_train))  # 预测
    error = y_pred != y_train  # 找到错误点
    error_num_now = np.sum(error)  # 计算错误点个数

    y_pred_test = np.sign(np.dot(w.T, X_test))  # 预测
    error_test = y_pred_test != y_test  # 找到错误点
    error_num_test = np.sum(error_test)  # 计算错误点个数

    E_in.append(error_num_now)
    E_out.append(error_num_test)
    # 设置迭代次数
    for i in range(iteration):
        # 随机选择一个错误点
        error_idex = np.where(error == True)[1]  # 返回错误点所在的索引值为一个一维的数组(np.array([]),),取[1]
        idex_1 = np.random.randint(error_num_now)  # 随机选择一个值的索引值
        idex = error_idex[idex_1]
        x_i = X_train[:, idex].reshape(X_train.shape[0], 1)
        y_i = y_train[0, idex].reshape(y_train.shape[0], 1)

        w_new = w + np.multiply(y_i, x_i)  # 修正

        # 判断是否更新
        y_pred_new = np.sign(np.dot(w_new.T, X_train))  # 预测
        error_new = y_pred_new != y_train  # 找到错误点
        error_num_new = np.sum(error_new)  # 计算错误点个数

        #print("更新前的错误点数为{}".format(error_num_now))
        #print("修正后{}".format(error_num_new))

        if error_num_new.item() < error_num_now.item():
            # 更新后错误率变小,更新;否则不更新
            w = w_new
            error = error_new
            error_num_now = error_num_new

            y_pred_test = np.sign(np.dot(w.T, X_test))  # 预测
            error_test = y_pred_test != y_test  # 找到错误点
            error_num_test = np.sum(error_test)  # 计算错误点个数

        E_in.append(error_num_now)
        E_out.append(error_num_test)
        if error_num_new.item() == 0:
            break
    return w, E_in, E_out, i		

2.附带代码:

1.随机初始化:

def init_para(train_x):
    #初始化参数,随机生成高斯分布
    nx = train_x.shape[0]
    w = np.random.randn(nx,1)
    return w

三、主函数

1.Y处理:由于PLA为二分类算法,因此将Y标签(100类)变为两类,小于50为一类,标为1,其余算为一类,标为-1.

def convert_y(y):
    y_index_large = y >= 50
    y[y_index_large] = -1
    y_index_small = y_index_large==False
    y[y_index_small] = 1
    return y

2.X处理:这门课中讲的算法,实际上将偏置直接算成了W0,合并在了W中,因此X也需要合并X0,即特征拼接一维全为1的向量。(由于编写PLA算法时按照这个思路写的,所以需要修改X,否则不修改的话直接用w,b也行)
在这里插入图片描述

def convert_x(x):
    #拼接x0
    x0 = np.ones((1,x.shape[1]))
    x_convert = np.concatenate((x,x0),axis=0)
    return x_convert

3.计算Error
拿在模型训练过程中统计的值进行归一化,并返回最终结果。

def error(X,E):
    num = X.shape[1]
    E = [round(x / num, 3) for x in E]
    Error = E[-1]
    return E,Error

计算Ein和Eout的方法:

y_pred_test = np.sign(np.dot(w.T, X_test))  # 预测
error_test = y_pred_test != y_test  # 找到错误点
error_num_test = np.sum(error_test)  # 计算错误点个数

懒得封装函数了,直接在PLA的模型中。其实就是比较预测结果和真值,最终得到预测错的点数。在error()函数中除以总数进行归一化,用的是0/1error。
在这里插入图片描述
PS:其实感觉直接np.mean(error_test)就可以了,不需要求和再归一化,好多numpy的函数还不知道,还得继续学习。
4.画图(E_in和E_out)
将E_in和E_out画在一个图上

def draw(E_in,E_out,iteration):
    # 画出E_in和E_out
    x = range(iteration + 1)
    # 设置图形大小
    plt.figure(figsize=(20, 8), dpi=80)
    # color可以百度颜色代码
    plt.plot(x, E_in, label="E_in", color="r")
    plt.plot(x, E_out, label="E_out", color="b")

    # 设置x轴刻度
    xtick_labels = ["{}".format(i) for i in x]
    plt.xticks(x, xtick_labels)

    # 添加图例
    plt.legend(loc="upper right")
    # 展示
    plt.show()

5.整合

def main():
    # 加载数据集
    X_train, X_test, y_train, y_test = load_cifar100(500)
    # 修正数据集
    X_train = convert_x(X_train)
    X_test = convert_x(X_test)
    y_train = convert_y(y_train)
    y_test = convert_y(y_test)
  
    iteration = 30
    w, ein, eout, i= model_PLA(X_train, y_train, X_test, y_test, iteration)

    E_in,E_in_final = error(X_train,ein)
    E_out, E_out_final = error(X_test, eout)
    print("Ein:{}".format(E_in_final))
    print("Eout:{}".format(E_out_final))
    draw(E_in,E_out,i+1)

至此,Pocket PLA(cifar100)的代码已经全部编写好啦!

四、总结

代码整合起来如下:

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

def init_para(train_x):
    #初始化参数,随机生成高斯分布
    nx = train_x.shape[0]
    w = np.random.randn(nx,1)
    return w

def convert_x(x):
    #拼接x0
    x0 = np.ones((1,x.shape[1]))
    x_convert = np.concatenate((x,x0),axis=0)
    return x_convert

def convert_y(y):
    y_index_large = y >= 50
    y[y_index_large] = -1
    y_index_small = y_index_large==False
    y[y_index_small] = 1
    return y

def load_cifar100(num):
    # 加载数据集
    (x_train, y_train), (x_test, y_test) = cifar100.load_data(label_mode='fine')
    x = x_train[:num]
    y = y_train[:num]
    X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.25, random_state=0)
    X_train = X_train.reshape((X_train.shape[0], -1)).T
    X_test = X_test.reshape((X_test.shape[0], -1)).T
    y_train = y_train.reshape((y_train.shape[0], -1)).T
    y_test = y_test.reshape((y_test.shape[0], -1)).T
    return X_train,X_test,y_train,y_test

def model_PLA(X_train, y_train, X_test, y_test, iteration):
    '''
    构建一个PLA模型
    :param X_train: feature   (nx+1,m)
    :param y_train: label     (1,m)
    :param iteration:迭代轮数
    :param w:参数     (nx+1,1)
    :return:
    '''

    #初始化参数
    w = init_para(X_train)
    # 存放E_in和E_out
    E_in = []
    E_out = []

    # 初始值的结果
    y_pred = np.sign(np.dot(w.T, X_train))  # 预测
    error = y_pred != y_train  # 找到错误点
    error_num_now = np.sum(error)  # 计算错误点个数

    y_pred_test = np.sign(np.dot(w.T, X_test))  # 预测
    error_test = y_pred_test != y_test  # 找到错误点
    error_num_test = np.sum(error_test)  # 计算错误点个数

    E_in.append(error_num_now)
    E_out.append(error_num_test)
    # 设置迭代次数
    for i in range(iteration):
        # 随机选择一个错误点
        error_idex = np.where(error == True)[1]  # 返回错误点所在的索引值为一个一维的数组(np.array([]),),取[1]
        idex_1 = np.random.randint(error_num_now)  # 随机选择一个值的索引值
        idex = error_idex[idex_1]
        x_i = X_train[:, idex].reshape(X_train.shape[0], 1)
        y_i = y_train[0, idex].reshape(y_train.shape[0], 1)

        w_new = w + np.multiply(y_i, x_i)  # 修正

        # 判断是否更新
        y_pred_new = np.sign(np.dot(w_new.T, X_train))  # 预测
        error_new = y_pred_new != y_train  # 找到错误点
        error_num_new = np.sum(error_new)  # 计算错误点个数

        if error_num_new.item() < error_num_now.item():
            # 更新后错误率变小,更新;否则不更新
            w = w_new
            error = error_new
            error_num_now = error_num_new

            y_pred_test = np.sign(np.dot(w.T, X_test))  # 预测
            error_test = y_pred_test != y_test  # 找到错误点
            error_num_test = np.sum(error_test)  # 计算错误点个数

        E_in.append(error_num_now)
        E_out.append(error_num_test)

        if error_num_new.item() == 0:
            break
    return w, E_in, E_out, i

def error(X,E):
    num = X.shape[1]
    E = [round(x / num, 3) for x in E]
    Error = E[-1]
    return E,Error

def draw(E_in,E_out,iteration):
    # 画出E_in和E_out
    x = range(iteration + 1)
    # 设置图形大小
    plt.figure(figsize=(20, 8), dpi=80)
    # color可以百度颜色代码
    plt.plot(x, E_in, label="E_in", color="r")
    plt.plot(x, E_out, label="E_out", color="b")

    # 设置x轴刻度
    xtick_labels = ["{}".format(i) for i in x]
    plt.xticks(x, xtick_labels)

    # 添加图例
    plt.legend(loc="upper right")
    # 展示
    plt.show()

def main():
    # 加载数据集
    X_train, X_test, y_train, y_test = load_cifar100(500)
    # 修正数据集
    X_train = convert_x(X_train)
    X_test = convert_x(X_test)
    y_train = convert_y(y_train)
    y_test = convert_y(y_test)

    iteration = 30
    w, ein, eout ,i= model_PLA(X_train, y_train, X_test, y_test, iteration)

    E_in,E_in_final = error(X_train,ein)
    E_out, E_out_final = error(X_test, eout)
    print("Ein:{}".format(E_in_final))
    print("Eout:{}".format(E_out_final))
    draw(E_in,E_out,i+1)
if __name__ == "__main__":
    main()

测试

怎么也想不到,这个数据集居然线性可分了。。。重新找了2个小时的bug,然后加了全部分类正确就break的代码。。。代码已经更新了,如果全部分类正确就可以直接结束。现在在线性可分和不可分的数据集上应该都能用了。
在这里插入图片描述
打印了一下最终训练好的w看了一下,好像也没什么问题。100类的图像分成两类居然线性可分。。。不知道是不是错了
在这里插入图片描述


总结

虽然在这个数据集上有点诡异,但是这个算法在自动生成的线性不可分的数据集make_moons()和mnist数据集上都可以正常运行,都验证过。结果大致是下图的形式。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值