接**上篇:上篇主要进行了PLA,Pocket算法的理论过程分析和在给定数据集上利用pocket算法对数据集进行分类学习,得到错分数量最少的分类面。上篇中pocket算法的过程已经进行了编程和测试,框架已经建立了起来,这一篇主要上篇中没有提到或涉及不深的几个问题。
1.数据集的构造。
上篇是直接使用了题目给的向量,这次来根据正态分布来产生数据集。np.random.normal函数可以根据均值和方差生成编程者想要的一系列数据。假定一部分数据以(-5,0)为均值,另一部分数据以(0,5)为均值,每组数据都有200个向量。数据的横坐标,纵坐标均值如上,且横纵坐标相互独立。前一组数据标签为+1,后一组数据标签为-1.

coordination_x1 = np.random.normal(-5, 1, 200)
coordination_y1 = np.random.normal(0, 1, 200)
coordination_x2 = np.random.normal(0, 1, 200)
coordination_y2 = np.random.normal(5, 1, 200)
Dataset = []
for i in range(0, 200):
    Dataset.append(np.array([1, coordination_x1[i], coordination_y1[i], 1]))
    Dataset.append(np.array([1, coordination_x2[i], coordination_y2[i], -1]))
random.shuffle(Dataset)
train_set = Dataset[:320]
test_set = Dataset[320:400]

数据添加进数据集的时候是插入一个正例在插入一个反例。为了加强数据集的随机性,在数据集录入数据完成后进行一次打乱。我们选择数据集的前320个样本作为训练集,后80个样本为测试集。之后的程序将会显示学习得到的分类器向量在训练集和测试集上的表现。
2.分类函数的完善。
上一篇中只对pocket算法有简单的实现。这次要进行数据集生成,PLA,pocket算法的实现和性能比较,结果可视化等多项工作,因此有必要对算法进行封装成函数,令其输出一些信息,并为函数的输入留出接口,确定他的输出,以便主函数调用。counterror函数也将保留,(这是pocket算法所不能缺少的关键步骤),对于普通的PLA算法其实过程中不需要这个函数,但为了处理数据集线性不可分的情况发生时对其的结果进行分析(并与pocket的结果进行比较),笔者开始准备让PLA函数也实时进行counterror操作,但为了避免时间浪费以及PLA本身特点,添加了一个标志变量表示这一轮能不能分类正确,有效的节省了运行时间。封装之后的函数如下:

def PLA(dataset, w, iterations):
    for i in range(0, iterations):
        flag = 0
        for j in range(0, len(dataset)):
            if np.dot(w, dataset[j][:-1]) * dataset[j][-1] <= 0:
                flag = 1
                w = w + dataset[j][-1] * dataset[j][:-1]  # 更新权向量
        if flag == 0:
            print('PLA successfully terminated at epoch: ', i)
            return w
        elif flag == 1 and i >= iterations - 1:
            count, choice = counterror(dataset, w)
            print('PLA cannot find classifier vector')
            print('PLA accuracy on training set= ', 1 - count / len(dataset))
            return w


def Pocket(dataset, w, w_hat, least_err_amount, iterations):
    for i in range(0, iterations):
        for j in range(0, len(dataset)):
            if np.dot(w, dataset[j][:-1]) * dataset[j][-1] <= 0:  # 分类错误
                w = w + dataset[j][-1] * dataset[j][:-1]  # 更新权向量
                count, choice = counterror(dataset=dataset, w=w)
                if count < least_err_amount:
                    w_hat = w
                    least_err_amount = count
                    w = w + choice[-1] * choice[:-1]
                if count == 0:
                    print('Pocket finds perfect classifier at epoch: ', i)
                    print('Pocket accuracy on training set= ', 1 - least_err_amount / len(dataset))
                    return w_hat, least_err_amount
    print('Pocket accuracy on training set= ', 1 - least_err_amount / len(dataset))
    return w_hat, least_err_amount

结果展示:这里只显示一组结果。其实笔者反复运行过很多次。可以发现这两组数据隔得很开,是线性可分的,两种算法执行的都很快,PLA要更快一些。
在这里插入图片描述
总控制函数如下:

if __name__ == '__main__':
    start1 = time.time()
    classifier_vector = PLA(dataset=train_set, w=W, iterations=Iterations)
    time1 = time.time() - start1
    print('PLA classifier vector= ', classifier_vector)
    print('PLA time used: ', time1 * 1000, ' ms')
    PLA_testerr, choice1 = counterror(dataset=test_set, w=classifier_vector)
    print('PLA accuracy on testing set= ', 1 - PLA_testerr / len(test_set))

    start2 = time.time()
    classifier_vector2, minerr = Pocket(dataset=train_set, w=W, w_hat=W_hat,
                                        least_err_amount=Least_err_amount, iterations=Iterations)
    time2 = time.time() - start2
    print('Pocket classifier vector= ', classifier_vector2, ' ', 'minimum errors= ', minerr)
    print('Pocket time used: ', time2 * 1000, ' ms')
    Pocket_testerr, chpice2 = counterror(dataset=test_set, w=classifier_vector2)
    print('Pocket accuracy on testing set= ', 1 - Pocket_testerr / len(test_set))

    draw(Dataset, classifier_vector, classifier_vector2, 2)

3.修改数据均值,观察现象。
将两组数据均值改为(-1,0),(0,1)再来观察现象。从散点图可以看出,这一组数据密集了很多。上一组数据显得泾渭分明,一眼就能看出线性可分性。这一组数据却犬牙交错,一眼就能看出线性不可分。两种算法都面临着挑战,可以想象得到程序运行时间会大幅增长。数据量和最大迭代次数仍然保持不变。示例结果如下:(电脑没插电,有点慢)
在这里插入图片描述
pocket算法的运行时间明显长于了PLA算法,得到的是更高精度的分类面。pocket算法在处理线性不可分的分类问题时体现出来自己的优势,能够选择在多次迭代当中表现最好的分类面。
4.结果的可视化。
利用matplotlib画图是笔者几乎没怎么学过的,笔者也利用这次机会学习一些常用的画图函数。
plt.scatter:画散点图,可以直观地表示数据集的分布,让编程者先用肉眼观察数据集生成正确与否,对两种算法的表现有一定预判。
plt.plot:画函数,选一列点x作为自变量,另一个向量y中每一个值都是x的函数值,利用plot函数将y=f(x)画在图上。
plt.legend:图的标识,可以把标识放在图的恰当位置。
函数具体的用法在代码中表现:

def draw(dataset, w_pla, w_pocket, mode):
    x1 = []
    y1 = []
    x2 = []
    y2 = []
    x3 = []
    x4 = []
    y3 = []
    y4 = []
    for i in range(0, len(dataset)):
        if dataset[i][-1] == 1:
            x1.append(dataset[i][1])
            y1.append(dataset[i][2])
        elif dataset[i][-1] == -1:
            x2.append(dataset[i][1])
            y2.append(dataset[i][2])

    if mode == 1:
        x3 = np.linspace(-9, 9, 100)
        y3 = -(w_pla[1] / w_pla[2]) * x3 + w_pla[0] / w_pla[2]
        x4 = np.linspace(-9, 9, 100)
        y4 = -(w_pocket[1] / w_pocket[2]) * x3 + w_pocket[0] / w_pocket[2]
    elif mode == 2:
        x3 = np.linspace(-3, 3, 100)
        y3 = -(w_pla[1] / w_pla[2]) * x3 + w_pla[0] / w_pla[2]
        x4 = np.linspace(-3, 3, 100)
        y4 = -(w_pocket[1] / w_pocket[2]) * x3 + w_pocket[0] / w_pocket[2]
    plt.scatter(x1, y1, marker='o', color='red', s=50, label='positive')
    plt.scatter(x2, y2, marker='x', color='green', s=50, label='negative')
    plt.plot(x3, y3, color='orange', label='PLA')
    plt.plot(x4, y4, color='purple', label='Pocket')
    plt.legend(loc='best')
    plt.show()

这里的mode取1时表示数据中心为-5,0和5,0,取2时表示为-1,0和1,0.两种情况下选择的坐标轴跨度不一样,在数据比较靠近原点时坐标轴跨度也小一些,画出的图看起来更清楚一些。
两种均值下分类面如下:
在这里插入图片描述
在这里插入图片描述利用matplotlib画图也是很重要的技能,希望以后可以更加掌握。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐